Skip to content

Commit

Permalink
Merge pull request #476 from wearespindle/master
Browse files Browse the repository at this point in the history
Add automatic_reboot parameter to proxmox_vm_qemu
  • Loading branch information
mleone87 authored Jan 2, 2022
2 parents a222eb6 + c31d967 commit 72c6277
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 25 deletions.
1 change: 1 addition & 0 deletions docs/resources/vm_qemu.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ The following arguments are supported in the top level resource block.
| `sshkeys` | `str` | | Newline delimited list of SSH public keys to add to authorized keys file for the cloud-init user. |
| `ipconfig0` | `str` | | The first IP address to assign to the guest. Format: `[gw=<GatewayIPv4>] [,gw6=<GatewayIPv6>] [,ip=<IPv4Format/CIDR>] [,ip6=<IPv6Format/CIDR>]`. |
| `ipconfig1` to `ipconfig15` | `str` | | The second IP address to assign to the guest. Same format as `ipconfig0`. |
| `automatic_reboot` | `bool` | `true` | Automatically reboot the VM when parameter changes require this. If disabled the provider will emit a warning when the VM needs to be rebooted. |

Note: Proxmox supports ipconfigN arbitrary numbers of interfaces, but at the moment this Terraform provider has support
for 0-5 addresses only. If there is interest, this could be refactored to support any number of interfaces.
Expand Down
80 changes: 55 additions & 25 deletions proxmox/resource_vm_qemu.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package proxmox

import (
"context"
"encoding/json"
"fmt"
"log"
Expand All @@ -10,6 +11,8 @@ import (
"time"

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"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
)
Expand All @@ -21,10 +24,10 @@ var thisResource *schema.Resource

func resourceVmQemu() *schema.Resource {
thisResource = &schema.Resource{
Create: resourceVmQemuCreate,
Read: resourceVmQemuRead,
Update: resourceVmQemuUpdate,
Delete: resourceVmQemuDelete,
Create: resourceVmQemuCreate,
Read: resourceVmQemuRead,
UpdateContext: resourceVmQemuUpdate,
Delete: resourceVmQemuDelete,
Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
},
Expand Down Expand Up @@ -668,14 +671,19 @@ func resourceVmQemu() *schema.Resource {
Optional: true,
Default: 100,
},
"automatic_reboot": {
Type: schema.TypeBool,
Optional: true,
Default: true,
Description: "Automatically reboot the VM if any of the modified parameters require a reboot to take effect.",
},
},
Timeouts: resourceTimeouts(),
}
return thisResource
}

func resourceVmQemuCreate(d *schema.ResourceData, meta interface{}) error {

// create a logger for this function
logger, _ := CreateSubLogger("resource_vm_create")

Expand Down Expand Up @@ -921,7 +929,7 @@ func resourceVmQemuCreate(d *schema.ResourceData, meta interface{}) error {
return resourceVmQemuRead(d, meta)
}

func resourceVmQemuUpdate(d *schema.ResourceData, meta interface{}) error {
func resourceVmQemuUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
pconf := meta.(*providerConfiguration)
lock := pmParallelBegin(pconf)
//defer lock.unlock()
Expand All @@ -932,15 +940,15 @@ func resourceVmQemuUpdate(d *schema.ResourceData, meta interface{}) error {
client := pconf.Client
_, _, vmID, err := parseResourceId(d.Id())
if err != nil {
return err
return diag.FromErr(err)
}

logger.Info().Int("vmid", vmID).Msg("Starting update of the VM resource")

vmr := pxapi.NewVmRef(vmID)
_, err = client.GetVmInfo(vmr)
if err != nil {
return err
return diag.FromErr(err)
}
vga := d.Get("vga").(*schema.Set)
qemuVgaList := vga.List()
Expand All @@ -957,7 +965,7 @@ func resourceVmQemuUpdate(d *schema.ResourceData, meta interface{}) error {

qemuNetworks, err := ExpandDevicesList(d.Get("network").([]interface{}))
if err != nil {
return fmt.Errorf("error while processing Network configuration: %v", err)
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)

Expand All @@ -968,7 +976,7 @@ func resourceVmQemuUpdate(d *schema.ResourceData, meta interface{}) error {
if d.HasChange("target_node") {
_, err := client.MigrateNode(vmr, d.Get("target_node").(string), true)
if err != nil {
return err
return diag.FromErr(err)
}
vmr.SetNode(d.Get("target_node").(string))
}
Expand Down Expand Up @@ -1032,7 +1040,7 @@ func resourceVmQemuUpdate(d *schema.ResourceData, meta interface{}) error {

err = config.UpdateConfig(vmr, client)
if err != nil {
return err
return diag.FromErr(err)
}

// Give some time to proxmox to catchup.
Expand All @@ -1054,13 +1062,13 @@ func resourceVmQemuUpdate(d *schema.ResourceData, meta interface{}) error {

_, err := client.UpdateVMPool(vmr, newPool)
if err != nil {
return err
return diag.FromErr(err)
}
}

err = initConnInfo(d, pconf, client, vmr, &config, lock)
if err != nil {
return err
return diag.FromErr(err)
}

// If any of the "critical" keys are changed then a reboot is required.
Expand Down Expand Up @@ -1171,21 +1179,35 @@ func resourceVmQemuUpdate(d *schema.ResourceData, meta interface{}) error {
}
}

// If a reboot is required: if the VM is running attempt graceful shutdown. If failed, try a forced poweroff.
var diags diag.Diagnostics

// Try rebooting the VM is a reboot is required and automatic_reboot is
// enabled. Attempt a graceful shutdown or if that fails, force poweroff.
vmState, err := client.GetVmState(vmr)
if err == nil && vmState["status"] != "stopped" && d.Get("reboot_required").(bool) {
log.Print("[DEBUG][QemuVmUpdate] shutting down VM")
_, err = client.ShutdownVm(vmr)
// note: the default timeout is 3 min, configurable per VM: Options/Start-Shutdown Order/Shutdown timeout
if err != nil {
log.Print("[DEBUG][QemuVmUpdate] shutdown failed, stopping VM forcefully")
_, err = client.StopVm(vmr)
if d.Get("automatic_reboot").(bool) {
log.Print("[DEBUG][QemuVmUpdate] shutting down VM")
_, err = client.ShutdownVm(vmr)
// note: the default timeout is 3 min, configurable per VM: Options/Start-Shutdown Order/Shutdown timeout
if err != nil {
return err
log.Print("[DEBUG][QemuVmUpdate] shutdown failed, stopping VM forcefully")
_, err = client.StopVm(vmr)
if err != nil {
return diag.FromErr(err)
}
}
} else {
// Automatic reboots is not enabled, show the user a warning message that
// the VM needs a reboot for the changed parameters to take in effect.
diags = append(diags, diag.Diagnostic{
Severity: diag.Warning,
Summary: "VM needs to be rebooted and automatic_reboot is disabled",
Detail: "One or more parameters are modified that only take effect after a reboot (shutdown & start).",
AttributePath: cty.Path{},
})
}
} else if err != nil {
return err
return diag.FromErr(err)
}

// Start VM only if it wasn't running.
Expand All @@ -1194,13 +1216,21 @@ func resourceVmQemuUpdate(d *schema.ResourceData, meta interface{}) error {
log.Print("[DEBUG][QemuVmUpdate] starting VM")
_, err = client.StartVm(vmr)
if err != nil {
return err
return diag.FromErr(err)
}
} else if err != nil {
return err
diags = append(diags, diag.FromErr(err)...)
return diags
}
lock.unlock()
return resourceVmQemuRead(d, meta)

err = resourceVmQemuRead(d, meta)
if err != nil {
diags = append(diags, diag.FromErr(err)...)
return diags
}

return diags
}

func resourceVmQemuRead(d *schema.ResourceData, meta interface{}) error {
Expand Down

0 comments on commit 72c6277

Please sign in to comment.