Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: vm_state = started #960

Merged
merged 3 commits into from
Mar 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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))
}
Loading