Skip to content

Commit

Permalink
allow using dynamic list for ipconfig instead of hardcoding the index…
Browse files Browse the repository at this point in the history
… in the key (#866)

* allow using dynamic list for ipconfig instead of hardcoding the index in the key

* adding documentation

---------

Co-authored-by: sammy <sammy@email.com>
  • Loading branch information
spettinichi and sammy committed Dec 6, 2023
1 parent 94f2ba5 commit d02bb5d
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 35 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,6 @@ examples/*_override.tf
bin
.vscode
*.log
*env
*env
vendor/*

8 changes: 7 additions & 1 deletion docs/resources/vm_qemu.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ parameter to create based
on [a Cloud-init configuration file](https://cloudinit.readthedocs.io/en/latest/topics/examples.html) or use the Proxmox
variable `ciuser`, `cipassword`, `ipconfig0`, `ipconfig1`, `ipconfig2`, `ipconfig3`, `ipconfig4`, `ipconfig5`,
`ipconfig6`, `ipconfig7`, `ipconfig8`, `ipconfig9`, `ipconfig10`, `ipconfig11`, `ipconfig12`, `ipconfig13`,
`ipconfig14`,`ipconfig15`, `searchdomain`, `nameserver` and `sshkeys`.
`ipconfig14`,`ipconfig15`, `searchdomain`, `nameserver` and `sshkeys`. A variable amount of static IPs can be configured using the dynamic [`ipconfig` block](#ipconfig-block) to list multiple IP addresses.

For more information, see the [Cloud-init guide](../guides/cloud_init.md).

Expand Down Expand Up @@ -164,6 +164,12 @@ details.
| `queues` | `int` | `1` | Number of packet queues to be used on the device. Requires `virtio` model to have an effect. |
| `link_down` | `bool` | `false` | Whether this interface should be disconnected (like pulling the plug). |

### Ipconfig Block

The `ipconfig` block is used to configure multiple static IP addresses. It may be specified multiple times.
| Argument | Type | Default Value | Description |
| `config` | `str` | | IP address to assign to the guest. Format: [gw=<GatewayIPv4>] [,gw6=<GatewayIPv6>] [,ip=<IPv4Format/CIDR>] [,ip6=<IPv6Format/CIDR>]. |

### Disk Block

The `disk` block is used to configure the disk devices. It may be specified multiple times. The order in which the
Expand Down
116 changes: 83 additions & 33 deletions proxmox/resource_vm_qemu.go
Original file line number Diff line number Diff line change
Expand Up @@ -837,6 +837,19 @@ func resourceVmQemu() *schema.Resource {
return strings.TrimSpace(old) == strings.TrimSpace(new)
},
},
"ipconfig": {
Type: schema.TypeList,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"config": {
Type: schema.TypeString,
Required: true,
Description: "ipconfig string in format: [gw=<GatewayIPv4>] [,gw6=<GatewayIPv6>] [,ip=<IPv4Format/CIDR>] [,ip6=<IPv6Format/CIDR>]",
},
},
},
},
"ipconfig0": {
Type: schema.TypeString,
Optional: true,
Expand Down Expand Up @@ -987,6 +1000,19 @@ func resourceVmQemuCreate(ctx context.Context, d *schema.ResourceData, meta inte

qemuUsbs, _ := ExpandDevicesList(d.Get("usb").([]interface{}))

// Populate Ipconfig map from iterable variable
qemuIpconfig, _ := ExpandIpconfigList(d.Get("ipconfig").([]interface{}))
// keeping this for backwards compatibility
if len(qemuIpconfig) == 0 {
// Populate Ipconfig map from explicit vars
for i := 0; i < 16; i++ {
iface := fmt.Sprintf("ipconfig%d", i)
if v, ok := d.GetOk(iface); ok {
qemuIpconfig[i] = v.(string)
}
}
}

config := pxapi.ConfigQemu{
Name: vmName,
Description: d.Get("desc").(string),
Expand Down Expand Up @@ -1027,15 +1053,9 @@ func resourceVmQemuCreate(ctx context.Context, d *schema.ResourceData, meta inte
Searchdomain: d.Get("searchdomain").(string),
Nameserver: d.Get("nameserver").(string),
Sshkeys: d.Get("sshkeys").(string),
Ipconfig: pxapi.IpconfigMap{},
}
// Populate Ipconfig map
for i := 0; i < 16; i++ {
iface := fmt.Sprintf("ipconfig%d", i)
if v, ok := d.GetOk(iface); ok {
config.Ipconfig[i] = v.(string)
}
Ipconfig: qemuIpconfig,
}

if len(qemuVgaList) > 0 {
config.QemuVga = qemuVgaList[0].(map[string]interface{})
}
Expand Down Expand Up @@ -1316,6 +1336,22 @@ func resourceVmQemuUpdate(ctx context.Context, d *schema.ResourceData, meta inte
return diag.FromErr(fmt.Errorf("error while processing Usb configuration: %v", err))
}

// Populate Ipconfig map from iterable variable
qemuIpconfig, err := ExpandIpconfigList(d.Get("ipconfig").([]interface{}))
if err != nil {
return diag.FromErr(fmt.Errorf("error while processing ipconfig configuration: %v", err))
}
// keeping this for backwards compatibility
if len(qemuIpconfig) == 0 {
// Populate Ipconfig map from explicit vars
for i := 0; i < 16; i++ {
iface := fmt.Sprintf("ipconfig%d", i)
if v, ok := d.GetOk(iface); ok {
qemuIpconfig[i] = v.(string)
}
}
}

d.Partial(true)
if d.HasChange("target_node") {
// check if it must be migrated manually or it has been migrated by the promox high availability system
Expand Down Expand Up @@ -1371,24 +1407,7 @@ func resourceVmQemuUpdate(ctx context.Context, d *schema.ResourceData, meta inte
Searchdomain: d.Get("searchdomain").(string),
Nameserver: d.Get("nameserver").(string),
Sshkeys: d.Get("sshkeys").(string),
Ipconfig: pxapi.IpconfigMap{
0: d.Get("ipconfig0").(string),
1: d.Get("ipconfig1").(string),
2: d.Get("ipconfig2").(string),
3: d.Get("ipconfig3").(string),
4: d.Get("ipconfig4").(string),
5: d.Get("ipconfig5").(string),
6: d.Get("ipconfig6").(string),
7: d.Get("ipconfig7").(string),
8: d.Get("ipconfig8").(string),
9: d.Get("ipconfig9").(string),
10: d.Get("ipconfig10").(string),
11: d.Get("ipconfig11").(string),
12: d.Get("ipconfig12").(string),
13: d.Get("ipconfig13").(string),
14: d.Get("ipconfig14").(string),
15: d.Get("ipconfig15").(string),
},
Ipconfig: qemuIpconfig,
}
if len(qemuVgaList) > 0 {
config.QemuVga = qemuVgaList[0].(map[string]interface{})
Expand Down Expand Up @@ -1446,6 +1465,7 @@ func resourceVmQemuUpdate(ctx context.Context, d *schema.ResourceData, meta inte
"searchdomain",
"nameserver",
"sshkeys",
"ipconfig",
"ipconfig0",
"ipconfig1",
"ipconfig2",
Expand Down Expand Up @@ -1747,6 +1767,7 @@ func resourceVmQemuRead(ctx context.Context, d *schema.ResourceData, meta interf
d.Set("searchdomain", config.Searchdomain)
d.Set("nameserver", config.Nameserver)
d.Set("sshkeys", config.Sshkeys)
d.Set("ipconfig", config.Ipconfig)
d.Set("ipconfig0", config.Ipconfig[0])
d.Set("ipconfig1", config.Ipconfig[1])
d.Set("ipconfig2", config.Ipconfig[2])
Expand Down Expand Up @@ -2178,6 +2199,34 @@ func ExpandDevicesList(deviceList []interface{}) (pxapi.QemuDevices, error) {
return expandedDevices, nil
}

// Consumes a terraform TypeList of a Qemu Device (network, hard drive, etc) and returns the "Expanded"
// version of the equivalent configuration that the API understands (the struct pxapi.IpconfigMap).
func ExpandIpconfigList(ipconfigList []interface{}) (pxapi.IpconfigMap, error) {
expandedDevices := make(pxapi.IpconfigMap)

if len(ipconfigList) == 0 {
return expandedDevices, nil
}

ipconfigDevices, err := ExpandDevicesList(ipconfigList)
if err != nil {
return expandedDevices, err
}

for index, thisDeviceMap := range ipconfigDevices {
// thisDeviceMap := deviceInterface

// bail out if the device is empty, it is meaningless in this context
if thisDeviceMap == nil {
continue
}

expandedDevices[index] = thisDeviceMap["config"]
}

return expandedDevices, nil
}

// Update schema.TypeSet with new values comes from Proxmox API.
// TODO: remove these set functions and convert attributes using a set to a list instead.
func UpdateDevicesSet(
Expand Down Expand Up @@ -2332,8 +2381,8 @@ func initConnInfo(ctx context.Context,
if config.HasCloudInit() {
log.Print("[DEBUG][initConnInfo] vm has a cloud-init configuration")
logger.Debug().Int("vmid", vmr.VmId()).Msgf(" vm has a cloud-init configuration")
_, ipconfig0Set := d.GetOk("ipconfig0")
if ipconfig0Set {
_, ipconfigSet := d.GetOk("ipconfig")
if ipconfigSet {
vmState, err := client.GetVmState(vmr)
log.Printf("[DEBUG][initConnInfo] cloudinitcheck vm state %v", vmState)
logger.Debug().Int("vmid", vmr.VmId()).Msgf("cloudinitcheck vm state %v", vmState)
Expand All @@ -2343,16 +2392,17 @@ func initConnInfo(ctx context.Context,
return diag.FromErr(err)
}

if d.Get("ipconfig0").(string) != "ip=dhcp" || vmState["agent"] == nil || vmState["agent"].(float64) != 1 {
// parse IP address out of ipconfig0
ipMatch := rxIPconfig.FindStringSubmatch(d.Get("ipconfig0").(string))
ipConfig := d.Get("ipconfig").(map[int]string)
if ipConfig[0] != "ip=dhcp" || vmState["agent"] == nil || vmState["agent"].(float64) != 1 {
// parse IP address out of ipconfig
ipMatch := rxIPconfig.FindStringSubmatch(ipConfig[0])
if sshHost == "" {
sshHost = ipMatch[1]
}
ipconfig0 := net.ParseIP(strings.Split(ipMatch[1], ":")[0])
interfaces, err = client.GetVmAgentNetworkInterfaces(vmr)
log.Printf("[DEBUG][initConnInfo] ipconfig0 interfaces: %v", interfaces)
logger.Debug().Int("vmid", vmr.VmId()).Msgf("ipconfig0 interfaces %v", interfaces)
log.Printf("[DEBUG][initConnInfo] ipconfig[0] interfaces: %v", interfaces)
logger.Debug().Int("vmid", vmr.VmId()).Msgf("ipconfig[0] interfaces %v", interfaces)
if err != nil {
return diag.FromErr(err)
} else {
Expand Down

0 comments on commit d02bb5d

Please sign in to comment.