XCP-ng
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Register
    • Login

    Delete VM Snapshot over REST

    Scheduled Pinned Locked Moved Solved REST API
    7 Posts 4 Posters 1.1k Views 3 Watching
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • R Offline
      RaHu
      last edited by

      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

      lsouai-vatesL 2 Replies Last reply Reply Quote 0
      • olivierlambertO Offline
        olivierlambert Vates πŸͺ Co-Founder CEO
        last edited by

        Let me ask to @lsouai-vates πŸ™‚

        1 Reply Last reply Reply Quote 0
        • lsouai-vatesL Offline
          lsouai-vates Vates πŸͺ Product team @RaHu
          last edited by

          Hello @RaHu !
          I am asking the XO team and keep you in touch πŸ™‚

          1 Reply Last reply Reply Quote 0
          • lsouai-vatesL Offline
            lsouai-vates Vates πŸͺ Product team @RaHu
            last edited by

            @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 πŸ™‚

            R 1 Reply Last reply Reply Quote 0
            • R Offline
              RaHu @lsouai-vates
              last edited by RaHu

              @lsouai-vates

              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 ?

              DanpD 1 Reply Last reply Reply Quote 0
              • DanpD Offline
                Danp Pro Support Team @RaHu
                last edited by

                @RaHu Your are correct. To delete an individual snapshot, change /vms/ to /vm-snapshots/.

                R 1 Reply Last reply Reply Quote 0
                • R Offline
                  RaHu @Danp
                  last edited by RaHu

                  @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!

                  1 Reply Last reply Reply Quote 2
                  • olivierlambertO olivierlambert marked this topic as a question on
                  • olivierlambertO olivierlambert has marked this topic as solved on

                  Hello! It looks like you're interested in this conversation, but you don't have an account yet.

                  Getting fed up of having to scroll through the same posts each visit? When you register for an account, you'll always come back to exactly where you were before, and choose to be notified of new replies (either via email, or push notification). You'll also be able to save bookmarks and upvote posts to show your appreciation to other community members.

                  With your input, this post could be even better πŸ’—

                  Register Login
                  • First post
                    Last post