#!/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
# Last Updated : Craib Bell, IBM  6/1/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.
#
# This version of the script is for use with a PAIR of single drive 
# libraries rather than one single drive library and a large disk
# storage pool.
#
# 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:
#   Two Storage Pools and their corresponding single-drive tape/optical 
#   libraries.  These storage pools must be in a hierarchy, ie. one stgpool
#   is the "next=" value for the other stgpool.  The last stgpool in the 
#   heirarchy is considered the "lower" stgpool, and the stgpool whose "next="
#   parameter is set to the lower stgpool is the "upper" stgpool.
#
# How it works...................
# The script finds volumes on your lower sequential storage pool (LSP) whose   
# occupancy is *LESS* 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 LSP volume on to the upper storage pool (USP).
#
# If the USP gets full, ADSM will (if set) automaticaly migrate the data
# back to the LSP thus consolidating the data.
#
# The USP must have enough available space to hold the reclaimable 
# data that need to be moved to empty a volume.
#
# And the process repeats itself until there are no more volumes in the LSP
# 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
#
#   USP        - The *UPPER* storage pool name to use for reclamation of LSP.
#               - This is were the data will be moved to
#               - This storage pool should migrate to your LSP (see next item).
#               - Cache'ing should be turned off.
#
#   LSP         - 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 LSP storage pool.
#
#   Next Storage Pool       - This must be set on your USP storage pool.
#                           - This should be your LSP
#
# 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="rcb"
ADSM_PASSWD="rcb"
#
# Upper Storage Pool name (where to move the data to).
USP="TAPE"
#
# Storage Pool Name to Reclamate
LSP="TAPE2"
#
# 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 LSP 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 -SE=SHILOH-0"
#
# 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 LSP 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 LSP 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
#
# Upper Storage Pool's (USP) next storage pool to send data to.
USP_NSP=""
#
#
# ********************** Define Macro Functions **************
#
# Function to load information about LSP for Reclamation.
# Returns 0=success, 1=failed.
Get_LSP_Info ()
{
	Rtn=0
	#
	$DSMADMC q stg $LSP 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 $LSP \"Reclamation Threshold\" value is invalid."
		Rtn=1
	fi
	#
	if [ $MSVA -lt 1 ]
	then
		Log "! Storage Pool $LSP \"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 USP
# Returns 0=success, 1=failed.
Get_USP_Info ()
{
	Rtn=0
	#
	$DSMADMC q stg $USP 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')
				USP_NSP=`echo "$var" | awk ' { print $1 } '`
				;;
		esac
	done

	#
	# Did we get everything we need to have?
	#
	if [ "$USP_NSP" = "" ]
	then
		Log "! Storage Pool $USP must have a \"Next Storage Pool\" value."
		Rtn=1
	fi
	#
	return $Rtn
}
#
#
# Function to load the LSP 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 LSP?
#			`echo "$LSP"`
#			`echo "$LSP" | grep -i $stgpool_name`
			if [ "$stgpool_name" != "" ]
			then
				Rtn=`echo "$LSP" | grep -i $stgpool_name`
			else
				Rtn=""
			fi
			if [ "$Rtn" != "" ]
			then
				# If here, then this is a valid storage pool to work with.
				echo "Found storage pool"
				if [ "$status" = "Full" ]
				then
					# Volume is in a status of full, might be reclaimable
					echo "Found full volume"
					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 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
		LSP_util=`echo "$line" | awk ' FS=":" { print $1 } '`
		LSP_vol=`echo "$line" | awk ' FS=":" { print $2 } '`
		#
		# Volume utilization greater than the Reclamation Threshold (VTH)?
		if [ $LSP_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.
		# Begin the moving of data off of this volume.
		RL_RET=0
		MR_PROC_ID=0
		Move_Data $LSP_vol $USP
		Rtn=$?
		if [ $Rtn != 0 ]
		then
			if [ $Rtn = 2 ]
			then
				Log "= No data on volume $LSP_vol to move to stgpool $USP."
			else
				Log "! Failed to begin a MOVE DATA on volume $LSP_vol to stgpool $USP."
			fi
		else
			Log "+ Move Data started on vol $LSP_vol, process id is $MR_PROC_ID."
			#
			# Monitor the process, when its finished, then continue with
			# the next volume.
			while true
			do
				sleep 60
				# 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
	#
	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
}
#
# 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
#	Check_For_Migration $USP
	# Load USP information just in case it changed while we were working.
	Get_USP_Info
	if [ $? != 0 ]
	then
		break
	fi
	#
	# Load LSP information just in case it changed while we were working.
	Get_LSP_Info
	if [ $? != 0 ]
	then
		break
	fi
	#

	Log "= Storage Pool: $LSP, Reclamation Threshold: $RTH."
	Log "=  Filled volumes with $VTH % occupancy or less will be reclaimed."
	Log "= Storage Pool: $LSP, 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
	#
	break
done
#
# Cleanup
rm -f $REC_TABLE
#
Log "+ Ended."
#
# remove lock file
rm -f $LCKF
#
#
# (end script)















