2013/11/15

Automatically delete old NetApp snapshots left by backups

Backup software may sometimes leave behind a snapshot.  This can increase the space consumption a lot over time.

Here's a script that can be run against NetApp filers to clean up those "stale" backups.  Note that in my case, I configured CommVault to name the snapshots with the string "snapshot_for_backup", though the default is just "ndmp".  You may change that as needed.




#!/bin/bash

# This script is to look for snaps that are left over from backups, that # are no longer in use, and delete them.
# This script requires two parameters:
# snap_cleanup     (list or delete all stale snaps on the specified servers)

NasUser="root"
SnapshotString="snapshot_for_backup"
SshIdentityFile="/root/.ssh/id_rsa_auslxfs00_rsync"
SshBinary=/usr/bin/ssh


# This function receives a volume name as a parameter, and returns # the number of snaps that are elligible for deletion, defined by being:
#                             1. Having a certain string in the snapshot name;
#                             2. Not marked as "busy"
function CountStaleSnaps ()
{
                local VolToCheck=$1
                local StaleSnaps=`$SshCmd "snap list $VolToCheck" | grep $SnapshotString | grep -v "busy" | wc -l`
                if [ $? -ne 0 ]; then
                {
                                echo "getting count of elligible snapshots returned an error"
                                exit 1
                } else
                {
                                echo $StaleSnaps
                } fi
}

# Function GetStaleSnapNames
# This function creates, given a volume name, an array of snapshots that are # candidates for deletion.
# Parameters
#             1. volume to check
function GetStaleSnapNames ()
{
                local Volume=$1
                StaleSnapNames=( $($SshCmd "snap list $Volume" | grep $SnapshotString| grep -v busy | cut -c 39- | cut -f 1 -d " ") )
                if [ $? -ne 0 ]; then
                {
                                echo "getting names of elligible snapshots returned an error"
                                exit 1
                } else
                {
                                printf -- '%s\n' "${StaleSnapNames[@]}"
                } fi
}

# Function DelStaleSnaps
# This function deletes stale snaps for the Volume name passed to it.
# Parameters
#  1. volume
#  2. snapshot_name
function DelStaleSnaps ()
{
                local Volume=$1
                local SnapToDelete=$2
                $SshCmd "snap delete $Volume $SnapToDelete"
                if [ $? -ne 0 ]; then
                {
                                echo "error deleting snapshot $Volume:$SnapToDelete"
                                exit 1
                } else
                {
                                echo "successfully deleted snapshot $Volume:$SnapToDelete"
                } fi
}

# Parse command line parameters
case $2 in
                "")
                                echo "ERROR: you must specify as a second parameter a host name on which you want to delete snapshots."
                                ;;
                *)
                                NasName=$2
                                ;;
esac
case $1 in
                list)
                                Operation=list
                                ;;
                delete)
                                Operation=delete
                                ;;
                *)
                                echo "ERROR: invalid operation specified on command line.  Please specify either 'list' or 'delete' followed by the servername on which you want to delete the snapshots."
                                exit 1
                                ;;
esac

SshCmd="/usr/bin/ssh -i $SshIdentityFile $NasUser@$NasName"

VolumesToCheck=( `$SshCmd "vol status -b" | cut -f 1 -d " " | egrep -v "Volume|-----"` )

for CurrentVol in ${VolumesToCheck[@]}; do {
                echo -n "checking $NasName:$CurrentVol... "
                StaleSnaps=`CountStaleSnaps $CurrentVol`
                echo $StaleSnaps            
                if [ $Operation = list ]; then
                                if [ $StaleSnaps -ne 0 ]; then
                                                GetStaleSnapNames $CurrentVol |  awk '{ print "    " $1 }'
                                fi
                elif [ $Operation = delete ]; then
                                GetStaleSnapNames $CurrentVol
                                ArrayOfSnaps=( $(GetStaleSnapNames $CurrentVol) )
                                for TargetSnap in `printf -- '%s\n' "${ArrayOfSnaps[@]}"`; do
                                                                DelStaleSnaps $CurrentVol $TargetSnap
                                done
                fi
                               
}; done