Delete VM Snapshot over REST
-
Hi Community
I was able to create a bash script that uses the local machines uuid (from bios) in order to trigger a snapshot creation (of the vm itself) over xo rest api.
Now, I would like to do some very basic kind of retention. I want my script to get all snapshots of the same VM and remove older snapshots that exceed a limit provided over command line. Otherwise, you'd have to care manually about removing older snapshots.
However, I was unable to find a how to for deleting a snapshot over rest. Is this not possible yet?
Thanks,
Rainer -
Let me ask to @lsouai-vates
-
Hello @RaHu !
I am asking the XO team and keep you in touch -
@RaHu you can take a look there: https://github.com/vatesfr/xen-orchestra/blob/master/packages/xo-server/docs/rest-api.md#vm-destruction
curl \ -X DELETE \ -b authenticationToken=KQxQdm2vMiv7jBIK0hgkmgxKzemd8wSJ7ugFGKFkTbs \ 'https://xo.company.lan/rest/v0/vms/770aa52a-fd42-8faf-f167-8c5c4a237cac'
Hope it will help you
-
Thanks! Will try it out!
//update: but if I see this correctly, this would delete the whole vm, not a snapshot - or is this just an example that could be applied to https://xo.company.lan/rest/v0/vm-snapshots/uuid ?
-
@RaHu Your are correct. To delete an individual snapshot, change
/vms/
to/vm-snapshots/
. -
@Danp Thanks! Finally I was able to get my script working. Also some contribution to "GROK" who assisted quite well!
It allows a VM to Snapshot itself, before running automated maintenance over cron. And it allows minimalistic retention of the snapshots created this way.
Happy to share it with the community:
#!/bin/bash : <<'END' Snapshot Management Script for Xen Orchestra Usage: ./snapshot-vm.sh [OPTIONS] Options: -v Enable verbose output (detailed logging) -s Silent mode (suppress all non-error output) -r <number> Set retention limit (delete oldest snapshots beyond this number) -n No-snapshot mode (skip snapshot creation, only manage retention) Examples: ./snapshot-vm.sh # Create a snapshot, no retention ./snapshot-vm.sh -v -r 3 # Create a snapshot, keep latest 3, verbose ./snapshot-vm.sh -r 5 -n # Skip snapshot, keep latest 5 ./snapshot-vm.sh -v -s -r 2 # Create a snapshot, keep latest 2, verbose but silent Configuration: Edit XO_URL, TOKEN, and SNAPSHOT_PREFIX at the top of the script. END # Configuration variables XO_URL="http://your-xen-orchestra-server" TOKEN="your-authentication-token" VM_UUID=$(dmidecode -s system-uuid | tr '[:upper:]' '[:lower:]') SNAPSHOT_PREFIX="AutoMaintenance" # Prefix for snapshot names # Default retention (no deletion if not specified) RETENTION=-1 # Flags VERBOSE=0 SILENT=0 NOSNAPSHOT=0 # Parse command-line options while getopts "vsr:n" opt; do case $opt in v) VERBOSE=1;; s) SILENT=1;; r) RETENTION="$OPTARG";; n) NOSNAPSHOT=1;; esac done # Function to print messages based on mode print_msg() { if [ $SILENT -eq 0 ]; then if [ $VERBOSE -eq 1 ]; then echo "$1" else echo -e "$1" fi fi } # Check if we got the UUID if [ -z "$VM_UUID" ]; then echo "Error: Could not retrieve system UUID" exit 1 fi # Function to check if jq is installed check_jq() { if ! command -v jq &> /dev/null; then echo "Error: jq is required but not installed" exit 1 fi } # Function to get VM name get_vm_name() { local vm_info=$(curl -s -X GET \ -b "authenticationToken=$TOKEN" \ -H "Accept: application/json" \ "$XO_URL/rest/v0/vms/$VM_UUID") vm_name=$(echo "$vm_info" | jq -r '.name_label // "Unknown"') } # Function to trigger snapshot trigger_snapshot() { local SNAPSHOT_NAME="${SNAPSHOT_PREFIX}-$(date +%Y%m%d-%H%M%S)" if [ $VERBOSE -eq 1 ]; then local curl_cmd="curl -s -X POST \ -b \"authenticationToken=$TOKEN\" \ -H \"Content-Type: application/json\" \ -H \"Accept: application/json\" \ -d '{\"name_label\": \"$SNAPSHOT_NAME\"}' \ \"$XO_URL/rest/v0/vms/$VM_UUID/actions/snapshot\"" print_msg "Executing snapshot trigger command:" print_msg "$curl_cmd" print_msg "-----" fi response=$(curl -s -X POST \ -b "authenticationToken=$TOKEN" \ -H "Content-Type: application/json" \ -H "Accept: application/json" \ -d "{\"name_label\": \"$SNAPSHOT_NAME\"}" \ "$XO_URL/rest/v0/vms/$VM_UUID/actions/snapshot") if [ $VERBOSE -eq 1 ]; then print_msg "Snapshot trigger response: $response" print_msg "-----" fi if [[ "$response" =~ /rest/v0/tasks/([a-z0-9]+) ]]; then task_id="${BASH_REMATCH[1]}" if [ $VERBOSE -eq 1 ]; then print_msg "Snapshot triggered (Task ID: $task_id)" fi return 0 elif [[ "$response" =~ ^[a-z0-9]+$ ]]; then task_id="$response" if [ $VERBOSE -eq 1 ]; then print_msg "Snapshot triggered (Task ID: $task_id)" fi return 0 else print_msg "Error: Failed to get task ID. Response: $response" exit 1 fi } # Function to monitor task status monitor_task() { local task_id=$1 local max_attempts=30 local attempt=1 if [ $VERBOSE -eq 1 ]; then print_msg "Monitoring task $task_id..." fi while [ $attempt -le $max_attempts ]; do status_response=$(curl -s -X GET \ -b "authenticationToken=$TOKEN" \ -H "Accept: application/json" \ "$XO_URL/rest/v0/tasks/$task_id") if [ $VERBOSE -eq 1 ]; then print_msg "Attempt $attempt - Raw response: $status_response" fi if [ -n "$status_response" ] && echo "$status_response" | jq -e . >/dev/null 2>&1; then status=$(echo "$status_response" | jq -r '.status') if [ $VERBOSE -eq 1 ]; then print_msg "Status: $status" else print_msg "Taking Snapshot of VM: $vm_name... Status: $status" fi case "$status" in "pending"|"running") if [ $VERBOSE -eq 1 ]; then print_msg "Task still in progress" fi sleep 5 ((attempt++)) ;; "success") if [ $VERBOSE -eq 1 ]; then print_msg "Snapshot completed successfully" else print_msg "Taking Snapshot of VM: $vm_name... Status: Completed" fi return 0 ;; "failure") error=$(echo "$status_response" | jq -r '.result.message // "Unknown error"') print_msg "Task failed: $error" exit 1 ;; *) if [ $VERBOSE -eq 1 ]; then print_msg "Unknown status: $status" fi sleep 5 ((attempt++)) ;; esac else if [ $VERBOSE -eq 1 ]; then print_msg "Invalid or empty response" fi sleep 5 ((attempt++)) fi done print_msg "Timeout waiting for task completion" exit 1 } # Function to manage snapshot retention manage_retention() { if [ $RETENTION -lt 0 ]; then print_msg "Retention not specified, skipping cleanup" return 0 fi # Get VM data with snapshot UUIDs vm_data=$(curl -s -X GET \ -b "authenticationToken=$TOKEN" \ -H "Accept: application/json" \ "$XO_URL/rest/v0/vms/$VM_UUID") if [ $VERBOSE -eq 1 ]; then print_msg "Raw VM data response: $vm_data" print_msg "-----" fi if ! echo "$vm_data" | jq -e . >/dev/null 2>&1; then print_msg "Error: Invalid JSON response from VM endpoint" print_msg "Response: $vm_data" exit 1 fi # Extract snapshot UUIDs snapshot_uuids=$(echo "$vm_data" | jq -r '.snapshots[]') # Fetch details for each snapshot, verify existence, and filter by prefix auto_snapshots="" for uuid in $snapshot_uuids; do # Check existence with a HEAD request to avoid full body download http_status=$(curl -s -o /dev/null -w "%{http_code}" -I \ -b "authenticationToken=$TOKEN" \ "$XO_URL/rest/v0/vm-snapshots/$uuid") if [ "$http_status" -eq 404 ]; then if [ $VERBOSE -eq 1 ]; then print_msg "Snapshot $uuid does not exist (HTTP 404), skipping" print_msg "-----" fi continue elif [ "$http_status" -ne 200 ]; then if [ $VERBOSE -eq 1 ]; then print_msg "Unexpected HTTP status $http_status for snapshot $uuid, skipping" print_msg "-----" fi continue fi # Fetch full snapshot data if it exists snapshot_data=$(curl -s -X GET \ -b "authenticationToken=$TOKEN" \ -H "Accept: application/json" \ "$XO_URL/rest/v0/vm-snapshots/$uuid") if [ $VERBOSE -eq 1 ]; then print_msg "Snapshot $uuid data: $snapshot_data" print_msg "-----" fi # Verify itβs a valid JSON response if ! echo "$snapshot_data" | jq -e . >/dev/null 2>&1; then print_msg "Error: Invalid JSON response for snapshot $uuid" print_msg "Response: $snapshot_data" continue fi # Filter for snapshots with the specified prefix if echo "$snapshot_data" | jq -e ".name_label | startswith(\"$SNAPSHOT_PREFIX\")" >/dev/null 2>&1; then snapshot_line=$(echo "$snapshot_data" | jq -r '[.id, .name_label, .snapshot_time] | join("\t")') auto_snapshots="$auto_snapshots$snapshot_line\n" fi done # Sort by snapshot_time auto_snapshots=$(echo -e "$auto_snapshots" | sort -k3 -n | grep -v '^$') if [ $VERBOSE -eq 1 ]; then print_msg "Filtered and sorted $SNAPSHOT_PREFIX snapshots:" print_msg "$auto_snapshots" print_msg "-----" fi # Count current snapshots snapshot_count=$(echo "$auto_snapshots" | wc -l) print_msg "Current $SNAPSHOT_PREFIX snapshot count: $snapshot_count" # If over retention limit, delete oldest if [ $snapshot_count -gt $RETENTION ]; then excess=$((snapshot_count - RETENTION)) print_msg "Excess snapshots to delete: $excess" # Get IDs of oldest snapshots to delete delete_ids=$(echo "$auto_snapshots" | head -n $excess | cut -f1) for id in $delete_ids; do if [ $VERBOSE -eq 1 ]; then print_msg "Deleting snapshot $id" print_msg "Delete command: curl -s -X DELETE -b \"authenticationToken=$TOKEN\" \"$XO_URL/rest/v0/vm-snapshots/$id\"" fi delete_response=$(curl -s -X DELETE \ -b "authenticationToken=$TOKEN" \ "$XO_URL/rest/v0/vm-snapshots/$id") if [ $VERBOSE -eq 1 ]; then print_msg "Delete response: $delete_response" print_msg "-----" fi # Treat empty response, {"status": "success"}, or "OK" as success if [ -z "$delete_response" ] || echo "$delete_response" | jq -e '.status == "success"' >/dev/null 2>&1 || [ "$delete_response" = "OK" ]; then print_msg "Successfully deleted snapshot $id" else print_msg "Warning: Failed to delete snapshot $id. Response: $delete_response" fi done else if [ $VERBOSE -eq 1 ]; then print_msg "No excess snapshots to delete" fi fi } # Main execution check_jq get_vm_name if [ $VERBOSE -eq 1 ]; then print_msg "Using VM UUID: $VM_UUID" fi if [ $NOSNAPSHOT -eq 0 ]; then trigger_snapshot monitor_task "$task_id" else if [ $VERBOSE -eq 1 ]; then print_msg "Skipping snapshot creation due to --nosnapshot flag" fi fi manage_retention exit 0
Cheers!
-
O olivierlambert marked this topic as a question
-
O olivierlambert has marked this topic as solved