using System; using System.Collections.Generic; using Newtonsoft.Json.Linq; using PSProxmoxVE.Core.Authentication; using PSProxmoxVE.Core.Client; using PSProxmoxVE.Core.Models.Vms; namespace PSProxmoxVE.Core.Services { /// /// Service for Proxmox VE VM snapshot API operations. /// All operations apply to QEMU/KVM VMs via the /nodes/{node}/qemu/{vmid}/snapshot endpoints. /// public class SnapshotService { /// /// Returns all snapshots for a VM. /// /// The authenticated PVE session. /// The cluster node name. /// The VM ID. public PveSnapshot[] GetSnapshots(PveSession session, string node, int vmid) { if (session == null) throw new ArgumentNullException(nameof(session)); if (string.IsNullOrWhiteSpace(node)) throw new ArgumentNullException(nameof(node)); using var client = new PveHttpClient(session); var response = client.GetAsync($"nodes/{node}/qemu/{vmid}/snapshot") .GetAwaiter().GetResult(); var data = JObject.Parse(response)["data"]; return data?.ToObject() ?? Array.Empty(); } /// /// Creates a snapshot of a VM. Returns the task UPID. /// /// The authenticated PVE session. /// The cluster node name. /// The VM ID. /// Snapshot name (alphanumeric, no spaces). /// Optional description. /// Whether to save VM RAM state (live snapshot). Default false. public PveTask CreateSnapshot( PveSession session, string node, int vmid, string snapname, string? description = null, bool vmstate = false) { if (session == null) throw new ArgumentNullException(nameof(session)); if (string.IsNullOrWhiteSpace(node)) throw new ArgumentNullException(nameof(node)); if (string.IsNullOrWhiteSpace(snapname)) throw new ArgumentNullException(nameof(snapname)); var formData = new Dictionary { ["snapname"] = snapname, ["vmstate"] = vmstate ? "1" : "0" }; if (!string.IsNullOrEmpty(description)) formData["description"] = description!; using var client = new PveHttpClient(session); var response = client.PostAsync($"nodes/{node}/qemu/{vmid}/snapshot", formData) .GetAwaiter().GetResult(); return ParseTask(response, node); } /// /// Removes a snapshot from a VM. Returns the task UPID. /// /// The authenticated PVE session. /// The cluster node name. /// The VM ID. /// The snapshot name to remove. public PveTask RemoveSnapshot( PveSession session, string node, int vmid, string snapname) { if (session == null) throw new ArgumentNullException(nameof(session)); if (string.IsNullOrWhiteSpace(node)) throw new ArgumentNullException(nameof(node)); if (string.IsNullOrWhiteSpace(snapname)) throw new ArgumentNullException(nameof(snapname)); using var client = new PveHttpClient(session); var response = client.DeleteAsync($"nodes/{node}/qemu/{vmid}/snapshot/{snapname}") .GetAwaiter().GetResult(); return ParseTask(response, node); } /// /// Rolls a VM back to a snapshot. Returns the task UPID. /// /// The authenticated PVE session. /// The cluster node name. /// The VM ID. /// The snapshot name to roll back to. public PveTask RollbackSnapshot( PveSession session, string node, int vmid, string snapname) { if (session == null) throw new ArgumentNullException(nameof(session)); if (string.IsNullOrWhiteSpace(node)) throw new ArgumentNullException(nameof(node)); if (string.IsNullOrWhiteSpace(snapname)) throw new ArgumentNullException(nameof(snapname)); using var client = new PveHttpClient(session); var response = client.PostAsync($"nodes/{node}/qemu/{vmid}/snapshot/{snapname}/rollback") .GetAwaiter().GetResult(); return ParseTask(response, node); } // ------------------------------------------------------------------------- // Private helpers // ------------------------------------------------------------------------- private static PveTask ParseTask(string response, string node) { var data = JObject.Parse(response)["data"]; if (data?.Type == JTokenType.String) return new PveTask { Upid = data.ToString(), Node = node }; var task = data?.ToObject() ?? new PveTask(); task.Node = node; return task; } } }