I suspect you are talking about "zpool iostat".
Yes sorry, I have corrected my post.
The only problem with it is every time I ran it, it had a write value of 2, never could get 0.
Yeah, not sure why it always shows activity on the first iostat, but then settles down to 0 activity if left to run continuously. This anomaly makes life a little more tricky for the shell scripter... :)
Also there is a format associated with the output, not sure how to sift through that to get the read and write data only. Do you have an example command I could punching into the command line that works better?
I'm not sure if it is possible to control the format of iostat, but a simple grep should handle it - basically you're just looking for 4 space separated zeroes at the end of each line ("....0......0......0......0$") in order to decide if the count should be incremented or reset.
It would be nice if the disk timeout could be extracted from the config database, but each disk has it's own timeout in which case this would only make sense if it were possible to determine which drive belongs to which pool, and maintain individual counts for each disk...
I knocked up this quick script, which seems to work for my setup (my pool is called "share", and I have a working zdb cachefile which is important!). It's not great, and might be more efficient if written entirely in perl or awk, but it does the job and shouldn't tax any system even as is.
Code:
#!/bin/sh
#set -xv
#VERSION: 0.1
#
#AUTHOR: Milhouse
#
#DESCRIPTION: Created on FreeNAS 8.0.1-BETA4 with LSI 9211-8i HBA
# Tested on FreeNAS FreeNAS 8.0.1-BETA4 with LSI 9211-8i HBA
#
# This script will attempt to stop disks in a ZFS pool connected
# by a SAS HBA controller, using the camcontrol command. In effect,
# this is a poor mans ATAIDLE.
#
# The script is designed to run as a "daemon" process, running
# constantly from bootup. The idea is that it will consume standard
# input, which will itself be the output of "zpool iostat n" where n
# is a time interval, usually 60 (seconds).
#
# A count (--timeout) is specified as an argument for the script, and this
# quantity is decremented each time there is no iostat activity, and reset
# whenever there is iostat activity. When the count reaches zero, the disks
# will be stopped using "camcontrol stop" (unless the --test option is
# specified).
#
# The amount of time elapsed before disks are stopped is calculated as
# the product of the timeout and the value n specified for zpool iostat.
# For example, a timeout of 15 and an interval of 60 gives 15 * 60, or
# 900 seconds (ie. 15 minutes). The default timeout is 30.
#
# By default, disks will be stopped asynchronously (ie. simultaneously)
# however if this is a problem, specify the --sync option to stop disks
# in sequence, one after the other.
#
# If a system has multiple pools, run individual commands for each pool.
#
# If the script is unable to automatically detect disks/devices, specific
# devices can be specified using the --devices argument.
#
#CHANGELOG:
#
# 0.1: 2011-08-01 - Initial version
#
#
#USAGE:
# Execute the script with the -h or --help arguments.
#
#EXAMPLES:
# zpool iostat tank 60 | sh scsi_standby.sh --timeout 15
# zpool iostat tank 60 | sh scsi_standby.sh --timeout 15 -devices "/dev/da[2-3]"
# zpool iostat tank 60 | sh scsi_standby.sh --timeout 15 --sync
# zpool iostat tank 60 | sh scsi_standby.sh --timeout 15 --test --verbose
#
TIMEOUT=30
DEVICES=
ASYNC=Y
DOSTOP=Y
DEBUG=
QUIET=N
while [ $# -gt 0 ]; do
case ${1} in
"-t" | "--timeout") shift 1; TIMEOUT=${1};;
"-s" | "--sync") ASYNC=;;
"-d" | "--devices") shift 1; DEVICES=${1};;
"-x" | "--test") DOSTOP=;;
"-q" | "--quiet") DEBUG=; QUIET=;;
"-v" | "--verbose") DEBUG=Y; QUIET=;;
"-h" | "--help" | *)
echo "usage: ${0} [-t --timeout #] [-v --verbose] [-h --help]
-t --timeout timeout delay (default 30)
-s --sync stop disks synchronously (default, async)
-d --devices override device detection by specifying devices eg. "/dev/da[0-4]"
-x --test do not stop disks (simulation)
-q --quiet supress all informational and debug messages
-v --verbose output debug messages
-h --help display this help";
exit;;
esac
shift 1
done
STOPPED=
let COUNT=${TIMEOUT} >/dev/null
# Show config info...
if [ "$DEBUG" -o "$QUIET" ]; then
echo "`date +'%Y/%m/%d %H:%M:%S'` $0 starting"
echo
echo "--------------------------------------------------"
echo "Timeout interval: ${TIMEOUT}"
echo -n "ASync Enabled: "
[ ${ASYNC} ] && echo "Yes" || echo "No"
echo -n "Simulated Stop: "
[ ${DOSTOP} ] && echo "No" || echo "Yes"
[ "${DEVICES}" ]&& echo "Pool Devices: " ${DEVICES}
[ ! "${DEVICES}" ]&& echo "Pool devices will be determined automatically"
echo "--------------------------------------------------"
echo
fi
# Skip 3 lines of "zpool iostat" headers...
for H in 1 2 3; do
read HEADER
[ "$DEBUG" ] && printf "%.3d: %s\n" $COUNT "$HEADER"
done
# Main infinite loop...
while [ true ]; do
read POOL_NAME POOL_USED POOL_AVAIL POOL_OP_READ POOL_OP_WRITE POOL_BW_READ POOL_BW_WRITE
# Determine non-gptid devices if not already known...
if [ ! "$DEVICES" ]; then
[ "$DEBUG" -o "$QUIET" ] && echo "Identifying devices for pool \"${POOL_NAME}\"..."
MYCOMPONENTS=`gpart status -s | sed "s/^ //" | sed "s/ / /g" | cut -d" " -f3 | sort -u`
MYGUIDS=`zdb -C ${POOL_NAME} | grep "path" | sed "s/.*path='//" | sed "s/p2//" | sed "s/'//" | sed "s#/dev/gptid/##"`
# Extract any non-gptid devices that may be in use...
DEVICES=`echo "$MYGUIDS" | grep "^/dev"`
# Leaving only gptids for further processing
MYGUIDS=`echo "${MYGUIDS}" | grep -v "^/dev"`
# Now, for each gpt component (disk), attempt to identify it's corresponding gptid
for COMPONENT in ${MYCOMPONENTS}; do
for GUID in ${MYGUIDS}; do
if [ "`gpart list ${COMPONENT} | grep \"rawuuid: ${GUID}\"`" ]; then
# Add the matched component to the list of devices
[ ! -z "${DEVICES}" ] && DEVICES="${DEVICES} "
DEVICES="${DEVICES}/dev/${COMPONENT}"
# Remove the matched GUID so we don't process it again
MYGUIDS="`echo ${MYGUIDS} | sed "s/${GUID}//"`"
break
fi
done
done
# Eliminate any duplicates, stop disk in sequence...
DEVICES=`echo ${DEVICES}| tr -s " " "\n" | sort -u`
[ "$DEBUG" -o "$QUIET" ] && echo "Pool Devices:" ${DEVICES}
fi
# If no activity, decrement count, else reset it
if [ ${POOL_OP_READ-1} = 0 -a ${POOL_OP_WRITE-1} = 0 -a \
${POOL_BW_READ-1} = 0 -a ${POOL_BW_WRITE-1} = 0 ]; then
let COUNT=COUNT-1 >/dev/null
else
let COUNT=${TIMEOUT} >/dev/null
STOPPED=
fi
[ "${DEBUG}" ] && printf "%.3d: %-10s %5s %5s %5s %5s %5s %5s\n" \
${COUNT} ${POOL_NAME} ${POOL_USED} ${POOL_AVAIL} \
${POOL_OP_READ} ${POOL_OP_WRITE} ${POOL_BW_READ} ${POOL_BW_WRITE}
# If count reaches zero, stop devices
if [ ${COUNT} -le 0 ]; then
let COUNT=1 >/dev/null
if [ ! "${STOPPED}" ]; then
[ "$DEBUG" -o "$QUIET" ] && echo "`date +'%Y/%m/%d %H:%M:%S'` ** Stopping devices in pool \"${POOL_NAME}\" **"
for DISK in ${DEVICES}; do
if [ "$DOSTOP" ]; then
[ "$DEBUG" -o "$QUIET" ] && echo "camcontrol stop ${DISK}"
[ "${ASYNC}" ] && camcontrol stop ${DISK} &
[ ! "${ASYNC}" ] && camcontrol stop ${DISK}
else
[ "$DEBUG" -o "$QUIET" ] && echo "#camcontrol stop ${DISK}"
fi
done
STOPPED=Y
fi
fi
done
EDIT: See post #79 for the latest version of the above script.
Call it "scsi_standby.sh", then to run it using the following command which will check every 1 second (the zpool interval) and wait 10 zpool intervals before stopping the disks in the appropriate pool (as determined from zdb):
Code:
zpool iostat share 1 | sh /tmp/scsi_standby.sh -t 10 -v
Obviously in a "production" environment the following would be more appropriate (30 x 1 minute intervals):
Code:
zpool iostat share 60 | sh /tmp/scsi_standby.sh -t 30
If you have multiple pools you'll need to run the above command once for each pool.