Get all VM snapshots

D

Deleted member 115820

Guest
Hi,
How to get all snapshots from a VM ?
When a take a snapshot with the UI, a new disk is created but there is not no new disk with the command
Code:
qm snapshot <vmID>
I don't see any new disk.
Could somebody help me ?
Thanks
 
hi,

have you tried qm listsnapshot VMID? this should show you a tree-like view of your snapshots for that VM
 
I missed it sorry
I think I could now delete all snapshots from one or several VMs
 
Hey,
I stumbled into that aswell and made a simple script that lists all of them in a json format (it also creates a file snapshot.json)

Remember to always review and test scripts before u just run them. Im not responsible if anything breaks (Eventho nothing should. It just lists them.)

it also uses the pct listsnapshot <LXCID> and qm listsnapshot <VMID> command

- Lists all snapshots for both VMs and Containers
- Excludes "current" snapshots
(which are not real snapshots - these You are here thing)
- Extracts snapshot name, timestamp, and description
- Formats the output in clean JSON for easy parsing

just save the script as list_snapshots on your proxmox node and execute it with bash list_snapshots

if u want you could put it in the /bin directory dont want to navigate to the actual file and execute it. eventho im not sure if that would brake incase of an update.

you are welcome

Bash:
#!/bin/bash

# Initialize JSON object
echo "{ \"vms\": [" > snapshots.json

# Fetch VM snapshots
first_vm=true
for vm in $(qm list | awk 'NR>1 {print $1}'); do
    snapshot_list=$(qm listsnapshot $vm | grep -v "current" | awk '{$1=""; print $0}' | sed 's/^ *//')

    if [[ ! -z "$snapshot_list" ]]; then
        if [ "$first_vm" = false ]; then
            echo "," >> snapshots.json
        fi
        first_vm=false
        echo "[" >> snapshots.json

        while read -r name date time desc; do
            name=$(echo "$name" | sed 's/`->//g' | xargs)  # Remove leading marker and trim spaces
            timestamp="$date $time"
            description=$(echo "$desc" | xargs)  # Trim spaces

            # Handle missing description case
            if [[ -z "$description" ]]; then
                description="no-description"
            fi

            echo "  { \"vmid\": \"$vm\", \"name\": \"$name\", \"timestamp\": \"$timestamp\", \"description\": \"$description\" }," >> snapshots.json
        done <<< "$snapshot_list"

        # Remove the trailing comma and close JSON array
        sed -i '$ s/,$//' snapshots.json
        echo "]" >> snapshots.json
    fi
done

echo "], \"containers\": [" >> snapshots.json

# Fetch Container snapshots
first_ct=true
for ct in $(pct list | awk 'NR>1 {print $1}'); do
    snapshot_list=$(pct listsnapshot $ct | grep -v "current" | awk '{$1=""; print $0}' | sed 's/^ *//')

    if [[ ! -z "$snapshot_list" ]]; then
        if [ "$first_ct" = false ]; then
            echo "," >> snapshots.json
        fi
        first_ct=false
        echo "[" >> snapshots.json

        while read -r name date time desc; do
            name=$(echo "$name" | sed 's/`->//g' | xargs)  # Remove leading marker and trim spaces
            timestamp="$date $time"
            description=$(echo "$desc" | xargs)  # Trim spaces

            # Handle missing description case
            if [[ -z "$description" ]]; then
                description="no-description"
            fi

            echo "  { \"ctid\": \"$ct\", \"name\": \"$name\", \"timestamp\": \"$timestamp\", \"description\": \"$description\" }," >> snapshots.json
        done <<< "$snapshot_list"

        # Remove the trailing comma and close JSON array
        sed -i '$ s/,$//' snapshots.json
        echo "]" >> snapshots.json
    fi
done

echo "] }" >> snapshots.json

# Output the JSON
cat snapshots.json

expected output:
JSON:
{
  "vms": [
    [
      { "vmid": "112", "name": "goti", "timestamp": "2024-05-17 21:27:03", "description": "no-description" }
    ],
    [
      { "vmid": "131", "name": "ansible_test", "timestamp": "2024-11-04 12:32:39", "description": "no-description" }
    ],
    [
      { "vmid": "530", "name": "totally_legit_snapshot_name", "timestamp": "2025-01-17 14:29:16", "description": "no-description" }
    ]
  ],
  "containers": [
    [
      { "ctid": "135", "name": "update", "timestamp": "2024-11-30 12:15:26", "description": "no-description" }
    ]
  ]
}
 
Last edited:
  • Like
Reactions: Kingneutron
Je n'ai pas le même résultat avec la commande qm listsnapshot.

Bash:
root@pve-01:~# qm listsnapshot 100
`-> snap_clean 2025-02-12 09:08:01 Installation
`-> courant Vous êtes ici !

Les backtick complexifie le rendu !

Bash:
# qm listsnapshot 100 | grep -v "current"
grep: (standard input): binary file matches
 
Hey @BeWog,
thanks for the feedback, u r lucky and I already remade the script from groundup because it had issues.
maybe this also fixes your Problem but I cant test it since im not french.

on a sidenote I would recommend running proxmox on an english version of Linux because many scripts have issues with different language and in the worst case it can even cause downtime if a code snippet from proxmox isnt translated right and causes a bug.

note: this script need jq to be installed or the output wont be beautified.

Bash:
#!/bin/bash

vms_array=""
cts_array=""

# ---------- VMs ----------
readarray -t vm_ids < <(qm list | awk 'NR>1 {print $1}')
# If no VMs are available, vm_ids will be empty

# Collect all snapshots for each VM ID
vm_json_parts=()  # Here we store sub-arrays (if the VM has snapshots)

for vm in "${vm_ids[@]}"; do
  [[ -z "$vm" ]] && continue
  snapshot_list=$(qm listsnapshot "$vm" | grep -v "current" | awk '{$1=""; print $0}' | sed 's/^ *//')

  # If no snapshots, skip
  [[ -z "$snapshot_list" ]] && continue

  # We build a JSON array for this VM, where each element is a JSON object representing a snapshot.
  this_vm_snapshots="["

  # Parse line by line
  first_line=true
  while read -r line; do
    [[ -z "$line" ]] && continue
    # line typically contains:  <snapshotName> <YYYY-MM-DD> <HH:MM:SS> <description...>
    # We split it:
    name=$(awk '{print $1}' <<< "$line" | sed 's/->//g')
    date=$(awk '{print $2}' <<< "$line")
    time=$(awk '{print $3}' <<< "$line")
    desc=$(awk '{$1=""; $2=""; $3=""; print $0}' <<< "$line" | xargs)

    [[ -z "$desc" ]] && desc="no-description"
    timestamp="$date $time"

    # First entry -> no comma, after that always add a comma
    if $first_line; then
      first_line=false
    else
      this_vm_snapshots+=","
    fi

    # Convert into JSON objects
    this_vm_snapshots+="{\"vmid\":\"$vm\",\"name\":\"$name\",\"timestamp\":\"$timestamp\",\"description\":\"$desc\"}"
  done <<< "$snapshot_list"

  this_vm_snapshots+="]"  # Close array

  # Add this array block to vms_array
  vm_json_parts+=( "$this_vm_snapshots" )
done

# Now assemble vm_json_parts into a JSON array
# (Each element of vm_json_parts is already an array, e.g., "[ {...}, {...} ]")
if ((${#vm_json_parts[@]} == 0)); then
  # No snapshots found -> empty array
  vms_array="[]"
else
  # Join with commas
  vms_array="["
  for (( i=0; i<${#vm_json_parts[@]}; i++ )); do
    [[ $i -gt 0 ]] && vms_array+=","
    vms_array+="${vm_json_parts[$i]}"
  done
  vms_array+="]"
fi

# ---------- Containers ----------
readarray -t ct_ids < <(pct list | awk 'NR>1 {print $1}')

ct_json_parts=()
for ct in "${ct_ids[@]}"; do
  [[ -z "$ct" ]] && continue
  snapshot_list=$(pct listsnapshot "$ct" | grep -v "current" | awk '{$1=""; print $0}' | sed 's/^ *//')

  [[ -z "$snapshot_list" ]] && continue

  this_ct_snapshots="["
  first_line=true
  while read -r line; do
    [[ -z "$line" ]] && continue
    name=$(awk '{print $1}' <<< "$line" | sed 's/->//g')
    date=$(awk '{print $2}' <<< "$line")
    time=$(awk '{print $3}' <<< "$line")
    desc=$(awk '{$1=""; $2=""; $3=""; print $0}' <<< "$line" | xargs)

    [[ -z "$desc" ]] && desc="no-description"
    timestamp="$date $time"

    if $first_line; then
      first_line=false
    else
      this_ct_snapshots+=","
    fi

    this_ct_snapshots+="{\"ctid\":\"$ct\",\"name\":\"$name\",\"timestamp\":\"$timestamp\",\"description\":\"$desc\"}"
  done <<< "$snapshot_list"

  this_ct_snapshots+="]"
  ct_json_parts+=( "$this_ct_snapshots" )
done

if ((${#ct_json_parts[@]} == 0)); then
  cts_array="[]"
else
  cts_array="["
  for (( i=0; i<${#ct_json_parts[@]}; i++ )); do
    [[ $i -gt 0 ]] && cts_array+=","
    cts_array+="${ct_json_parts[$i]}"
  done
  cts_array+="]"
fi

# Assemble final JSON
snapshots_json="{\"vms\":$vms_array,\"containers\":$cts_array}"

echo "$snapshots_json" | jq
 
Thank for your help.
Locale is alreay en_US.UTF-8.
I don't know where come from backtick !

PVE 8.3.3 (on Debian 12.9)