From c34f16167400ab0c3f7c5346aa6047e2d87315bb Mon Sep 17 00:00:00 2001 From: Tinyblargon <76069640+Tinyblargon@users.noreply.github.com> Date: Tue, 24 Sep 2024 00:03:15 +0200 Subject: [PATCH 1/6] refactor: move func to seperate packge for better reuse --- proxmox/Internal/util/util.go | 6 ++++ proxmox/heper_qemu_test.go | 25 +++++++-------- proxmox/resource_lxc.go | 5 +-- proxmox/resource_pool.go | 5 +-- proxmox/resource_vm_qemu.go | 57 ++++++++++++++++++----------------- proxmox/util.go | 5 --- 6 files changed, 54 insertions(+), 49 deletions(-) create mode 100644 proxmox/Internal/util/util.go diff --git a/proxmox/Internal/util/util.go b/proxmox/Internal/util/util.go new file mode 100644 index 00000000..5aac8c51 --- /dev/null +++ b/proxmox/Internal/util/util.go @@ -0,0 +1,6 @@ +package util + +// Gets inlined by the compiler, so it's not a performance hit +func Pointer[T any](v T) *T { + return &v +} diff --git a/proxmox/heper_qemu_test.go b/proxmox/heper_qemu_test.go index 79119cb9..32ba8226 100644 --- a/proxmox/heper_qemu_test.go +++ b/proxmox/heper_qemu_test.go @@ -5,6 +5,7 @@ import ( "testing" pxapi "github.com/Telmate/proxmox-api-go/proxmox" + "github.com/Telmate/terraform-provider-proxmox/v2/proxmox/Internal/util" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/stretchr/testify/require" ) @@ -188,22 +189,22 @@ func Test_ParseCloudInitInterface(t *testing.T) { output: connectionInfo{SkipIPv4: true}}, {name: `IPv4=Static`, input: testInput{ci: pxapi.CloudInitNetworkConfig{IPv4: &pxapi.CloudInitIPv4Config{ - Address: pointer(pxapi.IPv4CIDR("192.168.1.1/24"))}}}, + Address: util.Pointer(pxapi.IPv4CIDR("192.168.1.1/24"))}}}, output: connectionInfo{IPs: primaryIPs{ IPv4: "192.168.1.1"}, SkipIPv6: true}}, {name: `IPv4=Static ciCustom`, input: testInput{ ci: pxapi.CloudInitNetworkConfig{IPv4: &pxapi.CloudInitIPv4Config{ - Address: pointer(pxapi.IPv4CIDR("192.168.1.1/24"))}}, + Address: util.Pointer(pxapi.IPv4CIDR("192.168.1.1/24"))}}, ciCustom: true}, output: connectionInfo{IPs: primaryIPs{IPv4: "192.168.1.1"}}}, {name: `IPv4=Static IPv6=Static`, input: testInput{ci: pxapi.CloudInitNetworkConfig{ IPv4: &pxapi.CloudInitIPv4Config{ - Address: pointer(pxapi.IPv4CIDR("192.168.1.1/24"))}, + Address: util.Pointer(pxapi.IPv4CIDR("192.168.1.1/24"))}, IPv6: &pxapi.CloudInitIPv6Config{ - Address: pointer(pxapi.IPv6CIDR("2001:0db8:85a3:0000:0000:8a2e:0370:7334/64"))}}}, + Address: util.Pointer(pxapi.IPv6CIDR("2001:0db8:85a3:0000:0000:8a2e:0370:7334/64"))}}}, output: connectionInfo{IPs: primaryIPs{ IPv4: "192.168.1.1", IPv6: "2001:0db8:85a3:0000:0000:8a2e:0370:7334"}}}, @@ -211,9 +212,9 @@ func Test_ParseCloudInitInterface(t *testing.T) { input: testInput{ ci: pxapi.CloudInitNetworkConfig{ IPv4: &pxapi.CloudInitIPv4Config{ - Address: pointer(pxapi.IPv4CIDR("192.168.1.1/24"))}, + Address: util.Pointer(pxapi.IPv4CIDR("192.168.1.1/24"))}, IPv6: &pxapi.CloudInitIPv6Config{ - Address: pointer(pxapi.IPv6CIDR("2001:0db8:85a3:0000:0000:8a2e:0370:7334/64"))}}, + Address: util.Pointer(pxapi.IPv6CIDR("2001:0db8:85a3:0000:0000:8a2e:0370:7334/64"))}}, ciCustom: true}, output: connectionInfo{IPs: primaryIPs{ IPv4: "192.168.1.1", @@ -221,7 +222,7 @@ func Test_ParseCloudInitInterface(t *testing.T) { {name: `IPv4=Static SkipIPv4`, input: testInput{ ci: pxapi.CloudInitNetworkConfig{IPv4: &pxapi.CloudInitIPv4Config{ - Address: pointer(pxapi.IPv4CIDR("192.168.1.1/24"))}}, + Address: util.Pointer(pxapi.IPv4CIDR("192.168.1.1/24"))}}, skipIPv4: true}, output: connectionInfo{IPs: primaryIPs{ IPv4: "192.168.1.1"}, @@ -230,7 +231,7 @@ func Test_ParseCloudInitInterface(t *testing.T) { {name: `IPv4=Static SkipIPv4 ciCustom`, input: testInput{ ci: pxapi.CloudInitNetworkConfig{IPv4: &pxapi.CloudInitIPv4Config{ - Address: pointer(pxapi.IPv4CIDR("192.168.1.1/24"))}}, + Address: util.Pointer(pxapi.IPv4CIDR("192.168.1.1/24"))}}, ciCustom: true, skipIPv4: true}, output: connectionInfo{IPs: primaryIPs{ @@ -262,20 +263,20 @@ func Test_ParseCloudInitInterface(t *testing.T) { output: connectionInfo{SkipIPv6: true}}, {name: `IPv6=Static`, input: testInput{ci: pxapi.CloudInitNetworkConfig{IPv6: &pxapi.CloudInitIPv6Config{ - Address: pointer(pxapi.IPv6CIDR("2001:0db8:85a3:0000:0000:8a2e:0370:7334/64"))}}}, + Address: util.Pointer(pxapi.IPv6CIDR("2001:0db8:85a3:0000:0000:8a2e:0370:7334/64"))}}}, output: connectionInfo{IPs: primaryIPs{ IPv6: "2001:0db8:85a3:0000:0000:8a2e:0370:7334"}, SkipIPv4: true}}, {name: `IPv6=Static ciCustom`, input: testInput{ ci: pxapi.CloudInitNetworkConfig{IPv6: &pxapi.CloudInitIPv6Config{ - Address: pointer(pxapi.IPv6CIDR("2001:0db8:85a3:0000:0000:8a2e:0370:7334/64"))}}, + Address: util.Pointer(pxapi.IPv6CIDR("2001:0db8:85a3:0000:0000:8a2e:0370:7334/64"))}}, ciCustom: true}, output: connectionInfo{IPs: primaryIPs{IPv6: "2001:0db8:85a3:0000:0000:8a2e:0370:7334"}}}, {name: `IPv6=Static SkipIPv6`, input: testInput{ ci: pxapi.CloudInitNetworkConfig{IPv6: &pxapi.CloudInitIPv6Config{ - Address: pointer(pxapi.IPv6CIDR("2001:0db8:85a3:0000:0000:8a2e:0370:7334/64"))}}, + Address: util.Pointer(pxapi.IPv6CIDR("2001:0db8:85a3:0000:0000:8a2e:0370:7334/64"))}}, skipIPv6: true}, output: connectionInfo{IPs: primaryIPs{ IPv6: "2001:0db8:85a3:0000:0000:8a2e:0370:7334"}, @@ -284,7 +285,7 @@ func Test_ParseCloudInitInterface(t *testing.T) { {name: `IPv6=Static SkipIPv6 ciCustom`, input: testInput{ ci: pxapi.CloudInitNetworkConfig{IPv6: &pxapi.CloudInitIPv6Config{ - Address: pointer(pxapi.IPv6CIDR("2001:0db8:85a3:0000:0000:8a2e:0370:7334/64"))}}, + Address: util.Pointer(pxapi.IPv6CIDR("2001:0db8:85a3:0000:0000:8a2e:0370:7334/64"))}}, ciCustom: true, skipIPv6: true}, output: connectionInfo{IPs: primaryIPs{ diff --git a/proxmox/resource_lxc.go b/proxmox/resource_lxc.go index 4dfa579e..016a6fb0 100644 --- a/proxmox/resource_lxc.go +++ b/proxmox/resource_lxc.go @@ -9,6 +9,7 @@ import ( pxapi "github.com/Telmate/proxmox-api-go/proxmox" "github.com/Telmate/terraform-provider-proxmox/v2/proxmox/Internal/pxapi/guest/tags" + "github.com/Telmate/terraform-provider-proxmox/v2/proxmox/Internal/util" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -479,7 +480,7 @@ func resourceLxcCreate(d *schema.ResourceData, meta interface{}) error { config.OnBoot = d.Get("onboot").(bool) config.OsType = d.Get("ostype").(string) config.Password = d.Get("password").(string) - config.Pool = pointer(pxapi.PoolName(d.Get("pool").(string))) + config.Pool = util.Pointer(pxapi.PoolName(d.Get("pool").(string))) config.Protection = d.Get("protection").(bool) config.Restore = d.Get("restore").(bool) config.SearchDomain = d.Get("searchdomain").(string) @@ -651,7 +652,7 @@ func resourceLxcUpdate(d *schema.ResourceData, meta interface{}) error { config.OnBoot = d.Get("onboot").(bool) config.OsType = d.Get("ostype").(string) config.Password = d.Get("password").(string) - config.Pool = pointer(pxapi.PoolName(d.Get("pool").(string))) + config.Pool = util.Pointer(pxapi.PoolName(d.Get("pool").(string))) config.Protection = d.Get("protection").(bool) config.Restore = d.Get("restore").(bool) config.SearchDomain = d.Get("searchdomain").(string) diff --git a/proxmox/resource_pool.go b/proxmox/resource_pool.go index ffaa9eef..f03407ed 100644 --- a/proxmox/resource_pool.go +++ b/proxmox/resource_pool.go @@ -4,6 +4,7 @@ import ( "fmt" pxapi "github.com/Telmate/proxmox-api-go/proxmox" + "github.com/Telmate/terraform-provider-proxmox/v2/proxmox/Internal/util" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -46,7 +47,7 @@ func resourcePoolCreate(d *schema.ResourceData, meta interface{}) error { err := pxapi.ConfigPool{ Name: pxapi.PoolName(poolid), - Comment: pointer(d.Get("comment").(string)), + Comment: util.Pointer(d.Get("comment").(string)), }.Create(client) if err != nil { return err @@ -114,7 +115,7 @@ func resourcePoolUpdate(d *schema.ResourceData, meta interface{}) error { if d.HasChange("comment") { err := pxapi.ConfigPool{ Name: pxapi.PoolName(poolID), - Comment: pointer(d.Get("comment").(string)), + Comment: util.Pointer(d.Get("comment").(string)), }.Update(client) if err != nil { return err diff --git a/proxmox/resource_vm_qemu.go b/proxmox/resource_vm_qemu.go index 7ba9b247..e29203b3 100755 --- a/proxmox/resource_vm_qemu.go +++ b/proxmox/resource_vm_qemu.go @@ -26,6 +26,7 @@ import ( "github.com/Telmate/terraform-provider-proxmox/v2/proxmox/Internal/pxapi/dns/nameservers" "github.com/Telmate/terraform-provider-proxmox/v2/proxmox/Internal/pxapi/guest/sshkeys" "github.com/Telmate/terraform-provider-proxmox/v2/proxmox/Internal/pxapi/guest/tags" + "github.com/Telmate/terraform-provider-proxmox/v2/proxmox/Internal/util" ) // using a global variable here so that we have an internally accessible @@ -961,19 +962,19 @@ func resourceVmQemuCreate(ctx context.Context, d *schema.ResourceData, meta inte config := pxapi.ConfigQemu{ Name: vmName, CPU: mapToSDK_CPU(d), - Description: pointer(d.Get("desc").(string)), - Pool: pointer(pxapi.PoolName(d.Get("pool").(string))), + Description: util.Pointer(d.Get("desc").(string)), + Pool: util.Pointer(pxapi.PoolName(d.Get("pool").(string))), Bios: d.Get("bios").(string), - Onboot: pointer(d.Get("onboot").(bool)), + Onboot: util.Pointer(d.Get("onboot").(bool)), Startup: d.Get("startup").(string), - Protection: pointer(d.Get("protection").(bool)), - Tablet: pointer(d.Get("tablet").(bool)), + Protection: util.Pointer(d.Get("protection").(bool)), + Tablet: util.Pointer(d.Get("tablet").(bool)), Boot: d.Get("boot").(string), BootDisk: d.Get("bootdisk").(string), Agent: mapToSDK_QemuGuestAgent(d), Memory: mapToSDK_Memory(d), Machine: d.Get("machine").(string), - QemuKVM: pointer(d.Get("kvm").(bool)), + QemuKVM: util.Pointer(d.Get("kvm").(bool)), Hotplug: d.Get("hotplug").(string), Scsihw: d.Get("scsihw").(string), HaState: d.Get("hastate").(string), @@ -1219,19 +1220,19 @@ func resourceVmQemuUpdate(ctx context.Context, d *schema.ResourceData, meta inte config := pxapi.ConfigQemu{ Name: d.Get("name").(string), CPU: mapToSDK_CPU(d), - Description: pointer(d.Get("desc").(string)), - Pool: pointer(pxapi.PoolName(d.Get("pool").(string))), + Description: util.Pointer(d.Get("desc").(string)), + Pool: util.Pointer(pxapi.PoolName(d.Get("pool").(string))), Bios: d.Get("bios").(string), - Onboot: pointer(d.Get("onboot").(bool)), + Onboot: util.Pointer(d.Get("onboot").(bool)), Startup: d.Get("startup").(string), - Protection: pointer(d.Get("protection").(bool)), - Tablet: pointer(d.Get("tablet").(bool)), + Protection: util.Pointer(d.Get("protection").(bool)), + Tablet: util.Pointer(d.Get("tablet").(bool)), Boot: d.Get("boot").(string), BootDisk: d.Get("bootdisk").(string), Agent: mapToSDK_QemuGuestAgent(d), Memory: mapToSDK_Memory(d), Machine: d.Get("machine").(string), - QemuKVM: pointer(d.Get("kvm").(bool)), + QemuKVM: util.Pointer(d.Get("kvm").(bool)), Hotplug: d.Get("hotplug").(string), Scsihw: d.Get("scsihw").(string), HaState: d.Get("hastate").(string), @@ -2968,14 +2969,14 @@ func mapToSDK_CloudInit(d *schema.ResourceData) *pxapi.CloudInit { Vendor: &pxapi.CloudInitSnippet{}, }, DNS: &pxapi.GuestDNS{ - SearchDomain: pointer(d.Get("searchdomain").(string)), + SearchDomain: util.Pointer(d.Get("searchdomain").(string)), NameServers: nameservers.Split(d.Get("nameserver").(string)), }, NetworkInterfaces: pxapi.CloudInitNetworkInterfaces{}, PublicSSHkeys: sshkeys.Split(d.Get("sshkeys").(string)), - UpgradePackages: pointer(d.Get("ciupgrade").(bool)), - UserPassword: pointer(d.Get("cipassword").(string)), - Username: pointer(d.Get("ciuser").(string)), + UpgradePackages: util.Pointer(d.Get("ciupgrade").(bool)), + UserPassword: util.Pointer(d.Get("cipassword").(string)), + Username: util.Pointer(d.Get("ciuser").(string)), } params := splitStringOfSettings(d.Get("cicustom").(string)) if v, isSet := params["meta"]; isSet { @@ -2999,13 +3000,13 @@ func mapToSDK_CloudInit(d *schema.ResourceData) *pxapi.CloudInit { func mapToSDK_CloudInitNetworkConfig(param string) pxapi.CloudInitNetworkConfig { config := pxapi.CloudInitNetworkConfig{ IPv4: &pxapi.CloudInitIPv4Config{ - Address: pointer(pxapi.IPv4CIDR("")), + Address: util.Pointer(pxapi.IPv4CIDR("")), DHCP: false, - Gateway: pointer(pxapi.IPv4Address(""))}, + Gateway: util.Pointer(pxapi.IPv4Address(""))}, IPv6: &pxapi.CloudInitIPv6Config{ - Address: pointer(pxapi.IPv6CIDR("")), + Address: util.Pointer(pxapi.IPv6CIDR("")), DHCP: false, - Gateway: pointer(pxapi.IPv6Address("")), + Gateway: util.Pointer(pxapi.IPv6Address("")), SLAAC: false}} params := splitStringOfSettings(param) if v, isSet := params["ip"]; isSet { @@ -3045,19 +3046,19 @@ func mapToSDK_CloudInitSnippet(param string) *pxapi.CloudInitSnippet { func mapToSDK_Memory(d *schema.ResourceData) *pxapi.QemuMemory { return &pxapi.QemuMemory{ - CapacityMiB: pointer(pxapi.QemuMemoryCapacity(d.Get("memory").(int))), - MinimumCapacityMiB: pointer(pxapi.QemuMemoryBalloonCapacity(d.Get("balloon").(int))), - Shares: pointer(pxapi.QemuMemoryShares(0)), + CapacityMiB: util.Pointer(pxapi.QemuMemoryCapacity(d.Get("memory").(int))), + MinimumCapacityMiB: util.Pointer(pxapi.QemuMemoryBalloonCapacity(d.Get("balloon").(int))), + Shares: util.Pointer(pxapi.QemuMemoryShares(0)), } } func mapToSDK_CPU(d *schema.ResourceData) *pxapi.QemuCPU { return &pxapi.QemuCPU{ - Cores: pointer(pxapi.QemuCpuCores(d.Get("cores").(int))), - Numa: pointer(d.Get("numa").(bool)), - Sockets: pointer(pxapi.QemuCpuSockets(d.Get("sockets").(int))), - Type: pointer(pxapi.CpuType(d.Get("cpu").(string))), - VirtualCores: pointer(pxapi.CpuVirtualCores(d.Get("vcpus").(int)))} + Cores: util.Pointer(pxapi.QemuCpuCores(d.Get("cores").(int))), + Numa: util.Pointer(d.Get("numa").(bool)), + Sockets: util.Pointer(pxapi.QemuCpuSockets(d.Get("sockets").(int))), + Type: util.Pointer(pxapi.CpuType(d.Get("cpu").(string))), + VirtualCores: util.Pointer(pxapi.CpuVirtualCores(d.Get("vcpus").(int)))} } func mapToSDK_QemuCdRom_Disk(slot string, schema map[string]interface{}) (*pxapi.QemuCdRom, diag.Diagnostics) { diff --git a/proxmox/util.go b/proxmox/util.go index f9bf962f..1acd0ee9 100644 --- a/proxmox/util.go +++ b/proxmox/util.go @@ -480,11 +480,6 @@ func testOptionalArguments(t *testing.T, s *schema.Resource) { } } -// Gets inlined by the compiler, so it's not a performance hit -func pointer[T any](item T) *T { - return &item -} - func permissions_check(s1 []string, s2 []string) []string { var diff []string From f67cc889baad7807a1247aafbf61edc002608922 Mon Sep 17 00:00:00 2001 From: Tinyblargon <76069640+Tinyblargon@users.noreply.github.com> Date: Mon, 14 Oct 2024 08:27:58 +0200 Subject: [PATCH 2/6] docs: Cloud-Init getting started guide --- docs/guides/cloud-init getting started.md | 132 ++++++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 docs/guides/cloud-init getting started.md diff --git a/docs/guides/cloud-init getting started.md b/docs/guides/cloud-init getting started.md new file mode 100644 index 00000000..17913b16 --- /dev/null +++ b/docs/guides/cloud-init getting started.md @@ -0,0 +1,132 @@ +# Cloud-Init Getting Started + +This guide will help you get started with Cloud-Init on Proxmox Virtual Environment `PVE`. Cloud Init is a multi-distribution package that handles early initialization of a virtual machine. It is used for configuring the hostname, setting up SSH keys, and other tasks that need to be done before the virtual machine is ready for use. + +Note: **all command are performed from the PVE shell**. + +## Creating a Cloud Init Template + +Before you can use Cloud-Init, you need to create a template that will be used to clone new virtual machines. This template will have the Cloud-Init package installed and configured. The following steps will guide you through creating a Cloud Init template: + +### Downloading a Cloud-Init Image + +For this guide, we will use the Debian 12 Cloud-Init image. You can download the image from the following link: + +```bash +wget https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-genericcloud-amd64.qcow2 +``` + +### Importing the Cloud-Init Image + +Before we can import the Cloud-Init image, we need to create a VM to give the image to. The following command will create a new VM with the ID `9000`: + +```bash +qm create 9000 --name debian12-cloudinit +``` + +Note: **Terraform is meant to manage the full life cycle of the VM, therefore we won't make any further changes to the VM**. + +Once the VM is created, we can import the Cloud-Init image using the following command: + +```bash +qm set 9000 --scsi0 local-lvm:0,import-from=/root/debian-12-genericcloud-amd64.qcow2 +``` + +### Creating a Template from the VM + +Now that we have the Cloud-Init image imported, we can create a template from the VM. The following command will convert the VM with ID `9000` to a template: + +```bash +qm template 9000 +``` + +## Creating a Snippet + +Snippets are used to pass additional configuration to the Cloud-Init package. For this guide we will create a snippet that ensures the `qemu-guest-agent` package is installed on the virtual machine. Before we can create a snippet, we need to create a place to store it. Preferably in the same storage as the template. Do keep in mind that the cloned VMs can't start if the snippet is not accessible. Throughout this guide we will use the `local` storage. + +```bash +mkdir /var/lib/vz/snippets +``` + +Now that we have a place to store the snippet, we can create the snippet itself. The following command will create a snippet that installs the `qemu-guest-agent.yml` package: + +```bash +tee /var/lib/vz/snippets/qemu-guest-agent.yml < Date: Mon, 14 Oct 2024 17:30:01 +0200 Subject: [PATCH 3/6] docs: use correct source --- docs/guides/cloud-init getting started.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/guides/cloud-init getting started.md b/docs/guides/cloud-init getting started.md index 17913b16..aa954583 100644 --- a/docs/guides/cloud-init getting started.md +++ b/docs/guides/cloud-init getting started.md @@ -123,8 +123,7 @@ resource "proxmox_vm_qemu" "cloudinit-example" { terraform { required_providers { proxmox = { - source = "registry.example.com/telmate/proxmox" - #source = "Telmate/proxmox" + source = "Telmate/proxmox" version = ">=3.0.1rc4" } } From 52b9a8187a47848f24db6bc16c7d9c41e04767a8 Mon Sep 17 00:00:00 2001 From: Tinyblargon <76069640+Tinyblargon@users.noreply.github.com> Date: Wed, 23 Oct 2024 19:01:49 +0200 Subject: [PATCH 4/6] chore: bump proxmox-api-go version --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index a219b18c..579bd2e3 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.21 toolchain go1.21.0 require ( - github.com/Telmate/proxmox-api-go v0.0.0-20240827160542-0d787afdba05 + github.com/Telmate/proxmox-api-go v0.0.0-20241023165359-9a3f359f942c github.com/google/uuid v1.6.0 github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 github.com/hashicorp/terraform-plugin-sdk/v2 v2.34.0 diff --git a/go.sum b/go.sum index eab09510..39f95b4f 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,8 @@ github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migc github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/ProtonMail/go-crypto v1.1.0-alpha.2-proton h1:HKz85FwoXx86kVtTvFke7rgHvq/HoloSUvW5semjFWs= github.com/ProtonMail/go-crypto v1.1.0-alpha.2-proton/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= -github.com/Telmate/proxmox-api-go v0.0.0-20240827160542-0d787afdba05 h1:yYVjf1Bp6qYHFuOlV8eNwUh1kpYMnUFrop4iLvY+/ZY= -github.com/Telmate/proxmox-api-go v0.0.0-20240827160542-0d787afdba05/go.mod h1:Gu6n6vEn1hlyFUkjrvU+X1fdgaSXLoM9HKYYJqy1fsY= +github.com/Telmate/proxmox-api-go v0.0.0-20241023165359-9a3f359f942c h1:JJMXCLMlqiHH4tFr6sUjTXP63faXtJq9mRBk0v7XVNU= +github.com/Telmate/proxmox-api-go v0.0.0-20241023165359-9a3f359f942c/go.mod h1:Gu6n6vEn1hlyFUkjrvU+X1fdgaSXLoM9HKYYJqy1fsY= github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo= github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJEynmyUenKwP++x/+DdGV/Ec= From b7772a164599849fa4d168d091b8fa87787a0fad Mon Sep 17 00:00:00 2001 From: Tinyblargon <76069640+Tinyblargon@users.noreply.github.com> Date: Mon, 28 Oct 2024 08:01:23 +0100 Subject: [PATCH 5/6] feat: add `id` to network interfaces --- go.mod | 2 +- go.sum | 4 +- .../resource/guest/qemu/network/schema.go | 141 ++++++++++++++++++ .../resource/guest/qemu/network/sdk.go | 61 ++++++++ .../resource/guest/qemu/network/terraform.go | 52 +++++++ proxmox/resource_vm_qemu.go | 111 +++----------- 6 files changed, 274 insertions(+), 97 deletions(-) create mode 100644 proxmox/Internal/resource/guest/qemu/network/schema.go create mode 100644 proxmox/Internal/resource/guest/qemu/network/sdk.go create mode 100644 proxmox/Internal/resource/guest/qemu/network/terraform.go diff --git a/go.mod b/go.mod index a219b18c..c578d8e5 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.21 toolchain go1.21.0 require ( - github.com/Telmate/proxmox-api-go v0.0.0-20240827160542-0d787afdba05 + github.com/Telmate/proxmox-api-go v0.0.0-20241028065640-b38644dd6993 github.com/google/uuid v1.6.0 github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 github.com/hashicorp/terraform-plugin-sdk/v2 v2.34.0 diff --git a/go.sum b/go.sum index eab09510..af25fe42 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,8 @@ github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migc github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/ProtonMail/go-crypto v1.1.0-alpha.2-proton h1:HKz85FwoXx86kVtTvFke7rgHvq/HoloSUvW5semjFWs= github.com/ProtonMail/go-crypto v1.1.0-alpha.2-proton/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= -github.com/Telmate/proxmox-api-go v0.0.0-20240827160542-0d787afdba05 h1:yYVjf1Bp6qYHFuOlV8eNwUh1kpYMnUFrop4iLvY+/ZY= -github.com/Telmate/proxmox-api-go v0.0.0-20240827160542-0d787afdba05/go.mod h1:Gu6n6vEn1hlyFUkjrvU+X1fdgaSXLoM9HKYYJqy1fsY= +github.com/Telmate/proxmox-api-go v0.0.0-20241028065640-b38644dd6993 h1:HOkpCRbJXYt/dFecrbp6rs7hVaJjADHMXDVA07Kpewg= +github.com/Telmate/proxmox-api-go v0.0.0-20241028065640-b38644dd6993/go.mod h1:Gu6n6vEn1hlyFUkjrvU+X1fdgaSXLoM9HKYYJqy1fsY= github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo= github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJEynmyUenKwP++x/+DdGV/Ec= diff --git a/proxmox/Internal/resource/guest/qemu/network/schema.go b/proxmox/Internal/resource/guest/qemu/network/schema.go new file mode 100644 index 00000000..ca0f0054 --- /dev/null +++ b/proxmox/Internal/resource/guest/qemu/network/schema.go @@ -0,0 +1,141 @@ +package network + +import ( + "net" + + pxapi "github.com/Telmate/proxmox-api-go/proxmox" + + "github.com/hashicorp/go-cty/cty" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +const ( + Root string = "network" + + maximumNetworkInterfaces int = int(pxapi.QemuNetworkInterfacesAmount) + + schemaID string = "id" + + schemaBridge string = "bridge" + schemaFirewall string = "firewall" + schemaLinkDown string = "link_down" + schemaMAC string = "macaddr" + schemaMTU string = "mtu" + schemaModel string = "model" + schemaNativeVlan string = "tag" + schemaQueues string = "queues" + schemaRate string = "rate" +) + +func Schema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + Optional: true, + MaxItems: maximumNetworkInterfaces, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + schemaID: { + Type: schema.TypeInt, + Required: true, + ValidateDiagFunc: func(i interface{}, k cty.Path) diag.Diagnostics { + v := i.(int) + if v < 0 { + return diag.Errorf("%s must be in the range 0 - %d, got: %d", schemaID, pxapi.QemuNetworkInterfaceIDMaximum, v) + } + err := pxapi.QemuNetworkInterfaceID(v).Validate() + if err != nil { + return diag.Errorf("%s must be in the range 0 - %d, got: %d", schemaID, pxapi.QemuNetworkInterfaceIDMaximum, v) + } + return nil + }}, + schemaBridge: { + Type: schema.TypeString, + Optional: true, + Default: "nat"}, + schemaFirewall: { + Type: schema.TypeBool, + Optional: true, + Default: false}, + schemaLinkDown: { + Type: schema.TypeBool, + Optional: true, + Default: false}, + schemaMAC: { + Type: schema.TypeString, + Optional: true, + Computed: true, + DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { + oldMAC, _ := net.ParseMAC(old) + newMAC, _ := net.ParseMAC(new) + return oldMAC.String() == newMAC.String() + }, + ValidateDiagFunc: func(i interface{}, p cty.Path) diag.Diagnostics { + v := i.(string) + if _, err := net.ParseMAC(v); err != nil { + return diag.Errorf("invalid %s: %s", schemaMAC, v) + } + return nil + }}, + schemaMTU: { + Type: schema.TypeInt, + Optional: true, + ValidateDiagFunc: func(i interface{}, k cty.Path) diag.Diagnostics { + v := i.(int) + if v == 1 { + return nil + } + if v < 0 { + return diag.Errorf("%s must be equal or greater than 0, got: %d", schemaMTU, v) + } + if err := pxapi.MTU(v).Validate(); err != nil { + return diag.Errorf("%s must be in the range 576 - 65520, or 1 got: %d", schemaMTU, v) + } + return nil + }}, + schemaModel: { + Type: schema.TypeString, + Required: true, + ForceNew: false, + ValidateDiagFunc: func(i interface{}, k cty.Path) diag.Diagnostics { + v := i.(string) + if err := pxapi.QemuNetworkModel(v).Validate(); err != nil { + return diag.Errorf("invalid network %s: %s", schemaModel, v) + } + return nil + }}, + schemaNativeVlan: { + Type: schema.TypeInt, + Optional: true, + Description: "VLAN tag.", + ValidateDiagFunc: func(i interface{}, k cty.Path) diag.Diagnostics { + v := i.(int) + if v < 0 { + return diag.Errorf("%s must be equal or greater than 0, got: %d", schemaNativeVlan, v) + } + return nil + }}, + schemaQueues: { + Type: schema.TypeInt, + Optional: true, + ValidateDiagFunc: func(i interface{}, k cty.Path) diag.Diagnostics { + v := i.(int) + if v < 0 { + return diag.Errorf("%s must be equal or greater than 0, got: %d", schemaQueues, v) + } + if err := pxapi.QemuNetworkQueue(v).Validate(); err != nil { + return diag.Errorf("%s must be in the range 0 - %d, got: %d", schemaQueues, pxapi.QemuNetworkQueueMaximum, v) + } + return nil + }}, + schemaRate: { + Type: schema.TypeInt, + Optional: true, + ValidateDiagFunc: func(i interface{}, k cty.Path) diag.Diagnostics { + v := i.(int) + if v < 0 { + return diag.Errorf("%s must be equal or greater than 0, got: %d", schemaRate, v) + } + return nil + }}}}} +} diff --git a/proxmox/Internal/resource/guest/qemu/network/sdk.go b/proxmox/Internal/resource/guest/qemu/network/sdk.go new file mode 100644 index 00000000..9496e1b3 --- /dev/null +++ b/proxmox/Internal/resource/guest/qemu/network/sdk.go @@ -0,0 +1,61 @@ +package network + +import ( + "fmt" + "net" + "strconv" + "strings" + + pxapi "github.com/Telmate/proxmox-api-go/proxmox" + "github.com/Telmate/terraform-provider-proxmox/v2/proxmox/Internal/util" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +// Converts the Terraform configuration to the SDK configuration +func SDK(d *schema.ResourceData) (pxapi.QemuNetworkInterfaces, diag.Diagnostics) { + networks := make(pxapi.QemuNetworkInterfaces, maximumNetworkInterfaces) + for i := 0; i < maximumNetworkInterfaces; i++ { + networks[pxapi.QemuNetworkInterfaceID(i)] = pxapi.QemuNetworkInterface{Delete: true} + } + var diags diag.Diagnostics + for _, e := range d.Get(Root).([]interface{}) { + networkMap := e.(map[string]interface{}) + id := pxapi.QemuNetworkInterfaceID(networkMap[schemaID].(int)) + if v, duplicate := networks[id]; duplicate { + if !v.Delete { + diags = append(diags, diag.Errorf("Duplicate network interface %s %d", schemaID, id)...) + } + } + tmpMAC, _ := net.ParseMAC(networkMap[schemaMAC].(string)) + mtu := networkMap[schemaMTU].(int) + var tmpMTU pxapi.QemuMTU + model := pxapi.QemuNetworkModel(networkMap[schemaModel].(string)) + if mtu != 0 { + if string(pxapi.QemuNetworkModelVirtIO) == model.String() { + if mtu == 1 { + tmpMTU = pxapi.QemuMTU{Inherit: false} + } else { + tmpMTU = pxapi.QemuMTU{Value: pxapi.MTU(mtu)} + } + } else { + diags = append(diags, diag.Diagnostic{ + Severity: diag.Warning, + Summary: fmt.Sprintf("%s is only supported when model is %s", schemaMTU, pxapi.QemuNetworkModelVirtIO)}) + } + } + rateString, _, _ := strings.Cut(strconv.Itoa(networkMap[schemaRate].(int)), ".") + rate, _ := strconv.ParseInt(rateString, 10, 64) + networks[id] = pxapi.QemuNetworkInterface{ + Bridge: util.Pointer(networkMap[schemaBridge].(string)), + Connected: util.Pointer(!networkMap[schemaLinkDown].(bool)), + Delete: false, + Firewall: util.Pointer(networkMap[schemaFirewall].(bool)), + MAC: &tmpMAC, + MTU: &tmpMTU, + Model: util.Pointer(model), + MultiQueue: util.Pointer(pxapi.QemuNetworkQueue(networkMap[schemaQueues].(int))), + RateLimitKBps: util.Pointer(pxapi.QemuNetworkRate(rate))} + } + return networks, diags +} diff --git a/proxmox/Internal/resource/guest/qemu/network/terraform.go b/proxmox/Internal/resource/guest/qemu/network/terraform.go new file mode 100644 index 00000000..02d30a19 --- /dev/null +++ b/proxmox/Internal/resource/guest/qemu/network/terraform.go @@ -0,0 +1,52 @@ +package network + +import ( + pxapi "github.com/Telmate/proxmox-api-go/proxmox" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +// Converts the SDK configuration to the Terraform configuration +func Terraform(config pxapi.QemuNetworkInterfaces, d *schema.ResourceData) { + paramMap := make([]interface{}, 0, len(config)) + for i := 0; i < maximumNetworkInterfaces; i++ { + v, ok := config[pxapi.QemuNetworkInterfaceID(i)] + if !ok { + continue + } + params := map[string]interface{}{ + schemaID: int(i)} + if v.Bridge != nil { + params[schemaBridge] = string(*v.Bridge) + } + if v.Connected != nil { + params[schemaLinkDown] = !*v.Connected + } + if v.Firewall != nil { + params[schemaFirewall] = *v.Firewall + } + if v.MAC != nil { + params[schemaMAC] = v.MAC.String() + } + if v.MTU != nil { + if v.MTU.Inherit { + params[schemaMTU] = 1 + } else { + params[schemaMTU] = int(v.MTU.Value) + } + } + if v.Model != nil { + params[schemaModel] = string(*v.Model) + } + if v.MultiQueue != nil { + params[schemaQueues] = int(*v.MultiQueue) + } + if v.RateLimitKBps != nil { + params[schemaRate] = int(*v.RateLimitKBps * 1000) + } + if v.NativeVlan != nil { + params[schemaNativeVlan] = int(*v.NativeVlan) + } + paramMap = append(paramMap, params) + } + d.Set(Root, paramMap) +} diff --git a/proxmox/resource_vm_qemu.go b/proxmox/resource_vm_qemu.go index e29203b3..f24f8f5a 100755 --- a/proxmox/resource_vm_qemu.go +++ b/proxmox/resource_vm_qemu.go @@ -26,6 +26,7 @@ import ( "github.com/Telmate/terraform-provider-proxmox/v2/proxmox/Internal/pxapi/dns/nameservers" "github.com/Telmate/terraform-provider-proxmox/v2/proxmox/Internal/pxapi/guest/sshkeys" "github.com/Telmate/terraform-provider-proxmox/v2/proxmox/Internal/pxapi/guest/tags" + "github.com/Telmate/terraform-provider-proxmox/v2/proxmox/Internal/resource/guest/qemu/network" "github.com/Telmate/terraform-provider-proxmox/v2/proxmox/Internal/util" ) @@ -326,59 +327,7 @@ func resourceVmQemu() *schema.Resource { }, }, }, - "network": { - Type: schema.TypeList, - Optional: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "model": { - Type: schema.TypeString, - Required: true, - }, - "macaddr": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ValidateDiagFunc: MacAddressValidator(), - }, - "bridge": { - Type: schema.TypeString, - Optional: true, - Default: "nat", - }, - "tag": { - Type: schema.TypeInt, - Optional: true, - Description: "VLAN tag.", - Default: -1, - }, - "firewall": { - Type: schema.TypeBool, - Optional: true, - Default: false, - }, - "rate": { - Type: schema.TypeInt, - Optional: true, - Computed: true, - }, - "mtu": { - Type: schema.TypeInt, - Optional: true, - }, - "queues": { - Type: schema.TypeInt, - Optional: true, - Computed: true, - }, - "link_down": { - Type: schema.TypeBool, - Optional: true, - Default: false, - }, - }, - }, - }, + network.Root: network.Schema(), "smbios": { Type: schema.TypeList, Optional: true, @@ -952,7 +901,6 @@ func resourceVmQemuCreate(ctx context.Context, d *schema.ResourceData, meta inte vga := d.Get("vga").(*schema.Set) qemuVgaList := vga.List() - qemuNetworks, _ := ExpandDevicesList(d.Get("network").([]interface{})) qemuEfiDisks, _ := ExpandDevicesList(d.Get("efidisk").([]interface{})) qemuPCIDevices, _ := ExpandDevicesList(d.Get("hostpci").([]interface{})) @@ -982,7 +930,6 @@ func resourceVmQemuCreate(ctx context.Context, d *schema.ResourceData, meta inte QemuOs: d.Get("qemu_os").(string), Tags: tags.RemoveDuplicates(tags.Split(d.Get("tags").(string))), Args: d.Get("args").(string), - QemuNetworks: qemuNetworks, Serials: mapToSDK_Serials(d), QemuPCIDevices: qemuPCIDevices, QemuUsbs: qemuUsbs, @@ -990,11 +937,16 @@ func resourceVmQemuCreate(ctx context.Context, d *schema.ResourceData, meta inte CloudInit: mapToSDK_CloudInit(d), } - var diags diag.Diagnostics + var diags, tmpDiags diag.Diagnostics config.Disks, diags = mapToSDK_QemuStorages(d) if diags.HasError() { return diags } + config.Networks, tmpDiags = network.SDK(d) + diags = append(diags, tmpDiags...) + if tmpDiags.HasError() { + return diags + } if len(qemuVgaList) > 0 { config.QemuVga = qemuVgaList[0].(map[string]interface{}) @@ -1194,12 +1146,6 @@ func resourceVmQemuUpdate(ctx context.Context, d *schema.ResourceData, meta inte vga := d.Get("vga").(*schema.Set) qemuVgaList := vga.List() - qemuNetworks, err := ExpandDevicesList(d.Get("network").([]interface{})) - if err != nil { - return diag.FromErr(fmt.Errorf("error while processing Network configuration: %v", err)) - } - logger.Debug().Int("vmid", vmID).Msgf("Processed NetworkSet into qemuNetworks as %+v", qemuNetworks) - qemuPCIDevices, err := ExpandDevicesList(d.Get("hostpci").([]interface{})) if err != nil { return diag.FromErr(fmt.Errorf("error while processing HostPCI configuration: %v", err)) @@ -1240,7 +1186,6 @@ func resourceVmQemuUpdate(ctx context.Context, d *schema.ResourceData, meta inte QemuOs: d.Get("qemu_os").(string), Tags: tags.RemoveDuplicates(tags.Split(d.Get("tags").(string))), Args: d.Get("args").(string), - QemuNetworks: qemuNetworks, Serials: mapToSDK_Serials(d), QemuPCIDevices: qemuPCIDevices, QemuUsbs: qemuUsbs, @@ -1251,11 +1196,16 @@ func resourceVmQemuUpdate(ctx context.Context, d *schema.ResourceData, meta inte config.QemuVga = qemuVgaList[0].(map[string]interface{}) } - var diags diag.Diagnostics + var diags, tmpDiags diag.Diagnostics config.Disks, diags = mapToSDK_QemuStorages(d) if diags.HasError() { return diags } + config.Networks, tmpDiags = network.SDK(d) + diags = append(diags, tmpDiags...) + if tmpDiags.HasError() { + return diags + } logger.Debug().Int("vmid", vmID).Msgf("Updating VM with the following configuration: %+v", config) @@ -1528,6 +1478,9 @@ func resourceVmQemuRead(ctx context.Context, d *schema.ResourceData, meta interf mapToTerraform_CPU(config.CPU, d) mapToTerraform_CloudInit(config.CloudInit, d) mapToTerraform_Memory(config.Memory, d) + if len(config.Networks) != 0 { + network.Terraform(config.Networks, d) + } if len(config.Serials) != 0 { d.Set("serial", mapToTerraform_Serials(config.Serials)) } @@ -1579,33 +1532,6 @@ func resourceVmQemuRead(ctx context.Context, d *schema.ResourceData, meta interf d.Set("features", UpdateDeviceConfDefaults(config.QemuVga, activeVgaSet)) } - // Networks. - // add an explicit check that the keys in the config.QemuNetworks map are a strict subset of - // the keys in our resource schema. if they aren't things fail in a very weird and hidden way - logger.Debug().Int("vmid", vmID).Msgf("Analyzing Network blocks ") - for _, networkEntry := range config.QemuNetworks { - logger.Debug().Int("vmid", vmID).Msgf("Network block received '%v'", networkEntry) - // If network tag was not set, assign default value. - if networkEntry["tag"] == "" || networkEntry["tag"] == nil { - networkEntry["tag"] = thisResource.Schema["network"].Elem.(*schema.Resource).Schema["tag"].Default - } - for key := range networkEntry { - if _, ok := thisResource.Schema["network"].Elem.(*schema.Resource).Schema[key]; !ok { - if key == "id" { // we purposely ignore id here as that is implied by the order in the TypeList/QemuDevice(list) - continue - } - return diag.FromErr(fmt.Errorf("proxmox Provider Error: proxmox API returned new network parameter '%v' we cannot process", key)) - } - } - } - // flatten the structure into the format terraform needs and remove the "id" attribute as that will be encoded into - // the list structure. - flatNetworks, _ := FlattenDevicesList(config.QemuNetworks) - flatNetworks, _ = DropElementsFromMap([]string{"id"}, flatNetworks) - if err = d.Set("network", flatNetworks); err != nil { - return diag.FromErr(err) - } - d.Set("pool", vmr.Pool()) // Reset reboot_required variable. It should change only during updates. @@ -1614,9 +1540,6 @@ func resourceVmQemuRead(ctx context.Context, d *schema.ResourceData, meta interf // DEBUG print out the read result flatValue, _ := resourceDataToFlatValues(d, thisResource) jsonString, _ := json.Marshal(flatValue) - if len(flatNetworks) > 0 { - logger.Debug().Int("vmid", vmID).Msgf("VM Net Config '%+v' from '%+v' set as '%+v' type of '%T'", config.QemuNetworks, flatNetworks, d.Get("network"), flatNetworks[0]["macaddr"]) - } logger.Debug().Int("vmid", vmID).Msgf("Finished VM read resulting in data: '%+v'", string(jsonString)) return diags From bbba8ae40f91e3d704c744e2440aeb51464a39f6 Mon Sep 17 00:00:00 2001 From: Tinyblargon <76069640+Tinyblargon@users.noreply.github.com> Date: Mon, 28 Oct 2024 08:09:36 +0100 Subject: [PATCH 6/6] docs: add `network.id` --- docs/resources/vm_qemu.md | 6 ++++-- examples/cloudinit_example.tf | 1 + examples/pxe_example.tf | 1 + 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/resources/vm_qemu.md b/docs/resources/vm_qemu.md index 2977dd1f..69286037 100644 --- a/docs/resources/vm_qemu.md +++ b/docs/resources/vm_qemu.md @@ -66,6 +66,7 @@ resource "proxmox_vm_qemu" "pxe-minimal-example" { pxe = true target_node = "test" network { + id = 0 bridge = "vmbr0" firewall = false link_down = false @@ -165,8 +166,9 @@ second will be `net1` etc... See the [docs about network devices](https://pve.proxmox.com/pve-docs/chapter-qm.html#qm_network_device) for more details. -| Argument | Type | Default Value | Description | -| ----------- | ------ | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Argument | Type | Default Value | Description | +| ----------- | ------ | ------------- | ----------- | +| `id` | `int` | | **Required** The ID of the network device `0`-`31`. | | `model` | `str` | | **Required** Network Card Model. The virtio model provides the best performance with very low CPU overhead. If your guest does not support this driver, it is usually best to use e1000. Options: `e1000`, `e1000-82540em`, `e1000-82544gc`, `e1000-82545em`, `i82551`, `i82557b`, `i82559er`, `ne2k_isa`, `ne2k_pci`, `pcnet`, `rtl8139`, `virtio`, `vmxnet3`. | | `macaddr` | `str` | | Override the randomly generated MAC Address for the VM. Requires the MAC Address be Unicast. | | `bridge` | `str` | `"nat"` | Bridge to which the network device should be attached. The Proxmox VE standard bridge is called `vmbr0`. | diff --git a/examples/cloudinit_example.tf b/examples/cloudinit_example.tf index 0a4451b3..b851073a 100644 --- a/examples/cloudinit_example.tf +++ b/examples/cloudinit_example.tf @@ -56,6 +56,7 @@ resource "proxmox_vm_qemu" "cloudinit-test" { # Setup the network interface and assign a vlan tag: 256 network { + id = 0 model = "virtio" bridge = "vmbr0" tag = 256 diff --git a/examples/pxe_example.tf b/examples/pxe_example.tf index fbcaee0d..b0ba78d2 100644 --- a/examples/pxe_example.tf +++ b/examples/pxe_example.tf @@ -72,6 +72,7 @@ resource "proxmox_vm_qemu" "pxe-example" { } network { + id = 0 bridge = "vmbr0" firewall = false link_down = false