List all Proxmox VM snapshots on a given Ceph pool

It’s useful to occasionally review all of the snapshots that exist on your Ceph pool so that you can identify old ones that can probably be deleted – not just to free up space, but to ensure you are not wasting performance by having too many layers of unneeded snapshots hanging around.

The below script will make this easy and fast to do.

It is a refinement of Proxmox Forum user 43n12y’s excellent suggestion, and it looks like this in use – shown here being used in human-readable table mode, rather than the default JSON output – the JSON output is intended to be fed to some other script to carry out automated follow-up actions like sending an email, and/or deleting snapshots over a certain age automatically, etc:

root@vm00:~/scripts# ./snapshots.sh -p MYCEPHPOOL -t
Node VMID VM Name Snapshot Dates
vm01 1033 cpanel1.XXXXXXX.net-CL8 "2024-06-07"
vm01 121 fw0.XXXXXX.YYYYYYY.net "2024-08-27","2024-08-27","2024-08-27","2024-08-27","2024-08-27"
vm00 2004 unifi.ZZZZZZZ.co.uk "2024-09-14"
vm01 2007 vm-XXXXX.YYYYY.net "2024-07-19"
#/bin/bash

# Help Function
Help()
{
        echo
        echo "Show information about VM snapshots that exist on a given Ceph pool"
        echo
        echo "Syntax: $0 -p POOLNAME"
        echo
        echo "Required:"
        echo "-p NAME   Specify the pool name"
        echo
        echo "Options:"
        echo "-h        Print this help message"
        echo "-t        Print out as a table - i.e. don't output as the default JSON"
        echo
        echo "To get a list of pool names, you can use:"
        echo "ceph osd pool ls"
        echo
}

unset -v pool
unset -v text

while getopts hp:t opt; do
        case $opt in
                h) Help ; exit ;;
                p) pool=$OPTARG ;;
                t) text=true ;;
                *) Help ; exit ;;
        esac
done

: ${pool:?-p is required: You must specify the Ceph pool with -p NAME}

tmpfile=$(mktemp /tmp/snapshot-search-$pool.XXXXXX)

pvesh get /cluster/resources --type vm --output-format json-pretty >> ${tmpfile}

if [ $text ]; then
        unset -v tabledata
fi

for vmid in $(rbd ls -l -p $pool | grep "^vm-.*@" | cut -f 2 -d "-" | uniq); do
        vmname=$(jq -r ".[] | select ( .vmid == ${vmid}) | .name" ${tmpfile})
        node=$(jq -r ".[] | select ( .vmid == ${vmid}) | .node" ${tmpfile})
        filter=".[] | select ( .name != \"current\" ) + {\"vmname\": \"${vmname}\",\"vmid\": \"${vmid}\"} | .snaptime |= strftime(\"%Y-%m-%d\")"
        if [ $text ]; then
                unset -v snapdates
                snapdates=$(pvesh get "/nodes/$node/qemu/$vmid/snapshot" --output-format=json | jq -r '[.[] | select(.snaptime) | (.snaptime | tonumber | todate[:10])] | @csv');
                tabledata="$tabledata$node!$vmid!$vmname!$snapdates\n"
        else
                pvesh get /nodes/${node}/qemu/${vmid}/snapshot --output-format json-pretty | jq "${filter}"
        fi
done

if [ $text ]; then
        printf $tabledata | column -t -s "!" -N "Node,VMID,VM Name,Snapshot Dates" -T "Node","VM Name" -W "Snapshot Dates"
fi

rm ${tmpfile}

Leave a Reply