Skip to content

Commit

Permalink
Feature: vm_state = started (Telmate#960)
Browse files Browse the repository at this point in the history
* refactor: control flow for start and stopping vm

* feat: `vm_state` = `started`

* refactor: use constants in schema
  • Loading branch information
Tinyblargon authored Mar 13, 2024
1 parent 0a2135b commit 3eacccd
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 45 deletions.
2 changes: 1 addition & 1 deletion docs/resources/vm_qemu.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ The following arguments are supported in the top level resource block.
| `bios` | `str` | `"seabios"` | The BIOS to use, options are `seabios` or `ovmf` for UEFI. |
| `onboot` | `bool` | `false` | Whether to have the VM startup after the PVE node starts. |
| `startup` | `string` | `""` | The [startup and shutdown behaviour](https://pve.proxmox.com/pve-docs/pve-admin-guide.html#pct_startup_and_shutdown) |
| `vm_state` | `string` | `"running"` | The desired state of the VM, options are `running` or `stopped`. |
| `vm_state` | `string` | `"running"` | The desired state of the VM, options are `running`, `stopped` and `started`. Do note that `started` will only start the vm on creation and won't fully manage the power state unlike `running` and `stopped` do.|
| `oncreate` | `bool` | `true` | Whether to have the VM startup after the VM is created (deprecated, use `vm_state` instead) |
| `tablet` | `bool` | `true` | Enable/disable the USB tablet device. This device is usually needed to allow absolute mouse positioning with VNC. |
| `boot` | `str` | | The boot order for the VM. For example: `order=scsi0;ide2;net0`. The deprecated `legacy=` syntax is no longer supported. See the `boot` option in the [Proxmox manual](https://pve.proxmox.com/wiki/Manual:_qm.conf#_options) for more information. |
Expand Down
98 changes: 56 additions & 42 deletions proxmox/resource_vm_qemu.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ import (
// so that we can print (debug) our ResourceData constructs
var thisResource *schema.Resource

const (
stateRunning string = "running"
stateStarted string = "started"
stateStopped string = "stopped"
)

func resourceVmQemu() *schema.Resource {
thisResource = &schema.Resource{
CreateContext: resourceVmQemuCreate,
Expand Down Expand Up @@ -117,9 +123,12 @@ func resourceVmQemu() *schema.Resource {
"vm_state": {
Type: schema.TypeString,
Optional: true,
Default: "running",
Description: "The state of the VM (running or stopped)",
Default: stateRunning,
Description: "The state of the VM (" + stateRunning + ", " + stateStarted + ", " + stateStopped + ")",
ValidateDiagFunc: VMStateValidator(),
DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
return new == stateStarted
},
},
"onboot": {
Type: schema.TypeBool,
Expand Down Expand Up @@ -1018,7 +1027,7 @@ func resourceVmQemuCreate(ctx context.Context, d *schema.ResourceData, meta inte
// give sometime to proxmox to catchup
time.Sleep(time.Duration(d.Get("additional_wait").(int)) * time.Second)

if d.Get("vm_state").(string) == "running" {
if d.Get("vm_state").(string) == "running" || d.Get("vm_state").(string) == "started" {
log.Print("[DEBUG][QemuVmCreate] starting VM")
_, err := client.StartVm(vmr)
if err != nil {
Expand Down Expand Up @@ -1252,56 +1261,61 @@ func resourceVmQemuUpdate(ctx context.Context, d *schema.ResourceData, meta inte
// Try rebooting the VM is a reboot is required and automatic_reboot is
// enabled. Attempt a graceful shutdown or if that fails, force power-off.
vmState, err := client.GetVmState(vmr)
if err == nil && vmState["status"] != "stopped" && d.Get("vm_state").(string) == "stopped" {
log.Print("[DEBUG][QemuVmUpdate] shutting down VM to match `vm_state`")
_, 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 err != nil {
if err != nil {
diags = append(diags, diag.FromErr(err)...)
return diags
}
switch vmState["status"].(string) { // manage the VM state to match the `vm_state` attribute
// case stateStarted: does nothing during update as we don't enforce the VM state
case stateStopped:
if d.Get("vm_state").(string) == stateRunning { // start the VM
log.Print("[DEBUG][QemuVmUpdate] starting VM to match `vm_state`")
if _, err = client.StartVm(vmr); err != nil {
return diag.FromErr(err)
}
}
} else if err == nil && vmState["status"] != "stopped" && d.Get("reboot_required").(bool) {
if d.Get("automatic_reboot").(bool) {
log.Print("[DEBUG][QemuVmUpdate] rebooting the VM to match the configuration changes")
_, err = client.RebootVm(vmr)
case stateRunning:
if d.Get("vm_state").(string) == stateStopped { // shutdown the VM
log.Print("[DEBUG][QemuVmUpdate] shutting down VM to match `vm_state`")
_, 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] reboot failed, stopping VM forcefully")

if _, err := client.StopVm(vmr); err != nil {
log.Print("[DEBUG][QemuVmUpdate] shutdown failed, stopping VM forcefully")
if _, err = client.StopVm(vmr); err != nil {
return diag.FromErr(err)
}

// give sometime to proxmox to catchup
dur := time.Duration(d.Get("additional_wait").(int)) * time.Second
log.Printf("[DEBUG][QemuVmUpdate] waiting for (%v) before starting the VM again", dur)
time.Sleep(dur)

if _, err := client.StartVm(vmr); err != nil {
return diag.FromErr(err)
}
} else if d.Get("reboot_required").(bool) { // reboot the VM
if d.Get("automatic_reboot").(bool) { // automatic reboots is enabled
log.Print("[DEBUG][QemuVmUpdate] rebooting the VM to match the configuration changes")
_, err = client.RebootVm(vmr)
// note: the default timeout is 3 min, configurable per VM: Options/Start-Shutdown Order/Shutdown timeout
if err != nil {
log.Print("[DEBUG][QemuVmUpdate] reboot failed, stopping VM forcefully")
if _, err := client.StopVm(vmr); err != nil {
return diag.FromErr(err)
}
// give sometime to proxmox to catchup
dur := time.Duration(d.Get("additional_wait").(int)) * time.Second
log.Printf("[DEBUG][QemuVmUpdate] waiting for (%v) before starting the VM again", dur)
time.Sleep(dur)
if _, err := client.StartVm(vmr); err != nil {
return diag.FromErr(err)
}
}
} else { // automatic reboots is disabled
// 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 {
// 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 {
diags = append(diags, diag.FromErr(err)...)
return diags
}
}

// if vmState["status"] == "running" && d.Get("vm_state").(string) == "running" {
// diags = append(diags, initConnInfo(ctx, d, pconf, client, vmr, &config, lock)...)
// }
lock.unlock()

// err = resourceVmQemuRead(ctx, d, meta)
Expand Down
5 changes: 3 additions & 2 deletions proxmox/validators.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,8 @@ func BIOSValidator() schema.SchemaValidateDiagFunc {

func VMStateValidator() schema.SchemaValidateDiagFunc {
return validation.ToDiagFunc(validation.StringInSlice([]string{
"running",
"stopped",
stateRunning,
stateStopped,
stateStarted,
}, false))
}

0 comments on commit 3eacccd

Please sign in to comment.