#!/bin/ksh
#
# sdreclaim - script that automates manual reclmation for 
#             single drive sequential access storage pools
# Adapted from:
#  mrecsv - Manual Reclamation of Sequential access Volumes
# Written By   : Van Stokes, Jr  11/13/1995
# Last Updated : Mike Kaczmarski, IBM  1/3/1996
#
#
# DESCRIPTION:
# This script was made available by Van Stokes Jr, and modified slightly
# by IBM to provide a means for reclamation in ADSM single-drive 
# sequential storage pools.
#
# SUPPORT:
# This script is being provided AS IS - no waranties or support is
# provided.  There are no warranties, express or implied, including
# the warranties of merchantability and fitness for a particular 
# purpose.
#
# ***************** Work Flow - What is happening? ******************
#
# The ADSM server, both version 1.0+ and 2.0+ are not capable of doing a
# reclamation on a sequential access devices (tape, optical, etc) that only
# have one drive. Nor, to my knowledge, do they have any plans to make it
# possible to do so. Because I was in a bind and I didn't have the time to
# manually move data between volumes all the time, I wrote this script to
# automate this proceedure - Manual Reclamation of Sequential Access Volumes.
#
# Requirements:
#   Sequential Access Media - tape, optical, etc.
#   Random Access Media     - Device Class of Disk
#   Storage Pool for Manual Reclamation
#
# How it works...................
# The script finds volumes on your sequential storage pool (VSP) whose   
# occupancy is *LOWER* than 100-(Reclamation Threshold) and are in 
# the 'filled state'.
# When the script encounters such a volume, it then proceeds to move the
# data off of the VSP volume on to the manual reclamation storage pool (MRSP).
#
# If the MRSP gets full, ADSM will (if set) automaticaly migrate the data
# back to the VSP thus consolidating the data.
#
# The MSRP must have enough available space to hold the reclaimable 
# data that need to be moved to emtry a volume.
#
# If the MRSP doesn't get full, thats OK. The script will automatically move
# the data back to the VSP - again, consildating the data.
#
# And the process repeats itself until there are no more volumes in the VSP
# below the "Reclamation Threshold".
#
# It's really a "round-robin". Data is moved from a partially full sequential
# access media volume (tape, optical, etc) to a random access medial volume
# (disk) and then back to the sequential access media (tape, optical, etc).
#
# The goal is to get sequential access medial volumes that are not
# completely full and consolidate the data on to fewer volumes.
#
#
# !!!!!!!!!!!!!!!!!!!!!!!!!!!Notice!!!!!!!!!!!!!!!!!!!!!!!!!!!
# YOU MUST DO THE FOLLOWING to ensure the proper operation of this script:
#
# In this Script, you must set:
#   ADSM_ID     - The Admin ID to log into ADSM. Ensure proper permissions.
#   ADSM_PASSWD - The Admin ID's password to use
#
#   MRSP        - The *DISK* storage pool name to use for reclamation of VSP.
#               - This is were the data will be moved to temporarily.
#               - This storage pool *CAN NOT* be used by anything else.
#               - This storage pool should migrate to your VSP (see next item).
#               - Cache'ing should be turned off.
#               - Suggested Migration Thresholds: Low: 0%, High: 98%
#
#   VSP         - The name of the Storage Pool *TO* Reclamate.
#               - i.e. Your Tape or Optical storage pool name.
#
# On the ADSM server, you must set:
#   Reclamation Threshold   - This must be set on your VSP storage pool.
#
#   Next Storage Pool       - This must be set on your MRSP storage pool.
#                           - This should be your VSP, but doesn't have to be.
#
# In this script, Other settings to check:
#   SLTF                    - Send logs(status) to the log file (Y or N)?
#   SLTS                    - Send logs(status) to the screen (Y or N)?
#   LOG                     - Location of the log file (check path).
#   LCKF                    - Location of the lock file (check path).
#   REC_TABLE               - Reclamation Table (check path).
#
# ***************** Macro's that you change per system ****************
#
#
# Login and Password to use when issuing ADSM commands.
ADSM_ID="adminuser"
ADSM_PASSWD="adminpassword"
#
# Manual Reclamation Storage Pool name (where to move the data to).
MRSP="MRSP"
#
# Storage Pool Name to Reclamate
VSP="FILEPOOL"
#
# Send logs(status) to the log file (Y or N)?
SLTF="Y"
#
# Send logs(status) to the screen (Y or N)?
SLTS="Y"
#
# Location and Name of the Log File
LOG="/tmp/adsm.rec.log"
#
# Lock file to keep another instance of this script from running.
LCKF="/tmp/LCK.ADSMREC"
#
# Reclamation Table - a list of VSP volumes to reclamate (auto generated).
REC_TABLE="/tmp/adsm.rectbl"
#
# ************* Macro's that should not be changed manually  ************
#
# Default command that is normally issued.
DSMADMC="dsmadmc -ID=$ADSM_ID -PASSWORD=$ADSM_PASSWD -NOCONFIRM"
#
# Manual Reclamation Process ID (must be zero, will be set by script)
MR_PROC_ID=0
#
# Reclamation Threshold (must be zero, will be set by script).
# This item is retrieved from the VSP Reclamation Threshold field in ADSM.
RTH=0
#
# Volume Threshold (must be zero, will be set by script).
# This is the volume threshold used to choose volumes eligible for 
# reclmation.  The value is the maximum volume occupancy for full
# volumes, below which volumes will be reclaimed.  This is equivilent 
# to 100-RTH
VTH=0
#
# Maximum Scratch Volumes Allowed (must be zero, will be set by script)
# This item is retrieved from the VSP Max Scratch Volumes field in ADSM.
MSVA=0
#
# Current Number of Scratch Volumes (must be zero, will be set by script)
CNSV=0
#
# Current Number of Volumes being filled (must be zero, will be set by script)
CNVF=0
#
# Manual Reclamation Storage Pool's (MRSP) next storage pool to send data to.
MRSP_NSP=""
#
#
# ********************** Define Macro Functions **************
#
# Function to load information about VSP for Reclamation.
# Returns 0=success, 1=failed.
Get_VSP_Info ()
{
	Rtn=0
	#
	$DSMADMC q stg $VSP f=d | tail +8 | while read line
	do
		field=`echo "$line" | awk ' FS=":" { print $1 } '`
		var=`echo "$line" | awk ' FS=":" { print $2 } '`
		case $field in
			'Reclamation Threshold')
				RTH=`echo "$var" | awk ' { print $1 } '`
				;;
			'Maximum Scratch Volumes Allowed')
				MSVA=`echo "$var" | awk ' { print $1 } '`
				;;
		esac
	done


	#
	# Did we get everything we need to have?
	#
	if [ $RTH -lt 1 ]
	then
		Log "! Storage Pool $VSP \"Reclamation Threshold\" value is invalid."
		Rtn=1
	fi
	#
	if [ $MSVA -lt 1 ]
	then
		Log "! Storage Pool $VSP \"Maximum Scratch Volumes Allowed\" value is invalid."
		Rtn=1
	fi

	VTH=0

	# Calculate the volume threashold (for occupancy) that will
        # be used to determine if it can be reclaimed.  Note that this
        # is 100 - the recmation threshold, ince the threshold is 
        # specified in terms of percentage of emty space on the volume
	if [ $Rtn -eq 0 ]
	then
	  VTH=`expr 100 - $RTH`
        fi

	#
	return $Rtn
}
#
#
# Function to load information about MRSP
# Returns 0=success, 1=failed.
Get_MRSP_Info ()
{
	Rtn=0
	#
	$DSMADMC q stg $MRSP f=d | tail +8 | while read line
	do
		field=`echo "$line" | awk ' FS=":" { print $1 } '`
		var=`echo "$line" | awk ' FS=":" { print $2 } '`
		case $field in
			'Next Storage Pool')
				MRSP_NSP=`echo "$var" | awk ' { print $1 } '`
				;;
		esac
	done

	#
	# Did we get everything we need to have?
	#
	if [ "$MRSP_NSP" = "" ]
	then
		Log "! Storage Pool $MRSP must have a \"Next Storage Pool\" value."
		Rtn=1
	fi
	#
	return $Rtn
}
#
#
# Function to load the VSP volume tables for reclamation.
Load_Rec_Table ()
{
	# Remove any exisitng tables
	rm -f $REC_TABLE
	#
	$DSMADMC q vol | tail +12 | while read line
	do
		if [ "$line" != "" ]
		then
			#
			vol=`echo "$line" | awk '{ print $1 }'`
			stgpool_name=`echo "$line" | awk '{ print $2 }'`
			storage_class=`echo "$line" | awk '{ print $3 }'`
			util=`echo "$line" | awk '{ print $5 }'`
			status=`echo "$line" | awk '{ print $6 }'`
			#
			# Does this volume belong to VSP?
#			`echo "$VSP"`
#			`echo "$VSP" | grep $stgpool_name`
			Rtn=`echo "$VSP" | grep $stgpool_name`
			if [ "$Rtn" != "" ]
			then
				# If you are here, then this is a valid storage pool to work with.
				if [ "$status" = "Full" ]
				then
					# Volume is in a status of full, might be able to reclamate.
					echo "$util:$vol" >> $REC_TABLE
				fi
				#
				# Are we filling this volume? We need to know for CNSV
				if [ "$status" = "Filling" ]
				then
					# yep, were filling this volume
					CNVF=`expr $CNVF + 1`
				fi
			fi
		fi
	done
	#
	Rtn=$?
	if [ "$Rtn" = "0" ]
	then
		# got a good table, sort it and continue.
		cat $REC_TABLE | sort -n > $REC_TABLE
		return 0
	else
		# Opps, something bit it
		rm -f $REC_TABLE
		return 1
	fi
}
#
#
# Function that finds the current number of available scratch volumes
# to make sure we have enough to do a reclamation.
Calculate_CNSV ()
{
	CNUV=0
	#
	# Load the volumes into the Reclamation Table from ADSM
	Load_Rec_Table
	if [ $? != 0 ]
	then
		Log "! Failed to load the reclamation table."
		return 1
	fi
	#
	# Make sure there is a table to go by.
	if [ ! -f $REC_TABLE ]
	then
		# Opps, no table - how this happen?
		Log "! Reclamation Table missing. Unable to Calculate_CNSV"
		return 1
	fi
	#
	# Make sure MSVA has been set
	if [ $MSVA -lt 1 ]
	then
		# Opps, no table - how this happen?
		Log "! MSVA is missing! Unable to Calculate_CNSV"
		return 1
	fi
	#
	# Get the number of volumes being uses from REC_TABLE
	CNUV=`cat $REC_TABLE | wc -l | awk '{ print $1 }'`
	# Calculate the current number of scratch volumes available
	CNSV=`expr $MSVA - $CNVF - $CNUV`
	# Any scratch volumes available?
	if [ $CNSV -lt 1 ]
	then
		# Nope, out of scratch volumes to use for reclamation.
		return 1
	fi
	#
	# If you are here, all is well, lets continue.
	return 0
}
#
#
# Function that controls the reclamation
Reclamation_Loop ()
{
	# Make sure there is a table to go by.
	if [ ! -f $REC_TABLE ]
	then
		# Opps, no table - how this happen?
		return 1
	fi
	#
	RL_RET=1
	#
	cat $REC_TABLE | while read line
	do
		vsp_util=`echo "$line" | awk ' FS=":" { print $1 } '`
		vsp_vol=`echo "$line" | awk ' FS=":" { print $2 } '`
		#
		# Volume utilization greater than the Reclamation Threshold (VTH)?
		if [ $vsp_util -gt $VTH ]
		then
			# Yep, Nothing to migrate
			# All volumes packed beyond the RTH.
			# We'll exit the program with this return so we don't try another pack.
			return $RL_RET
		fi
		#
		# If you are here, then there is something to migrate.
		#
		# Lets ensure the MRSP is empty for reclamation
		Empty_MRSP
		#
		# Lets make sure there are scratch volumes to use for reclamation.
		Calculate_CNSV
		if [ $? != 0 ]
		then
			Log "! Insufficient scratch volumes available for reclamation."
			Log "= Storage Pool: $VSP, Scratch Volumes Available: $CNSV."
			return 1
		fi
		#
		# Begin the moving of data off of this volume.
		RL_RET=0
		MR_PROC_ID=0
		Move_Data $vsp_vol $MRSP
		Rtn=$?
		if [ $Rtn != 0 ]
		then
			if [ $Rtn = 2 ]
			then
				Log "= No data on volume $vsp_vol to move to stgpool $MRSP."
			else
				Log "! Failed to begin a MOVE DATA on volume $vsp_vol to stgpool $MRSP."
			fi
			# We'll try another volume just in case this one is checked out.
		else
			Log "+ Move Data started on vol $vsp_vol, process id is $MR_PROC_ID."
			#
			# Monitor the process, when its finished, then continue with
			# the next volume.
			while true
			do
				sleep 120
				# See if the move data process is still running
				Check_Proc_Status $MR_PROC_ID
				if [ $? != 0 ]
				then
					# Nope, lets break out and do the next volume.
					break
				fi
				# If you are here, our move data process is still running.
			done
		fi
	done
	#
	# Sigh - that's all done. But, lets do it again to make sure we
	# can't pack it even better.
	return $RL_RET
}
#
#
# Function that moves the data from $1 (volume) to $2 (storage pool)
# $1 must be the SOURCE volume to that contains data to be moved
# $2 must be the DESTINATION storage pool
# Returns:
#   0 = Move process was successfully started
#   1 = Failed to start the move process
#   2 = No Data to move
Move_Data ()
{
	$DSMADMC move data $1 stg=$2 | tail +5 | while read line
	do	
		# Find our process id
		msg=`echo "$line" | awk ' { print $1 } '`
		#
		# Successfuly started a Move Data Job?
		if [ "$msg" = "ANS5104I" ]
		then
			# yes, Get our process ID.
			MR_PROC_ID=`echo "$line" | awk ' { print $4 } '`
			return 0
		fi
		#
		# Was there any data to move?
		if [ "$msg" = "ANR2209W" ]
		then
			# No there wasn't any data to move
			return 2
		fi
		#
	done
	#
	# Something went wrong.
	return 1
}
#
#
# Function to check the status of a process
# $1 must be the process number to check
Check_Proc_Status ()
{
	Rtn=0
	#
	$DSMADMC q proc $1 | tail +5 | while read line
	do
		msg=`echo "$line" | awk ' { print $1 } '`
		if [ "$msg" = "ANR0942E" ]
		then
			# Process is no longer running
			Rtn=1
		fi
	done
	#
	return $Rtn
}
#
#
# This function checks $1 to ensure it isn't in migration
# $1 must be the storage pool to check for migration on
Check_For_Migration ()
{
	$DSMADMC q stg $1 f=d | grep 'Migration in Progress' | read line
	#
	status=`echo "$line" | awk ' FS=":" { print $2 } ' | awk ' { print $1  } '`
	#
	if [ "$status" = "Yes" ]
	then
		# Yes, migration has commenced.
		return 1
	else
		# Nope, migration is idle
		return 0
	fi
}
#
#
# Function that ensures the MRSP is empty
Empty_MRSP ()
{
	Log "= Attempting to empty storage pool $MRSP for use in reclamation."
	#
	# Make sure the system isn't Migrating MRSP
	while true
	do
		Check_For_Migration $MRSP
		if [ $? = 0 ]
		then
			# Nope, not in progress, yet.
			break
		fi
		#
		# Yep, there is a migration going. Wait until it's finished.
		Log "= Migration in progress on storage pool $MRSP, waiting."
		sleep 300
	done
	#
	#
	$DSMADMC q vol stg=$MRSP | tail +12 | while read line
	do
		if [ "$line" = "" ]
		then
			break
		fi
		#
		# Get the volume(s) associated with MRSP.
		mrsp_vol=`echo "$line" | awk ' { print $1 } '`
		#
		# Begin the moving of data off of this MRSP volume.
		MR_PROC_ID=0
		Move_Data $mrsp_vol $MRSP_NSP
		Rtn=$?
		if [ $Rtn != 0 ]
		then
			if [ $Rtn = 2 ]
			then
				Log "= No data on volume $mrsp_vol to move to stgpool $MRSP_NSP."
			else
				Log "! Failed to begin a MOVE DATA on volume $mrsp_vol to stgpool $MRSP_NSP."
			fi
			# We'll try another volume just in case this one is checked out.
		else
			Log "+ Move Data started on volume $mrsp_vol, process id is $MR_PROC_ID."
			#
			# Monitor the process, when its finished, then continue with
			# the next volume.
			while true
			do
				sleep 120
				# Is the move data process is still running?
				Check_Proc_Status $MR_PROC_ID
				if [ $? != 0 ]
				then
					# Nope, lets break out and do the next volume.
					break
				fi
				# If you are here, our move data process is still running.
			done
		fi
	done
	#
	return 0
}
#
#
# Log information to the log file
# $1 needs to be the information to log
Log ()
{
	# Following line sends information to the LOG file.
	if [ "$SLTF" = "Y" ]
	then
		echo "`date +%y/%m/%d` `date +%H:%M` $1" >> $LOG
	fi
	#
	# Following line sends information to the screen.
	if [ "$SLTS" = "Y" ]
	then
		echo "`date +%y/%m/%d` `date +%H:%M` $1"
	fi
}
#
#
#
# ********************** Main script area ******************
#
#
cd /usr/lpp/adsm/bin
#
# Make sure we are the only process running
if [ -f $LCKF ]
then
	echo "A reclamation process is already running, exiting."
	exit 1
else
	echo "`date`" > $LCKF
	Log "+ Started."
fi
#
#
while true
do
	# Load MRSP information just in case it changed while we were working.
	Get_MRSP_Info
	if [ $? != 0 ]
	then
		break
	fi
	#
	# Load VSP information just in case it changed while we were working.
	Get_VSP_Info
	if [ $? != 0 ]
	then
		break
	fi
	#

	Log "= Storage Pool: $VSP, Reclamation Threshold: $RTH."
	Log "=  Filled volumes with $VTH % occupancy or less will be reclaimed."
	Log "= Storage Pool: $VSP, Maximum Scratch Volumes Allowed: $MSVA."
	#
	# Load the volumes into the Reclamation Table from ADSM
	Load_Rec_Table
	if [ $? != 0 ]
	then
		Log "! Failed to load the reclamation table."
		break
	fi
	#
	# Begin the reclamation
	Reclamation_Loop
	Rtn=$?
	# Can we pack any more volumes?
	if [ $Rtn != 0 ]
	then
		# Nope, volumes packed beyond RTH, Reclamation is over.
		#
		# Lets ensure the MRSP is completely empty.
		Empty_MRSP
		#
		# And that's it for this time.
		break
	fi
	#
	# If you are here, Lets try one more reclamation to make sure we can't
	# pack the volumes even better.
done
#
# Cleanup
rm -f $REC_TABLE
#
Log "+ Ended."
#
# remove lock file
rm -f $LCKF
#
#
# (end script)
