fix: remediate findings F045, F047, F048, F064, F070, F071, F076-F079

Phase 1 — Trivial fixes:
- F071: Add Uri.EscapeDataString to GetPveTemplateCmdlet node path
- F077: Add ValidateRange(100, 999999999) to GetPveTaskListCmdlet.VmId
- F076: Create .github/dependabot.yml (nuget + github-actions, weekly)
- F079: Fix unit-tests.yml dotnet SDK from 9.0.x to 10.0.x
- F048: Mark wont_fix — sync-over-async accepted for PS 5.1 compat

Phase 2 — Framework targeting (D009 compliance):
- F047: Reduce publishable csproj to netstandard2.0 only, remove all
  #if NET48/NETSTANDARD2_0 conditionals from PveHttpClient.cs,
  restructure build.yml for netstandard2.0 publish + net10.0/net48 tests
- F064: Resolved by F047 — SMA 7.5.0 ItemGroup removed with net10.0 TFM
- F070: Add PS 5.1 smoke-test job to publish.yml (windows-latest)

Phase 3 — IPveHttpClient interface extraction (F045):
- Extract IPveHttpClient interface from PveHttpClient
- Add constructor injection to all 14 service classes
- Services use injected client when available, create+dispose when not

Phase 4 — Service unit tests (F078):
- 196 new xUnit tests across 10 service test files
- All services tested via Moq-mocked IPveHttpClient
- Total test count: 382 (was 186)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Clint Branham
2026-03-23 13:51:37 -05:00
parent 94424367bf
commit 68dadbfdc0
35 changed files with 5719 additions and 915 deletions
@@ -0,0 +1,48 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace PSProxmoxVE.Core.Client
{
/// <summary>
/// Abstraction over the PVE HTTP client for testability and dependency injection.
/// Services accept this interface via constructor injection; tests can mock it.
/// </summary>
public interface IPveHttpClient : IDisposable
{
/// <summary>Performs a GET request against the specified API resource path.</summary>
Task<string> GetAsync(string resource);
/// <summary>Performs a POST request against the specified API resource path.</summary>
Task<string> PostAsync(string resource, Dictionary<string, string>? data = null);
/// <summary>Performs a PUT request against the specified API resource path.</summary>
Task<string> PutAsync(string resource, Dictionary<string, string>? data = null);
/// <summary>Performs a DELETE request against the specified API resource path.</summary>
Task<string> DeleteAsync(string resource);
/// <summary>Synchronous wrapper for <see cref="GetAsync"/>.</summary>
string Get(string resource);
/// <summary>Synchronous wrapper for <see cref="PostAsync"/>.</summary>
string Post(string resource, Dictionary<string, string>? data = null);
/// <summary>Synchronous wrapper for <see cref="PutAsync"/>.</summary>
string Put(string resource, Dictionary<string, string>? data = null);
/// <summary>Synchronous wrapper for <see cref="DeleteAsync"/>.</summary>
string Delete(string resource);
/// <summary>
/// Uploads a file to a Proxmox VE storage endpoint using MultipartFormDataContent.
/// </summary>
Task<string> UploadFileAsync(
string resource,
string filePath,
Dictionary<string, string>? formFields = null,
string? checksum = null,
string? checksumAlgorithm = null,
Action<long, long>? progressCallback = null);
}
}
+1 -43
View File
@@ -11,10 +11,8 @@ using Newtonsoft.Json.Linq;
using PSProxmoxVE.Core.Authentication;
using PSProxmoxVE.Core.Exceptions;
#if NET48 || NETSTANDARD2_0
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
#endif
namespace PSProxmoxVE.Core.Client
{
@@ -22,7 +20,7 @@ namespace PSProxmoxVE.Core.Client
/// Low-level HTTP client for communicating with the Proxmox VE API.
/// Handles authentication headers, error parsing, and the ISO upload workaround.
/// </summary>
public class PveHttpClient : IDisposable
public class PveHttpClient : IPveHttpClient
{
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type
private readonly PveSession? _session;
@@ -44,7 +42,6 @@ namespace PSProxmoxVE.Core.Client
_session = session ?? throw new ArgumentNullException(nameof(session));
_baseUrl = session.BaseUrl;
#if NET48 || NETSTANDARD2_0
var handler = new HttpClientHandler();
if (session.SkipCertificateCheck)
{
@@ -52,21 +49,6 @@ namespace PSProxmoxVE.Core.Client
(HttpRequestMessage _, X509Certificate2 _, X509Chain _, SslPolicyErrors _) => true;
}
_httpClient = new HttpClient(handler);
#else
if (session.SkipCertificateCheck)
{
var handler = new HttpClientHandler
{
ServerCertificateCustomValidationCallback =
(_, _, _, _) => true
};
_httpClient = new HttpClient(handler);
}
else
{
_httpClient = new HttpClient();
}
#endif
_httpClient.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
@@ -84,7 +66,6 @@ namespace PSProxmoxVE.Core.Client
_session = null;
_baseUrl = $"https://{hostname}:{port}";
#if NET48 || NETSTANDARD2_0
var handler = new HttpClientHandler();
if (skipCertificateCheck)
{
@@ -92,21 +73,6 @@ namespace PSProxmoxVE.Core.Client
(HttpRequestMessage _, X509Certificate2 _, X509Chain _, SslPolicyErrors _) => true;
}
_httpClient = new HttpClient(handler);
#else
if (skipCertificateCheck)
{
var handler = new HttpClientHandler
{
ServerCertificateCustomValidationCallback =
(_, _, _, _) => true
};
_httpClient = new HttpClient(handler);
}
else
{
_httpClient = new HttpClient();
}
#endif
_httpClient.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
@@ -295,11 +261,7 @@ namespace PSProxmoxVE.Core.Client
}
finally
{
#if NET48 || NETSTANDARD2_0
fileStream.Dispose();
#else
await fileStream.DisposeAsync().ConfigureAwait(false);
#endif
}
}
@@ -397,12 +359,8 @@ namespace PSProxmoxVE.Core.Client
{
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
var bytes = new byte[32];
#if NET48 || NETSTANDARD2_0
using (var rng = RandomNumberGenerator.Create())
rng.GetBytes(bytes);
#else
RandomNumberGenerator.Fill(bytes);
#endif
var sb = new StringBuilder(32);
for (int i = 0; i < 32; i++)
sb.Append(chars[bytes[i] % chars.Length]);
+2 -13
View File
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;net10.0;net48</TargetFrameworks>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>10.0</LangVersion>
<Nullable>enable</Nullable>
<RootNamespace>PSProxmoxVE.Core</RootNamespace>
@@ -16,18 +16,7 @@
</AssemblyAttribute>
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="SharpCompress" Version="0.38.0" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net48'">
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="SharpCompress" Version="0.38.0" />
<Reference Include="System.Net.Http" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net10.0'">
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="SharpCompress" Version="0.38.0" />
</ItemGroup>
+93 -26
View File
@@ -13,6 +13,24 @@ namespace PSProxmoxVE.Core.Services
/// </summary>
public class BackupService
{
private readonly IPveHttpClient? _injectedClient;
/// <summary>
/// Initializes a new instance of <see cref="BackupService"/> with no injected client.
/// Each method will create and dispose its own <see cref="PveHttpClient"/>.
/// </summary>
public BackupService() { }
/// <summary>
/// Initializes a new instance of <see cref="BackupService"/> with an injected HTTP client.
/// The caller owns the client's lifetime; this service will not dispose it.
/// </summary>
/// <param name="client">The HTTP client to use for all requests.</param>
public BackupService(IPveHttpClient client)
{
_injectedClient = client ?? throw new ArgumentNullException(nameof(client));
}
// -------------------------------------------------------------------------
// Ad-hoc backup (vzdump)
// -------------------------------------------------------------------------
@@ -29,10 +47,17 @@ namespace PSProxmoxVE.Core.Services
if (string.IsNullOrWhiteSpace(node)) throw new ArgumentNullException(nameof(node));
if (config == null) throw new ArgumentNullException(nameof(config));
using var client = new PveHttpClient(session);
var response = client.PostAsync($"nodes/{Uri.EscapeDataString(node)}/vzdump", config)
.GetAwaiter().GetResult();
return ParseTask(response, node);
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.PostAsync($"nodes/{Uri.EscapeDataString(node)}/vzdump", config)
.GetAwaiter().GetResult();
return ParseTask(response, node);
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
// -------------------------------------------------------------------------
@@ -46,10 +71,17 @@ namespace PSProxmoxVE.Core.Services
{
if (session == null) throw new ArgumentNullException(nameof(session));
using var client = new PveHttpClient(session);
var response = client.GetAsync("cluster/backup").GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveBackupJob[]>() ?? Array.Empty<PveBackupJob>();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.GetAsync("cluster/backup").GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveBackupJob[]>() ?? Array.Empty<PveBackupJob>();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -60,11 +92,18 @@ namespace PSProxmoxVE.Core.Services
if (session == null) throw new ArgumentNullException(nameof(session));
if (string.IsNullOrWhiteSpace(id)) throw new ArgumentNullException(nameof(id));
using var client = new PveHttpClient(session);
var response = client.GetAsync($"cluster/backup/{Uri.EscapeDataString(id)}")
.GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveBackupJob>();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.GetAsync($"cluster/backup/{Uri.EscapeDataString(id)}")
.GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveBackupJob>();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -75,8 +114,15 @@ namespace PSProxmoxVE.Core.Services
if (session == null) throw new ArgumentNullException(nameof(session));
if (config == null) throw new ArgumentNullException(nameof(config));
using var client = new PveHttpClient(session);
client.PostAsync("cluster/backup", config).GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.PostAsync("cluster/backup", config).GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -88,9 +134,16 @@ namespace PSProxmoxVE.Core.Services
if (string.IsNullOrWhiteSpace(id)) throw new ArgumentNullException(nameof(id));
if (config == null) throw new ArgumentNullException(nameof(config));
using var client = new PveHttpClient(session);
client.PutAsync($"cluster/backup/{Uri.EscapeDataString(id)}", config)
.GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.PutAsync($"cluster/backup/{Uri.EscapeDataString(id)}", config)
.GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -101,9 +154,16 @@ namespace PSProxmoxVE.Core.Services
if (session == null) throw new ArgumentNullException(nameof(session));
if (string.IsNullOrWhiteSpace(id)) throw new ArgumentNullException(nameof(id));
using var client = new PveHttpClient(session);
client.DeleteAsync($"cluster/backup/{Uri.EscapeDataString(id)}")
.GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.DeleteAsync($"cluster/backup/{Uri.EscapeDataString(id)}")
.GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
// -------------------------------------------------------------------------
@@ -117,11 +177,18 @@ namespace PSProxmoxVE.Core.Services
{
if (session == null) throw new ArgumentNullException(nameof(session));
using var client = new PveHttpClient(session);
var response = client.GetAsync("cluster/backup-info/not-backed-up")
.GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data as JArray ?? new JArray();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.GetAsync("cluster/backup-info/not-backed-up")
.GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data as JArray ?? new JArray();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
// -------------------------------------------------------------------------
@@ -14,6 +14,8 @@ namespace PSProxmoxVE.Core.Services
/// </summary>
public class CloudInitService
{
private readonly IPveHttpClient? _injectedClient;
// Cloud-Init field names as used in the PVE API
private static readonly string[] CloudInitFields =
{
@@ -22,6 +24,20 @@ namespace PSProxmoxVE.Core.Services
"nameserver", "searchdomain", "cicustom"
};
/// <summary>
/// Initializes a new instance of the <see cref="CloudInitService"/> class.
/// </summary>
public CloudInitService() { }
/// <summary>
/// Initializes a new instance of the <see cref="CloudInitService"/> class with an injected HTTP client.
/// </summary>
/// <param name="client">The HTTP client to use for API calls. The caller owns its lifetime.</param>
public CloudInitService(IPveHttpClient client)
{
_injectedClient = client ?? throw new ArgumentNullException(nameof(client));
}
/// <summary>
/// Retrieves the Cloud-Init specific configuration fields for a VM.
/// Internally fetches the full VM config and extracts the CI fields.
@@ -31,21 +47,28 @@ namespace PSProxmoxVE.Core.Services
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}/config")
.GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
if (data == null) return new PveCloudInitConfig();
// Extract only the Cloud-Init fields into a reduced JObject for deserialization
var ciObj = new JObject();
foreach (var field in CloudInitFields)
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
if (data[field] != null)
ciObj[field] = data[field];
}
var response = client.GetAsync($"nodes/{node}/qemu/{vmid}/config")
.GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
if (data == null) return new PveCloudInitConfig();
return ciObj.ToObject<PveCloudInitConfig>() ?? new PveCloudInitConfig();
// Extract only the Cloud-Init fields into a reduced JObject for deserialization
var ciObj = new JObject();
foreach (var field in CloudInitFields)
{
if (data[field] != null)
ciObj[field] = data[field];
}
return ciObj.ToObject<PveCloudInitConfig>() ?? new PveCloudInitConfig();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -66,12 +89,19 @@ namespace PSProxmoxVE.Core.Services
if (string.IsNullOrWhiteSpace(node)) throw new ArgumentNullException(nameof(node));
if (config == null) throw new ArgumentNullException(nameof(config));
using var client = new PveHttpClient(session);
var formData = config.ToDictionary(
kvp => kvp.Key,
kvp => kvp.Value?.ToString() ?? string.Empty);
client.PutAsync($"nodes/{node}/qemu/{vmid}/config", formData)
.GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var formData = config.ToDictionary(
kvp => kvp.Key,
kvp => kvp.Value?.ToString() ?? string.Empty);
client.PutAsync($"nodes/{node}/qemu/{vmid}/config", formData)
.GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -87,13 +117,19 @@ namespace PSProxmoxVE.Core.Services
if (session == null) throw new ArgumentNullException(nameof(session));
if (string.IsNullOrWhiteSpace(node)) throw new ArgumentNullException(nameof(node));
using var client = new PveHttpClient(session);
// Request a Cloud-Init dump (user-data section) — this causes PVE to rebuild the image
var dumpResponse = client.GetAsync($"nodes/{node}/qemu/{vmid}/cloudinit/dump?type=user")
.GetAwaiter().GetResult();
var data = JObject.Parse(dumpResponse)["data"];
return data?.ToString() ?? string.Empty;
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
// Request a Cloud-Init dump (user-data section) — this causes PVE to rebuild the image
var dumpResponse = client.GetAsync($"nodes/{node}/qemu/{vmid}/cloudinit/dump?type=user")
.GetAwaiter().GetResult();
var data = JObject.Parse(dumpResponse)["data"];
return data?.ToString() ?? string.Empty;
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
}
}
+41 -11
View File
@@ -11,6 +11,22 @@ namespace PSProxmoxVE.Core.Services
/// </summary>
public class ClusterService
{
private readonly IPveHttpClient? _injectedClient;
/// <summary>
/// Initializes a new instance of the <see cref="ClusterService"/> class.
/// </summary>
public ClusterService() { }
/// <summary>
/// Initializes a new instance of the <see cref="ClusterService"/> class with an injected HTTP client.
/// </summary>
/// <param name="client">The HTTP client to use for API calls. The caller owns its lifetime.</param>
public ClusterService(IPveHttpClient client)
{
_injectedClient = client ?? throw new ArgumentNullException(nameof(client));
}
/// <summary>
/// Returns the current cluster status. The response is a mixed array of
/// "cluster" and "node" type entries.
@@ -19,10 +35,17 @@ namespace PSProxmoxVE.Core.Services
{
if (session == null) throw new ArgumentNullException(nameof(session));
using var client = new PveHttpClient(session);
var response = client.GetAsync("cluster/status").GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveClusterStatus[]>() ?? Array.Empty<PveClusterStatus>();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.GetAsync("cluster/status").GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveClusterStatus[]>() ?? Array.Empty<PveClusterStatus>();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -34,14 +57,21 @@ namespace PSProxmoxVE.Core.Services
{
if (session == null) throw new ArgumentNullException(nameof(session));
using var client = new PveHttpClient(session);
var resource = "cluster/resources";
if (!string.IsNullOrEmpty(type))
resource += $"?type={Uri.EscapeDataString(type!)}";
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var resource = "cluster/resources";
if (!string.IsNullOrEmpty(type))
resource += $"?type={Uri.EscapeDataString(type!)}";
var response = client.GetAsync(resource).GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveClusterResource[]>() ?? Array.Empty<PveClusterResource>();
var response = client.GetAsync(resource).GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveClusterResource[]>() ?? Array.Empty<PveClusterResource>();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
}
}
+206 -76
View File
@@ -14,8 +14,19 @@ namespace PSProxmoxVE.Core.Services
/// </summary>
public class ContainerService
{
private readonly IPveHttpClient? _injectedClient;
private readonly NodeService _nodeService = new NodeService();
/// <summary>Initializes a new instance that creates its own HTTP clients.</summary>
public ContainerService() { }
/// <summary>Initializes a new instance that uses the supplied HTTP client for all requests.</summary>
/// <param name="client">The HTTP client to use. The caller owns its lifetime.</param>
public ContainerService(IPveHttpClient client)
{
_injectedClient = client ?? throw new ArgumentNullException(nameof(client));
}
// -------------------------------------------------------------------------
// Read operations
// -------------------------------------------------------------------------
@@ -51,10 +62,17 @@ namespace PSProxmoxVE.Core.Services
private PveContainer[] GetContainersOnNode(PveSession session, string node)
{
using var client = new PveHttpClient(session);
var response = client.GetAsync($"nodes/{Uri.EscapeDataString(node)}/lxc").GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveContainer[]>() ?? Array.Empty<PveContainer>();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.GetAsync($"nodes/{Uri.EscapeDataString(node)}/lxc").GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveContainer[]>() ?? Array.Empty<PveContainer>();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -81,11 +99,18 @@ namespace PSProxmoxVE.Core.Services
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/{Uri.EscapeDataString(node)}/lxc/{vmid}/config")
.GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveContainerConfig>() ?? new PveContainerConfig();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.GetAsync($"nodes/{Uri.EscapeDataString(node)}/lxc/{vmid}/config")
.GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveContainerConfig>() ?? new PveContainerConfig();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
// -------------------------------------------------------------------------
@@ -105,12 +130,19 @@ namespace PSProxmoxVE.Core.Services
if (string.IsNullOrWhiteSpace(node)) throw new ArgumentNullException(nameof(node));
if (config == null) throw new ArgumentNullException(nameof(config));
using var client = new PveHttpClient(session);
var formData = config.ToDictionary(
kvp => kvp.Key,
kvp => kvp.Value?.ToString() ?? string.Empty);
client.PutAsync($"nodes/{Uri.EscapeDataString(node)}/lxc/{vmid}/config", formData)
.GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var formData = config.ToDictionary(
kvp => kvp.Key,
kvp => kvp.Value?.ToString() ?? string.Empty);
client.PutAsync($"nodes/{Uri.EscapeDataString(node)}/lxc/{vmid}/config", formData)
.GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
// -------------------------------------------------------------------------
@@ -125,11 +157,18 @@ namespace PSProxmoxVE.Core.Services
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/{Uri.EscapeDataString(node)}/lxc/{vmid}/snapshot")
.GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveSnapshot[]>() ?? Array.Empty<PveSnapshot>();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.GetAsync($"nodes/{Uri.EscapeDataString(node)}/lxc/{vmid}/snapshot")
.GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveSnapshot[]>() ?? Array.Empty<PveSnapshot>();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -153,10 +192,17 @@ namespace PSProxmoxVE.Core.Services
if (!string.IsNullOrEmpty(description))
formData["description"] = description!;
using var client = new PveHttpClient(session);
var response = client.PostAsync($"nodes/{Uri.EscapeDataString(node)}/lxc/{vmid}/snapshot", formData)
.GetAwaiter().GetResult();
return ParseTask(response, node);
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.PostAsync($"nodes/{Uri.EscapeDataString(node)}/lxc/{vmid}/snapshot", formData)
.GetAwaiter().GetResult();
return ParseTask(response, node);
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -172,10 +218,17 @@ namespace PSProxmoxVE.Core.Services
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/{Uri.EscapeDataString(node)}/lxc/{vmid}/snapshot/{Uri.EscapeDataString(snapname)}")
.GetAwaiter().GetResult();
return ParseTask(response, node);
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.DeleteAsync($"nodes/{Uri.EscapeDataString(node)}/lxc/{vmid}/snapshot/{Uri.EscapeDataString(snapname)}")
.GetAwaiter().GetResult();
return ParseTask(response, node);
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -191,10 +244,17 @@ namespace PSProxmoxVE.Core.Services
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/{Uri.EscapeDataString(node)}/lxc/{vmid}/snapshot/{Uri.EscapeDataString(snapname)}/rollback")
.GetAwaiter().GetResult();
return ParseTask(response, node);
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.PostAsync($"nodes/{Uri.EscapeDataString(node)}/lxc/{vmid}/snapshot/{Uri.EscapeDataString(snapname)}/rollback")
.GetAwaiter().GetResult();
return ParseTask(response, node);
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
// -------------------------------------------------------------------------
@@ -213,13 +273,20 @@ namespace PSProxmoxVE.Core.Services
if (string.IsNullOrWhiteSpace(node)) throw new ArgumentNullException(nameof(node));
if (config == null) throw new ArgumentNullException(nameof(config));
using var client = new PveHttpClient(session);
var formData = config.ToDictionary(
kvp => kvp.Key,
kvp => kvp.Value?.ToString() ?? string.Empty);
var response = client.PostAsync($"nodes/{Uri.EscapeDataString(node)}/lxc", formData)
.GetAwaiter().GetResult();
return ParseTask(response, node);
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var formData = config.ToDictionary(
kvp => kvp.Key,
kvp => kvp.Value?.ToString() ?? string.Empty);
var response = client.PostAsync($"nodes/{Uri.EscapeDataString(node)}/lxc", formData)
.GetAwaiter().GetResult();
return ParseTask(response, node);
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>Starts a container. Returns the task UPID.</summary>
@@ -244,10 +311,17 @@ namespace PSProxmoxVE.Core.Services
if (timeoutSeconds.HasValue)
formData["timeout"] = timeoutSeconds.Value.ToString();
using var client = new PveHttpClient(session);
var response = client.PostAsync($"nodes/{Uri.EscapeDataString(node)}/lxc/{vmid}/status/shutdown", formData)
.GetAwaiter().GetResult();
return ParseTask(response, node);
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.PostAsync($"nodes/{Uri.EscapeDataString(node)}/lxc/{vmid}/status/shutdown", formData)
.GetAwaiter().GetResult();
return ParseTask(response, node);
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>Removes a container. Returns the task UPID.</summary>
@@ -261,10 +335,17 @@ namespace PSProxmoxVE.Core.Services
if (string.IsNullOrWhiteSpace(node)) throw new ArgumentNullException(nameof(node));
var purgeParam = purge ? "?purge=1" : "?purge=0";
using var client = new PveHttpClient(session);
var response = client.DeleteAsync($"nodes/{Uri.EscapeDataString(node)}/lxc/{vmid}{purgeParam}")
.GetAwaiter().GetResult();
return ParseTask(response, node);
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.DeleteAsync($"nodes/{Uri.EscapeDataString(node)}/lxc/{vmid}{purgeParam}")
.GetAwaiter().GetResult();
return ParseTask(response, node);
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>Clones a container. Returns the task UPID.</summary>
@@ -288,10 +369,17 @@ namespace PSProxmoxVE.Core.Services
if (!string.IsNullOrEmpty(hostname)) formData["hostname"] = hostname!;
if (!string.IsNullOrEmpty(targetNode)) formData["target"] = targetNode!;
using var client = new PveHttpClient(session);
var response = client.PostAsync($"nodes/{Uri.EscapeDataString(node)}/lxc/{vmid}/clone", formData)
.GetAwaiter().GetResult();
return ParseTask(response, node);
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.PostAsync($"nodes/{Uri.EscapeDataString(node)}/lxc/{vmid}/clone", formData)
.GetAwaiter().GetResult();
return ParseTask(response, node);
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>Migrates a container to another node. Returns the task UPID.</summary>
@@ -312,10 +400,17 @@ namespace PSProxmoxVE.Core.Services
["online"] = online ? "1" : "0"
};
using var client = new PveHttpClient(session);
var response = client.PostAsync($"nodes/{Uri.EscapeDataString(node)}/lxc/{vmid}/migrate", formData)
.GetAwaiter().GetResult();
return ParseTask(response, node);
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.PostAsync($"nodes/{Uri.EscapeDataString(node)}/lxc/{vmid}/migrate", formData)
.GetAwaiter().GetResult();
return ParseTask(response, node);
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
// -------------------------------------------------------------------------
@@ -350,10 +445,17 @@ namespace PSProxmoxVE.Core.Services
["size"] = size
};
using var client = new PveHttpClient(session);
var response = client.PutAsync($"nodes/{Uri.EscapeDataString(node)}/lxc/{vmid}/resize", formData)
.GetAwaiter().GetResult();
return ParseTask(response, node);
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.PutAsync($"nodes/{Uri.EscapeDataString(node)}/lxc/{vmid}/resize", formData)
.GetAwaiter().GetResult();
return ParseTask(response, node);
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -373,10 +475,17 @@ namespace PSProxmoxVE.Core.Services
["delete"] = delete ? "1" : "0"
};
using var client = new PveHttpClient(session);
var response = client.PostAsync($"nodes/{Uri.EscapeDataString(node)}/lxc/{vmid}/move_volume", formData)
.GetAwaiter().GetResult();
return ParseTask(response, node);
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.PostAsync($"nodes/{Uri.EscapeDataString(node)}/lxc/{vmid}/move_volume", formData)
.GetAwaiter().GetResult();
return ParseTask(response, node);
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
// -------------------------------------------------------------------------
@@ -391,10 +500,17 @@ namespace PSProxmoxVE.Core.Services
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.PostAsync($"nodes/{Uri.EscapeDataString(node)}/lxc/{vmid}/template")
.GetAwaiter().GetResult();
return ParseTask(response, node);
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.PostAsync($"nodes/{Uri.EscapeDataString(node)}/lxc/{vmid}/template")
.GetAwaiter().GetResult();
return ParseTask(response, node);
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
// -------------------------------------------------------------------------
@@ -409,11 +525,18 @@ namespace PSProxmoxVE.Core.Services
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/{Uri.EscapeDataString(node)}/lxc/{vmid}/interfaces")
.GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveContainerInterface[]>() ?? Array.Empty<PveContainerInterface>();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.GetAsync($"nodes/{Uri.EscapeDataString(node)}/lxc/{vmid}/interfaces")
.GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveContainerInterface[]>() ?? Array.Empty<PveContainerInterface>();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
// -------------------------------------------------------------------------
@@ -425,10 +548,17 @@ namespace PSProxmoxVE.Core.Services
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.PostAsync($"nodes/{Uri.EscapeDataString(node)}/lxc/{vmid}/status/{action}")
.GetAwaiter().GetResult();
return ParseTask(response, node);
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.PostAsync($"nodes/{Uri.EscapeDataString(node)}/lxc/{vmid}/status/{action}")
.GetAwaiter().GetResult();
return ParseTask(response, node);
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
private static PveTask ParseTask(string response, string node)
+267 -80
View File
@@ -13,6 +13,18 @@ namespace PSProxmoxVE.Core.Services
/// </summary>
public class FirewallService
{
private readonly IPveHttpClient? _injectedClient;
/// <summary>Initializes a new instance that creates its own HTTP clients.</summary>
public FirewallService() { }
/// <summary>Initializes a new instance that uses the supplied HTTP client for all requests.</summary>
/// <param name="client">The HTTP client to use. The caller owns its lifetime.</param>
public FirewallService(IPveHttpClient client)
{
_injectedClient = client ?? throw new ArgumentNullException(nameof(client));
}
// -------------------------------------------------------------------------
// Rules
// -------------------------------------------------------------------------
@@ -25,10 +37,17 @@ namespace PSProxmoxVE.Core.Services
if (session == null) throw new ArgumentNullException(nameof(session));
var basePath = BuildBasePath(level, node, vmid);
using var client = new PveHttpClient(session);
var response = client.GetAsync($"{basePath}/rules").GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveFirewallRule[]>() ?? Array.Empty<PveFirewallRule>();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.GetAsync($"{basePath}/rules").GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveFirewallRule[]>() ?? Array.Empty<PveFirewallRule>();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -41,8 +60,15 @@ namespace PSProxmoxVE.Core.Services
if (config == null) throw new ArgumentNullException(nameof(config));
var basePath = BuildBasePath(level, node, vmid);
using var client = new PveHttpClient(session);
client.PostAsync($"{basePath}/rules", config).GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.PostAsync($"{basePath}/rules", config).GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -55,8 +81,15 @@ namespace PSProxmoxVE.Core.Services
if (config == null) throw new ArgumentNullException(nameof(config));
var basePath = BuildBasePath(level, node, vmid);
using var client = new PveHttpClient(session);
client.PutAsync($"{basePath}/rules/{pos}", config).GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.PutAsync($"{basePath}/rules/{pos}", config).GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -68,8 +101,15 @@ namespace PSProxmoxVE.Core.Services
if (session == null) throw new ArgumentNullException(nameof(session));
var basePath = BuildBasePath(level, node, vmid);
using var client = new PveHttpClient(session);
client.DeleteAsync($"{basePath}/rules/{pos}").GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.DeleteAsync($"{basePath}/rules/{pos}").GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
// -------------------------------------------------------------------------
@@ -83,10 +123,17 @@ namespace PSProxmoxVE.Core.Services
{
if (session == null) throw new ArgumentNullException(nameof(session));
using var client = new PveHttpClient(session);
var response = client.GetAsync("cluster/firewall/groups").GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveFirewallGroup[]>() ?? Array.Empty<PveFirewallGroup>();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.GetAsync("cluster/firewall/groups").GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveFirewallGroup[]>() ?? Array.Empty<PveFirewallGroup>();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -101,8 +148,15 @@ namespace PSProxmoxVE.Core.Services
if (!string.IsNullOrEmpty(comment))
formData["comment"] = comment!;
using var client = new PveHttpClient(session);
client.PostAsync("cluster/firewall/groups", formData).GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.PostAsync("cluster/firewall/groups", formData).GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -113,9 +167,16 @@ namespace PSProxmoxVE.Core.Services
if (session == null) throw new ArgumentNullException(nameof(session));
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullException(nameof(name));
using var client = new PveHttpClient(session);
client.DeleteAsync($"cluster/firewall/groups/{Uri.EscapeDataString(name)}")
.GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.DeleteAsync($"cluster/firewall/groups/{Uri.EscapeDataString(name)}")
.GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -126,11 +187,18 @@ namespace PSProxmoxVE.Core.Services
if (session == null) throw new ArgumentNullException(nameof(session));
if (string.IsNullOrWhiteSpace(group)) throw new ArgumentNullException(nameof(group));
using var client = new PveHttpClient(session);
var response = client.GetAsync($"cluster/firewall/groups/{Uri.EscapeDataString(group)}")
.GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveFirewallRule[]>() ?? Array.Empty<PveFirewallRule>();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.GetAsync($"cluster/firewall/groups/{Uri.EscapeDataString(group)}")
.GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveFirewallRule[]>() ?? Array.Empty<PveFirewallRule>();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -142,9 +210,16 @@ namespace PSProxmoxVE.Core.Services
if (string.IsNullOrWhiteSpace(group)) throw new ArgumentNullException(nameof(group));
if (config == null) throw new ArgumentNullException(nameof(config));
using var client = new PveHttpClient(session);
client.PostAsync($"cluster/firewall/groups/{Uri.EscapeDataString(group)}", config)
.GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.PostAsync($"cluster/firewall/groups/{Uri.EscapeDataString(group)}", config)
.GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -156,9 +231,16 @@ namespace PSProxmoxVE.Core.Services
if (string.IsNullOrWhiteSpace(group)) throw new ArgumentNullException(nameof(group));
if (config == null) throw new ArgumentNullException(nameof(config));
using var client = new PveHttpClient(session);
client.PutAsync($"cluster/firewall/groups/{Uri.EscapeDataString(group)}/{pos}", config)
.GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.PutAsync($"cluster/firewall/groups/{Uri.EscapeDataString(group)}/{pos}", config)
.GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -169,9 +251,16 @@ namespace PSProxmoxVE.Core.Services
if (session == null) throw new ArgumentNullException(nameof(session));
if (string.IsNullOrWhiteSpace(group)) throw new ArgumentNullException(nameof(group));
using var client = new PveHttpClient(session);
client.DeleteAsync($"cluster/firewall/groups/{Uri.EscapeDataString(group)}/{pos}")
.GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.DeleteAsync($"cluster/firewall/groups/{Uri.EscapeDataString(group)}/{pos}")
.GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
// -------------------------------------------------------------------------
@@ -186,10 +275,17 @@ namespace PSProxmoxVE.Core.Services
if (session == null) throw new ArgumentNullException(nameof(session));
var basePath = BuildBasePath(level, node, vmid);
using var client = new PveHttpClient(session);
var response = client.GetAsync($"{basePath}/aliases").GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveFirewallAlias[]>() ?? Array.Empty<PveFirewallAlias>();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.GetAsync($"{basePath}/aliases").GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveFirewallAlias[]>() ?? Array.Empty<PveFirewallAlias>();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -211,8 +307,15 @@ namespace PSProxmoxVE.Core.Services
if (!string.IsNullOrEmpty(comment))
formData["comment"] = comment!;
using var client = new PveHttpClient(session);
client.PostAsync($"{basePath}/aliases", formData).GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.PostAsync($"{basePath}/aliases", formData).GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -231,9 +334,16 @@ namespace PSProxmoxVE.Core.Services
if (!string.IsNullOrEmpty(comment))
formData["comment"] = comment!;
using var client = new PveHttpClient(session);
client.PutAsync($"{basePath}/aliases/{Uri.EscapeDataString(name)}", formData)
.GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.PutAsync($"{basePath}/aliases/{Uri.EscapeDataString(name)}", formData)
.GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -246,9 +356,16 @@ namespace PSProxmoxVE.Core.Services
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullException(nameof(name));
var basePath = BuildBasePath(level, node, vmid);
using var client = new PveHttpClient(session);
client.DeleteAsync($"{basePath}/aliases/{Uri.EscapeDataString(name)}")
.GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.DeleteAsync($"{basePath}/aliases/{Uri.EscapeDataString(name)}")
.GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
// -------------------------------------------------------------------------
@@ -263,10 +380,17 @@ namespace PSProxmoxVE.Core.Services
if (session == null) throw new ArgumentNullException(nameof(session));
var basePath = BuildBasePath(level, node, vmid);
using var client = new PveHttpClient(session);
var response = client.GetAsync($"{basePath}/ipset").GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveFirewallIpSet[]>() ?? Array.Empty<PveFirewallIpSet>();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.GetAsync($"{basePath}/ipset").GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveFirewallIpSet[]>() ?? Array.Empty<PveFirewallIpSet>();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -283,8 +407,15 @@ namespace PSProxmoxVE.Core.Services
if (!string.IsNullOrEmpty(comment))
formData["comment"] = comment!;
using var client = new PveHttpClient(session);
client.PostAsync($"{basePath}/ipset", formData).GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.PostAsync($"{basePath}/ipset", formData).GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -297,9 +428,16 @@ namespace PSProxmoxVE.Core.Services
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullException(nameof(name));
var basePath = BuildBasePath(level, node, vmid);
using var client = new PveHttpClient(session);
client.DeleteAsync($"{basePath}/ipset/{Uri.EscapeDataString(name)}")
.GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.DeleteAsync($"{basePath}/ipset/{Uri.EscapeDataString(name)}")
.GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
// -------------------------------------------------------------------------
@@ -316,11 +454,18 @@ namespace PSProxmoxVE.Core.Services
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullException(nameof(name));
var basePath = BuildBasePath(level, node, vmid);
using var client = new PveHttpClient(session);
var response = client.GetAsync($"{basePath}/ipset/{Uri.EscapeDataString(name)}")
.GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveFirewallIpSetEntry[]>() ?? Array.Empty<PveFirewallIpSetEntry>();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.GetAsync($"{basePath}/ipset/{Uri.EscapeDataString(name)}")
.GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveFirewallIpSetEntry[]>() ?? Array.Empty<PveFirewallIpSetEntry>();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -340,9 +485,16 @@ namespace PSProxmoxVE.Core.Services
if (!string.IsNullOrEmpty(comment))
formData["comment"] = comment!;
using var client = new PveHttpClient(session);
client.PostAsync($"{basePath}/ipset/{Uri.EscapeDataString(name)}", formData)
.GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.PostAsync($"{basePath}/ipset/{Uri.EscapeDataString(name)}", formData)
.GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -362,10 +514,17 @@ namespace PSProxmoxVE.Core.Services
if (!string.IsNullOrEmpty(comment))
formData["comment"] = comment!;
using var client = new PveHttpClient(session);
client.PutAsync(
$"{basePath}/ipset/{Uri.EscapeDataString(name)}/{Uri.EscapeDataString(cidr)}",
formData).GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.PutAsync(
$"{basePath}/ipset/{Uri.EscapeDataString(name)}/{Uri.EscapeDataString(cidr)}",
formData).GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -379,10 +538,17 @@ namespace PSProxmoxVE.Core.Services
if (string.IsNullOrWhiteSpace(cidr)) throw new ArgumentNullException(nameof(cidr));
var basePath = BuildBasePath(level, node, vmid);
using var client = new PveHttpClient(session);
client.DeleteAsync(
$"{basePath}/ipset/{Uri.EscapeDataString(name)}/{Uri.EscapeDataString(cidr)}")
.GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.DeleteAsync(
$"{basePath}/ipset/{Uri.EscapeDataString(name)}/{Uri.EscapeDataString(cidr)}")
.GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
// -------------------------------------------------------------------------
@@ -398,10 +564,17 @@ namespace PSProxmoxVE.Core.Services
if (session == null) throw new ArgumentNullException(nameof(session));
var basePath = BuildBasePath(level, node, vmid);
using var client = new PveHttpClient(session);
var response = client.GetAsync($"{basePath}/options").GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveFirewallOptions>() ?? new PveFirewallOptions();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.GetAsync($"{basePath}/options").GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveFirewallOptions>() ?? new PveFirewallOptions();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -414,8 +587,15 @@ namespace PSProxmoxVE.Core.Services
if (config == null) throw new ArgumentNullException(nameof(config));
var basePath = BuildBasePath(level, node, vmid);
using var client = new PveHttpClient(session);
client.PutAsync($"{basePath}/options", config).GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.PutAsync($"{basePath}/options", config).GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
// -------------------------------------------------------------------------
@@ -435,10 +615,17 @@ namespace PSProxmoxVE.Core.Services
if (!string.IsNullOrEmpty(type))
resource += $"?type={Uri.EscapeDataString(type!)}";
using var client = new PveHttpClient(session);
var response = client.GetAsync(resource).GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveFirewallRef[]>() ?? Array.Empty<PveFirewallRef>();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.GetAsync(resource).GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveFirewallRef[]>() ?? Array.Empty<PveFirewallRef>();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
// -------------------------------------------------------------------------
+338 -134
View File
@@ -15,6 +15,18 @@ namespace PSProxmoxVE.Core.Services
/// </summary>
public class NetworkService
{
private readonly IPveHttpClient? _injectedClient;
/// <summary>Initializes a new instance that creates its own HTTP clients.</summary>
public NetworkService() { }
/// <summary>Initializes a new instance that uses the supplied HTTP client for all requests.</summary>
/// <param name="client">The HTTP client to use. The caller owns its lifetime.</param>
public NetworkService(IPveHttpClient client)
{
_injectedClient = client ?? throw new ArgumentNullException(nameof(client));
}
// -------------------------------------------------------------------------
// Node network interfaces
// -------------------------------------------------------------------------
@@ -36,10 +48,17 @@ namespace PSProxmoxVE.Core.Services
if (!string.IsNullOrEmpty(type))
resource += $"?type={Uri.EscapeDataString(type!)}";
using var client = new PveHttpClient(session);
var response = client.GetAsync(resource).GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveNetwork[]>() ?? Array.Empty<PveNetwork>();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.GetAsync(resource).GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveNetwork[]>() ?? Array.Empty<PveNetwork>();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -58,14 +77,21 @@ namespace PSProxmoxVE.Core.Services
if (string.IsNullOrWhiteSpace(node)) throw new ArgumentNullException(nameof(node));
if (config == null) throw new ArgumentNullException(nameof(config));
using var client = new PveHttpClient(session);
var formData = config.ToDictionary(
kvp => kvp.Key,
kvp => kvp.Value?.ToString() ?? string.Empty);
var response = client.PostAsync($"nodes/{node}/network", formData)
.GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveNetwork>() ?? new PveNetwork();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var formData = config.ToDictionary(
kvp => kvp.Key,
kvp => kvp.Value?.ToString() ?? string.Empty);
var response = client.PostAsync($"nodes/{node}/network", formData)
.GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveNetwork>() ?? new PveNetwork();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -87,12 +113,19 @@ namespace PSProxmoxVE.Core.Services
if (string.IsNullOrWhiteSpace(iface)) throw new ArgumentNullException(nameof(iface));
if (config == null) throw new ArgumentNullException(nameof(config));
using var client = new PveHttpClient(session);
var formData = config.ToDictionary(
kvp => kvp.Key,
kvp => kvp.Value?.ToString() ?? string.Empty);
client.PutAsync($"nodes/{node}/network/{iface}", formData)
.GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var formData = config.ToDictionary(
kvp => kvp.Key,
kvp => kvp.Value?.ToString() ?? string.Empty);
client.PutAsync($"nodes/{node}/network/{iface}", formData)
.GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -108,8 +141,15 @@ namespace PSProxmoxVE.Core.Services
if (string.IsNullOrWhiteSpace(node)) throw new ArgumentNullException(nameof(node));
if (string.IsNullOrWhiteSpace(iface)) throw new ArgumentNullException(nameof(iface));
using var client = new PveHttpClient(session);
client.DeleteAsync($"nodes/{node}/network/{iface}").GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.DeleteAsync($"nodes/{node}/network/{iface}").GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -122,10 +162,17 @@ namespace PSProxmoxVE.Core.Services
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.PutAsync($"nodes/{node}/network")
.GetAwaiter().GetResult();
return ParseTask(response, node);
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.PutAsync($"nodes/{node}/network")
.GetAwaiter().GetResult();
return ParseTask(response, node);
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
// -------------------------------------------------------------------------
@@ -140,11 +187,17 @@ namespace PSProxmoxVE.Core.Services
{
if (session == null) throw new ArgumentNullException(nameof(session));
using var client = new PveHttpClient(session);
var response = client.GetAsync("cluster/sdn/zones").GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveSdnZone[]>() ?? Array.Empty<PveSdnZone>();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.GetAsync("cluster/sdn/zones").GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveSdnZone[]>() ?? Array.Empty<PveSdnZone>();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -155,11 +208,17 @@ namespace PSProxmoxVE.Core.Services
{
if (session == null) throw new ArgumentNullException(nameof(session));
using var client = new PveHttpClient(session);
var response = client.GetAsync("cluster/sdn/vnets").GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveSdnVnet[]>() ?? Array.Empty<PveSdnVnet>();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.GetAsync("cluster/sdn/vnets").GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveSdnVnet[]>() ?? Array.Empty<PveSdnVnet>();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -172,17 +231,23 @@ namespace PSProxmoxVE.Core.Services
Dictionary<string, object> config)
{
if (session == null) throw new ArgumentNullException(nameof(session));
if (config == null) throw new ArgumentNullException(nameof(config));
using var client = new PveHttpClient(session);
var formData = config.ToDictionary(
kvp => kvp.Key,
kvp => kvp.Value?.ToString() ?? string.Empty);
var response = client.PostAsync("cluster/sdn/zones", formData)
.GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveSdnZone>() ?? new PveSdnZone();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var formData = config.ToDictionary(
kvp => kvp.Key,
kvp => kvp.Value?.ToString() ?? string.Empty);
var response = client.PostAsync("cluster/sdn/zones", formData)
.GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveSdnZone>() ?? new PveSdnZone();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -193,11 +258,17 @@ namespace PSProxmoxVE.Core.Services
public void RemoveSdnZone(PveSession session, string zone)
{
if (session == null) throw new ArgumentNullException(nameof(session));
if (string.IsNullOrWhiteSpace(zone)) throw new ArgumentNullException(nameof(zone));
using var client = new PveHttpClient(session);
client.DeleteAsync($"cluster/sdn/zones/{zone}").GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.DeleteAsync($"cluster/sdn/zones/{zone}").GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -210,17 +281,23 @@ namespace PSProxmoxVE.Core.Services
Dictionary<string, object> config)
{
if (session == null) throw new ArgumentNullException(nameof(session));
if (config == null) throw new ArgumentNullException(nameof(config));
using var client = new PveHttpClient(session);
var formData = config.ToDictionary(
kvp => kvp.Key,
kvp => kvp.Value?.ToString() ?? string.Empty);
var response = client.PostAsync("cluster/sdn/vnets", formData)
.GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveSdnVnet>() ?? new PveSdnVnet();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var formData = config.ToDictionary(
kvp => kvp.Key,
kvp => kvp.Value?.ToString() ?? string.Empty);
var response = client.PostAsync("cluster/sdn/vnets", formData)
.GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveSdnVnet>() ?? new PveSdnVnet();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -231,11 +308,17 @@ namespace PSProxmoxVE.Core.Services
public void RemoveSdnVnet(PveSession session, string vnet)
{
if (session == null) throw new ArgumentNullException(nameof(session));
if (string.IsNullOrWhiteSpace(vnet)) throw new ArgumentNullException(nameof(vnet));
using var client = new PveHttpClient(session);
client.DeleteAsync($"cluster/sdn/vnets/{vnet}").GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.DeleteAsync($"cluster/sdn/vnets/{vnet}").GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
// -------------------------------------------------------------------------
@@ -250,14 +333,20 @@ namespace PSProxmoxVE.Core.Services
public PveSdnSubnet[] GetSdnSubnets(PveSession session, string vnet)
{
if (session == null) throw new ArgumentNullException(nameof(session));
if (string.IsNullOrWhiteSpace(vnet)) throw new ArgumentNullException(nameof(vnet));
using var client = new PveHttpClient(session);
var response = client.GetAsync($"cluster/sdn/vnets/{Uri.EscapeDataString(vnet)}/subnets")
.GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveSdnSubnet[]>() ?? Array.Empty<PveSdnSubnet>();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.GetAsync($"cluster/sdn/vnets/{Uri.EscapeDataString(vnet)}/subnets")
.GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveSdnSubnet[]>() ?? Array.Empty<PveSdnSubnet>();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -272,16 +361,22 @@ namespace PSProxmoxVE.Core.Services
Dictionary<string, object> config)
{
if (session == null) throw new ArgumentNullException(nameof(session));
if (string.IsNullOrWhiteSpace(vnet)) throw new ArgumentNullException(nameof(vnet));
if (config == null) throw new ArgumentNullException(nameof(config));
using var client = new PveHttpClient(session);
var formData = config.ToDictionary(
kvp => kvp.Key,
kvp => kvp.Value?.ToString() ?? string.Empty);
client.PostAsync($"cluster/sdn/vnets/{Uri.EscapeDataString(vnet)}/subnets", formData)
.GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var formData = config.ToDictionary(
kvp => kvp.Key,
kvp => kvp.Value?.ToString() ?? string.Empty);
client.PostAsync($"cluster/sdn/vnets/{Uri.EscapeDataString(vnet)}/subnets", formData)
.GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -293,14 +388,20 @@ namespace PSProxmoxVE.Core.Services
public void RemoveSdnSubnet(PveSession session, string vnet, string subnet)
{
if (session == null) throw new ArgumentNullException(nameof(session));
if (string.IsNullOrWhiteSpace(vnet)) throw new ArgumentNullException(nameof(vnet));
if (string.IsNullOrWhiteSpace(subnet)) throw new ArgumentNullException(nameof(subnet));
using var client = new PveHttpClient(session);
client.DeleteAsync(
$"cluster/sdn/vnets/{Uri.EscapeDataString(vnet)}/subnets/{Uri.EscapeDataString(subnet)}")
.GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.DeleteAsync(
$"cluster/sdn/vnets/{Uri.EscapeDataString(vnet)}/subnets/{Uri.EscapeDataString(subnet)}")
.GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
// -------------------------------------------------------------------------
@@ -314,11 +415,17 @@ namespace PSProxmoxVE.Core.Services
{
if (session == null) throw new ArgumentNullException(nameof(session));
using var client = new PveHttpClient(session);
var response = client.GetAsync("cluster/sdn/ipams").GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveSdnIpam[]>() ?? Array.Empty<PveSdnIpam>();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.GetAsync("cluster/sdn/ipams").GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveSdnIpam[]>() ?? Array.Empty<PveSdnIpam>();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -327,11 +434,17 @@ namespace PSProxmoxVE.Core.Services
public void CreateSdnIpam(PveSession session, Dictionary<string, string> config)
{
if (session == null) throw new ArgumentNullException(nameof(session));
if (config == null) throw new ArgumentNullException(nameof(config));
using var client = new PveHttpClient(session);
client.PostAsync("cluster/sdn/ipams", config).GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.PostAsync("cluster/sdn/ipams", config).GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -340,12 +453,18 @@ namespace PSProxmoxVE.Core.Services
public void RemoveSdnIpam(PveSession session, string ipam)
{
if (session == null) throw new ArgumentNullException(nameof(session));
if (string.IsNullOrWhiteSpace(ipam)) throw new ArgumentNullException(nameof(ipam));
using var client = new PveHttpClient(session);
client.DeleteAsync($"cluster/sdn/ipams/{Uri.EscapeDataString(ipam)}")
.GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.DeleteAsync($"cluster/sdn/ipams/{Uri.EscapeDataString(ipam)}")
.GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
// -------------------------------------------------------------------------
@@ -359,11 +478,17 @@ namespace PSProxmoxVE.Core.Services
{
if (session == null) throw new ArgumentNullException(nameof(session));
using var client = new PveHttpClient(session);
var response = client.GetAsync("cluster/sdn/dns").GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveSdnDns[]>() ?? Array.Empty<PveSdnDns>();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.GetAsync("cluster/sdn/dns").GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveSdnDns[]>() ?? Array.Empty<PveSdnDns>();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -372,11 +497,17 @@ namespace PSProxmoxVE.Core.Services
public void CreateSdnDnsPlugin(PveSession session, Dictionary<string, string> config)
{
if (session == null) throw new ArgumentNullException(nameof(session));
if (config == null) throw new ArgumentNullException(nameof(config));
using var client = new PveHttpClient(session);
client.PostAsync("cluster/sdn/dns", config).GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.PostAsync("cluster/sdn/dns", config).GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -385,12 +516,18 @@ namespace PSProxmoxVE.Core.Services
public void RemoveSdnDnsPlugin(PveSession session, string dns)
{
if (session == null) throw new ArgumentNullException(nameof(session));
if (string.IsNullOrWhiteSpace(dns)) throw new ArgumentNullException(nameof(dns));
using var client = new PveHttpClient(session);
client.DeleteAsync($"cluster/sdn/dns/{Uri.EscapeDataString(dns)}")
.GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.DeleteAsync($"cluster/sdn/dns/{Uri.EscapeDataString(dns)}")
.GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
// -------------------------------------------------------------------------
@@ -404,11 +541,17 @@ namespace PSProxmoxVE.Core.Services
{
if (session == null) throw new ArgumentNullException(nameof(session));
using var client = new PveHttpClient(session);
var response = client.GetAsync("cluster/sdn/controllers").GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveSdnController[]>() ?? Array.Empty<PveSdnController>();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.GetAsync("cluster/sdn/controllers").GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveSdnController[]>() ?? Array.Empty<PveSdnController>();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -417,11 +560,17 @@ namespace PSProxmoxVE.Core.Services
public void CreateSdnController(PveSession session, Dictionary<string, string> config)
{
if (session == null) throw new ArgumentNullException(nameof(session));
if (config == null) throw new ArgumentNullException(nameof(config));
using var client = new PveHttpClient(session);
client.PostAsync("cluster/sdn/controllers", config).GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.PostAsync("cluster/sdn/controllers", config).GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -430,12 +579,18 @@ namespace PSProxmoxVE.Core.Services
public void RemoveSdnController(PveSession session, string controller)
{
if (session == null) throw new ArgumentNullException(nameof(session));
if (string.IsNullOrWhiteSpace(controller)) throw new ArgumentNullException(nameof(controller));
using var client = new PveHttpClient(session);
client.DeleteAsync($"cluster/sdn/controllers/{Uri.EscapeDataString(controller)}")
.GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.DeleteAsync($"cluster/sdn/controllers/{Uri.EscapeDataString(controller)}")
.GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
// -------------------------------------------------------------------------
@@ -449,9 +604,16 @@ namespace PSProxmoxVE.Core.Services
{
if (session == null) throw new ArgumentNullException(nameof(session));
using var client = new PveHttpClient(session);
client.PutAsync("cluster/sdn", new Dictionary<string, string>())
.GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.PutAsync("cluster/sdn", new Dictionary<string, string>())
.GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -463,9 +625,16 @@ namespace PSProxmoxVE.Core.Services
if (string.IsNullOrWhiteSpace(zone)) throw new ArgumentNullException(nameof(zone));
if (config == null) throw new ArgumentNullException(nameof(config));
using var client = new PveHttpClient(session);
client.PutAsync($"cluster/sdn/zones/{Uri.EscapeDataString(zone)}", config)
.GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.PutAsync($"cluster/sdn/zones/{Uri.EscapeDataString(zone)}", config)
.GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -477,9 +646,16 @@ namespace PSProxmoxVE.Core.Services
if (string.IsNullOrWhiteSpace(vnet)) throw new ArgumentNullException(nameof(vnet));
if (config == null) throw new ArgumentNullException(nameof(config));
using var client = new PveHttpClient(session);
client.PutAsync($"cluster/sdn/vnets/{Uri.EscapeDataString(vnet)}", config)
.GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.PutAsync($"cluster/sdn/vnets/{Uri.EscapeDataString(vnet)}", config)
.GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -492,10 +668,17 @@ namespace PSProxmoxVE.Core.Services
if (string.IsNullOrWhiteSpace(subnet)) throw new ArgumentNullException(nameof(subnet));
if (config == null) throw new ArgumentNullException(nameof(config));
using var client = new PveHttpClient(session);
client.PutAsync(
$"cluster/sdn/vnets/{Uri.EscapeDataString(vnet)}/subnets/{Uri.EscapeDataString(subnet)}",
config).GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.PutAsync(
$"cluster/sdn/vnets/{Uri.EscapeDataString(vnet)}/subnets/{Uri.EscapeDataString(subnet)}",
config).GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -507,9 +690,16 @@ namespace PSProxmoxVE.Core.Services
if (string.IsNullOrWhiteSpace(controller)) throw new ArgumentNullException(nameof(controller));
if (config == null) throw new ArgumentNullException(nameof(config));
using var client = new PveHttpClient(session);
client.PutAsync($"cluster/sdn/controllers/{Uri.EscapeDataString(controller)}", config)
.GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.PutAsync($"cluster/sdn/controllers/{Uri.EscapeDataString(controller)}", config)
.GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -521,9 +711,16 @@ namespace PSProxmoxVE.Core.Services
if (string.IsNullOrWhiteSpace(ipam)) throw new ArgumentNullException(nameof(ipam));
if (config == null) throw new ArgumentNullException(nameof(config));
using var client = new PveHttpClient(session);
client.PutAsync($"cluster/sdn/ipams/{Uri.EscapeDataString(ipam)}", config)
.GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.PutAsync($"cluster/sdn/ipams/{Uri.EscapeDataString(ipam)}", config)
.GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -535,9 +732,16 @@ namespace PSProxmoxVE.Core.Services
if (string.IsNullOrWhiteSpace(dns)) throw new ArgumentNullException(nameof(dns));
if (config == null) throw new ArgumentNullException(nameof(config));
using var client = new PveHttpClient(session);
client.PutAsync($"cluster/sdn/dns/{Uri.EscapeDataString(dns)}", config)
.GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.PutAsync($"cluster/sdn/dns/{Uri.EscapeDataString(dns)}", config)
.GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
// -------------------------------------------------------------------------
+114 -33
View File
@@ -14,6 +14,24 @@ namespace PSProxmoxVE.Core.Services
/// </summary>
public class NodeService
{
private readonly IPveHttpClient? _injectedClient;
/// <summary>
/// Initializes a new instance of <see cref="NodeService"/> with no injected client.
/// Each method will create and dispose its own <see cref="PveHttpClient"/>.
/// </summary>
public NodeService() { }
/// <summary>
/// Initializes a new instance of <see cref="NodeService"/> with an injected HTTP client.
/// The caller owns the client's lifetime; this service will not dispose it.
/// </summary>
/// <param name="client">The HTTP client to use for all requests.</param>
public NodeService(IPveHttpClient client)
{
_injectedClient = client ?? throw new ArgumentNullException(nameof(client));
}
/// <summary>
/// Returns all cluster nodes.
/// </summary>
@@ -21,10 +39,17 @@ namespace PSProxmoxVE.Core.Services
{
if (session == null) throw new ArgumentNullException(nameof(session));
using var client = new PveHttpClient(session);
var response = client.GetAsync("nodes").GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveNode[]>() ?? Array.Empty<PveNode>();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.GetAsync("nodes").GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveNode[]>() ?? Array.Empty<PveNode>();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -35,10 +60,17 @@ namespace PSProxmoxVE.Core.Services
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}/status").GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveNodeStatus>() ?? new PveNodeStatus();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.GetAsync($"nodes/{node}/status").GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveNodeStatus>() ?? new PveNodeStatus();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -51,10 +83,17 @@ namespace PSProxmoxVE.Core.Services
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}/config").GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data as JObject ?? new JObject();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.GetAsync($"nodes/{node}/config").GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data as JObject ?? new JObject();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -69,8 +108,15 @@ namespace PSProxmoxVE.Core.Services
if (string.IsNullOrWhiteSpace(node)) throw new ArgumentNullException(nameof(node));
if (config == null) throw new ArgumentNullException(nameof(config));
using var client = new PveHttpClient(session);
client.PutAsync($"nodes/{node}/config", config).GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.PutAsync($"nodes/{node}/config", config).GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -83,10 +129,17 @@ namespace PSProxmoxVE.Core.Services
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}/dns").GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data as JObject ?? new JObject();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.GetAsync($"nodes/{node}/dns").GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data as JObject ?? new JObject();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -101,8 +154,15 @@ namespace PSProxmoxVE.Core.Services
if (string.IsNullOrWhiteSpace(node)) throw new ArgumentNullException(nameof(node));
if (config == null) throw new ArgumentNullException(nameof(config));
using var client = new PveHttpClient(session);
client.PutAsync($"nodes/{node}/dns", config).GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.PutAsync($"nodes/{node}/dns", config).GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -117,9 +177,16 @@ namespace PSProxmoxVE.Core.Services
if (string.IsNullOrWhiteSpace(node)) throw new ArgumentNullException(nameof(node));
var formData = config ?? new Dictionary<string, string>();
using var client = new PveHttpClient(session);
var response = client.PostAsync($"nodes/{node}/startall", formData).GetAwaiter().GetResult();
return ParseTask(response, node);
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.PostAsync($"nodes/{node}/startall", formData).GetAwaiter().GetResult();
return ParseTask(response, node);
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -134,9 +201,16 @@ namespace PSProxmoxVE.Core.Services
if (string.IsNullOrWhiteSpace(node)) throw new ArgumentNullException(nameof(node));
var formData = config ?? new Dictionary<string, string>();
using var client = new PveHttpClient(session);
var response = client.PostAsync($"nodes/{node}/stopall", formData).GetAwaiter().GetResult();
return ParseTask(response, node);
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.PostAsync($"nodes/{node}/stopall", formData).GetAwaiter().GetResult();
return ParseTask(response, node);
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -146,13 +220,20 @@ namespace PSProxmoxVE.Core.Services
{
if (session == null) throw new ArgumentNullException(nameof(session));
using var client = new PveHttpClient(session);
var response = client.GetAsync("version").GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
var versionStr = data?["version"]?.ToString();
if (string.IsNullOrEmpty(versionStr))
throw new InvalidOperationException("Failed to retrieve PVE version from API response.");
return PveVersion.Parse(versionStr!);
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.GetAsync("version").GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
var versionStr = data?["version"]?.ToString();
if (string.IsNullOrEmpty(versionStr))
throw new InvalidOperationException("Failed to retrieve PVE version from API response.");
return PveVersion.Parse(versionStr!);
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
// -------------------------------------------------------------------------
+73 -20
View File
@@ -12,6 +12,24 @@ namespace PSProxmoxVE.Core.Services
/// </summary>
public class PoolService
{
private readonly IPveHttpClient? _injectedClient;
/// <summary>
/// Initializes a new instance of <see cref="PoolService"/> with no injected client.
/// Each method will create and dispose its own <see cref="PveHttpClient"/>.
/// </summary>
public PoolService() { }
/// <summary>
/// Initializes a new instance of <see cref="PoolService"/> with an injected HTTP client.
/// The caller owns the client's lifetime; this service will not dispose it.
/// </summary>
/// <param name="client">The HTTP client to use for all requests.</param>
public PoolService(IPveHttpClient client)
{
_injectedClient = client ?? throw new ArgumentNullException(nameof(client));
}
/// <summary>
/// Returns all resource pools.
/// </summary>
@@ -19,10 +37,17 @@ namespace PSProxmoxVE.Core.Services
{
if (session == null) throw new ArgumentNullException(nameof(session));
using var client = new PveHttpClient(session);
var response = client.GetAsync("pools").GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PvePool[]>() ?? Array.Empty<PvePool>();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.GetAsync("pools").GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PvePool[]>() ?? Array.Empty<PvePool>();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -33,11 +58,18 @@ namespace PSProxmoxVE.Core.Services
if (session == null) throw new ArgumentNullException(nameof(session));
if (string.IsNullOrWhiteSpace(poolId)) throw new ArgumentNullException(nameof(poolId));
using var client = new PveHttpClient(session);
var response = client.GetAsync($"pools/{Uri.EscapeDataString(poolId)}")
.GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PvePool>();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.GetAsync($"pools/{Uri.EscapeDataString(poolId)}")
.GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PvePool>();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -48,12 +80,19 @@ namespace PSProxmoxVE.Core.Services
if (session == null) throw new ArgumentNullException(nameof(session));
if (string.IsNullOrWhiteSpace(poolId)) throw new ArgumentNullException(nameof(poolId));
using var client = new PveHttpClient(session);
var config = new Dictionary<string, string> { { "poolid", poolId } };
if (!string.IsNullOrEmpty(comment))
config["comment"] = comment!;
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var config = new Dictionary<string, string> { { "poolid", poolId } };
if (!string.IsNullOrEmpty(comment))
config["comment"] = comment!;
client.PostAsync("pools", config).GetAwaiter().GetResult();
client.PostAsync("pools", config).GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -65,9 +104,16 @@ namespace PSProxmoxVE.Core.Services
if (string.IsNullOrWhiteSpace(poolId)) throw new ArgumentNullException(nameof(poolId));
if (config == null) throw new ArgumentNullException(nameof(config));
using var client = new PveHttpClient(session);
client.PutAsync($"pools/{Uri.EscapeDataString(poolId)}", config)
.GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.PutAsync($"pools/{Uri.EscapeDataString(poolId)}", config)
.GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -78,9 +124,16 @@ namespace PSProxmoxVE.Core.Services
if (session == null) throw new ArgumentNullException(nameof(session));
if (string.IsNullOrWhiteSpace(poolId)) throw new ArgumentNullException(nameof(poolId));
using var client = new PveHttpClient(session);
client.DeleteAsync($"pools/{Uri.EscapeDataString(poolId)}")
.GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.DeleteAsync($"pools/{Uri.EscapeDataString(poolId)}")
.GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
}
}
@@ -13,6 +13,22 @@ namespace PSProxmoxVE.Core.Services
/// </summary>
public class SnapshotService
{
private readonly IPveHttpClient? _injectedClient;
/// <summary>
/// Initializes a new instance of the <see cref="SnapshotService"/> class.
/// </summary>
public SnapshotService() { }
/// <summary>
/// Initializes a new instance of the <see cref="SnapshotService"/> class with an injected HTTP client.
/// </summary>
/// <param name="client">The HTTP client to use for API calls. The caller owns its lifetime.</param>
public SnapshotService(IPveHttpClient client)
{
_injectedClient = client ?? throw new ArgumentNullException(nameof(client));
}
/// <summary>
/// Returns all snapshots for a VM.
/// </summary>
@@ -24,11 +40,18 @@ namespace PSProxmoxVE.Core.Services
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/{Uri.EscapeDataString(node)}/qemu/{vmid}/snapshot")
.GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveSnapshot[]>() ?? Array.Empty<PveSnapshot>();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.GetAsync($"nodes/{Uri.EscapeDataString(node)}/qemu/{vmid}/snapshot")
.GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveSnapshot[]>() ?? Array.Empty<PveSnapshot>();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -60,10 +83,17 @@ namespace PSProxmoxVE.Core.Services
if (!string.IsNullOrEmpty(description))
formData["description"] = description!;
using var client = new PveHttpClient(session);
var response = client.PostAsync($"nodes/{Uri.EscapeDataString(node)}/qemu/{vmid}/snapshot", formData)
.GetAwaiter().GetResult();
return ParseTask(response, node);
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.PostAsync($"nodes/{Uri.EscapeDataString(node)}/qemu/{vmid}/snapshot", formData)
.GetAwaiter().GetResult();
return ParseTask(response, node);
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -83,10 +113,17 @@ namespace PSProxmoxVE.Core.Services
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/{Uri.EscapeDataString(node)}/qemu/{vmid}/snapshot/{Uri.EscapeDataString(snapname)}")
.GetAwaiter().GetResult();
return ParseTask(response, node);
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.DeleteAsync($"nodes/{Uri.EscapeDataString(node)}/qemu/{vmid}/snapshot/{Uri.EscapeDataString(snapname)}")
.GetAwaiter().GetResult();
return ParseTask(response, node);
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -106,10 +143,17 @@ namespace PSProxmoxVE.Core.Services
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/{Uri.EscapeDataString(node)}/qemu/{vmid}/snapshot/{Uri.EscapeDataString(snapname)}/rollback")
.GetAwaiter().GetResult();
return ParseTask(response, node);
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.PostAsync($"nodes/{Uri.EscapeDataString(node)}/qemu/{vmid}/snapshot/{Uri.EscapeDataString(snapname)}/rollback")
.GetAwaiter().GetResult();
return ParseTask(response, node);
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
// -------------------------------------------------------------------------
+143 -48
View File
@@ -14,6 +14,24 @@ namespace PSProxmoxVE.Core.Services
/// </summary>
public class StorageService
{
private readonly IPveHttpClient? _injectedClient;
/// <summary>
/// Initializes a new instance of <see cref="StorageService"/> with no injected client.
/// Each method will create and dispose its own <see cref="PveHttpClient"/>.
/// </summary>
public StorageService() { }
/// <summary>
/// Initializes a new instance of <see cref="StorageService"/> with an injected HTTP client.
/// The caller owns the client's lifetime; this service will not dispose it.
/// </summary>
/// <param name="client">The HTTP client to use for all requests.</param>
public StorageService(IPveHttpClient client)
{
_injectedClient = client ?? throw new ArgumentNullException(nameof(client));
}
// -------------------------------------------------------------------------
// Read operations
// -------------------------------------------------------------------------
@@ -32,10 +50,17 @@ namespace PSProxmoxVE.Core.Services
? $"nodes/{Uri.EscapeDataString(node)}/storage"
: "storage";
using var client = new PveHttpClient(session);
var response = client.GetAsync(resource).GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveStorage[]>() ?? Array.Empty<PveStorage>();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.GetAsync(resource).GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveStorage[]>() ?? Array.Empty<PveStorage>();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -61,10 +86,17 @@ namespace PSProxmoxVE.Core.Services
if (!string.IsNullOrEmpty(contentType))
resource += $"?content={Uri.EscapeDataString(contentType!)}";
using var client = new PveHttpClient(session);
var response = client.GetAsync(resource).GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveStorageContent[]>() ?? Array.Empty<PveStorageContent>();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.GetAsync(resource).GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveStorageContent[]>() ?? Array.Empty<PveStorageContent>();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
// -------------------------------------------------------------------------
@@ -100,16 +132,23 @@ namespace PSProxmoxVE.Core.Services
["content"] = "iso"
};
using var client = new PveHttpClient(session);
var response = client.UploadFileAsync(
$"nodes/{Uri.EscapeDataString(node)}/storage/{Uri.EscapeDataString(storage)}/upload",
filePath,
formFields,
checksum,
checksumAlgorithm,
progressCallback)
.GetAwaiter().GetResult();
return ParseTask(response, node);
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.UploadFileAsync(
$"nodes/{Uri.EscapeDataString(node)}/storage/{Uri.EscapeDataString(storage)}/upload",
filePath,
formFields,
checksum,
checksumAlgorithm,
progressCallback)
.GetAwaiter().GetResult();
return ParseTask(response, node);
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -143,10 +182,17 @@ namespace PSProxmoxVE.Core.Services
["content"] = contentType
};
using var client = new PveHttpClient(session);
var response = client.PostAsync($"nodes/{Uri.EscapeDataString(node)}/storage/{Uri.EscapeDataString(storage)}/download-url", formData)
.GetAwaiter().GetResult();
return ParseTask(response, node);
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.PostAsync($"nodes/{Uri.EscapeDataString(node)}/storage/{Uri.EscapeDataString(storage)}/download-url", formData)
.GetAwaiter().GetResult();
return ParseTask(response, node);
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
// -------------------------------------------------------------------------
@@ -164,13 +210,20 @@ namespace PSProxmoxVE.Core.Services
if (session == null) throw new ArgumentNullException(nameof(session));
if (config == null) throw new ArgumentNullException(nameof(config));
using var client = new PveHttpClient(session);
var formData = config.ToDictionary(
kvp => kvp.Key,
kvp => kvp.Value?.ToString() ?? string.Empty);
var response = client.PostAsync("storage", formData).GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveStorage>() ?? new PveStorage();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var formData = config.ToDictionary(
kvp => kvp.Key,
kvp => kvp.Value?.ToString() ?? string.Empty);
var response = client.PostAsync("storage", formData).GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveStorage>() ?? new PveStorage();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -183,8 +236,15 @@ namespace PSProxmoxVE.Core.Services
if (session == null) throw new ArgumentNullException(nameof(session));
if (string.IsNullOrWhiteSpace(storage)) throw new ArgumentNullException(nameof(storage));
using var client = new PveHttpClient(session);
client.DeleteAsync($"storage/{Uri.EscapeDataString(storage)}").GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.DeleteAsync($"storage/{Uri.EscapeDataString(storage)}").GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -199,9 +259,16 @@ namespace PSProxmoxVE.Core.Services
if (string.IsNullOrWhiteSpace(storage)) throw new ArgumentNullException(nameof(storage));
if (config == null) throw new ArgumentNullException(nameof(config));
using var client = new PveHttpClient(session);
client.PutAsync($"storage/{Uri.EscapeDataString(storage)}", config)
.GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.PutAsync($"storage/{Uri.EscapeDataString(storage)}", config)
.GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
// -------------------------------------------------------------------------
@@ -220,10 +287,17 @@ namespace PSProxmoxVE.Core.Services
if (string.IsNullOrWhiteSpace(node)) throw new ArgumentNullException(nameof(node));
if (string.IsNullOrWhiteSpace(storage)) throw new ArgumentNullException(nameof(storage));
using var client = new PveHttpClient(session);
var response = client.GetAsync($"nodes/{Uri.EscapeDataString(node)}/storage/{Uri.EscapeDataString(storage)}/status").GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveStorageStatus>() ?? new PveStorageStatus();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.GetAsync($"nodes/{Uri.EscapeDataString(node)}/storage/{Uri.EscapeDataString(storage)}/status").GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveStorageStatus>() ?? new PveStorageStatus();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -240,9 +314,16 @@ namespace PSProxmoxVE.Core.Services
if (string.IsNullOrWhiteSpace(storage)) throw new ArgumentNullException(nameof(storage));
if (string.IsNullOrWhiteSpace(volume)) throw new ArgumentNullException(nameof(volume));
using var client = new PveHttpClient(session);
client.DeleteAsync($"nodes/{Uri.EscapeDataString(node)}/storage/{Uri.EscapeDataString(storage)}/content/{Uri.EscapeDataString(volume)}")
.GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.DeleteAsync($"nodes/{Uri.EscapeDataString(node)}/storage/{Uri.EscapeDataString(storage)}/content/{Uri.EscapeDataString(volume)}")
.GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -261,9 +342,16 @@ namespace PSProxmoxVE.Core.Services
if (string.IsNullOrWhiteSpace(volume)) throw new ArgumentNullException(nameof(volume));
if (config == null) throw new ArgumentNullException(nameof(config));
using var client = new PveHttpClient(session);
client.PutAsync($"nodes/{Uri.EscapeDataString(node)}/storage/{Uri.EscapeDataString(storage)}/content/{Uri.EscapeDataString(volume)}", config)
.GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.PutAsync($"nodes/{Uri.EscapeDataString(node)}/storage/{Uri.EscapeDataString(storage)}/content/{Uri.EscapeDataString(volume)}", config)
.GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -280,10 +368,17 @@ namespace PSProxmoxVE.Core.Services
if (string.IsNullOrWhiteSpace(storage)) throw new ArgumentNullException(nameof(storage));
if (config == null) throw new ArgumentNullException(nameof(config));
using var client = new PveHttpClient(session);
var response = client.PostAsync($"nodes/{Uri.EscapeDataString(node)}/storage/{Uri.EscapeDataString(storage)}/content", config)
.GetAwaiter().GetResult();
return ParseTask(response, node);
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.PostAsync($"nodes/{Uri.EscapeDataString(node)}/storage/{Uri.EscapeDataString(storage)}/content", config)
.GetAwaiter().GetResult();
return ParseTask(response, node);
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
// -------------------------------------------------------------------------
+74 -34
View File
@@ -14,10 +14,22 @@ namespace PSProxmoxVE.Core.Services
/// </summary>
public class TaskService
{
private readonly IPveHttpClient? _injectedClient;
private static readonly TimeSpan DefaultTimeout = TimeSpan.FromMinutes(10);
private static readonly TimeSpan DefaultPollInterval = TimeSpan.FromSeconds(2);
private static readonly TimeSpan MinPollInterval = TimeSpan.FromSeconds(1);
/// <summary>Initializes a new instance that creates its own HTTP clients.</summary>
public TaskService() { }
/// <summary>Initializes a new instance that uses the supplied HTTP client for all requests.</summary>
/// <param name="client">The HTTP client to use. The caller owns its lifetime.</param>
public TaskService(IPveHttpClient client)
{
_injectedClient = client ?? throw new ArgumentNullException(nameof(client));
}
/// <summary>
/// Returns the current status of a task identified by its UPID.
/// </summary>
@@ -27,14 +39,21 @@ namespace PSProxmoxVE.Core.Services
if (string.IsNullOrWhiteSpace(node)) throw new ArgumentNullException(nameof(node));
if (string.IsNullOrWhiteSpace(upid)) throw new ArgumentNullException(nameof(upid));
using var client = new PveHttpClient(session);
var encodedUpid = Uri.EscapeDataString(upid);
var response = client.GetAsync($"nodes/{node}/tasks/{encodedUpid}/status")
.GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
var task = data?.ToObject<PveTask>() ?? new PveTask { Upid = upid };
task.Node = node;
return task;
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var encodedUpid = Uri.EscapeDataString(upid);
var response = client.GetAsync($"nodes/{node}/tasks/{encodedUpid}/status")
.GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
var task = data?.ToObject<PveTask>() ?? new PveTask { Upid = upid };
task.Node = node;
return task;
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -46,12 +65,19 @@ namespace PSProxmoxVE.Core.Services
if (string.IsNullOrWhiteSpace(node)) throw new ArgumentNullException(nameof(node));
if (string.IsNullOrWhiteSpace(upid)) throw new ArgumentNullException(nameof(upid));
using var client = new PveHttpClient(session);
var encodedUpid = Uri.EscapeDataString(upid);
var response = client.GetAsync($"nodes/{node}/tasks/{encodedUpid}/log")
.GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveTaskLog[]>() ?? Array.Empty<PveTaskLog>();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var encodedUpid = Uri.EscapeDataString(upid);
var response = client.GetAsync($"nodes/{node}/tasks/{encodedUpid}/log")
.GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveTaskLog[]>() ?? Array.Empty<PveTaskLog>();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -119,23 +145,30 @@ namespace PSProxmoxVE.Core.Services
if (session == null) throw new ArgumentNullException(nameof(session));
if (string.IsNullOrWhiteSpace(node)) throw new ArgumentNullException(nameof(node));
using var client = new PveHttpClient(session);
var queryParts = new List<string> { $"limit={limit}" };
if (vmid.HasValue)
queryParts.Add($"vmid={vmid.Value}");
if (!string.IsNullOrEmpty(source))
queryParts.Add($"source={Uri.EscapeDataString(source!)}");
if (!string.IsNullOrEmpty(typeFilter))
queryParts.Add($"typefilter={Uri.EscapeDataString(typeFilter!)}");
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var queryParts = new List<string> { $"limit={limit}" };
if (vmid.HasValue)
queryParts.Add($"vmid={vmid.Value}");
if (!string.IsNullOrEmpty(source))
queryParts.Add($"source={Uri.EscapeDataString(source!)}");
if (!string.IsNullOrEmpty(typeFilter))
queryParts.Add($"typefilter={Uri.EscapeDataString(typeFilter!)}");
var query = string.Join("&", queryParts);
var response = client.GetAsync($"nodes/{Uri.EscapeDataString(node)}/tasks?{query}")
.GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
var tasks = data?.ToObject<PveTask[]>() ?? Array.Empty<PveTask>();
foreach (var t in tasks)
t.Node ??= node;
return tasks;
var query = string.Join("&", queryParts);
var response = client.GetAsync($"nodes/{Uri.EscapeDataString(node)}/tasks?{query}")
.GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
var tasks = data?.ToObject<PveTask[]>() ?? Array.Empty<PveTask>();
foreach (var t in tasks)
t.Node ??= node;
return tasks;
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -150,10 +183,17 @@ namespace PSProxmoxVE.Core.Services
if (string.IsNullOrWhiteSpace(node)) throw new ArgumentNullException(nameof(node));
if (string.IsNullOrWhiteSpace(upid)) throw new ArgumentNullException(nameof(upid));
using var client = new PveHttpClient(session);
var encodedUpid = Uri.EscapeDataString(upid);
client.DeleteAsync($"nodes/{Uri.EscapeDataString(node)}/tasks/{encodedUpid}")
.GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var encodedUpid = Uri.EscapeDataString(upid);
client.DeleteAsync($"nodes/{Uri.EscapeDataString(node)}/tasks/{encodedUpid}")
.GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
}
}
@@ -13,8 +13,23 @@ namespace PSProxmoxVE.Core.Services
/// </summary>
public class TemplateService
{
private readonly IPveHttpClient? _injectedClient;
private readonly VmService _vmService = new VmService();
/// <summary>
/// Initializes a new instance of the <see cref="TemplateService"/> class.
/// </summary>
public TemplateService() { }
/// <summary>
/// Initializes a new instance of the <see cref="TemplateService"/> class with an injected HTTP client.
/// </summary>
/// <param name="client">The HTTP client to use for API calls. The caller owns its lifetime.</param>
public TemplateService(IPveHttpClient client)
{
_injectedClient = client ?? throw new ArgumentNullException(nameof(client));
}
/// <summary>
/// Returns all VM templates. If <paramref name="node"/> is null, searches all cluster nodes.
/// </summary>
@@ -43,10 +58,17 @@ namespace PSProxmoxVE.Core.Services
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.PostAsync($"nodes/{node}/qemu/{vmid}/template")
.GetAwaiter().GetResult();
return ParseTask(response, node);
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.PostAsync($"nodes/{node}/qemu/{vmid}/template")
.GetAwaiter().GetResult();
return ParseTask(response, node);
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
+293 -107
View File
@@ -13,6 +13,24 @@ namespace PSProxmoxVE.Core.Services
/// </summary>
public class UserService
{
private readonly IPveHttpClient? _injectedClient;
/// <summary>
/// Initializes a new instance of <see cref="UserService"/> with no injected client.
/// Each method will create and dispose its own <see cref="PveHttpClient"/>.
/// </summary>
public UserService() { }
/// <summary>
/// Initializes a new instance of <see cref="UserService"/> with an injected HTTP client.
/// The caller owns the client's lifetime; this service will not dispose it.
/// </summary>
/// <param name="client">The HTTP client to use for all requests.</param>
public UserService(IPveHttpClient client)
{
_injectedClient = client ?? throw new ArgumentNullException(nameof(client));
}
// -------------------------------------------------------------------------
// Users
// -------------------------------------------------------------------------
@@ -23,10 +41,17 @@ namespace PSProxmoxVE.Core.Services
{
if (session == null) throw new ArgumentNullException(nameof(session));
using var client = new PveHttpClient(session);
var response = client.GetAsync("access/users").GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveUser[]>() ?? Array.Empty<PveUser>();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.GetAsync("access/users").GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveUser[]>() ?? Array.Empty<PveUser>();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>Returns a single user by their user ID (e.g. "admin@pam").</summary>
@@ -37,15 +62,22 @@ namespace PSProxmoxVE.Core.Services
if (session == null) throw new ArgumentNullException(nameof(session));
if (string.IsNullOrWhiteSpace(userId)) throw new ArgumentNullException(nameof(userId));
using var client = new PveHttpClient(session);
var encodedId = Uri.EscapeDataString(userId);
var response = client.GetAsync($"access/users/{encodedId}").GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
var user = data?.ToObject<PveUser>() ?? new PveUser();
// The single-user endpoint may not echo back the userid
if (string.IsNullOrEmpty(user.UserId))
user.UserId = userId;
return user;
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var encodedId = Uri.EscapeDataString(userId);
var response = client.GetAsync($"access/users/{encodedId}").GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
var user = data?.ToObject<PveUser>() ?? new PveUser();
// The single-user endpoint may not echo back the userid
if (string.IsNullOrEmpty(user.UserId))
user.UserId = userId;
return user;
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -69,8 +101,15 @@ namespace PSProxmoxVE.Core.Services
formData[kvp.Key] = kvp.Value?.ToString() ?? string.Empty;
}
using var client = new PveHttpClient(session);
client.PostAsync("access/users", formData).GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.PostAsync("access/users", formData).GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>Removes a user account.</summary>
@@ -81,9 +120,16 @@ namespace PSProxmoxVE.Core.Services
if (session == null) throw new ArgumentNullException(nameof(session));
if (string.IsNullOrWhiteSpace(userId)) throw new ArgumentNullException(nameof(userId));
using var client = new PveHttpClient(session);
var encodedId = Uri.EscapeDataString(userId);
client.DeleteAsync($"access/users/{encodedId}").GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var encodedId = Uri.EscapeDataString(userId);
client.DeleteAsync($"access/users/{encodedId}").GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>Updates one or more properties of an existing user.</summary>
@@ -99,12 +145,19 @@ namespace PSProxmoxVE.Core.Services
if (string.IsNullOrWhiteSpace(userId)) throw new ArgumentNullException(nameof(userId));
if (config == null) throw new ArgumentNullException(nameof(config));
using var client = new PveHttpClient(session);
var encodedId = Uri.EscapeDataString(userId);
var formData = config.ToDictionary(
kvp => kvp.Key,
kvp => kvp.Value?.ToString() ?? string.Empty);
client.PutAsync($"access/users/{encodedId}", formData).GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var encodedId = Uri.EscapeDataString(userId);
var formData = config.ToDictionary(
kvp => kvp.Key,
kvp => kvp.Value?.ToString() ?? string.Empty);
client.PutAsync($"access/users/{encodedId}", formData).GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
// -------------------------------------------------------------------------
@@ -119,14 +172,21 @@ namespace PSProxmoxVE.Core.Services
if (session == null) throw new ArgumentNullException(nameof(session));
if (string.IsNullOrWhiteSpace(userId)) throw new ArgumentNullException(nameof(userId));
using var client = new PveHttpClient(session);
var encodedId = Uri.EscapeDataString(userId);
var response = client.GetAsync($"access/users/{encodedId}/token").GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
var tokens = data?.ToObject<PveApiToken[]>() ?? Array.Empty<PveApiToken>();
foreach (var t in tokens)
t.UserId = userId;
return tokens;
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var encodedId = Uri.EscapeDataString(userId);
var response = client.GetAsync($"access/users/{encodedId}/token").GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
var tokens = data?.ToObject<PveApiToken[]>() ?? Array.Empty<PveApiToken>();
foreach (var t in tokens)
t.UserId = userId;
return tokens;
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -158,18 +218,25 @@ namespace PSProxmoxVE.Core.Services
if (expire.HasValue) formData["expire"] = expire.Value.ToString();
if (privilegeSeparation.HasValue) formData["privsep"] = privilegeSeparation.Value ? "1" : "0";
using var client = new PveHttpClient(session);
var encodedUser = Uri.EscapeDataString(userId);
var encodedToken = Uri.EscapeDataString(tokenId);
var response = client.PostAsync(
$"access/users/{encodedUser}/token/{encodedToken}", formData)
.GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var encodedUser = Uri.EscapeDataString(userId);
var encodedToken = Uri.EscapeDataString(tokenId);
var response = client.PostAsync(
$"access/users/{encodedUser}/token/{encodedToken}", formData)
.GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
var token = data?.ToObject<PveApiToken>() ?? new PveApiToken();
token.UserId = userId;
token.TokenId = tokenId;
return token;
var data = JObject.Parse(response)["data"];
var token = data?.ToObject<PveApiToken>() ?? new PveApiToken();
token.UserId = userId;
token.TokenId = tokenId;
return token;
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>Removes an API token.</summary>
@@ -182,11 +249,18 @@ namespace PSProxmoxVE.Core.Services
if (string.IsNullOrWhiteSpace(userId)) throw new ArgumentNullException(nameof(userId));
if (string.IsNullOrWhiteSpace(tokenId)) throw new ArgumentNullException(nameof(tokenId));
using var client = new PveHttpClient(session);
var encodedUser = Uri.EscapeDataString(userId);
var encodedToken = Uri.EscapeDataString(tokenId);
client.DeleteAsync($"access/users/{encodedUser}/token/{encodedToken}")
.GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var encodedUser = Uri.EscapeDataString(userId);
var encodedToken = Uri.EscapeDataString(tokenId);
client.DeleteAsync($"access/users/{encodedUser}/token/{encodedToken}")
.GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -199,11 +273,18 @@ namespace PSProxmoxVE.Core.Services
if (string.IsNullOrWhiteSpace(tokenId)) throw new ArgumentNullException(nameof(tokenId));
if (config == null) throw new ArgumentNullException(nameof(config));
using var client = new PveHttpClient(session);
var encodedUser = Uri.EscapeDataString(userId);
var encodedToken = Uri.EscapeDataString(tokenId);
client.PutAsync($"access/users/{encodedUser}/token/{encodedToken}", config)
.GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var encodedUser = Uri.EscapeDataString(userId);
var encodedToken = Uri.EscapeDataString(tokenId);
client.PutAsync($"access/users/{encodedUser}/token/{encodedToken}", config)
.GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
// -------------------------------------------------------------------------
@@ -216,10 +297,17 @@ namespace PSProxmoxVE.Core.Services
{
if (session == null) throw new ArgumentNullException(nameof(session));
using var client = new PveHttpClient(session);
var response = client.GetAsync("access/roles").GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveRole[]>() ?? Array.Empty<PveRole>();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.GetAsync("access/roles").GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveRole[]>() ?? Array.Empty<PveRole>();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>Creates a new role.</summary>
@@ -235,8 +323,15 @@ namespace PSProxmoxVE.Core.Services
if (!string.IsNullOrEmpty(privileges))
formData["privs"] = privileges!;
using var client = new PveHttpClient(session);
client.PostAsync("access/roles", formData).GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.PostAsync("access/roles", formData).GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>Removes a role.</summary>
@@ -247,9 +342,16 @@ namespace PSProxmoxVE.Core.Services
if (session == null) throw new ArgumentNullException(nameof(session));
if (string.IsNullOrWhiteSpace(roleId)) throw new ArgumentNullException(nameof(roleId));
using var client = new PveHttpClient(session);
client.DeleteAsync($"access/roles/{Uri.EscapeDataString(roleId)}")
.GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.DeleteAsync($"access/roles/{Uri.EscapeDataString(roleId)}")
.GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -265,9 +367,16 @@ namespace PSProxmoxVE.Core.Services
if (string.IsNullOrWhiteSpace(privileges)) throw new ArgumentNullException(nameof(privileges));
var formData = new Dictionary<string, string> { ["privs"] = privileges };
using var client = new PveHttpClient(session);
client.PutAsync($"access/roles/{Uri.EscapeDataString(roleId)}", formData)
.GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.PutAsync($"access/roles/{Uri.EscapeDataString(roleId)}", formData)
.GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
// -------------------------------------------------------------------------
@@ -280,10 +389,17 @@ namespace PSProxmoxVE.Core.Services
{
if (session == null) throw new ArgumentNullException(nameof(session));
using var client = new PveHttpClient(session);
var response = client.GetAsync("access/groups").GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveGroup[]>() ?? Array.Empty<PveGroup>();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.GetAsync("access/groups").GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveGroup[]>() ?? Array.Empty<PveGroup>();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>Creates a new group.</summary>
@@ -299,8 +415,15 @@ namespace PSProxmoxVE.Core.Services
if (!string.IsNullOrEmpty(comment))
formData["comment"] = comment!;
using var client = new PveHttpClient(session);
client.PostAsync("access/groups", formData).GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.PostAsync("access/groups", formData).GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>Updates a group's properties.</summary>
@@ -313,9 +436,16 @@ namespace PSProxmoxVE.Core.Services
if (string.IsNullOrWhiteSpace(groupId)) throw new ArgumentNullException(nameof(groupId));
if (config == null) throw new ArgumentNullException(nameof(config));
using var client = new PveHttpClient(session);
client.PutAsync($"access/groups/{Uri.EscapeDataString(groupId)}", config)
.GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.PutAsync($"access/groups/{Uri.EscapeDataString(groupId)}", config)
.GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>Removes a group.</summary>
@@ -326,9 +456,16 @@ namespace PSProxmoxVE.Core.Services
if (session == null) throw new ArgumentNullException(nameof(session));
if (string.IsNullOrWhiteSpace(groupId)) throw new ArgumentNullException(nameof(groupId));
using var client = new PveHttpClient(session);
client.DeleteAsync($"access/groups/{Uri.EscapeDataString(groupId)}")
.GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.DeleteAsync($"access/groups/{Uri.EscapeDataString(groupId)}")
.GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
// -------------------------------------------------------------------------
@@ -341,10 +478,17 @@ namespace PSProxmoxVE.Core.Services
{
if (session == null) throw new ArgumentNullException(nameof(session));
using var client = new PveHttpClient(session);
var response = client.GetAsync("access/domains").GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveDomain[]>() ?? Array.Empty<PveDomain>();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.GetAsync("access/domains").GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveDomain[]>() ?? Array.Empty<PveDomain>();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>Creates a new authentication domain/realm.</summary>
@@ -355,8 +499,15 @@ namespace PSProxmoxVE.Core.Services
if (session == null) throw new ArgumentNullException(nameof(session));
if (config == null) throw new ArgumentNullException(nameof(config));
using var client = new PveHttpClient(session);
client.PostAsync("access/domains", config).GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.PostAsync("access/domains", config).GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>Updates an authentication domain/realm.</summary>
@@ -369,9 +520,16 @@ namespace PSProxmoxVE.Core.Services
if (string.IsNullOrWhiteSpace(realm)) throw new ArgumentNullException(nameof(realm));
if (config == null) throw new ArgumentNullException(nameof(config));
using var client = new PveHttpClient(session);
client.PutAsync($"access/domains/{Uri.EscapeDataString(realm)}", config)
.GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.PutAsync($"access/domains/{Uri.EscapeDataString(realm)}", config)
.GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>Removes an authentication domain/realm.</summary>
@@ -382,9 +540,16 @@ namespace PSProxmoxVE.Core.Services
if (session == null) throw new ArgumentNullException(nameof(session));
if (string.IsNullOrWhiteSpace(realm)) throw new ArgumentNullException(nameof(realm));
using var client = new PveHttpClient(session);
client.DeleteAsync($"access/domains/{Uri.EscapeDataString(realm)}")
.GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.DeleteAsync($"access/domains/{Uri.EscapeDataString(realm)}")
.GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
// -------------------------------------------------------------------------
@@ -407,8 +572,15 @@ namespace PSProxmoxVE.Core.Services
["password"] = password
};
using var client = new PveHttpClient(session);
client.PutAsync("access/password", formData).GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.PutAsync("access/password", formData).GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
// -------------------------------------------------------------------------
@@ -437,22 +609,29 @@ namespace PSProxmoxVE.Core.Services
if (queryParts.Count > 0)
resource += "?" + string.Join("&", queryParts);
using var client = new PveHttpClient(session);
var response = client.GetAsync(resource).GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
// /access/permissions returns an object keyed by path, not an array
if (data == null) return Array.Empty<PvePermission>();
if (data.Type == JTokenType.Array)
return data.ToObject<PvePermission[]>() ?? Array.Empty<PvePermission>();
// Unwrap path-keyed object into flat list
var result = new List<PvePermission>();
foreach (var prop in ((JObject)data).Properties())
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var perm = new PvePermission { Path = prop.Name };
result.Add(perm);
var response = client.GetAsync(resource).GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
// /access/permissions returns an object keyed by path, not an array
if (data == null) return Array.Empty<PvePermission>();
if (data.Type == JTokenType.Array)
return data.ToObject<PvePermission[]>() ?? Array.Empty<PvePermission>();
// Unwrap path-keyed object into flat list
var result = new List<PvePermission>();
foreach (var prop in ((JObject)data).Properties())
{
var perm = new PvePermission { Path = prop.Name };
result.Add(perm);
}
return result.ToArray();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
return result.ToArray();
}
/// <summary>
@@ -488,8 +667,15 @@ namespace PSProxmoxVE.Core.Services
if (!string.IsNullOrEmpty(users)) formData["users"] = users!;
if (!string.IsNullOrEmpty(groups)) formData["groups"] = groups!;
using var client = new PveHttpClient(session);
client.PutAsync("access/acl", formData).GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.PutAsync("access/acl", formData).GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
}
}
+314 -131
View File
@@ -14,8 +14,19 @@ namespace PSProxmoxVE.Core.Services
/// </summary>
public class VmService
{
private readonly IPveHttpClient? _injectedClient;
private readonly NodeService _nodeService = new NodeService();
/// <summary>Initializes a new instance that creates its own HTTP clients.</summary>
public VmService() { }
/// <summary>Initializes a new instance that uses the supplied HTTP client for all requests.</summary>
/// <param name="client">The HTTP client to use. The caller owns its lifetime.</param>
public VmService(IPveHttpClient client)
{
_injectedClient = client ?? throw new ArgumentNullException(nameof(client));
}
// -------------------------------------------------------------------------
// Read operations
// -------------------------------------------------------------------------
@@ -55,10 +66,17 @@ namespace PSProxmoxVE.Core.Services
private PveVm[] GetVmsOnNode(PveSession session, string node)
{
using var client = new PveHttpClient(session);
var response = client.GetAsync($"nodes/{Uri.EscapeDataString(node)}/qemu").GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveVm[]>() ?? Array.Empty<PveVm>();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.GetAsync($"nodes/{Uri.EscapeDataString(node)}/qemu").GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveVm[]>() ?? Array.Empty<PveVm>();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -93,19 +111,26 @@ namespace PSProxmoxVE.Core.Services
if (string.IsNullOrWhiteSpace(node)) throw new ArgumentNullException(nameof(node));
if (vm == null) throw new ArgumentNullException(nameof(vm));
using var client = new PveHttpClient(session);
var response = client.GetAsync($"nodes/{Uri.EscapeDataString(node)}/qemu/{vm.VmId}/status/current")
.GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
if (data == null) return;
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.GetAsync($"nodes/{Uri.EscapeDataString(node)}/qemu/{vm.VmId}/status/current")
.GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
if (data == null) return;
vm.QmpStatus = data["qmpstatus"]?.ToString();
vm.Status = data["status"]?.ToString() ?? vm.Status;
vm.Pid = data["pid"]?.ToObject<int?>();
vm.Uptime = data["uptime"]?.ToObject<long?>();
vm.CpuCount = data["cpus"]?.ToObject<int?>() ?? vm.CpuCount;
vm.MaxMem = data["maxmem"]?.ToObject<long?>() ?? vm.MaxMem;
vm.MaxDisk = data["maxdisk"]?.ToObject<long?>() ?? vm.MaxDisk;
vm.QmpStatus = data["qmpstatus"]?.ToString();
vm.Status = data["status"]?.ToString() ?? vm.Status;
vm.Pid = data["pid"]?.ToObject<int?>();
vm.Uptime = data["uptime"]?.ToObject<long?>();
vm.CpuCount = data["cpus"]?.ToObject<int?>() ?? vm.CpuCount;
vm.MaxMem = data["maxmem"]?.ToObject<long?>() ?? vm.MaxMem;
vm.MaxDisk = data["maxdisk"]?.ToObject<long?>() ?? vm.MaxDisk;
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -119,11 +144,18 @@ namespace PSProxmoxVE.Core.Services
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/{Uri.EscapeDataString(node)}/qemu/{vmid}/config")
.GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveVmConfig>() ?? new PveVmConfig();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.GetAsync($"nodes/{Uri.EscapeDataString(node)}/qemu/{vmid}/config")
.GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveVmConfig>() ?? new PveVmConfig();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
// -------------------------------------------------------------------------
@@ -143,12 +175,19 @@ namespace PSProxmoxVE.Core.Services
if (string.IsNullOrWhiteSpace(node)) throw new ArgumentNullException(nameof(node));
if (config == null) throw new ArgumentNullException(nameof(config));
using var client = new PveHttpClient(session);
var formData = config.ToDictionary(
kvp => kvp.Key,
kvp => kvp.Value?.ToString() ?? string.Empty);
client.PutAsync($"nodes/{Uri.EscapeDataString(node)}/qemu/{vmid}/config", formData)
.GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var formData = config.ToDictionary(
kvp => kvp.Key,
kvp => kvp.Value?.ToString() ?? string.Empty);
client.PutAsync($"nodes/{Uri.EscapeDataString(node)}/qemu/{vmid}/config", formData)
.GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
// -------------------------------------------------------------------------
@@ -199,11 +238,18 @@ namespace PSProxmoxVE.Core.Services
[disk] = diskValue
};
using var client = new PveHttpClient(session);
// POST (not PUT) because import-from triggers a background task
var response = client.PostAsync($"nodes/{Uri.EscapeDataString(node)}/qemu/{vmid}/config", formData)
.GetAwaiter().GetResult();
return ParseTask(response, node);
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
// POST (not PUT) because import-from triggers a background task
var response = client.PostAsync($"nodes/{Uri.EscapeDataString(node)}/qemu/{vmid}/config", formData)
.GetAwaiter().GetResult();
return ParseTask(response, node);
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
// -------------------------------------------------------------------------
@@ -222,13 +268,20 @@ namespace PSProxmoxVE.Core.Services
if (string.IsNullOrWhiteSpace(node)) throw new ArgumentNullException(nameof(node));
if (config == null) throw new ArgumentNullException(nameof(config));
using var client = new PveHttpClient(session);
var formData = config.ToDictionary(
kvp => kvp.Key,
kvp => kvp.Value?.ToString() ?? string.Empty);
var response = client.PostAsync($"nodes/{Uri.EscapeDataString(node)}/qemu", formData)
.GetAwaiter().GetResult();
return ParseTask(response, node);
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var formData = config.ToDictionary(
kvp => kvp.Key,
kvp => kvp.Value?.ToString() ?? string.Empty);
var response = client.PostAsync($"nodes/{Uri.EscapeDataString(node)}/qemu", formData)
.GetAwaiter().GetResult();
return ParseTask(response, node);
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>Starts a VM. Returns the task UPID.</summary>
@@ -259,10 +312,17 @@ namespace PSProxmoxVE.Core.Services
if (timeoutSeconds.HasValue)
formData["timeout"] = timeoutSeconds.Value.ToString();
using var client = new PveHttpClient(session);
var response = client.PostAsync($"nodes/{Uri.EscapeDataString(node)}/qemu/{vmid}/status/shutdown", formData)
.GetAwaiter().GetResult();
return ParseTask(response, node);
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.PostAsync($"nodes/{Uri.EscapeDataString(node)}/qemu/{vmid}/status/shutdown", formData)
.GetAwaiter().GetResult();
return ParseTask(response, node);
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>Resets a VM (hard reset). Returns the task UPID.</summary>
@@ -303,10 +363,17 @@ namespace PSProxmoxVE.Core.Services
if (string.IsNullOrWhiteSpace(node)) throw new ArgumentNullException(nameof(node));
var purgeParam = purge ? "?purge=1" : "?purge=0";
using var client = new PveHttpClient(session);
var response = client.DeleteAsync($"nodes/{Uri.EscapeDataString(node)}/qemu/{vmid}{purgeParam}")
.GetAwaiter().GetResult();
return ParseTask(response, node);
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.DeleteAsync($"nodes/{Uri.EscapeDataString(node)}/qemu/{vmid}{purgeParam}")
.GetAwaiter().GetResult();
return ParseTask(response, node);
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -339,10 +406,17 @@ namespace PSProxmoxVE.Core.Services
if (!string.IsNullOrEmpty(name)) formData["name"] = name!;
if (!string.IsNullOrEmpty(targetNode)) formData["target"] = targetNode!;
using var client = new PveHttpClient(session);
var response = client.PostAsync($"nodes/{Uri.EscapeDataString(node)}/qemu/{vmid}/clone", formData)
.GetAwaiter().GetResult();
return ParseTask(response, node);
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.PostAsync($"nodes/{Uri.EscapeDataString(node)}/qemu/{vmid}/clone", formData)
.GetAwaiter().GetResult();
return ParseTask(response, node);
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -370,10 +444,17 @@ namespace PSProxmoxVE.Core.Services
["online"] = online ? "1" : "0"
};
using var client = new PveHttpClient(session);
var response = client.PostAsync($"nodes/{Uri.EscapeDataString(node)}/qemu/{vmid}/migrate", formData)
.GetAwaiter().GetResult();
return ParseTask(response, node);
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.PostAsync($"nodes/{Uri.EscapeDataString(node)}/qemu/{vmid}/migrate", formData)
.GetAwaiter().GetResult();
return ParseTask(response, node);
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -404,10 +485,17 @@ namespace PSProxmoxVE.Core.Services
["size"] = size
};
using var client = new PveHttpClient(session);
var response = client.PutAsync($"nodes/{Uri.EscapeDataString(node)}/qemu/{vmid}/resize", formData)
.GetAwaiter().GetResult();
return ParseTask(response, node);
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.PutAsync($"nodes/{Uri.EscapeDataString(node)}/qemu/{vmid}/resize", formData)
.GetAwaiter().GetResult();
return ParseTask(response, node);
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
// -------------------------------------------------------------------------
@@ -419,10 +507,17 @@ namespace PSProxmoxVE.Core.Services
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.PostAsync($"nodes/{Uri.EscapeDataString(node)}/qemu/{vmid}/status/{action}")
.GetAwaiter().GetResult();
return ParseTask(response, node);
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.PostAsync($"nodes/{Uri.EscapeDataString(node)}/qemu/{vmid}/status/{action}")
.GetAwaiter().GetResult();
return ParseTask(response, node);
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
private static PveTask ParseTask(string response, string node)
@@ -449,7 +544,7 @@ namespace PSProxmoxVE.Core.Services
if (session == null) throw new ArgumentNullException(nameof(session));
if (string.IsNullOrWhiteSpace(node)) throw new ArgumentNullException(nameof(node));
using var client = new PveHttpClient(session);
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.PostAsync($"nodes/{Uri.EscapeDataString(node)}/qemu/{vmid}/agent/ping").GetAwaiter().GetResult();
@@ -459,6 +554,10 @@ namespace PSProxmoxVE.Core.Services
{
return false;
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -469,12 +568,19 @@ namespace PSProxmoxVE.Core.Services
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/{Uri.EscapeDataString(node)}/qemu/{vmid}/agent/network-get-interfaces")
.GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
var result = data?["result"];
return result?.ToObject<PveGuestNetworkInterface[]>() ?? Array.Empty<PveGuestNetworkInterface>();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.GetAsync($"nodes/{Uri.EscapeDataString(node)}/qemu/{vmid}/agent/network-get-interfaces")
.GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
var result = data?["result"];
return result?.ToObject<PveGuestNetworkInterface[]>() ?? Array.Empty<PveGuestNetworkInterface>();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -488,23 +594,30 @@ namespace PSProxmoxVE.Core.Services
if (string.IsNullOrWhiteSpace(node)) throw new ArgumentNullException(nameof(node));
if (string.IsNullOrWhiteSpace(command)) throw new ArgumentNullException(nameof(command));
using var client = new PveHttpClient(session);
var data = new Dictionary<string, string>
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
["command"] = command
};
var data = new Dictionary<string, string>
{
["command"] = command
};
if (args != null && args.Length > 0)
{
// PVE expects input-data for arguments passed as a JSON-encoded string array
var argsJson = Newtonsoft.Json.JsonConvert.SerializeObject(args);
data["input-data"] = argsJson;
if (args != null && args.Length > 0)
{
// PVE expects input-data for arguments passed as a JSON-encoded string array
var argsJson = Newtonsoft.Json.JsonConvert.SerializeObject(args);
data["input-data"] = argsJson;
}
var response = client.PostAsync($"nodes/{Uri.EscapeDataString(node)}/qemu/{vmid}/agent/exec", data)
.GetAwaiter().GetResult();
var pid = JObject.Parse(response)["data"]?["pid"]?.ToObject<int>() ?? 0;
return pid;
}
finally
{
if (_injectedClient == null) client.Dispose();
}
var response = client.PostAsync($"nodes/{Uri.EscapeDataString(node)}/qemu/{vmid}/agent/exec", data)
.GetAwaiter().GetResult();
var pid = JObject.Parse(response)["data"]?["pid"]?.ToObject<int>() ?? 0;
return pid;
}
/// <summary>
@@ -515,10 +628,17 @@ namespace PSProxmoxVE.Core.Services
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/{Uri.EscapeDataString(node)}/qemu/{vmid}/agent/exec-status?pid={pid}")
.GetAwaiter().GetResult();
return JObject.Parse(response)["data"] as JObject ?? new JObject();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.GetAsync($"nodes/{Uri.EscapeDataString(node)}/qemu/{vmid}/agent/exec-status?pid={pid}")
.GetAwaiter().GetResult();
return JObject.Parse(response)["data"] as JObject ?? new JObject();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
// -------------------------------------------------------------------------
@@ -544,10 +664,17 @@ namespace PSProxmoxVE.Core.Services
if (!string.IsNullOrEmpty(format))
formData["format"] = format!;
using var client = new PveHttpClient(session);
var response = client.PostAsync($"nodes/{Uri.EscapeDataString(node)}/qemu/{vmid}/move_disk", formData)
.GetAwaiter().GetResult();
return ParseTask(response, node);
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.PostAsync($"nodes/{Uri.EscapeDataString(node)}/qemu/{vmid}/move_disk", formData)
.GetAwaiter().GetResult();
return ParseTask(response, node);
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -566,9 +693,16 @@ namespace PSProxmoxVE.Core.Services
if (force)
formData["force"] = "1";
using var client = new PveHttpClient(session);
client.PutAsync($"nodes/{Uri.EscapeDataString(node)}/qemu/{vmid}/unlink", formData)
.GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.PutAsync($"nodes/{Uri.EscapeDataString(node)}/qemu/{vmid}/unlink", formData)
.GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
// -------------------------------------------------------------------------
@@ -583,12 +717,19 @@ namespace PSProxmoxVE.Core.Services
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/{Uri.EscapeDataString(node)}/qemu/{vmid}/agent/get-osinfo")
.GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
var result = data?["result"];
return result?.ToObject<PveGuestOsInfo>();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.GetAsync($"nodes/{Uri.EscapeDataString(node)}/qemu/{vmid}/agent/get-osinfo")
.GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
var result = data?["result"];
return result?.ToObject<PveGuestOsInfo>();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -599,12 +740,19 @@ namespace PSProxmoxVE.Core.Services
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/{Uri.EscapeDataString(node)}/qemu/{vmid}/agent/get-fsinfo")
.GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
var result = data?["result"];
return result?.ToObject<PveGuestFsInfo[]>() ?? Array.Empty<PveGuestFsInfo>();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.GetAsync($"nodes/{Uri.EscapeDataString(node)}/qemu/{vmid}/agent/get-fsinfo")
.GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
var result = data?["result"];
return result?.ToObject<PveGuestFsInfo[]>() ?? Array.Empty<PveGuestFsInfo>();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -616,11 +764,18 @@ namespace PSProxmoxVE.Core.Services
if (string.IsNullOrWhiteSpace(node)) throw new ArgumentNullException(nameof(node));
if (string.IsNullOrWhiteSpace(file)) throw new ArgumentNullException(nameof(file));
using var client = new PveHttpClient(session);
var response = client.GetAsync($"nodes/{Uri.EscapeDataString(node)}/qemu/{vmid}/agent/file-read?file={Uri.EscapeDataString(file)}")
.GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?["content"]?.ToString() ?? string.Empty;
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.GetAsync($"nodes/{Uri.EscapeDataString(node)}/qemu/{vmid}/agent/file-read?file={Uri.EscapeDataString(file)}")
.GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?["content"]?.ToString() ?? string.Empty;
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -638,9 +793,16 @@ namespace PSProxmoxVE.Core.Services
["content"] = content
};
using var client = new PveHttpClient(session);
client.PostAsync($"nodes/{Uri.EscapeDataString(node)}/qemu/{vmid}/agent/file-write", formData)
.GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.PostAsync($"nodes/{Uri.EscapeDataString(node)}/qemu/{vmid}/agent/file-write", formData)
.GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -660,9 +822,16 @@ namespace PSProxmoxVE.Core.Services
if (crypted)
formData["crypted"] = "1";
using var client = new PveHttpClient(session);
client.PostAsync($"nodes/{Uri.EscapeDataString(node)}/qemu/{vmid}/agent/set-user-password", formData)
.GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.PostAsync($"nodes/{Uri.EscapeDataString(node)}/qemu/{vmid}/agent/set-user-password", formData)
.GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
/// <summary>
@@ -673,9 +842,16 @@ namespace PSProxmoxVE.Core.Services
if (session == null) throw new ArgumentNullException(nameof(session));
if (string.IsNullOrWhiteSpace(node)) throw new ArgumentNullException(nameof(node));
using var client = new PveHttpClient(session);
client.PostAsync($"nodes/{Uri.EscapeDataString(node)}/qemu/{vmid}/agent/fstrim")
.GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.PostAsync($"nodes/{Uri.EscapeDataString(node)}/qemu/{vmid}/agent/fstrim")
.GetAwaiter().GetResult();
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
// -------------------------------------------------------------------------
@@ -710,17 +886,24 @@ namespace PSProxmoxVE.Core.Services
["content"] = "import"
};
using var client = new PveHttpClient(session);
var response = client.UploadFileAsync(
$"nodes/{Uri.EscapeDataString(node)}/storage/{Uri.EscapeDataString(storage)}/upload",
ovaPath,
formFields,
progressCallback: progressCallback)
.GetAwaiter().GetResult();
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.UploadFileAsync(
$"nodes/{Uri.EscapeDataString(node)}/storage/{Uri.EscapeDataString(storage)}/upload",
ovaPath,
formFields,
progressCallback: progressCallback)
.GetAwaiter().GetResult();
var root = JObject.Parse(response);
var upid = root["data"]?.ToString() ?? string.Empty;
return new PveTask { Upid = upid, Node = node, Status = "running" };
var root = JObject.Parse(response);
var upid = root["data"]?.ToString() ?? string.Empty;
return new PveTask { Upid = upid, Node = node, Status = "running" };
}
finally
{
if (_injectedClient == null) client.Dispose();
}
}
}
}
@@ -26,6 +26,7 @@ namespace PSProxmoxVE.Cmdlets.Tasks
/// <para type="description">Filter tasks by VM ID.</para>
/// </summary>
[Parameter(Mandatory = false, HelpMessage = "Filter tasks by VM ID.")]
[ValidateRange(100, 999999999)]
public int? VmId { get; set; }
/// <summary>
@@ -1,3 +1,4 @@
using System;
using System.Management.Automation;
using Newtonsoft.Json.Linq;
using PSProxmoxVE.Core.Client;
@@ -49,7 +50,7 @@ namespace PSProxmoxVE.Cmdlets.Templates
{
if (string.IsNullOrEmpty(node)) continue;
var json = client.GetAsync($"nodes/{node}/qemu").GetAwaiter().GetResult();
var json = client.GetAsync($"nodes/{Uri.EscapeDataString(node)}/qemu").GetAwaiter().GetResult();
var root = JObject.Parse(json);
var data = root["data"] as JArray ?? new JArray();
+1 -8
View File
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;net10.0;net48</TargetFrameworks>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>10.0</LangVersion>
<Nullable>enable</Nullable>
<RootNamespace>PSProxmoxVE</RootNamespace>
@@ -12,16 +12,9 @@
<ItemGroup>
<ProjectReference Include="..\PSProxmoxVE.Core\PSProxmoxVE.Core.csproj" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0' Or '$(TargetFramework)' == 'net48'">
<PackageReference Include="PowerShellStandard.Library" Version="5.1.1" PrivateAssets="all" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net10.0'">
<PackageReference Include="System.Management.Automation" Version="7.5.0" PrivateAssets="all" />
</ItemGroup>
<ItemGroup>
<None Include="PSProxmoxVE.psd1" CopyToOutputDirectory="PreserveNewest" />
<None Include="PSProxmoxVE.format.ps1xml" CopyToOutputDirectory="PreserveNewest" />