FreeNAS Bash script to start a ZFS scrub on each pool (scrub.sh)

This script will start a scrub on each ZFS pool (one at a time) and will send an e-mail or display the result when everyting is completed. I wrote this script to launch each scrub one after the other and a summary by e-mail that can tell me how much time it took to each ZFS scrub.

Put this script on your FreeNAS box using the File Manager or directly at the shell by copy-pasting it. I put it in “/mnt/usb-data/bin/scrub.sh”.

To make it run automatically, go into System –> Advanced –> Cron and add it as Monthly cron job.

NOTE: Don’t forget to change the e-mail addresses in the script!

FROM=from@devnull.com
TO=to@devnull.com

Here’s the script:

#!/bin/bash

#VERSION: 0.2
#AUTHOR: gimpe
#EMAIL: gimpe [at] hype-o-thetic.com
#WEBSITE: http://hype-o-thetic.com
#DESCRIPTION: Created on FreeNAS 0.7RC1 (Sardaukar)
# This script will start a scrub on each ZFS pool (one at a time) and
# will send an e-mail or display the result when everyting is completed.

#CHANGELOG
# 0.2: 2009-08-27 Code clean up
# 0.1: 2009-08-25 Make it work

#SOURCES:
# http://aspiringsysadmin.com/blog/2007/06/07/scrub-your-zfs-file-systems-regularly/
# http://www.sun.com/bigadmin/scripts/sunScripts/zfs_completion.bash.txt
# http://www.packetwatch.net/documents/guides/2009073001.php

# e-mail variables
FROM=from@devnull.com
TO=to@devnull.com
SUBJECT="$0 results"
BODY=""

# arguments
VERBOSE=0
SENDEMAIL=1
args=("$@")
for arg in $args; do
    case $arg in
        "-v" | "--verbose")
            VERBOSE=1
            ;;
        "-n" | "--noemail")
            SENDEMAIL=0
            ;;
        "-a" | "--author")
            echo "by gimpe at hype-o-thetic.com"
            exit
            ;;
        "-h" | "--help" | *)
            echo "
usage: $0 [-v --verbose|-n --noemail]
    -v --verbose    output display
    -n --noemail    don't send an e-mail with result
    -a --author     display author info (by gimpe at hype-o-thetic.com)
    -h --help       display this help
"
            exit
            ;;
    esac
done

# work variables
ERROR=0
SEP=" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "
RUNNING=1

# commands & configuration
ZPOOL=/sbin/zpool
PRINTF=/usr/bin/printf
MSMTP=/usr/local/bin/msmtp
MSMTPCONF=/var/etc/msmtp.conf

# print a message
function _log {
    DATE="`date +"%Y-%m-%d %H:%M:%S"`"
    # add message to e-mail body
    BODY="${BODY}$DATE: $1n"

    # output to console if verbose mode
    if [ $VERBOSE = 1 ]; then
        echo "$DATE: $1"
    fi
}

# find all pools
pools=$($ZPOOL list -H -o name)

# for each pool
for pool in $pools; do
    # start scrub for $pool
    _log "starting scrub on $pool"
    zpool scrub $pool
    RUNNING=1
    # wait until scrub for $pool has finished running
    while [ $RUNNING = 1 ];     do
        # still running?
        if $ZPOOL status -v $pool | grep -q "scrub in progress"; then
            sleep 60
        # not running
        else
            # finished with this pool, exit
            _log "scrub ended on $pool"
            _log "`$ZPOOL status -v $pool`"
            _log "$SEP"
            RUNNING=0
            # check for errors
            if ! $ZPOOL status -v $pool | grep -q "No known data errors"; then
                _log "data errors detected on $pool"
                ERROR=1
            fi
        fi
    done
done

# change e-mail subject if there was error
if [ $ERROR = 1 ]; then
    SUBJECT="${SUBJECT}: ERROR(S) DETECTED"
fi

# send e-mail
if [ $SENDEMAIL = 1 ]; then
    $PRINTF "From:$FROMnTo:$TOnSubject:$SUBJECTnn$BODY" | $MSMTP --file=$MSMTPCONF -t
fi

Here’s what the e-mail content looks like:

2009-09-01 02:00:01: starting scrub on backup
2009-09-01 02:35:19: scrub ended on backup
2009-09-01 02:35:19:   pool: backup
 state: ONLINE
 scrub: scrub completed with 0 errors on Tue Sep  1 02:34:50 2009
config:

       NAME        STATE     READ WRITE CKSUM
       backup      ONLINE       0     0     0
         mirror    ONLINE       0     0     0
           ad8     ONLINE       0     0     0
           ad10    ONLINE       0     0     0

errors: No known data errors
2009-09-01 02:35:19:  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2009-09-01 02:35:19: starting scrub on data
2009-09-01 02:41:24: scrub ended on data
2009-09-01 02:41:24:   pool: data
 state: ONLINE
 scrub: scrub completed with 0 errors on Tue Sep  1 02:40:50 2009
config:

       NAME        STATE     READ WRITE CKSUM
       data        ONLINE       0     0     0
         ad6       ONLINE       0     0     0

errors: No known data errors
2009-09-01 02:41:24:  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2009-09-01 02:41:24: starting scrub on media
2009-09-01 05:41:58: scrub ended on media
2009-09-01 05:41:58:   pool: media
 state: ONLINE
 scrub: scrub completed with 0 errors on Tue Sep  1 05:41:00 2009
config:

       NAME        STATE     READ WRITE CKSUM
       media       ONLINE       0     0     0
         ad14      ONLINE       0     0     0

errors: No known data errors
2009-09-01 05:41:58:  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Please, leave me a comment if you use it to let me know what you think!

7 thoughts on “FreeNAS Bash script to start a ZFS scrub on each pool (scrub.sh)

  1. Web seems to kill the formatting, so I need to make the following changes to get this to work:

    *there is a missing ` mark at the end of the last line of each of the hourly/daily files.
    *had to replace $1 with $pool throughout
    *firefox doesn’t change & quot marks back to double quotes, so had to find and replace. FormatSelector gets tripped up by the same issues.

  2. I am sorry for the incovienient, I updated it and tested the “copy to clipboard” button in the upper right corner of the code box and the result is now right.

    Strange the “$1″ in the log function looks fine to me…

  3. Running the script with two arguments (e.g. “./scrub.sh -n -v”) causes the second argument to be ignored under FreeNAS 0.7.2. I changed “args=(“$@”)” to read “args=(“$*”)” and all is well. The other choice is to put both the arguments to scrub.sh in double quotes but I don’t believe this would be intuitive to most people.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>