diff --git a/src/PSProxmoxVE.Core/Models/Vms/OvfMetadata.cs b/src/PSProxmoxVE.Core/Models/Vms/OvfMetadata.cs
index cded707..b9f8364 100644
--- a/src/PSProxmoxVE.Core/Models/Vms/OvfMetadata.cs
+++ b/src/PSProxmoxVE.Core/Models/Vms/OvfMetadata.cs
@@ -28,6 +28,9 @@ namespace PSProxmoxVE.Core.Models.Vms
/// The connection name (e.g. "VM Network", "bridged").
public string ConnectionName { get; set; } = string.Empty;
+
+ /// The OVF ResourceSubType (e.g. "E1000", "vmxnet3", "VirtualE1000e").
+ public string ResourceSubType { get; set; } = string.Empty;
}
///
@@ -227,10 +230,12 @@ namespace PSProxmoxVE.Core.Models.Vms
?? item.SelectSingleNode("rasd:Caption", nsm)?.InnerText
?? "Network adapter";
var connection = item.SelectSingleNode("rasd:Connection", nsm)?.InnerText ?? string.Empty;
+ var nicSubType = item.SelectSingleNode("rasd:ResourceSubType", nsm)?.InnerText ?? string.Empty;
metadata.NetworkAdapters.Add(new OvfNetworkAdapter
{
AdapterName = adapterName,
- ConnectionName = connection
+ ConnectionName = connection,
+ ResourceSubType = nicSubType
});
break;
}
@@ -323,6 +328,33 @@ namespace PSProxmoxVE.Core.Models.Vms
return (int)value;
}
+ ///
+ /// Maps an OVF ResourceSubType for a NIC to a PVE network model string.
+ ///
+ public static string MapNicModel(string resourceSubType)
+ {
+ if (string.IsNullOrEmpty(resourceSubType))
+ return "virtio";
+
+ var lower = resourceSubType.ToLowerInvariant();
+
+ if (lower.Contains("vmxnet3"))
+ return "vmxnet3";
+ if (lower.Contains("e1000e") || lower.Contains("virtuale1000e"))
+ return "e1000e";
+ if (lower.Contains("e1000") || lower.Contains("virtuale1000"))
+ return "e1000";
+ if (lower.Contains("vmxnet2") || lower.Contains("vmxnet"))
+ return "vmxnet3"; // PVE doesn't support vmxnet2, use vmxnet3
+ if (lower.Contains("pcnet"))
+ return "e1000"; // pcnet not supported on PVE, fallback to e1000
+ if (lower.Contains("virtio"))
+ return "virtio";
+
+ // Unknown model — default to e1000 (widely compatible, no driver install needed)
+ return "e1000";
+ }
+
private static string MapOsType(string osDescription)
{
if (string.IsNullOrEmpty(osDescription))
diff --git a/src/PSProxmoxVE/Cmdlets/Vms/ImportPveOvaCmdlet.cs b/src/PSProxmoxVE/Cmdlets/Vms/ImportPveOvaCmdlet.cs
index 694d95b..506e77f 100644
--- a/src/PSProxmoxVE/Cmdlets/Vms/ImportPveOvaCmdlet.cs
+++ b/src/PSProxmoxVE/Cmdlets/Vms/ImportPveOvaCmdlet.cs
@@ -223,20 +223,30 @@ namespace PSProxmoxVE.Cmdlets.Vms
if (!string.IsNullOrEmpty(metadata.OsTypeHint) && metadata.OsTypeHint != "other")
vmConfig["ostype"] = metadata.OsTypeHint;
- // Add disk import-from parameters
+ // Add disk import-from parameters (use scsi bus like PVE UI)
+ string? firstDisk = null;
for (int i = 0; i < metadata.Disks.Count; i++)
{
var disk = metadata.Disks[i];
- var diskSlot = $"{disk.BusType}{i}";
+ var diskSlot = $"scsi{i}";
var importFrom = $"{Storage}:import/{fileName}/{disk.FileName}";
vmConfig[diskSlot] = $"{TargetStorage}:0,import-from={importFrom}";
+ firstDisk ??= diskSlot;
WriteVerbose($" Disk {diskSlot}: import-from={importFrom} -> {TargetStorage}");
}
- // Add network adapters
+ // Set boot order to the first disk
+ if (firstDisk != null)
+ vmConfig["boot"] = $"order={firstDisk}";
+
+ // Set sockets (PVE default)
+ vmConfig["sockets"] = 1;
+
+ // Add network adapters with model from OVF
for (int i = 0; i < metadata.NetworkAdapters.Count; i++)
{
- vmConfig[$"net{i}"] = "virtio,bridge=vmbr0";
+ var nicModel = OvfMetadata.MapNicModel(metadata.NetworkAdapters[i].ResourceSubType);
+ vmConfig[$"net{i}"] = $"{nicModel},bridge=vmbr0";
}
var createTask = vmService.CreateVm(session, Node, vmConfig);
diff --git a/tests/PSProxmoxVE.Tests/Integration/Integration.Tests.ps1 b/tests/PSProxmoxVE.Tests/Integration/Integration.Tests.ps1
index a933470..8be1073 100644
--- a/tests/PSProxmoxVE.Tests/Integration/Integration.Tests.ps1
+++ b/tests/PSProxmoxVE.Tests/Integration/Integration.Tests.ps1
@@ -1214,7 +1214,7 @@ Describe 'Integration Tests' -Tag 'Integration' {
$metadata.Disks.Count | Should -BeGreaterThan 0
}
- It 'Should import OVA as a VM (Import-PveOva)' {
+ It 'Should import OVA as a VM with correct metadata (Import-PveOva)' {
if (Skip-IfNoTarget) { return }
if (-not $script:OvaPath -or -not (Test-Path $script:OvaPath)) {
Set-ItResult -Skipped -Because 'PVETEST_OVA_PATH not set or file not found'
@@ -1228,9 +1228,18 @@ Describe 'Integration Tests' -Tag 'Integration' {
$vm | Should -Not -BeNullOrEmpty
$script:CreatedVmIds.Add($vm.VmId)
- # Verify the VM exists
+ # Verify VM exists with correct name
$found = Get-PveVm -Node $script:Node -Name 'pester-ova-vm' | Select-Object -First 1
$found | Should -Not -BeNullOrEmpty
+ $found.Name | Should -Be 'pester-ova-vm'
+
+ # Verify config matches OVF metadata
+ $config = Get-PveVmConfig -Node $script:Node -VmId $vm.VmId
+ $config.Memory | Should -BeGreaterThan 0
+ $config.Cores | Should -BeGreaterThan 0
+ $config.OsType | Should -Be 'l26'
+ $config.Scsi0 | Should -Not -BeNullOrEmpty -Because 'disk should be imported to scsi0'
+ $config.Net0 | Should -Not -BeNullOrEmpty -Because 'network adapter should be configured'
}
It 'Should start the OVA-imported VM' {