mirror of
https://github.com/bpg/terraform-provider-proxmox.git
synced 2025-06-30 02:31:10 +00:00
feat(vm): add VLAN
trunk support (#1086)
* feat(vm): add `VLAN` trunk support Signed-off-by: Jack Hodgkiss <identity@jackhodgkiss.uk> * update docs * better error handling * add trunks to acceptance test Signed-off-by: Pavel Boldyrev <627562+bpg@users.noreply.github.com> --------- Signed-off-by: Jack Hodgkiss <identity@jackhodgkiss.uk> Signed-off-by: Pavel Boldyrev <627562+bpg@users.noreply.github.com> Co-authored-by: Pavel Boldyrev <627562+bpg@users.noreply.github.com>
This commit is contained in:
parent
3195b3cdf4
commit
cb5fc279cd
@ -414,6 +414,9 @@ output "ubuntu_vm_public_key" {
|
||||
- `queues` - (Optional) The number of queues for VirtIO (1..64).
|
||||
- `rate_limit` - (Optional) The rate limit in megabytes per second.
|
||||
- `vlan_id` - (Optional) The VLAN identifier.
|
||||
- `trunks` - (Optional) String containing a `;` separated list of VLAN trunks
|
||||
("10;20;30"). Note that the VLAN-aware feature need to be enabled on the PVE
|
||||
Linux Bridge to use trunks.
|
||||
- `node_name` - (Required) The name of the node to assign the virtual machine
|
||||
to.
|
||||
- `on_boot` - (Optional) Specifies whether a VM will be started during system
|
||||
|
53
example/resource_virtual_environment_trunks.tf
Normal file
53
example/resource_virtual_environment_trunks.tf
Normal file
@ -0,0 +1,53 @@
|
||||
resource "proxmox_virtual_environment_vm" "trunks-example" {
|
||||
name = "trunks-example"
|
||||
node_name = data.proxmox_virtual_environment_nodes.example.names[0]
|
||||
description = "Example of a VM using trunks to pass multiple VLANs on a single network interface."
|
||||
|
||||
disk {
|
||||
datastore_id = local.datastore_id
|
||||
file_id = proxmox_virtual_environment_download_file.latest_debian_12_bookworm_qcow2_img.id
|
||||
interface = "scsi0"
|
||||
discard = "on"
|
||||
cache = "writeback"
|
||||
ssd = true
|
||||
}
|
||||
|
||||
initialization {
|
||||
datastore_id = local.datastore_id
|
||||
interface = "scsi4"
|
||||
|
||||
dns {
|
||||
servers = ["1.1.1.1", "8.8.8.8"]
|
||||
}
|
||||
|
||||
ip_config {
|
||||
ipv4 {
|
||||
address = "dhcp"
|
||||
}
|
||||
}
|
||||
user_data_file_id = proxmox_virtual_environment_file.user_config.id
|
||||
vendor_data_file_id = proxmox_virtual_environment_file.vendor_config.id
|
||||
meta_data_file_id = proxmox_virtual_environment_file.meta_config.id
|
||||
}
|
||||
|
||||
memory {
|
||||
dedicated = 1024
|
||||
}
|
||||
|
||||
cpu {
|
||||
cores = 2
|
||||
}
|
||||
|
||||
agent {
|
||||
enabled = true
|
||||
}
|
||||
|
||||
boot_order = ["scsi0"]
|
||||
scsi_hardware = "virtio-scsi-pci"
|
||||
|
||||
network_device {
|
||||
model = "virtio"
|
||||
bridge = "vmbr0"
|
||||
trunks = "10;20;30"
|
||||
}
|
||||
}
|
@ -78,13 +78,11 @@ func TestAccResourceVM(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAccResourceVMNetwork(t *testing.T) {
|
||||
t.Skip("This test is hanging up")
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
step resource.TestStep
|
||||
}{
|
||||
{"network interfaces mac", resource.TestStep{
|
||||
{"network interfaces", resource.TestStep{
|
||||
Config: `
|
||||
resource "proxmox_virtual_environment_file" "cloud_config" {
|
||||
content_type = "snippets"
|
||||
@ -133,6 +131,7 @@ EOF
|
||||
}
|
||||
network_device {
|
||||
bridge = "vmbr0"
|
||||
trunks = "10;20;30"
|
||||
}
|
||||
}
|
||||
|
||||
@ -144,8 +143,12 @@ EOF
|
||||
overwrite_unmanaged = true
|
||||
}`,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("proxmox_virtual_environment_vm.test_vm_network1", "ipv4_addresses.#", "2"),
|
||||
resource.TestCheckResourceAttr("proxmox_virtual_environment_vm.test_vm_network1", "mac_addresses.#", "2"),
|
||||
testResourceAttributes("proxmox_virtual_environment_vm.test_vm_network1", map[string]string{
|
||||
"ipv4_addresses.#": "2",
|
||||
"mac_addresses.#": "2",
|
||||
"network_device.0.bridge": "vmbr0",
|
||||
"network_device.0.trunks": "10;20;30",
|
||||
}),
|
||||
),
|
||||
}},
|
||||
}
|
||||
|
@ -100,6 +100,7 @@ const (
|
||||
dvNetworkDeviceQueues = 0
|
||||
dvNetworkDeviceRateLimit = 0
|
||||
dvNetworkDeviceVLANID = 0
|
||||
dvNetworkDeviceTrunks = ""
|
||||
dvNetworkDeviceMTU = 0
|
||||
dvOperatingSystemType = "other"
|
||||
dvPoolID = ""
|
||||
@ -234,6 +235,7 @@ const (
|
||||
mkNetworkDeviceQueues = "queues"
|
||||
mkNetworkDeviceRateLimit = "rate_limit"
|
||||
mkNetworkDeviceVLANID = "vlan_id"
|
||||
mkNetworkDeviceTrunks = "trunks"
|
||||
mkNetworkDeviceMTU = "mtu"
|
||||
mkNetworkInterfaceNames = "network_interface_names"
|
||||
mkNodeName = "node_name"
|
||||
@ -1129,6 +1131,11 @@ func VM() *schema.Resource {
|
||||
Optional: true,
|
||||
Default: dvNetworkDeviceVLANID,
|
||||
},
|
||||
mkNetworkDeviceTrunks: {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Description: "List of VLAN trunks for the network interface",
|
||||
},
|
||||
mkNetworkDeviceMTU: {
|
||||
Type: schema.TypeInt,
|
||||
Description: "Maximum transmission unit (MTU)",
|
||||
@ -2012,7 +2019,10 @@ func vmCreateClone(ctx context.Context, d *schema.ResourceData, m interface{}) d
|
||||
}
|
||||
|
||||
if len(networkDevice) > 0 {
|
||||
updateBody.NetworkDevices = vmGetNetworkDeviceObjects(d)
|
||||
updateBody.NetworkDevices, err = vmGetNetworkDeviceObjects(d)
|
||||
if err != nil {
|
||||
return diag.FromErr(err)
|
||||
}
|
||||
|
||||
for i := 0; i < len(updateBody.NetworkDevices); i++ {
|
||||
if !updateBody.NetworkDevices[i].Enabled {
|
||||
@ -2402,7 +2412,10 @@ func vmCreateCustom(ctx context.Context, d *schema.ResourceData, m interface{})
|
||||
name := d.Get(mkName).(string)
|
||||
tags := d.Get(mkTags).([]interface{})
|
||||
|
||||
networkDeviceObjects := vmGetNetworkDeviceObjects(d)
|
||||
networkDeviceObjects, err := vmGetNetworkDeviceObjects(d)
|
||||
if err != nil {
|
||||
return diag.FromErr(err)
|
||||
}
|
||||
|
||||
nodeName := d.Get(mkNodeName).(string)
|
||||
|
||||
@ -3025,7 +3038,7 @@ func vmGetHostUSBDeviceObjects(d *schema.ResourceData) vms.CustomUSBDevices {
|
||||
return usbDeviceObjects
|
||||
}
|
||||
|
||||
func vmGetNetworkDeviceObjects(d *schema.ResourceData) vms.CustomNetworkDevices {
|
||||
func vmGetNetworkDeviceObjects(d *schema.ResourceData) (vms.CustomNetworkDevices, error) {
|
||||
networkDevice := d.Get(mkNetworkDevice).([]interface{})
|
||||
networkDeviceObjects := make(vms.CustomNetworkDevices, len(networkDevice))
|
||||
|
||||
@ -3040,6 +3053,7 @@ func vmGetNetworkDeviceObjects(d *schema.ResourceData) vms.CustomNetworkDevices
|
||||
queues := block[mkNetworkDeviceQueues].(int)
|
||||
rateLimit := block[mkNetworkDeviceRateLimit].(float64)
|
||||
vlanID := block[mkNetworkDeviceVLANID].(int)
|
||||
trunks := block[mkNetworkDeviceTrunks].(string)
|
||||
mtu := block[mkNetworkDeviceMTU].(int)
|
||||
|
||||
device := vms.CustomNetworkDevice{
|
||||
@ -3068,6 +3082,23 @@ func vmGetNetworkDeviceObjects(d *schema.ResourceData) vms.CustomNetworkDevices
|
||||
device.Tag = &vlanID
|
||||
}
|
||||
|
||||
if trunks != "" {
|
||||
splitTrunks := strings.Split(trunks, ";")
|
||||
|
||||
var trunksAsInt []int
|
||||
|
||||
for _, numStr := range splitTrunks {
|
||||
num, err := strconv.Atoi(numStr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing trunks: %w", err)
|
||||
}
|
||||
|
||||
trunksAsInt = append(trunksAsInt, num)
|
||||
}
|
||||
|
||||
device.Trunks = trunksAsInt
|
||||
}
|
||||
|
||||
if mtu != 0 {
|
||||
device.MTU = &mtu
|
||||
}
|
||||
@ -3075,7 +3106,7 @@ func vmGetNetworkDeviceObjects(d *schema.ResourceData) vms.CustomNetworkDevices
|
||||
networkDeviceObjects[i] = device
|
||||
}
|
||||
|
||||
return networkDeviceObjects
|
||||
return networkDeviceObjects, nil
|
||||
}
|
||||
|
||||
func vmGetSerialDeviceList(d *schema.ResourceData) vms.CustomSerialDevices {
|
||||
@ -4123,6 +4154,13 @@ func vmReadCustom(
|
||||
networkDevice[mkNetworkDeviceVLANID] = 0
|
||||
}
|
||||
|
||||
if nd.Trunks != nil {
|
||||
networkDevice[mkNetworkDeviceTrunks] = strings.Trim(
|
||||
strings.Join(strings.Fields(fmt.Sprint(nd.Trunks)), ";"), "[]")
|
||||
} else {
|
||||
networkDevice[mkNetworkDeviceTrunks] = ""
|
||||
}
|
||||
|
||||
if nd.MTU != nil {
|
||||
networkDevice[mkNetworkDeviceMTU] = nd.MTU
|
||||
} else {
|
||||
@ -5148,7 +5186,7 @@ func vmUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.D
|
||||
|
||||
// Prepare the new memory configuration.
|
||||
if d.HasChange(mkMemory) {
|
||||
memoryBlock, err := structure.GetSchemaBlock(
|
||||
memoryBlock, er := structure.GetSchemaBlock(
|
||||
resource,
|
||||
d,
|
||||
[]string{mkMemory},
|
||||
@ -5156,7 +5194,7 @@ func vmUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.D
|
||||
true,
|
||||
)
|
||||
if err != nil {
|
||||
return diag.FromErr(err)
|
||||
return diag.FromErr(er)
|
||||
}
|
||||
|
||||
memoryDedicated := memoryBlock[mkMemoryDedicated].(int)
|
||||
@ -5180,7 +5218,10 @@ func vmUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.D
|
||||
|
||||
// Prepare the new network device configuration.
|
||||
if d.HasChange(mkNetworkDevice) {
|
||||
updateBody.NetworkDevices = vmGetNetworkDeviceObjects(d)
|
||||
updateBody.NetworkDevices, err = vmGetNetworkDeviceObjects(d)
|
||||
if err != nil {
|
||||
return diag.FromErr(err)
|
||||
}
|
||||
|
||||
for i := 0; i < len(updateBody.NetworkDevices); i++ {
|
||||
if !updateBody.NetworkDevices[i].Enabled {
|
||||
|
Loading…
Reference in New Issue
Block a user