# zfs snapshot scritps



## darcsis (Jan 11, 2009)

I know there is a ruby one in the ports, and it is more sophisticated. Here is a simpler SH implementation.
You can add 'snapshot-all.s' into your crontab.

*NOTICE: these scripts are very ALPHA. You MUST NOT use them
on production systems or file systems with important data.*

----- snapshot.conf ---
I placed it under /opt/zfs/conf
This has a very simple format.
EXCLUDES: file systems you don't want to snapshot, in one line.
MAX_DATES: an integer. a MAX_DATES day older file system will be removed.
MAX_SHOTS: an integer. a file system can only have MAX_SHOTS snapshots.


```
# ZFS snapshot configuration for pluton.
#
# $Rev: 229 $
# $Date: 2009-01-03 16:52:03 +0800 (Sat, 03 Jan 2009) $
#

# excludes
EXCLUDES=rpool/opt rpool/var

# max dates for each file system
MAX_DATES=3

# max shots for each file system
MAX_SHOTS=6
```

----- snapshot.s -----
main snapshot script.
I placed it under /opt/zfs/rc.s/


```
#!/bin/sh
#
# $Rev: 231 $
# $Date: 2009-01-03 17:56:58 +0800 (Sat, 03 Jan 2009) $
#
# Use the command below to change time formats that
# ZFS file systems provide, into such a numerical
# format as '20081230-00:00:00', which can be handled
# well by scripting.
#
###################################################
# date -j \ 
#      -f "%a %b %d %k:%M %Y" \
#	  "Tue Dec 30 10:03 2008" \
#	  "+%Y%m%d-%H:%M:%S"
##################################################

# for dates conversion.
export LANG="C"
export LC_ALL="C"

SCRIPT_VERSION=`grep '^# \$Rev.*\$$' ${0} | cut -d ' ' -f 3`
SCRIPT_NAME=`basename ${0}`

display_usage() {
    echo "${SCRIPT_NAME}, version ${SCRIPT_VERSION}"
    echo "Usage: ${SCRIPT_NAME} [-hv] [-m max_copies] [-d max_dates] [-f filesystem]"
    echo "Automatic ZFS snapshoting script."
    echo ""
}

ZFS_CMD=/sbin/zfs
ZPOOL_NAME=`/sbin/zpool list -H -o name`

ZFS_SNAPSHOT_CMD="${ZFS_CMD} snapshot"
ZFS_DESTROY_CMD="${ZFS_CMD} destroy"
ZFS_RENAME_CMD="${ZFS_CMD} rename"
ZFS_SNAPSHOT_DATES=5

ARG_FS=
ARG_VERB=0
ARG_DATES=${ZFS_SNAPSHOT_DATES}

while getopts "Vhvd:f:m:" CMDLINE; do
  case "${CMDLINE}" in
    h)
	display_usage;
	exit 1
	;;
    v)
	ARG_VERB=1
	;;
    d)
	ARG_DATES="$OPTARG"
	;;
    f)
	ARG_FS="$OPTARG"
	;;
    m)
	ARG_MAXS="$OPTARG"
	;;
    *)
	display_usage;
	exit 1
	;;
  esac
done

[ ${ARG_VERB} -eq 1 ] && echo "===> verbose: ${ARG_VERB}"
[ ${ARG_VERB} -eq 1 ] && echo "===> dates: ${ARG_DATES}"
[ ${ARG_VERB} -eq 1 ] && echo "===> snapshot: ${ARG_FS}"
[ ${ARG_VERB} -eq 1 ] && echo "===> max shots: ${ARG_MAXS}"

check_filesystem () {

  if [ -z "${1}" ]; then
    exit 0
  fi

  target=${1}
  ${ZFS_CMD} list -H -t filesystem -o name| grep -q "${target}"
  results=$?

  if [ ${results} -eq 1 ]; then
    echo "===> No such ZFS file system: ${target}"
    exit 0
  fi
}

make_snapshot () {

    snapshot_date=`date "+%Y%m%d-%H:%M:%S"`
    target=${ZPOOL_NAME}/${1}@${snapshot_date}


    ${ZFS_SNAPSHOT_CMD} ${target} 2>&1 >/dev/null

    [ ${ARG_VERB} -eq 1 ] && echo "===> ${ZFS_SNAPSHOT_CMD} ${target}"
}

###################
## now kick off.

check_filesystem ${ARG_FS}

old_dates=${ARG_DATES}
current_date=`date "+%Y%m%d"`
all_snapshots=`${ZFS_CMD} list -H -t snapshot -o name | grep "${ARG_FS}"`

fs_id=1
# s for snapshot
action=s
fs_shots=`${ZFS_CMD} list -H -t snapshot -o name | grep "${ARG_FS}" |wc -l | sed 's| ||g'`
target=`echo ${all_snapshots} | cut -d ' ' -f 1`

while [ ! -z "${target}" ]; do

  creation_local=`${ZFS_CMD} list -H -o creation "${target}"`
  creation_real=`date -j -f "%a %b %d %k:%M %Y" "${creation_local}" "+%Y%m%d"`
  eval creation_minus=$(( ${current_date} - ${creation_real} ))

  if [ ${ARG_VERB} -eq 1 ]; then
    echo "===> snapshot name: ${target}"
    echo "===> snapshot date 1: ${creation_local}"
    echo "===> snapshot date 2: ${creation_real}"
    echo "===> dates difference: ${creation_minus}"
  fi

  if [ ${creation_minus} -ge ${old_dates} ]; then
    if [ ${ARG_VERB} -eq 1 ]; then
      echo "===> Older snapshots detected, removing ..."
      echo "===> ${ZFS_DESTROY_CMD} ${target}"
    fi
    # r for remove
    action=r
  fi

  if [ ${fs_shots} -ge ${ARG_MAXS} ]; then
    if [ ${ARG_VERB} -eq 1 ]; then
      echo "===> More snapshots detected, removing ..."
      echo "===> ${ZFS_DESTROY_CMD} ${target}"
    fi
    action=r
  fi

  case "${action}" in
    r)
      ${ZFS_DESTROY_CMD} ${target} 2>&1 >/dev/null
      eval fs_shots=$(( ${fs_shots} - 1))
      action=s
      ;;
    *)
      ;;
  esac

  eval fs_id=$(( ${fs_id} + 1 ))
  target_next=`echo ${all_snapshots} | cut -d ' ' -f "${fs_id}"`
  if [ "${target_next}" = "${target}" ]; then
    target=
  else
    target=${target_next}
  fi
done

if [ "${action}" = "s" ]; then
  make_snapshot ${ARG_FS}
fi
```

----- snapshot-all.s -----
I placed it under /opt/zfs/rc.s/
Notice it executes 'snapshot.s', and needs 'snapshot.conf',
so make sure this script can find the two scripts above.


```
#!/bin/sh
#
# $Rev: 230 $
# $Date: 2009-01-03 17:48:21 +0800 (Sat, 03 Jan 2009) $
#
# snapshot all ZFS file systems that are not listed in snapshot.conf
# current it only handles a single ZFS pool system.
#

SCRIPT_NAME=`basename ${0}`
SCRIPT_VERSION=`grep '^# $Rev:.*\$$' ${0} | cut -d ' ' -f 3`

ARG_CONFIG=
ARG_VERB=0

display_usage () {
    echo "${SCRIPT_NAME}: version ${SCRIPT_VERSION}"
    echo "Usage: ${SCRIPT_NAME} [-v] [-c config_file]"
    echo ""
}

while getopts "vc:" CMDLINE; do
    case "${CMDLINE}" in
	v)
	    ARG_VERB=1
	    ;;
	c)
	    ARG_CONFIG="${OPTARG}"
	    ;;
	*)
	    display_usage
	    exit 0
	    ;;
    esac
done

ZPOOL_NAME=`/sbin/zpool list -H -o name`
SNAPSHOT_CONFIG=${ARG_CONFIG:-/opt/zfs/conf/snapshot.conf}
SNAPSHOT_CMD=/opt/zfs/rc.s/snapshot.s
SNAPSHOT_FS=`/sbin/zfs list -H -t filesystem -o name`

if [ ! -f "${SNAPSHOT_CONFIG}" ]; then
  [ ${ARG_VERB} -eq 1 ] && echo "===> No such config file: ${SNAPSHOT_CONFIG}"
  exit 1
else
  SNAPSHOT_EXCLUDES=`grep "EXCLUDES=" ${SNAPSHOT_CONFIG} | sed 's|EXCLUDES=||g'`
  SNAPSHOT_MAXDATES=`grep "MAX_DATES=" ${SNAPSHOT_CONFIG} | sed 's|MAX_DATES=||g'`
  SNAPSHOT_MAXSHOTS=`grep "MAX_SHOTS=" ${SNAPSHOT_CONFIG} | sed 's|MAX_SHOTS=||g'`
fi

for ex in ${SNAPSHOT_EXCLUDES}; do
  SNAPSHOT_FS=`echo ${SNAPSHOT_FS} | sed "s|${ex}||g"`
done

fs_id=1
fs=`echo -n ${SNAPSHOT_FS} | cut -d ' ' -f 1`

while [ ! -z "${fs}" ]; do
    if [ "${fs}" != "${ZPOOL_NAME}" ]; then
	real_fs=`basename "${fs}"`
	if [ ${ARG_VERB} -eq 1 ]; then
	    ${SNAPSHOT_CMD} -v \
		-m ${SNAPSHOT_MAXSHOTS} \
		-d ${SNAPSHOT_MAXDATES} \
		-f ${real_fs}
	else
	    ${SNAPSHOT_CMD} \
		-m ${SNAPSHOT_MAXSHOTS} \
		-d ${SNAPSHOT_MAXDATES} \
		-f ${real_fs}
	fi
    fi
    eval fs_id=$(( ${fs_id} + 1))
    fs_next=`echo ${SNAPSHOT_FS} | cut -d ' ' -f ${fs_id}`
    if [ "${fs_next}" = "${fs}" ]; then
      fs=
    else
      fs=${fs_next}
    fi
done
```


----------



## darcsis (Jan 11, 2009)

*how to edit my post????*

MAX_DATES: it should be "a MAX_DATES day older snapshot will be removed." not "file system"


----------

