feat(models): implement typed response models for PVE 8.x and 9.x

Add C# model classes for all PVE API resources: nodes, VMs, VM config,
containers, storage, network interfaces, SDN zones/vnets, users, roles,
permissions, snapshots, tasks, and cluster status. All models use dual
JSON attributes (System.Text.Json + Newtonsoft.Json), nullable optional
fields, and human-readable ToString() overrides.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Clint Branham
2026-03-17 15:45:06 -05:00
parent 2c7e90d185
commit 4ac38f7714
20 changed files with 2085 additions and 0 deletions
@@ -0,0 +1,69 @@
using System.Text.Json.Serialization;
using Newtonsoft.Json;
namespace PSProxmoxVE.Core.Models.Cluster;
/// <summary>
/// Represents a node entry within the cluster status response,
/// as returned by the /cluster/status endpoint when the item type is "node".
/// </summary>
public class PveClusterNode
{
/// <summary>
/// The node name (e.g., "pve", "pve2").
/// </summary>
[JsonPropertyName("name")]
[JsonProperty("name")]
public string Name { get; set; } = string.Empty;
/// <summary>
/// Indicates whether this node is currently online (1) or offline (0).
/// </summary>
[JsonPropertyName("online")]
[JsonProperty("online")]
public int? Online { get; set; }
/// <summary>
/// Indicates whether this is the local node making the request (1) or a remote node (0).
/// </summary>
[JsonPropertyName("local")]
[JsonProperty("local")]
public int? Local { get; set; }
/// <summary>
/// The numeric node ID within the cluster Corosync configuration.
/// </summary>
[JsonPropertyName("nodeid")]
[JsonProperty("nodeid")]
public int? NodeId { get; set; }
/// <summary>
/// The IP address used for cluster communication on this node.
/// </summary>
[JsonPropertyName("ip")]
[JsonProperty("ip")]
public string? Ip { get; set; }
/// <summary>
/// The cluster membership level of this node (e.g., "c" for configured member).
/// </summary>
[JsonPropertyName("level")]
[JsonProperty("level")]
public string? Level { get; set; }
/// <summary>
/// The item type as returned by the cluster status endpoint (always "node" for this model).
/// </summary>
[JsonPropertyName("type")]
[JsonProperty("type")]
public string? Type { get; set; }
/// <inheritdoc />
public override string ToString()
{
var onlineStr = Online is 1 ? "online" : "offline";
var localStr = Local is 1 ? " [local]" : string.Empty;
return $"Node: {Name}{localStr} | ID: {NodeId?.ToString() ?? "N/A"} | "
+ $"IP: {Ip ?? "N/A"} | {onlineStr}";
}
}
@@ -0,0 +1,95 @@
using System.Text.Json.Serialization;
using Newtonsoft.Json;
namespace PSProxmoxVE.Core.Models.Cluster;
/// <summary>
/// Represents an entry in the mixed array returned by the /cluster/status endpoint.
/// Each entry has a Type of either "cluster" (cluster-wide info) or "node" (per-node info).
/// Filter by <see cref="Type"/> to distinguish cluster vs. node entries.
/// </summary>
public class PveClusterStatus
{
/// <summary>
/// The entry type: "cluster" for the cluster-wide record, or "node" for a node record.
/// </summary>
[JsonPropertyName("type")]
[JsonProperty("type")]
public string? Type { get; set; }
/// <summary>
/// The name of the cluster (present when Type == "cluster") or the node name (when Type == "node").
/// </summary>
[JsonPropertyName("name")]
[JsonProperty("name")]
public string? Name { get; set; }
/// <summary>
/// The total number of nodes in the cluster. Only present when Type == "cluster".
/// </summary>
[JsonPropertyName("nodes")]
[JsonProperty("nodes")]
public int? Nodes { get; set; }
/// <summary>
/// Indicates whether the cluster currently has quorum (1) or not (0).
/// Without quorum, most write operations are blocked to maintain data consistency.
/// Only present when Type == "cluster".
/// </summary>
[JsonPropertyName("quorate")]
[JsonProperty("quorate")]
public int? Quorate { get; set; }
/// <summary>
/// The cluster configuration version number. Only present when Type == "cluster".
/// </summary>
[JsonPropertyName("version")]
[JsonProperty("version")]
public int? Version { get; set; }
/// <summary>
/// The IP address used for cluster communication on this node.
/// Only present when Type == "node".
/// </summary>
[JsonPropertyName("ip")]
[JsonProperty("ip")]
public string? Ip { get; set; }
/// <summary>
/// Indicates whether this node is currently online (1) or offline (0).
/// Only present when Type == "node".
/// </summary>
[JsonPropertyName("online")]
[JsonProperty("online")]
public int? Online { get; set; }
/// <summary>
/// Indicates whether this is the local node making the request (1) or a remote node (0).
/// Only present when Type == "node".
/// </summary>
[JsonPropertyName("local")]
[JsonProperty("local")]
public int? Local { get; set; }
/// <summary>
/// The numeric node ID within the cluster Corosync configuration.
/// Only present when Type == "node".
/// </summary>
[JsonPropertyName("nodeid")]
[JsonProperty("nodeid")]
public int? NodeId { get; set; }
/// <inheritdoc />
public override string ToString()
{
if (Type == "node")
{
var onlineStr = Online is 1 ? "online" : "offline";
var localStr = Local is 1 ? " [local]" : string.Empty;
return $"Node: {Name ?? "N/A"}{localStr} | IP: {Ip ?? "N/A"} | {onlineStr}";
}
var quorateStr = Quorate is 1 ? "quorate" : "NO QUORUM";
return $"Cluster: {Name ?? "N/A"} | Nodes: {Nodes?.ToString() ?? "N/A"} | "
+ $"{quorateStr} | Config Version: {Version?.ToString() ?? "N/A"}";
}
}
@@ -0,0 +1,114 @@
using System;
using System.Text.Json.Serialization;
using Newtonsoft.Json;
namespace PSProxmoxVE.Core.Models.Containers;
/// <summary>
/// Represents a Linux Container (LXC) as returned by the cluster or node container list endpoints.
/// </summary>
public class PveContainer
{
/// <summary>
/// The unique container identifier.
/// </summary>
[JsonPropertyName("vmid")]
[JsonProperty("vmid")]
public int VmId { get; set; }
/// <summary>
/// The hostname / display name of the container.
/// </summary>
[JsonPropertyName("name")]
[JsonProperty("name")]
public string? Name { get; set; }
/// <summary>
/// The current runtime status of the container (e.g., "running", "stopped").
/// </summary>
[JsonPropertyName("status")]
[JsonProperty("status")]
public string? Status { get; set; }
/// <summary>
/// The node on which the container resides.
/// </summary>
[JsonPropertyName("node")]
[JsonProperty("node")]
public string? Node { get; set; }
/// <summary>
/// The number of CPU cores assigned to the container.
/// </summary>
[JsonPropertyName("cpus")]
[JsonProperty("cpus")]
public int? CpuCount { get; set; }
/// <summary>
/// Maximum memory allocated to the container, in bytes.
/// </summary>
[JsonPropertyName("maxmem")]
[JsonProperty("maxmem")]
public long? MaxMem { get; set; }
/// <summary>
/// Maximum swap space allocated to the container, in bytes.
/// </summary>
[JsonPropertyName("maxswap")]
[JsonProperty("maxswap")]
public long? MaxSwap { get; set; }
/// <summary>
/// Root filesystem disk size allocated to the container, in bytes.
/// </summary>
[JsonPropertyName("maxdisk")]
[JsonProperty("maxdisk")]
public long? RootFsSize { get; set; }
/// <summary>
/// The OS template type used for this container (e.g., "debian", "ubuntu").
/// </summary>
[JsonPropertyName("ostype")]
[JsonProperty("ostype")]
public string? OsType { get; set; }
/// <summary>
/// Indicates whether the container runs in unprivileged mode (1) or privileged mode (0).
/// </summary>
[JsonPropertyName("unprivileged")]
[JsonProperty("unprivileged")]
public int? Unprivileged { get; set; }
/// <summary>
/// The current lock type applied to the container, if any (e.g., "migrate", "backup").
/// </summary>
[JsonPropertyName("lock")]
[JsonProperty("lock")]
public string? Lock { get; set; }
/// <summary>
/// Container uptime in seconds.
/// </summary>
[JsonPropertyName("uptime")]
[JsonProperty("uptime")]
public long? Uptime { get; set; }
/// <summary>
/// Semicolon-separated list of tags assigned to the container.
/// </summary>
[JsonPropertyName("tags")]
[JsonProperty("tags")]
public string? Tags { get; set; }
/// <inheritdoc />
public override string ToString()
{
var maxMemMb = MaxMem.HasValue ? $"{MaxMem.Value / 1024 / 1024} MB" : "N/A";
var rootFsGb = RootFsSize.HasValue ? $"{RootFsSize.Value / 1024 / 1024 / 1024} GB" : "N/A";
var uptimeStr = Uptime.HasValue ? TimeSpan.FromSeconds(Uptime.Value).ToString(@"d\.hh\:mm\:ss") : "N/A";
var privStr = Unprivileged is 1 ? "unprivileged" : "privileged";
return $"CT {VmId}: {Name ?? "(unnamed)"} | Node: {Node ?? "N/A"} | "
+ $"Status: {Status ?? "N/A"} | CPUs: {CpuCount?.ToString() ?? "N/A"} | "
+ $"Mem: {maxMemMb} | Disk: {rootFsGb} | {privStr} | Uptime: {uptimeStr}";
}
}
@@ -0,0 +1,236 @@
using System.Text.Json.Serialization;
using Newtonsoft.Json;
namespace PSProxmoxVE.Core.Models.Containers;
/// <summary>
/// Represents the configuration of a Linux Container (LXC),
/// as returned by the /nodes/{node}/lxc/{vmid}/config endpoint.
/// </summary>
public class PveContainerConfig
{
// -------------------------------------------------------------------------
// Identity / OS
// -------------------------------------------------------------------------
/// <summary>
/// The container hostname.
/// </summary>
[JsonPropertyName("hostname")]
[JsonProperty("hostname")]
public string? Hostname { get; set; }
/// <summary>
/// The OS type used for the container (e.g., "debian", "ubuntu", "alpine").
/// </summary>
[JsonPropertyName("ostype")]
[JsonProperty("ostype")]
public string? OsType { get; set; }
/// <summary>
/// Container architecture (e.g., "amd64", "arm64").
/// </summary>
[JsonPropertyName("arch")]
[JsonProperty("arch")]
public string? Arch { get; set; }
/// <summary>
/// Indicates whether the container runs unprivileged (1) or privileged (0).
/// </summary>
[JsonPropertyName("unprivileged")]
[JsonProperty("unprivileged")]
public int? Unprivileged { get; set; }
// -------------------------------------------------------------------------
// Resources
// -------------------------------------------------------------------------
/// <summary>
/// Memory limit in megabytes.
/// </summary>
[JsonPropertyName("memory")]
[JsonProperty("memory")]
public int? Memory { get; set; }
/// <summary>
/// Swap space limit in megabytes. 0 disables swap.
/// </summary>
[JsonPropertyName("swap")]
[JsonProperty("swap")]
public int? Swap { get; set; }
/// <summary>
/// Number of CPU cores allocated to the container.
/// </summary>
[JsonPropertyName("cores")]
[JsonProperty("cores")]
public int? Cores { get; set; }
/// <summary>
/// Root filesystem configuration string (e.g., "local-lvm:8").
/// </summary>
[JsonPropertyName("rootfs")]
[JsonProperty("rootfs")]
public string? RootFs { get; set; }
// -------------------------------------------------------------------------
// Metadata
// -------------------------------------------------------------------------
/// <summary>
/// Human-readable description or notes for the container.
/// </summary>
[JsonPropertyName("description")]
[JsonProperty("description")]
public string? Description { get; set; }
/// <summary>
/// Semicolon-separated list of tags assigned to the container.
/// </summary>
[JsonPropertyName("tags")]
[JsonProperty("tags")]
public string? Tags { get; set; }
/// <summary>
/// When set to 1, prevents the container from being deleted or modified accidentally.
/// </summary>
[JsonPropertyName("protection")]
[JsonProperty("protection")]
public int? Protection { get; set; }
/// <summary>
/// Startup / shutdown order and delay configuration string.
/// </summary>
[JsonPropertyName("startup")]
[JsonProperty("startup")]
public string? Startup { get; set; }
/// <summary>
/// Feature flags for the container (e.g., "nesting=1,keyctl=1").
/// </summary>
[JsonPropertyName("features")]
[JsonProperty("features")]
public string? Features { get; set; }
// -------------------------------------------------------------------------
// Network interfaces (0-7)
// -------------------------------------------------------------------------
/// <summary>Network interface 0 configuration string.</summary>
[JsonPropertyName("net0")]
[JsonProperty("net0")]
public string? Net0 { get; set; }
/// <summary>Network interface 1 configuration string.</summary>
[JsonPropertyName("net1")]
[JsonProperty("net1")]
public string? Net1 { get; set; }
/// <summary>Network interface 2 configuration string.</summary>
[JsonPropertyName("net2")]
[JsonProperty("net2")]
public string? Net2 { get; set; }
/// <summary>Network interface 3 configuration string.</summary>
[JsonPropertyName("net3")]
[JsonProperty("net3")]
public string? Net3 { get; set; }
/// <summary>Network interface 4 configuration string.</summary>
[JsonPropertyName("net4")]
[JsonProperty("net4")]
public string? Net4 { get; set; }
/// <summary>Network interface 5 configuration string.</summary>
[JsonPropertyName("net5")]
[JsonProperty("net5")]
public string? Net5 { get; set; }
/// <summary>Network interface 6 configuration string.</summary>
[JsonPropertyName("net6")]
[JsonProperty("net6")]
public string? Net6 { get; set; }
/// <summary>Network interface 7 configuration string.</summary>
[JsonPropertyName("net7")]
[JsonProperty("net7")]
public string? Net7 { get; set; }
// -------------------------------------------------------------------------
// Mount points (0-7)
// -------------------------------------------------------------------------
/// <summary>Mount point 0 configuration string (e.g., "local-lvm:10,mp=/data").</summary>
[JsonPropertyName("mp0")]
[JsonProperty("mp0")]
public string? Mp0 { get; set; }
/// <summary>Mount point 1 configuration string.</summary>
[JsonPropertyName("mp1")]
[JsonProperty("mp1")]
public string? Mp1 { get; set; }
/// <summary>Mount point 2 configuration string.</summary>
[JsonPropertyName("mp2")]
[JsonProperty("mp2")]
public string? Mp2 { get; set; }
/// <summary>Mount point 3 configuration string.</summary>
[JsonPropertyName("mp3")]
[JsonProperty("mp3")]
public string? Mp3 { get; set; }
/// <summary>Mount point 4 configuration string.</summary>
[JsonPropertyName("mp4")]
[JsonProperty("mp4")]
public string? Mp4 { get; set; }
/// <summary>Mount point 5 configuration string.</summary>
[JsonPropertyName("mp5")]
[JsonProperty("mp5")]
public string? Mp5 { get; set; }
/// <summary>Mount point 6 configuration string.</summary>
[JsonPropertyName("mp6")]
[JsonProperty("mp6")]
public string? Mp6 { get; set; }
/// <summary>Mount point 7 configuration string.</summary>
[JsonPropertyName("mp7")]
[JsonProperty("mp7")]
public string? Mp7 { get; set; }
// -------------------------------------------------------------------------
// DNS / SSH
// -------------------------------------------------------------------------
/// <summary>
/// DNS nameserver(s) for the container.
/// </summary>
[JsonPropertyName("nameserver")]
[JsonProperty("nameserver")]
public string? Nameserver { get; set; }
/// <summary>
/// DNS search domain for the container.
/// </summary>
[JsonPropertyName("searchdomain")]
[JsonProperty("searchdomain")]
public string? Searchdomain { get; set; }
/// <summary>
/// SSH public keys injected into the container's root account.
/// </summary>
[JsonPropertyName("ssh-public-keys")]
[JsonProperty("ssh-public-keys")]
public string? SshPublicKeys { get; set; }
/// <inheritdoc />
public override string ToString()
{
return $"Config | Hostname: {Hostname ?? "N/A"} | OS: {OsType ?? "N/A"} | "
+ $"Cores: {Cores?.ToString() ?? "N/A"} | Memory: {Memory?.ToString() ?? "N/A"} MB | "
+ $"Swap: {Swap?.ToString() ?? "N/A"} MB | Arch: {Arch ?? "N/A"} | "
+ $"Unprivileged: {(Unprivileged is 1 ? "yes" : "no")}";
}
}
@@ -0,0 +1,130 @@
using System.Text.Json.Serialization;
using Newtonsoft.Json;
namespace PSProxmoxVE.Core.Models.Network;
/// <summary>
/// Represents a network interface configuration on a Proxmox VE node,
/// as returned by the /nodes/{node}/network endpoint.
/// </summary>
public class PveNetwork
{
/// <summary>
/// The interface name (e.g., "vmbr0", "eth0", "bond0").
/// </summary>
[JsonPropertyName("iface")]
[JsonProperty("iface")]
public string Iface { get; set; } = string.Empty;
/// <summary>
/// The interface type (e.g., "bridge", "bond", "eth", "vlan", "OVSBridge").
/// </summary>
[JsonPropertyName("type")]
[JsonProperty("type")]
public string? Type { get; set; }
/// <summary>
/// The IPv4 address assigned to this interface.
/// </summary>
[JsonPropertyName("address")]
[JsonProperty("address")]
public string? Address { get; set; }
/// <summary>
/// The IPv4 netmask for this interface.
/// </summary>
[JsonPropertyName("netmask")]
[JsonProperty("netmask")]
public string? Netmask { get; set; }
/// <summary>
/// The IPv4 default gateway associated with this interface.
/// </summary>
[JsonPropertyName("gateway")]
[JsonProperty("gateway")]
public string? Gateway { get; set; }
/// <summary>
/// Space-separated list of physical ports attached to this bridge.
/// </summary>
[JsonPropertyName("bridge_ports")]
[JsonProperty("bridge_ports")]
public string? BridgePorts { get; set; }
/// <summary>
/// Space-separated list of slave interfaces for bond/team interfaces.
/// </summary>
[JsonPropertyName("slaves")]
[JsonProperty("slaves")]
public string? BondSlaves { get; set; }
/// <summary>
/// VLAN ID for VLAN sub-interfaces.
/// </summary>
[JsonPropertyName("vlan-id")]
[JsonProperty("vlan-id")]
public int? VlanId { get; set; }
/// <summary>
/// The Maximum Transmission Unit in bytes.
/// </summary>
[JsonPropertyName("mtu")]
[JsonProperty("mtu")]
public int? Mtu { get; set; }
/// <summary>
/// Indicates whether the interface is brought up automatically at boot (1) or not (0).
/// </summary>
[JsonPropertyName("autostart")]
[JsonProperty("autostart")]
public int? Autostart { get; set; }
/// <summary>
/// Indicates whether the interface is currently active/up (1) or down (0).
/// </summary>
[JsonPropertyName("active")]
[JsonProperty("active")]
public int? Active { get; set; }
/// <summary>
/// Optional comment or description for this interface.
/// </summary>
[JsonPropertyName("comments")]
[JsonProperty("comments")]
public string? Comments { get; set; }
/// <summary>
/// IPv4 address in CIDR notation (e.g., "192.168.1.1/24").
/// </summary>
[JsonPropertyName("cidr")]
[JsonProperty("cidr")]
public string? Cidr { get; set; }
/// <summary>
/// Indicates whether VLAN-aware bridging is enabled on this bridge (1) or not (0).
/// </summary>
[JsonPropertyName("bridge_vlan_aware")]
[JsonProperty("bridge_vlan_aware")]
public int? BridgeVlanAware { get; set; }
/// <summary>
/// The bonding mode for bond interfaces (e.g., "active-backup", "802.3ad", "balance-rr").
/// </summary>
[JsonPropertyName("bond_mode")]
[JsonProperty("bond_mode")]
public string? BondMode { get; set; }
/// <summary>
/// The node this network interface belongs to. Populated by cmdlets after retrieval.
/// </summary>
public string? Node { get; set; }
/// <inheritdoc />
public override string ToString()
{
var addr = Cidr ?? Address ?? "N/A";
var activeStr = Active is 1 ? "up" : "down";
return $"Network: {Iface} | Type: {Type ?? "N/A"} | Address: {addr} | "
+ $"GW: {Gateway ?? "N/A"} | MTU: {Mtu?.ToString() ?? "N/A"} | {activeStr}";
}
}
@@ -0,0 +1,62 @@
using System.Text.Json.Serialization;
using Newtonsoft.Json;
namespace PSProxmoxVE.Core.Models.Network;
/// <summary>
/// Represents a Software-Defined Networking (SDN) virtual network (VNet) as returned by
/// the /cluster/sdn/vnets endpoint.
/// Available in Proxmox VE 8.0+.
/// </summary>
public class PveSdnVnet
{
/// <summary>
/// The unique VNet identifier.
/// </summary>
[JsonPropertyName("vnet")]
[JsonProperty("vnet")]
public string Vnet { get; set; } = string.Empty;
/// <summary>
/// The SDN zone this VNet belongs to.
/// </summary>
[JsonPropertyName("zone")]
[JsonProperty("zone")]
public string? Zone { get; set; }
/// <summary>
/// The VLAN or VXLAN tag associated with this VNet.
/// </summary>
[JsonPropertyName("tag")]
[JsonProperty("tag")]
public int? Tag { get; set; }
/// <summary>
/// An optional human-readable alias for this VNet.
/// </summary>
[JsonPropertyName("alias")]
[JsonProperty("alias")]
public string? Alias { get; set; }
/// <summary>
/// Indicates whether VLAN-aware mode is enabled on this VNet (1) or not (0).
/// </summary>
[JsonPropertyName("vlanaware")]
[JsonProperty("vlanaware")]
public int? VlanAware { get; set; }
/// <summary>
/// Optional comment or description for this SDN VNet.
/// </summary>
[JsonPropertyName("comments")]
[JsonProperty("comments")]
public string? Comments { get; set; }
/// <inheritdoc />
public override string ToString()
{
var vlanAwareStr = VlanAware is 1 ? " [vlan-aware]" : string.Empty;
return $"SDN VNet: {Vnet}{vlanAwareStr} | Zone: {Zone ?? "N/A"} | "
+ $"Tag: {Tag?.ToString() ?? "N/A"} | Alias: {Alias ?? "N/A"}";
}
}
@@ -0,0 +1,90 @@
using System.Text.Json.Serialization;
using Newtonsoft.Json;
namespace PSProxmoxVE.Core.Models.Network;
/// <summary>
/// Represents a Software-Defined Networking (SDN) zone as returned by
/// the /cluster/sdn/zones endpoint.
/// Available in Proxmox VE 8.0+.
/// </summary>
public class PveSdnZone
{
/// <summary>
/// The unique zone identifier.
/// </summary>
[JsonPropertyName("zone")]
[JsonProperty("zone")]
public string Zone { get; set; } = string.Empty;
/// <summary>
/// The zone type (e.g., "simple", "vlan", "qinq", "vxlan", "evpn").
/// </summary>
[JsonPropertyName("type")]
[JsonProperty("type")]
public string? Type { get; set; }
/// <summary>
/// The DNS server address associated with this zone.
/// </summary>
[JsonPropertyName("dns")]
[JsonProperty("dns")]
public string? Dns { get; set; }
/// <summary>
/// The reverse DNS zone name for this SDN zone.
/// </summary>
[JsonPropertyName("reversedns")]
[JsonProperty("reversedns")]
public string? Reversedns { get; set; }
/// <summary>
/// The DNS zone name used for forward lookups.
/// </summary>
[JsonPropertyName("dnszone")]
[JsonProperty("dnszone")]
public string? DnsZone { get; set; }
/// <summary>
/// The IPAM plugin to use for IP address management in this zone.
/// </summary>
[JsonPropertyName("ipam")]
[JsonProperty("ipam")]
public string? Ipam { get; set; }
/// <summary>
/// The Maximum Transmission Unit in bytes for this zone.
/// </summary>
[JsonPropertyName("mtu")]
[JsonProperty("mtu")]
public int? Mtu { get; set; }
/// <summary>
/// Comma-separated list of nodes that participate in this SDN zone.
/// </summary>
[JsonPropertyName("nodes")]
[JsonProperty("nodes")]
public string? Nodes { get; set; }
/// <summary>
/// Indicates whether the zone has pending (unapplied) configuration changes (1) or not (0).
/// </summary>
[JsonPropertyName("pending")]
[JsonProperty("pending")]
public int? Pending { get; set; }
/// <summary>
/// Optional comment or description for this SDN zone.
/// </summary>
[JsonPropertyName("comments")]
[JsonProperty("comments")]
public string? Comments { get; set; }
/// <inheritdoc />
public override string ToString()
{
var pendingStr = Pending is 1 ? " [pending]" : string.Empty;
return $"SDN Zone: {Zone}{pendingStr} | Type: {Type ?? "N/A"} | DNS: {Dns ?? "N/A"} | "
+ $"IPAM: {Ipam ?? "N/A"} | Nodes: {Nodes ?? "all"}";
}
}
@@ -0,0 +1,84 @@
using System;
using System.Text.Json.Serialization;
using Newtonsoft.Json;
namespace PSProxmoxVE.Core.Models.Nodes;
/// <summary>
/// Represents a Proxmox VE cluster node as returned by the /nodes endpoint.
/// </summary>
public class PveNode
{
/// <summary>
/// The name of the node.
/// </summary>
[JsonPropertyName("node")]
[JsonProperty("node")]
public string Name { get; set; } = string.Empty;
/// <summary>
/// The current status of the node (e.g., "online" or "offline").
/// </summary>
[JsonPropertyName("status")]
[JsonProperty("status")]
public string Status { get; set; } = string.Empty;
/// <summary>
/// The total number of CPU cores available on the node.
/// </summary>
[JsonPropertyName("maxcpu")]
[JsonProperty("maxcpu")]
public int? CpuCount { get; set; }
/// <summary>
/// Total memory in bytes available on the node.
/// </summary>
[JsonPropertyName("maxmem")]
[JsonProperty("maxmem")]
public long? MemoryTotal { get; set; }
/// <summary>
/// Memory currently in use on the node, in bytes.
/// </summary>
[JsonPropertyName("mem")]
[JsonProperty("mem")]
public long? MemoryUsed { get; set; }
/// <summary>
/// Node uptime in seconds.
/// </summary>
[JsonPropertyName("uptime")]
[JsonProperty("uptime")]
public long? Uptime { get; set; }
/// <summary>
/// The version of Proxmox VE running on the node.
/// </summary>
[JsonPropertyName("pveversion")]
[JsonProperty("pveversion")]
public string? PveVersion { get; set; }
/// <summary>
/// The Linux kernel version running on the node.
/// </summary>
[JsonPropertyName("kversion")]
[JsonProperty("kversion")]
public string? KernelVersion { get; set; }
/// <summary>
/// The 1-minute, 5-minute, and 15-minute load averages for the node.
/// </summary>
[JsonPropertyName("loadavg")]
[JsonProperty("loadavg")]
public double[]? LoadAverage { get; set; }
/// <inheritdoc />
public override string ToString()
{
var memUsedMb = MemoryUsed.HasValue ? $"{MemoryUsed.Value / 1024 / 1024} MB" : "N/A";
var memTotalMb = MemoryTotal.HasValue ? $"{MemoryTotal.Value / 1024 / 1024} MB" : "N/A";
var uptimeStr = Uptime.HasValue ? TimeSpan.FromSeconds(Uptime.Value).ToString(@"d\.hh\:mm\:ss") : "N/A";
return $"Node: {Name} | Status: {Status} | CPUs: {CpuCount?.ToString() ?? "N/A"} | "
+ $"Mem: {memUsedMb}/{memTotalMb} | Uptime: {uptimeStr} | PVE: {PveVersion ?? "N/A"}";
}
}
@@ -0,0 +1,148 @@
using System;
using System.Text.Json.Serialization;
using Newtonsoft.Json;
namespace PSProxmoxVE.Core.Models.Nodes;
/// <summary>
/// Represents detailed status information for a single Proxmox VE node,
/// as returned by the /nodes/{node}/status endpoint.
/// </summary>
public class PveNodeStatus
{
/// <summary>
/// The name of the node.
/// </summary>
[JsonPropertyName("node")]
[JsonProperty("node")]
public string Name { get; set; } = string.Empty;
/// <summary>
/// The current status of the node (e.g., "online" or "offline").
/// </summary>
[JsonPropertyName("status")]
[JsonProperty("status")]
public string Status { get; set; } = string.Empty;
/// <summary>
/// The total number of CPU cores available on the node.
/// </summary>
[JsonPropertyName("maxcpu")]
[JsonProperty("maxcpu")]
public int? CpuCount { get; set; }
/// <summary>
/// Total memory in bytes available on the node.
/// </summary>
[JsonPropertyName("maxmem")]
[JsonProperty("maxmem")]
public long? MemoryTotal { get; set; }
/// <summary>
/// Memory currently in use on the node, in bytes.
/// </summary>
[JsonPropertyName("mem")]
[JsonProperty("mem")]
public long? MemoryUsed { get; set; }
/// <summary>
/// Node uptime in seconds.
/// </summary>
[JsonPropertyName("uptime")]
[JsonProperty("uptime")]
public long? Uptime { get; set; }
/// <summary>
/// The version of Proxmox VE running on the node.
/// </summary>
[JsonPropertyName("pveversion")]
[JsonProperty("pveversion")]
public string? PveVersion { get; set; }
/// <summary>
/// The Linux kernel version running on the node.
/// </summary>
[JsonPropertyName("kversion")]
[JsonProperty("kversion")]
public string? KernelVersion { get; set; }
/// <summary>
/// The 1-minute, 5-minute, and 15-minute load averages for the node.
/// </summary>
[JsonPropertyName("loadavg")]
[JsonProperty("loadavg")]
public double[]? LoadAverage { get; set; }
/// <summary>
/// Total root filesystem size in bytes.
/// </summary>
[JsonPropertyName("rootfs.total")]
[JsonProperty("rootfs.total")]
public long? DiskTotal { get; set; }
/// <summary>
/// Used root filesystem space in bytes.
/// </summary>
[JsonPropertyName("rootfs.used")]
[JsonProperty("rootfs.used")]
public long? DiskUsed { get; set; }
/// <summary>
/// Current CPU utilization as a fraction between 0.0 and 1.0.
/// </summary>
[JsonPropertyName("cpu")]
[JsonProperty("cpu")]
public double? CpuUsage { get; set; }
/// <summary>
/// Memory utilization as a percentage (0100), computed from MemoryUsed / MemoryTotal.
/// </summary>
[System.Text.Json.Serialization.JsonIgnore]
[Newtonsoft.Json.JsonIgnore]
public double? MemoryUsage =>
MemoryTotal.HasValue && MemoryTotal.Value > 0 && MemoryUsed.HasValue
? (double)MemoryUsed.Value / MemoryTotal.Value * 100.0
: null;
/// <summary>
/// Total swap space in bytes.
/// </summary>
[JsonPropertyName("swap.total")]
[JsonProperty("swap.total")]
public long? SwapTotal { get; set; }
/// <summary>
/// Used swap space in bytes.
/// </summary>
[JsonPropertyName("swap.used")]
[JsonProperty("swap.used")]
public long? SwapUsed { get; set; }
/// <summary>
/// Total bytes received over the network since boot.
/// </summary>
[JsonPropertyName("netin")]
[JsonProperty("netin")]
public long? NetworkIn { get; set; }
/// <summary>
/// Total bytes transmitted over the network since boot.
/// </summary>
[JsonPropertyName("netout")]
[JsonProperty("netout")]
public long? NetworkOut { get; set; }
/// <inheritdoc />
public override string ToString()
{
var memUsedMb = MemoryUsed.HasValue ? $"{MemoryUsed.Value / 1024 / 1024} MB" : "N/A";
var memTotalMb = MemoryTotal.HasValue ? $"{MemoryTotal.Value / 1024 / 1024} MB" : "N/A";
var cpuPct = CpuUsage.HasValue ? $"{CpuUsage.Value * 100:F1}%" : "N/A";
var diskUsedGb = DiskUsed.HasValue ? $"{DiskUsed.Value / 1024 / 1024 / 1024} GB" : "N/A";
var diskTotalGb = DiskTotal.HasValue ? $"{DiskTotal.Value / 1024 / 1024 / 1024} GB" : "N/A";
var uptimeStr = Uptime.HasValue ? TimeSpan.FromSeconds(Uptime.Value).ToString(@"d\.hh\:mm\:ss") : "N/A";
return $"Node: {Name} | Status: {Status} | CPU: {cpuPct} | "
+ $"Mem: {memUsedMb}/{memTotalMb} ({MemoryUsage:F1}%) | "
+ $"Disk: {diskUsedGb}/{diskTotalGb} | Uptime: {uptimeStr}";
}
}
@@ -0,0 +1,89 @@
using System.Text.Json.Serialization;
using Newtonsoft.Json;
namespace PSProxmoxVE.Core.Models.Storage;
/// <summary>
/// Represents a Proxmox VE storage entry as returned by the /storage or /nodes/{node}/storage endpoints.
/// </summary>
public class PveStorage
{
/// <summary>
/// The unique identifier / name of the storage pool.
/// </summary>
[JsonPropertyName("storage")]
[JsonProperty("storage")]
public string Storage { get; set; } = string.Empty;
/// <summary>
/// The storage backend type (e.g., "dir", "lvm", "zfspool", "nfs", "cifs", "rbd").
/// </summary>
[JsonPropertyName("type")]
[JsonProperty("type")]
public string? Type { get; set; }
/// <summary>
/// Comma-separated list of content types this storage supports
/// (e.g., "images,rootdir,backup,snippets,iso,vztmpl").
/// </summary>
[JsonPropertyName("content")]
[JsonProperty("content")]
public string? Content { get; set; }
/// <summary>
/// Total capacity of the storage pool, in bytes.
/// </summary>
[JsonPropertyName("total")]
[JsonProperty("total")]
public long? Total { get; set; }
/// <summary>
/// Used capacity of the storage pool, in bytes.
/// </summary>
[JsonPropertyName("used")]
[JsonProperty("used")]
public long? Used { get; set; }
/// <summary>
/// Available (free) capacity of the storage pool, in bytes.
/// </summary>
[JsonPropertyName("avail")]
[JsonProperty("avail")]
public long? Available { get; set; }
/// <summary>
/// Indicates whether the storage is enabled (1) or disabled (0) in the configuration.
/// </summary>
[JsonPropertyName("enabled")]
[JsonProperty("enabled")]
public int? Enabled { get; set; }
/// <summary>
/// Indicates whether the storage is shared across all cluster nodes (1) or node-local (0).
/// </summary>
[JsonPropertyName("shared")]
[JsonProperty("shared")]
public int? Shared { get; set; }
/// <summary>
/// Indicates whether the storage is currently active and reachable (1) or not (0).
/// </summary>
[JsonPropertyName("active")]
[JsonProperty("active")]
public int? Active { get; set; }
/// <summary>The node this storage entry is associated with. Populated by cmdlets after retrieval.</summary>
public string? Node { get; set; }
/// <inheritdoc />
public override string ToString()
{
var usedGb = Used.HasValue ? $"{Used.Value / 1024.0 / 1024 / 1024:F1} GB" : "N/A";
var totalGb = Total.HasValue ? $"{Total.Value / 1024.0 / 1024 / 1024:F1} GB" : "N/A";
var availGb = Available.HasValue ? $"{Available.Value / 1024.0 / 1024 / 1024:F1} GB" : "N/A";
var activeStr = Active is 1 ? "active" : "inactive";
var sharedStr = Shared is 1 ? "shared" : "local";
return $"Storage: {Storage} | Type: {Type ?? "N/A"} | {activeStr} | {sharedStr} | "
+ $"Used: {usedGb}/{totalGb} | Free: {availGb} | Content: {Content ?? "N/A"}";
}
}
@@ -0,0 +1,87 @@
using System;
using System.Text.Json.Serialization;
using Newtonsoft.Json;
namespace PSProxmoxVE.Core.Models.Storage;
/// <summary>
/// Represents a single content item (disk image, backup, ISO, template, etc.)
/// stored within a Proxmox VE storage pool, as returned by
/// the /nodes/{node}/storage/{storage}/content endpoint.
/// </summary>
public class PveStorageContent
{
/// <summary>
/// The full volume identifier in the form "storage:volumename"
/// (e.g., "local-lvm:vm-100-disk-0").
/// </summary>
[JsonPropertyName("volid")]
[JsonProperty("volid")]
public string VolId { get; set; } = string.Empty;
/// <summary>
/// The content type of this volume (e.g., "images", "backup", "iso", "vztmpl", "rootdir").
/// </summary>
[JsonPropertyName("content")]
[JsonProperty("content")]
public string? Content { get; set; }
/// <summary>
/// The disk image format (e.g., "raw", "qcow2", "vmdk", "subvol").
/// </summary>
[JsonPropertyName("format")]
[JsonProperty("format")]
public string? Format { get; set; }
/// <summary>
/// The size of the volume in bytes.
/// </summary>
[JsonPropertyName("size")]
[JsonProperty("size")]
public long? Size { get; set; }
/// <summary>
/// Unix timestamp of when the volume was created.
/// </summary>
[JsonPropertyName("ctime")]
[JsonProperty("ctime")]
public long? CreationTime { get; set; }
/// <summary>
/// The VM or container ID that owns this volume, if applicable (e.g., for disk images and backups).
/// </summary>
[JsonPropertyName("vmid")]
[JsonProperty("vmid")]
public int? VmId { get; set; }
/// <summary>
/// Optional notes or description attached to the volume (commonly used on backup files).
/// </summary>
[JsonPropertyName("notes")]
[JsonProperty("notes")]
public string? Notes { get; set; }
/// <summary>
/// Verification status of the volume, if applicable (e.g., for backups verified with vzdump).
/// </summary>
[JsonPropertyName("verification")]
[JsonProperty("verification")]
public string? Verification { get; set; }
/// <summary>The storage identifier this content resides on. Populated by cmdlets after retrieval.</summary>
public string? Storage { get; set; }
/// <summary>The node this content was retrieved from. Populated by cmdlets after retrieval.</summary>
public string? Node { get; set; }
/// <inheritdoc />
public override string ToString()
{
var sizeGb = Size.HasValue ? $"{Size.Value / 1024.0 / 1024 / 1024:F2} GB" : "N/A";
var created = CreationTime.HasValue
? DateTimeOffset.FromUnixTimeSeconds(CreationTime.Value).ToString("yyyy-MM-dd HH:mm:ss")
: "N/A";
return $"Volume: {VolId} | Content: {Content ?? "N/A"} | Format: {Format ?? "N/A"} | "
+ $"Size: {sizeGb} | Created: {created}";
}
}
@@ -0,0 +1,47 @@
using System.Text.Json.Serialization;
using Newtonsoft.Json;
namespace PSProxmoxVE.Core.Models.Users;
/// <summary>
/// Represents a single Access Control List (ACL) entry as returned by the /access/acl endpoint.
/// An ACL entry grants a specific role to a user or group on a particular resource path.
/// </summary>
public class PvePermission
{
/// <summary>
/// The resource path this ACL entry applies to (e.g., "/", "/nodes/pve", "/vms/100").
/// </summary>
[JsonPropertyName("path")]
[JsonProperty("path")]
public string? Path { get; set; }
/// <summary>
/// The role ID granted by this ACL entry (e.g., "Administrator", "PVEVMAdmin").
/// </summary>
[JsonPropertyName("roleid")]
[JsonProperty("roleid")]
public string? RoleId { get; set; }
/// <summary>
/// Indicates whether the permission propagates to sub-paths (1) or is restricted to the exact path (0).
/// </summary>
[JsonPropertyName("propagate")]
[JsonProperty("propagate")]
public int? Propagate { get; set; }
/// <summary>
/// The user ID or group ID (with "@" prefix for groups) that this ACL entry applies to.
/// Maps to the "ugid" JSON field.
/// </summary>
[JsonPropertyName("ugid")]
[JsonProperty("ugid")]
public string? UserId { get; set; }
/// <inheritdoc />
public override string ToString()
{
var propagateStr = Propagate is 1 ? " [propagate]" : string.Empty;
return $"ACL: {UserId ?? "N/A"} | Path: {Path ?? "/"} | Role: {RoleId ?? "N/A"}{propagateStr}";
}
}
@@ -0,0 +1,41 @@
using System.Text.Json.Serialization;
using Newtonsoft.Json;
namespace PSProxmoxVE.Core.Models.Users;
/// <summary>
/// Represents a Proxmox VE role as returned by the /access/roles endpoint.
/// Roles are named collections of privileges that can be assigned to users or groups via ACLs.
/// </summary>
public class PveRole
{
/// <summary>
/// The unique role identifier (e.g., "Administrator", "PVEVMAdmin", "PVEDatastoreUser").
/// </summary>
[JsonPropertyName("roleid")]
[JsonProperty("roleid")]
public string RoleId { get; set; } = string.Empty;
/// <summary>
/// Comma-separated list of privilege strings assigned to this role
/// (e.g., "VM.Allocate,VM.Config.Disk,Datastore.AllocateSpace").
/// </summary>
[JsonPropertyName("privs")]
[JsonProperty("privs")]
public string? Privileges { get; set; }
/// <summary>
/// Indicates whether this is a built-in / special role (1) that cannot be deleted,
/// or a custom role (0).
/// </summary>
[JsonPropertyName("special")]
[JsonProperty("special")]
public int? Special { get; set; }
/// <inheritdoc />
public override string ToString()
{
var specialStr = Special is 1 ? " [built-in]" : string.Empty;
return $"Role: {RoleId}{specialStr} | Privs: {Privileges ?? "none"}";
}
}
@@ -0,0 +1,85 @@
using System;
using System.Linq;
using System.Text.Json.Serialization;
using Newtonsoft.Json;
namespace PSProxmoxVE.Core.Models.Users;
/// <summary>
/// Represents a Proxmox VE user account as returned by the /access/users endpoint.
/// </summary>
public class PveUser
{
/// <summary>
/// The full user ID in the format "username@realm" (e.g., "admin@pam", "user@pve").
/// </summary>
[JsonPropertyName("userid")]
[JsonProperty("userid")]
public string UserId { get; set; } = string.Empty;
/// <summary>
/// The user's first name.
/// </summary>
[JsonPropertyName("firstname")]
[JsonProperty("firstname")]
public string? FirstName { get; set; }
/// <summary>
/// The user's last name.
/// </summary>
[JsonPropertyName("lastname")]
[JsonProperty("lastname")]
public string? LastName { get; set; }
/// <summary>
/// The user's email address.
/// </summary>
[JsonPropertyName("email")]
[JsonProperty("email")]
public string? Email { get; set; }
/// <summary>
/// Indicates whether the user account is enabled (1) or disabled (0).
/// </summary>
[JsonPropertyName("enable")]
[JsonProperty("enable")]
public int? Enabled { get; set; }
/// <summary>
/// The authentication realm for this user (e.g., "pam", "pve", "ldap").
/// </summary>
[JsonPropertyName("realm")]
[JsonProperty("realm")]
public string? Realm { get; set; }
/// <summary>
/// Comma-separated list of groups this user is a member of.
/// </summary>
[JsonPropertyName("groups")]
[JsonProperty("groups")]
public string? Groups { get; set; }
/// <summary>
/// Optional comment or description for this user account.
/// </summary>
[JsonPropertyName("comment")]
[JsonProperty("comment")]
public string? Comment { get; set; }
/// <summary>
/// Account expiry as a Unix timestamp. 0 or null means the account never expires.
/// </summary>
[JsonPropertyName("expire")]
[JsonProperty("expire")]
public long? Expire { get; set; }
/// <inheritdoc />
public override string ToString()
{
var fullName = string.Join(" ", new[] { FirstName, LastName }.Where(s => !string.IsNullOrWhiteSpace(s)));
var enabledStr = Enabled is 1 ? "enabled" : "disabled";
var expireStr = Expire is > 0 ? DateTimeOffset.FromUnixTimeSeconds(Expire.Value).ToString("yyyy-MM-dd") : "never";
return $"User: {UserId} | Name: {(string.IsNullOrEmpty(fullName) ? "N/A" : fullName)} | "
+ $"{enabledStr} | Groups: {Groups ?? "none"} | Expires: {expireStr}";
}
}
@@ -0,0 +1,54 @@
using Newtonsoft.Json;
namespace PSProxmoxVE.Core.Models.Vms
{
/// <summary>
/// Represents the Cloud-Init specific configuration fields for a VM.
/// Fields are extracted from the full VM config.
/// </summary>
public class PveCloudInitConfig
{
/// <summary>Default user name injected by Cloud-Init.</summary>
[JsonProperty("ciuser")]
public string? CiUser { get; set; }
/// <summary>Default user password (may be hashed).</summary>
[JsonProperty("cipassword")]
public string? CiPassword { get; set; }
/// <summary>URL-encoded SSH public keys.</summary>
[JsonProperty("sshkeys")]
public string? SshKeys { get; set; }
/// <summary>IP configuration for interface 0 (e.g. "ip=dhcp" or "ip=192.168.1.10/24,gw=192.168.1.1").</summary>
[JsonProperty("ipconfig0")]
public string? IpConfig0 { get; set; }
/// <summary>IP configuration for interface 1.</summary>
[JsonProperty("ipconfig1")]
public string? IpConfig1 { get; set; }
/// <summary>IP configuration for interface 2.</summary>
[JsonProperty("ipconfig2")]
public string? IpConfig2 { get; set; }
/// <summary>IP configuration for interface 3.</summary>
[JsonProperty("ipconfig3")]
public string? IpConfig3 { get; set; }
/// <summary>DNS nameserver(s) space-separated.</summary>
[JsonProperty("nameserver")]
public string? Nameserver { get; set; }
/// <summary>DNS search domain.</summary>
[JsonProperty("searchdomain")]
public string? Searchdomain { get; set; }
/// <summary>Custom Cloud-Init config files (cicustom field).</summary>
[JsonProperty("cicustom")]
public string? CiCustom { get; set; }
public override string ToString() =>
$"CloudInit: User={CiUser ?? "N/A"} | IP0={IpConfig0 ?? "N/A"} | NS={Nameserver ?? "N/A"}";
}
}
@@ -0,0 +1,64 @@
using System;
using System.Text.Json.Serialization;
using Newtonsoft.Json;
namespace PSProxmoxVE.Core.Models.Vms;
/// <summary>
/// Represents a VM snapshot as returned by the /nodes/{node}/qemu/{vmid}/snapshot endpoint.
/// </summary>
public class PveSnapshot
{
/// <summary>
/// The snapshot name / identifier.
/// </summary>
[JsonPropertyName("name")]
[JsonProperty("name")]
public string Name { get; set; } = string.Empty;
/// <summary>
/// Optional human-readable description of what this snapshot captures.
/// </summary>
[JsonPropertyName("description")]
[JsonProperty("description")]
public string? Description { get; set; }
/// <summary>
/// Unix timestamp when the snapshot was created.
/// </summary>
[JsonPropertyName("snaptime")]
[JsonProperty("snaptime")]
public long? SnapTime { get; set; }
/// <summary>
/// Indicates whether the VM RAM state was saved with this snapshot (1) or not (0).
/// </summary>
[JsonPropertyName("vmstate")]
[JsonProperty("vmstate")]
public int? VmState { get; set; }
/// <summary>
/// The name of the parent snapshot, or null if this is the root snapshot.
/// </summary>
[JsonPropertyName("parent")]
[JsonProperty("parent")]
public string? Parent { get; set; }
/// <summary>The VM ID this snapshot belongs to. Populated by cmdlets after retrieval.</summary>
public int VmId { get; set; }
/// <summary>The node the VM resides on. Populated by cmdlets after retrieval.</summary>
public string? Node { get; set; }
/// <inheritdoc />
public override string ToString()
{
var timeStr = SnapTime.HasValue
? DateTimeOffset.FromUnixTimeSeconds(SnapTime.Value).ToString("yyyy-MM-dd HH:mm:ss")
: "N/A";
var stateStr = VmState is 1 ? " [+vmstate]" : string.Empty;
var parentStr = Parent is not null ? $" | Parent: {Parent}" : string.Empty;
return $"Snapshot: {Name}{stateStr} | Created: {timeStr} | "
+ $"Description: {Description ?? "(none)"}{parentStr}";
}
}
@@ -0,0 +1,95 @@
using System;
using System.Text.Json.Serialization;
using Newtonsoft.Json;
namespace PSProxmoxVE.Core.Models.Vms;
/// <summary>
/// Represents a Proxmox VE asynchronous task as returned by the task-related endpoints
/// (e.g., /nodes/{node}/tasks, /nodes/{node}/tasks/{upid}/status).
/// Most mutating API calls return a UPID string that identifies the created task.
/// </summary>
public class PveTask
{
/// <summary>
/// The Unique Process Identifier for this task
/// (e.g., "UPID:pve:000ABC:00000001:5F1234AB:qmstart:100:root@pam:").
/// </summary>
[JsonPropertyName("upid")]
[JsonProperty("upid")]
public string Upid { get; set; } = string.Empty;
/// <summary>
/// The task type string (e.g., "qmstart", "qmstop", "qmmigrate", "vzrestore").
/// </summary>
[JsonPropertyName("type")]
[JsonProperty("type")]
public string? Type { get; set; }
/// <summary>
/// The current task status: "running" while in progress, "stopped" when complete.
/// </summary>
[JsonPropertyName("status")]
[JsonProperty("status")]
public string? Status { get; set; }
/// <summary>
/// The exit status when the task has stopped (e.g., "OK" on success, or an error message).
/// </summary>
[JsonPropertyName("exitstatus")]
[JsonProperty("exitstatus")]
public string? ExitStatus { get; set; }
/// <summary>
/// The node on which this task is executing.
/// </summary>
[JsonPropertyName("node")]
[JsonProperty("node")]
public string? Node { get; set; }
/// <summary>
/// Unix timestamp of when the task started.
/// </summary>
[JsonPropertyName("starttime")]
[JsonProperty("starttime")]
public long? StartTime { get; set; }
/// <summary>
/// Unix timestamp of when the task ended. Null if still running.
/// </summary>
[JsonPropertyName("endtime")]
[JsonProperty("endtime")]
public long? EndTime { get; set; }
/// <summary>
/// The user that initiated the task (e.g., "root@pam").
/// </summary>
[JsonPropertyName("user")]
[JsonProperty("user")]
public string? User { get; set; }
/// <summary>
/// The object ID the task is operating on (e.g., a VM ID or storage name).
/// </summary>
[JsonPropertyName("id")]
[JsonProperty("id")]
public string? Id { get; set; }
/// <summary>
/// Returns true when the task completed successfully (stopped with exit status "OK").
/// </summary>
[System.Text.Json.Serialization.JsonIgnore]
[Newtonsoft.Json.JsonIgnore]
public bool IsSuccessful => Status == "stopped" && ExitStatus == "OK";
/// <inheritdoc />
public override string ToString()
{
var startStr = StartTime.HasValue
? DateTimeOffset.FromUnixTimeSeconds(StartTime.Value).ToString("yyyy-MM-dd HH:mm:ss")
: "N/A";
return $"Task [{Type ?? "unknown"}] UPID: {Upid} | Node: {Node ?? "N/A"} | "
+ $"Status: {Status ?? "unknown"} | ExitStatus: {ExitStatus ?? "-"} | "
+ $"User: {User ?? "N/A"} | Started: {startStr}";
}
}
@@ -0,0 +1,21 @@
using Newtonsoft.Json;
namespace PSProxmoxVE.Core.Models.Vms
{
/// <summary>
/// Represents a single log line from a Proxmox VE task log,
/// as returned by the /nodes/{node}/tasks/{upid}/log endpoint.
/// </summary>
public class PveTaskLog
{
/// <summary>Line number (1-based).</summary>
[JsonProperty("n")]
public int LineNumber { get; set; }
/// <summary>Log line text.</summary>
[JsonProperty("t")]
public string Text { get; set; } = string.Empty;
public override string ToString() => $"{LineNumber,4}: {Text}";
}
}
+121
View File
@@ -0,0 +1,121 @@
using System;
using System.Text.Json.Serialization;
using Newtonsoft.Json;
namespace PSProxmoxVE.Core.Models.Vms;
/// <summary>
/// Represents a QEMU/KVM virtual machine as returned by the cluster or node VM list endpoints.
/// </summary>
public class PveVm
{
/// <summary>
/// The unique VM identifier.
/// </summary>
[JsonPropertyName("vmid")]
[JsonProperty("vmid")]
public int VmId { get; set; }
/// <summary>
/// The name of the virtual machine.
/// </summary>
[JsonPropertyName("name")]
[JsonProperty("name")]
public string? Name { get; set; }
/// <summary>
/// The current runtime status of the VM (e.g., "running", "stopped").
/// </summary>
[JsonPropertyName("status")]
[JsonProperty("status")]
public string? Status { get; set; }
/// <summary>
/// The node on which the VM resides.
/// </summary>
[JsonPropertyName("node")]
[JsonProperty("node")]
public string? Node { get; set; }
/// <summary>
/// The number of virtual CPU cores assigned to the VM.
/// </summary>
[JsonPropertyName("cpus")]
[JsonProperty("cpus")]
public int? CpuCount { get; set; }
/// <summary>
/// Maximum memory allocated to the VM, in bytes.
/// </summary>
[JsonPropertyName("maxmem")]
[JsonProperty("maxmem")]
public long? MaxMem { get; set; }
/// <summary>
/// Maximum disk size allocated to the VM, in bytes.
/// </summary>
[JsonPropertyName("maxdisk")]
[JsonProperty("maxdisk")]
public long? MaxDisk { get; set; }
/// <summary>
/// VM uptime in seconds.
/// </summary>
[JsonPropertyName("uptime")]
[JsonProperty("uptime")]
public long? Uptime { get; set; }
/// <summary>
/// Semicolon-separated list of tags assigned to the VM.
/// </summary>
[JsonPropertyName("tags")]
[JsonProperty("tags")]
public string? Tags { get; set; }
/// <summary>
/// Indicates whether the VM is a template (1) or a regular VM (0).
/// </summary>
[JsonPropertyName("template")]
[JsonProperty("template")]
public int? Template { get; set; }
/// <summary>
/// The current lock type applied to the VM, if any (e.g., "migrate", "backup").
/// </summary>
[JsonPropertyName("lock")]
[JsonProperty("lock")]
public string? Lock { get; set; }
/// <summary>
/// The process ID of the running QEMU process, if applicable.
/// </summary>
[JsonPropertyName("pid")]
[JsonProperty("pid")]
public int? Pid { get; set; }
/// <summary>
/// The QMP (QEMU Machine Protocol) status string.
/// </summary>
[JsonPropertyName("qmpstatus")]
[JsonProperty("qmpstatus")]
public string? QmpStatus { get; set; }
/// <summary>
/// Indicates whether the QEMU guest agent is running (non-zero) inside the VM.
/// </summary>
[JsonPropertyName("agent")]
[JsonProperty("agent")]
public int? AgentStatus { get; set; }
/// <inheritdoc />
public override string ToString()
{
var maxMemMb = MaxMem.HasValue ? $"{MaxMem.Value / 1024 / 1024} MB" : "N/A";
var maxDiskGb = MaxDisk.HasValue ? $"{MaxDisk.Value / 1024 / 1024 / 1024} GB" : "N/A";
var uptimeStr = Uptime.HasValue ? TimeSpan.FromSeconds(Uptime.Value).ToString(@"d\.hh\:mm\:ss") : "N/A";
var templateStr = Template is 1 ? " [template]" : string.Empty;
return $"VM {VmId}{templateStr}: {Name ?? "(unnamed)"} | Node: {Node ?? "N/A"} | "
+ $"Status: {Status ?? "N/A"} | CPUs: {CpuCount?.ToString() ?? "N/A"} | "
+ $"Mem: {maxMemMb} | Disk: {maxDiskGb} | Uptime: {uptimeStr}";
}
}
@@ -0,0 +1,353 @@
using System.Text.Json.Serialization;
using Newtonsoft.Json;
namespace PSProxmoxVE.Core.Models.Vms;
/// <summary>
/// Represents the configuration of a QEMU/KVM virtual machine,
/// as returned by the /nodes/{node}/qemu/{vmid}/config endpoint.
/// </summary>
public class PveVmConfig
{
// -------------------------------------------------------------------------
// CPU / Memory
// -------------------------------------------------------------------------
/// <summary>
/// Number of CPU cores per socket.
/// </summary>
[JsonPropertyName("cores")]
[JsonProperty("cores")]
public int? Cores { get; set; }
/// <summary>
/// Number of CPU sockets.
/// </summary>
[JsonPropertyName("sockets")]
[JsonProperty("sockets")]
public int? Sockets { get; set; }
/// <summary>
/// Memory size in megabytes.
/// </summary>
[JsonPropertyName("memory")]
[JsonProperty("memory")]
public int? Memory { get; set; }
/// <summary>
/// Emulated CPU type (e.g., "host", "x86-64-v2-AES").
/// </summary>
[JsonPropertyName("cpu")]
[JsonProperty("cpu")]
public string? CpuType { get; set; }
// -------------------------------------------------------------------------
// Firmware / Machine
// -------------------------------------------------------------------------
/// <summary>
/// BIOS implementation to use: "seabios" (default) or "ovmf" (UEFI).
/// </summary>
[JsonPropertyName("bios")]
[JsonProperty("bios")]
public string? Bios { get; set; }
/// <summary>
/// Emulated machine type (e.g., "q35", "i440fx").
/// </summary>
[JsonPropertyName("machine")]
[JsonProperty("machine")]
public string? Machine { get; set; }
// -------------------------------------------------------------------------
// Boot / Args
// -------------------------------------------------------------------------
/// <summary>
/// Boot order specification string.
/// </summary>
[JsonPropertyName("boot")]
[JsonProperty("boot")]
public string? Boot { get; set; }
/// <summary>
/// Arbitrary QEMU command-line arguments appended to the QEMU launch command.
/// </summary>
[JsonPropertyName("args")]
[JsonProperty("args")]
public string? Args { get; set; }
// -------------------------------------------------------------------------
// Metadata
// -------------------------------------------------------------------------
/// <summary>
/// Human-readable description or notes for the VM.
/// </summary>
[JsonPropertyName("description")]
[JsonProperty("description")]
public string? Description { get; set; }
/// <summary>
/// Semicolon-separated list of tags assigned to the VM.
/// </summary>
[JsonPropertyName("tags")]
[JsonProperty("tags")]
public string? Tags { get; set; }
/// <summary>
/// When set to 1, prevents the VM from being deleted or modified accidentally.
/// </summary>
[JsonPropertyName("protection")]
[JsonProperty("protection")]
public int? Protection { get; set; }
/// <summary>
/// NUMA topology enabled (1) or disabled (0).
/// </summary>
[JsonPropertyName("numa")]
[JsonProperty("numa")]
public int? Numa { get; set; }
/// <summary>
/// VirtIO balloon device target memory in MB. 0 disables ballooning.
/// </summary>
[JsonPropertyName("balloon")]
[JsonProperty("balloon")]
public int? Balloon { get; set; }
/// <summary>
/// Guest OS type hint (e.g., "l26" for Linux 2.6+, "win10").
/// </summary>
[JsonPropertyName("ostype")]
[JsonProperty("ostype")]
public string? OsType { get; set; }
// -------------------------------------------------------------------------
// VirtIO disk slots (03, most commonly used)
// -------------------------------------------------------------------------
/// <summary>VirtIO disk slot 0 configuration string.</summary>
[JsonPropertyName("virtio0")]
[JsonProperty("virtio0")]
public string? Virtio0 { get; set; }
/// <summary>VirtIO disk slot 1 configuration string.</summary>
[JsonPropertyName("virtio1")]
[JsonProperty("virtio1")]
public string? Virtio1 { get; set; }
/// <summary>VirtIO disk slot 2 configuration string.</summary>
[JsonPropertyName("virtio2")]
[JsonProperty("virtio2")]
public string? Virtio2 { get; set; }
/// <summary>VirtIO disk slot 3 configuration string.</summary>
[JsonPropertyName("virtio3")]
[JsonProperty("virtio3")]
public string? Virtio3 { get; set; }
// -------------------------------------------------------------------------
// SCSI disk slots (07)
// -------------------------------------------------------------------------
/// <summary>SCSI disk slot 0 configuration string.</summary>
[JsonPropertyName("scsi0")]
[JsonProperty("scsi0")]
public string? Scsi0 { get; set; }
/// <summary>SCSI disk slot 1 configuration string.</summary>
[JsonPropertyName("scsi1")]
[JsonProperty("scsi1")]
public string? Scsi1 { get; set; }
/// <summary>SCSI disk slot 2 configuration string.</summary>
[JsonPropertyName("scsi2")]
[JsonProperty("scsi2")]
public string? Scsi2 { get; set; }
/// <summary>SCSI disk slot 3 configuration string.</summary>
[JsonPropertyName("scsi3")]
[JsonProperty("scsi3")]
public string? Scsi3 { get; set; }
/// <summary>SCSI disk slot 4 configuration string.</summary>
[JsonPropertyName("scsi4")]
[JsonProperty("scsi4")]
public string? Scsi4 { get; set; }
/// <summary>SCSI disk slot 5 configuration string.</summary>
[JsonPropertyName("scsi5")]
[JsonProperty("scsi5")]
public string? Scsi5 { get; set; }
/// <summary>SCSI disk slot 6 configuration string.</summary>
[JsonPropertyName("scsi6")]
[JsonProperty("scsi6")]
public string? Scsi6 { get; set; }
/// <summary>SCSI disk slot 7 configuration string.</summary>
[JsonPropertyName("scsi7")]
[JsonProperty("scsi7")]
public string? Scsi7 { get; set; }
// -------------------------------------------------------------------------
// IDE disk slots (03)
// -------------------------------------------------------------------------
/// <summary>IDE disk/CDROM slot 0 configuration string.</summary>
[JsonPropertyName("ide0")]
[JsonProperty("ide0")]
public string? Ide0 { get; set; }
/// <summary>IDE disk/CDROM slot 1 configuration string.</summary>
[JsonPropertyName("ide1")]
[JsonProperty("ide1")]
public string? Ide1 { get; set; }
/// <summary>IDE disk/CDROM slot 2 configuration string.</summary>
[JsonPropertyName("ide2")]
[JsonProperty("ide2")]
public string? Ide2 { get; set; }
/// <summary>IDE disk/CDROM slot 3 configuration string.</summary>
[JsonPropertyName("ide3")]
[JsonProperty("ide3")]
public string? Ide3 { get; set; }
// -------------------------------------------------------------------------
// SATA disk slots (05)
// -------------------------------------------------------------------------
/// <summary>SATA disk slot 0 configuration string.</summary>
[JsonPropertyName("sata0")]
[JsonProperty("sata0")]
public string? Sata0 { get; set; }
/// <summary>SATA disk slot 1 configuration string.</summary>
[JsonPropertyName("sata1")]
[JsonProperty("sata1")]
public string? Sata1 { get; set; }
/// <summary>SATA disk slot 2 configuration string.</summary>
[JsonPropertyName("sata2")]
[JsonProperty("sata2")]
public string? Sata2 { get; set; }
/// <summary>SATA disk slot 3 configuration string.</summary>
[JsonPropertyName("sata3")]
[JsonProperty("sata3")]
public string? Sata3 { get; set; }
/// <summary>SATA disk slot 4 configuration string.</summary>
[JsonPropertyName("sata4")]
[JsonProperty("sata4")]
public string? Sata4 { get; set; }
/// <summary>SATA disk slot 5 configuration string.</summary>
[JsonPropertyName("sata5")]
[JsonProperty("sata5")]
public string? Sata5 { get; set; }
// -------------------------------------------------------------------------
// Network interface slots (07)
// -------------------------------------------------------------------------
/// <summary>Network interface 0 configuration string (e.g., "virtio=XX:XX:XX:XX:XX:XX,bridge=vmbr0").</summary>
[JsonPropertyName("net0")]
[JsonProperty("net0")]
public string? Net0 { get; set; }
/// <summary>Network interface 1 configuration string.</summary>
[JsonPropertyName("net1")]
[JsonProperty("net1")]
public string? Net1 { get; set; }
/// <summary>Network interface 2 configuration string.</summary>
[JsonPropertyName("net2")]
[JsonProperty("net2")]
public string? Net2 { get; set; }
/// <summary>Network interface 3 configuration string.</summary>
[JsonPropertyName("net3")]
[JsonProperty("net3")]
public string? Net3 { get; set; }
/// <summary>Network interface 4 configuration string.</summary>
[JsonPropertyName("net4")]
[JsonProperty("net4")]
public string? Net4 { get; set; }
/// <summary>Network interface 5 configuration string.</summary>
[JsonPropertyName("net5")]
[JsonProperty("net5")]
public string? Net5 { get; set; }
/// <summary>Network interface 6 configuration string.</summary>
[JsonPropertyName("net6")]
[JsonProperty("net6")]
public string? Net6 { get; set; }
/// <summary>Network interface 7 configuration string.</summary>
[JsonPropertyName("net7")]
[JsonProperty("net7")]
public string? Net7 { get; set; }
// -------------------------------------------------------------------------
// Cloud-Init
// -------------------------------------------------------------------------
/// <summary>Cloud-Init default user name.</summary>
[JsonPropertyName("ciuser")]
[JsonProperty("ciuser")]
public string? CiUser { get; set; }
/// <summary>Cloud-Init default user password (hashed or plaintext depending on PVE version).</summary>
[JsonPropertyName("cipassword")]
[JsonProperty("cipassword")]
public string? CiPassword { get; set; }
/// <summary>URL-encoded SSH public keys injected by Cloud-Init.</summary>
[JsonPropertyName("sshkeys")]
[JsonProperty("sshkeys")]
public string? SshKeys { get; set; }
/// <summary>Cloud-Init IP configuration for interface 0.</summary>
[JsonPropertyName("ipconfig0")]
[JsonProperty("ipconfig0")]
public string? IpConfig0 { get; set; }
/// <summary>Cloud-Init IP configuration for interface 1.</summary>
[JsonPropertyName("ipconfig1")]
[JsonProperty("ipconfig1")]
public string? IpConfig1 { get; set; }
/// <summary>Cloud-Init IP configuration for interface 2.</summary>
[JsonPropertyName("ipconfig2")]
[JsonProperty("ipconfig2")]
public string? IpConfig2 { get; set; }
/// <summary>Cloud-Init IP configuration for interface 3.</summary>
[JsonPropertyName("ipconfig3")]
[JsonProperty("ipconfig3")]
public string? IpConfig3 { get; set; }
/// <summary>DNS nameserver(s) injected via Cloud-Init.</summary>
[JsonPropertyName("nameserver")]
[JsonProperty("nameserver")]
public string? Nameserver { get; set; }
/// <summary>DNS search domain injected via Cloud-Init.</summary>
[JsonPropertyName("searchdomain")]
[JsonProperty("searchdomain")]
public string? Searchdomain { get; set; }
/// <inheritdoc />
public override string ToString()
{
var totalCores = (Cores ?? 1) * (Sockets ?? 1);
return $"Config | CPUs: {totalCores} ({Sockets ?? 1}S x {Cores ?? 1}C) | "
+ $"Memory: {Memory?.ToString() ?? "N/A"} MB | BIOS: {Bios ?? "seabios"} | "
+ $"Machine: {Machine ?? "default"} | OS: {OsType ?? "N/A"}";
}
}