mirror of
https://github.com/GoodOlClint/PSProxmoxVE.git
synced 2026-08-30 00:47:07 +00:00
feat: add Import-PveOva cmdlet for OVA appliance import
Parses OVF metadata client-side from the OVA TAR archive to extract VM name, CPU, memory, disks, and network adapters. Then uploads the OVA with content=import, creates the VM, and imports each disk. - OvfMetadata parser with multi-target TAR support (System.Formats.Tar on net9.0, manual 512-byte header parsing on netstandard2.0/net48) - VmService.UploadOva() for content=import uploads - Import-PveOva cmdlet with -Name/-Memory/-Cores overrides and -Wait - Pester unit tests Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -492,5 +492,50 @@ namespace PSProxmoxVE.Core.Services
|
||||
.GetAwaiter().GetResult();
|
||||
return JObject.Parse(response)["data"] as JObject ?? new JObject();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// OVA Upload
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// Uploads an OVA file to storage with content=import. Returns the task UPID.
|
||||
/// </summary>
|
||||
/// <param name="session">The authenticated PVE session.</param>
|
||||
/// <param name="node">The cluster node name.</param>
|
||||
/// <param name="storage">The target storage identifier.</param>
|
||||
/// <param name="ovaPath">The local path to the OVA file.</param>
|
||||
/// <param name="progressCallback">
|
||||
/// Optional callback invoked periodically with (bytesSent, totalBytes).
|
||||
/// May be called from a background thread.
|
||||
/// </param>
|
||||
public PveTask UploadOva(
|
||||
PveSession session,
|
||||
string node,
|
||||
string storage,
|
||||
string ovaPath,
|
||||
Action<long, long>? progressCallback = null)
|
||||
{
|
||||
if (session == null) throw new ArgumentNullException(nameof(session));
|
||||
if (string.IsNullOrWhiteSpace(node)) throw new ArgumentNullException(nameof(node));
|
||||
if (string.IsNullOrWhiteSpace(storage)) throw new ArgumentNullException(nameof(storage));
|
||||
if (string.IsNullOrWhiteSpace(ovaPath)) throw new ArgumentNullException(nameof(ovaPath));
|
||||
|
||||
var formFields = new Dictionary<string, string>
|
||||
{
|
||||
["content"] = "import"
|
||||
};
|
||||
|
||||
using var client = new PveHttpClient(session);
|
||||
var response = client.UploadFileAsync(
|
||||
$"nodes/{node}/storage/{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" };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user