mirror of
https://github.com/bpg/terraform-provider-proxmox.git
synced 2025-06-30 02:31:10 +00:00
feat(vm): add support for watchdog
(#1556)
Signed-off-by: Pavel Boldyrev <627562+bpg@users.noreply.github.com>
This commit is contained in:
parent
471c5f23bb
commit
d226b59e2e
@ -555,6 +555,18 @@ output "ubuntu_vm_public_key" {
|
|||||||
- `clipboard` - (Optional) Enable VNC clipboard by setting to `vnc`. See the [Proxmox documentation](https://pve.proxmox.com/pve-docs/pve-admin-guide.html#qm_virtual_machines_settings) section 10.2.8 for more information.
|
- `clipboard` - (Optional) Enable VNC clipboard by setting to `vnc`. See the [Proxmox documentation](https://pve.proxmox.com/pve-docs/pve-admin-guide.html#qm_virtual_machines_settings) section 10.2.8 for more information.
|
||||||
- `vm_id` - (Optional) The VM identifier.
|
- `vm_id` - (Optional) The VM identifier.
|
||||||
- `hook_script_file_id` - (Optional) The identifier for a file containing a hook script (needs to be executable, e.g. by using the `proxmox_virtual_environment_file.file_mode` attribute).
|
- `hook_script_file_id` - (Optional) The identifier for a file containing a hook script (needs to be executable, e.g. by using the `proxmox_virtual_environment_file.file_mode` attribute).
|
||||||
|
- `watchdog` - (Optional) The watchdog configuration. Once enabled (by a guest action), the watchdog must be periodically polled by an agent inside the guest or else the watchdog will reset the guest (or execute the respective action specified).
|
||||||
|
- `enabled` - (Optional) Whether the watchdog is enabled (defaults to `false`).
|
||||||
|
- `model` - (Optional) The watchdog type to emulate (defaults to `i6300esb`).
|
||||||
|
- `i6300esb` - Intel 6300ESB.
|
||||||
|
- `ib700` - iBase IB700.
|
||||||
|
- `action` - (Optional) The action to perform if after activation the guest fails to poll the watchdog in time (defaults to `none`).
|
||||||
|
- `debug`
|
||||||
|
- `none`
|
||||||
|
- `pause`
|
||||||
|
- `poweroff`
|
||||||
|
- `reset`
|
||||||
|
- `shutdown`
|
||||||
|
|
||||||
## Attribute Reference
|
## Attribute Reference
|
||||||
|
|
||||||
|
@ -217,6 +217,62 @@ func TestAccResourceVM(t *testing.T) {
|
|||||||
),
|
),
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"update watchdog block", []resource.TestStep{{
|
||||||
|
Config: te.RenderConfig(`
|
||||||
|
resource "proxmox_virtual_environment_vm" "test_vm" {
|
||||||
|
node_name = "{{.NodeName}}"
|
||||||
|
started = false
|
||||||
|
|
||||||
|
watchdog {
|
||||||
|
enabled = "true"
|
||||||
|
}
|
||||||
|
}`),
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
ResourceAttributes("proxmox_virtual_environment_vm.test_vm", map[string]string{
|
||||||
|
"watchdog.0.model": "i6300esb",
|
||||||
|
"watchdog.0.action": "none",
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
}, {
|
||||||
|
Config: te.RenderConfig(`
|
||||||
|
resource "proxmox_virtual_environment_vm" "test_vm" {
|
||||||
|
node_name = "{{.NodeName}}"
|
||||||
|
started = false
|
||||||
|
|
||||||
|
watchdog {
|
||||||
|
enabled = "true"
|
||||||
|
model = "ib700"
|
||||||
|
action = "reset"
|
||||||
|
}
|
||||||
|
}`),
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
ResourceAttributes("proxmox_virtual_environment_vm.test_vm", map[string]string{
|
||||||
|
"watchdog.0.model": "ib700",
|
||||||
|
"watchdog.0.action": "reset",
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
}, {
|
||||||
|
Config: te.RenderConfig(`
|
||||||
|
resource "proxmox_virtual_environment_vm" "test_vm" {
|
||||||
|
node_name = "{{.NodeName}}"
|
||||||
|
started = false
|
||||||
|
|
||||||
|
watchdog {
|
||||||
|
enabled = "false"
|
||||||
|
model = "ib700"
|
||||||
|
action = "reset"
|
||||||
|
}
|
||||||
|
}`),
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
ResourceAttributes("proxmox_virtual_environment_vm.test_vm", map[string]string{
|
||||||
|
"watchdog.0.enabled": "false",
|
||||||
|
"watchdog.0.model": "ib700",
|
||||||
|
"watchdog.0.action": "reset",
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
}},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
|
@ -22,7 +22,7 @@ type CustomWatchdogDevice struct {
|
|||||||
// EncodeValues converts a CustomWatchdogDevice struct to a URL value.
|
// EncodeValues converts a CustomWatchdogDevice struct to a URL value.
|
||||||
func (r *CustomWatchdogDevice) EncodeValues(key string, v *url.Values) error {
|
func (r *CustomWatchdogDevice) EncodeValues(key string, v *url.Values) error {
|
||||||
values := []string{
|
values := []string{
|
||||||
fmt.Sprintf("model=%+v", r.Model),
|
fmt.Sprintf("model=%s", *r.Model),
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.Action != nil {
|
if r.Action != nil {
|
||||||
|
@ -129,6 +129,8 @@ const (
|
|||||||
dvSCSIHardware = "virtio-scsi-pci"
|
dvSCSIHardware = "virtio-scsi-pci"
|
||||||
dvStopOnDestroy = false
|
dvStopOnDestroy = false
|
||||||
dvHookScript = ""
|
dvHookScript = ""
|
||||||
|
dvWatchdogModel = "i6300esb"
|
||||||
|
dvWatchdogAction = "none"
|
||||||
|
|
||||||
maxResourceVirtualEnvironmentVMAudioDevices = 1
|
maxResourceVirtualEnvironmentVMAudioDevices = 1
|
||||||
maxResourceVirtualEnvironmentVMSerialDevices = 4
|
maxResourceVirtualEnvironmentVMSerialDevices = 4
|
||||||
@ -278,6 +280,11 @@ const (
|
|||||||
mkSCSIHardware = "scsi_hardware"
|
mkSCSIHardware = "scsi_hardware"
|
||||||
mkHookScriptFileID = "hook_script_file_id"
|
mkHookScriptFileID = "hook_script_file_id"
|
||||||
mkStopOnDestroy = "stop_on_destroy"
|
mkStopOnDestroy = "stop_on_destroy"
|
||||||
|
mkWatchdog = "watchdog"
|
||||||
|
// a workaround for the lack of proper support of default and undefined values in SDK.
|
||||||
|
mkWatchdogEnabled = "enabled"
|
||||||
|
mkWatchdogModel = "model"
|
||||||
|
mkWatchdogAction = "action"
|
||||||
)
|
)
|
||||||
|
|
||||||
// VM returns a resource that manages VMs.
|
// VM returns a resource that manages VMs.
|
||||||
@ -1457,6 +1464,56 @@ func VM() *schema.Resource {
|
|||||||
Optional: true,
|
Optional: true,
|
||||||
Default: dvStopOnDestroy,
|
Default: dvStopOnDestroy,
|
||||||
},
|
},
|
||||||
|
mkWatchdog: {
|
||||||
|
Type: schema.TypeList,
|
||||||
|
Description: "The watchdog configuration",
|
||||||
|
Optional: true,
|
||||||
|
DefaultFunc: func() (interface{}, error) {
|
||||||
|
return []interface{}{
|
||||||
|
map[string]interface{}{
|
||||||
|
mkWatchdogAction: dvWatchdogAction,
|
||||||
|
mkWatchdogEnabled: false,
|
||||||
|
mkWatchdogModel: dvWatchdogModel,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
Elem: &schema.Resource{
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
mkWatchdogAction: {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Description: "The watchdog action",
|
||||||
|
Optional: true,
|
||||||
|
Default: dvWatchdogAction,
|
||||||
|
ValidateDiagFunc: validation.ToDiagFunc(validation.StringInSlice([]string{
|
||||||
|
"debug",
|
||||||
|
"none",
|
||||||
|
"pause",
|
||||||
|
"poweroff",
|
||||||
|
"reset",
|
||||||
|
"shutdown",
|
||||||
|
}, true)),
|
||||||
|
},
|
||||||
|
mkWatchdogEnabled: {
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
Description: "Whether the watchdog is enabled",
|
||||||
|
Optional: true,
|
||||||
|
Default: false,
|
||||||
|
},
|
||||||
|
mkWatchdogModel: {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Description: "The watchdog model",
|
||||||
|
Optional: true,
|
||||||
|
Default: dvWatchdogModel,
|
||||||
|
ValidateDiagFunc: validation.ToDiagFunc(validation.StringInSlice([]string{
|
||||||
|
"i6300esb",
|
||||||
|
"ib700",
|
||||||
|
}, true)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MaxItems: 1,
|
||||||
|
MinItems: 0,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
structure.MergeSchema(s, disk.Schema())
|
structure.MergeSchema(s, disk.Schema())
|
||||||
@ -1801,6 +1858,7 @@ func vmCreateClone(ctx context.Context, d *schema.ResourceData, m interface{}) d
|
|||||||
protection := types.CustomBool(d.Get(mkProtection).(bool))
|
protection := types.CustomBool(d.Get(mkProtection).(bool))
|
||||||
template := types.CustomBool(d.Get(mkTemplate).(bool))
|
template := types.CustomBool(d.Get(mkTemplate).(bool))
|
||||||
vga := d.Get(mkVGA).([]interface{})
|
vga := d.Get(mkVGA).([]interface{})
|
||||||
|
watchdog := d.Get(mkWatchdog).([]interface{})
|
||||||
|
|
||||||
updateBody := &vms.UpdateRequestBody{
|
updateBody := &vms.UpdateRequestBody{
|
||||||
AudioDevices: audioDevices,
|
AudioDevices: audioDevices,
|
||||||
@ -2075,6 +2133,23 @@ func vmCreateClone(ctx context.Context, d *schema.ResourceData, m interface{}) d
|
|||||||
del = append(del, "hookscript")
|
del = append(del, "hookscript")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(watchdog) > 0 && watchdog[0] != nil {
|
||||||
|
watchdogBlock := watchdog[0].(map[string]interface{})
|
||||||
|
|
||||||
|
watchdogEnabled := types.CustomBool(
|
||||||
|
watchdogBlock[mkWatchdogEnabled].(bool),
|
||||||
|
)
|
||||||
|
if watchdogEnabled {
|
||||||
|
watchdogAction := watchdogBlock[mkWatchdogAction].(string)
|
||||||
|
watchdogModel := watchdogBlock[mkWatchdogModel].(string)
|
||||||
|
|
||||||
|
updateBody.WatchdogDevice = &vms.CustomWatchdogDevice{
|
||||||
|
Action: &watchdogAction,
|
||||||
|
Model: &watchdogModel,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
updateBody.Delete = del
|
updateBody.Delete = del
|
||||||
|
|
||||||
e = vmAPI.UpdateVM(ctx, updateBody)
|
e = vmAPI.UpdateVM(ctx, updateBody)
|
||||||
@ -2521,6 +2596,32 @@ func vmCreateCustom(ctx context.Context, d *schema.ResourceData, m interface{})
|
|||||||
|
|
||||||
scsiHardware := d.Get(mkSCSIHardware).(string)
|
scsiHardware := d.Get(mkSCSIHardware).(string)
|
||||||
|
|
||||||
|
var watchdogObject *vms.CustomWatchdogDevice
|
||||||
|
|
||||||
|
watchdogBlock, err := structure.GetSchemaBlock(
|
||||||
|
resource,
|
||||||
|
d,
|
||||||
|
[]string{mkWatchdog},
|
||||||
|
0,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return diag.FromErr(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
watchdogEnabled := types.CustomBool(
|
||||||
|
watchdogBlock[mkWatchdogEnabled].(bool),
|
||||||
|
)
|
||||||
|
if watchdogEnabled {
|
||||||
|
watchdogAction := watchdogBlock[mkWatchdogAction].(string)
|
||||||
|
watchdogModel := watchdogBlock[mkWatchdogModel].(string)
|
||||||
|
|
||||||
|
watchdogObject = &vms.CustomWatchdogDevice{
|
||||||
|
Action: &watchdogAction,
|
||||||
|
Model: &watchdogModel,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
createBody := &vms.CreateRequestBody{
|
createBody := &vms.CreateRequestBody{
|
||||||
ACPI: &acpi,
|
ACPI: &acpi,
|
||||||
Agent: &vms.CustomAgent{
|
Agent: &vms.CustomAgent{
|
||||||
@ -2563,6 +2664,7 @@ func vmCreateCustom(ctx context.Context, d *schema.ResourceData, m interface{})
|
|||||||
USBDevices: usbDeviceObjects,
|
USBDevices: usbDeviceObjects,
|
||||||
VGADevice: vgaDevice,
|
VGADevice: vgaDevice,
|
||||||
VMID: vmID,
|
VMID: vmID,
|
||||||
|
WatchdogDevice: watchdogObject,
|
||||||
CustomStorageDevices: diskDeviceObjects,
|
CustomStorageDevices: diskDeviceObjects,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4325,6 +4427,51 @@ func vmReadCustom(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
watchdog := map[string]interface{}{}
|
||||||
|
|
||||||
|
if vmConfig.WatchdogDevice != nil {
|
||||||
|
watchdog[mkWatchdogEnabled] = true
|
||||||
|
|
||||||
|
if vmConfig.WatchdogDevice.Action != nil {
|
||||||
|
watchdog[mkWatchdogAction] = *vmConfig.WatchdogDevice.Action
|
||||||
|
} else {
|
||||||
|
watchdog[mkWatchdogAction] = dvWatchdogAction
|
||||||
|
}
|
||||||
|
|
||||||
|
if vmConfig.WatchdogDevice.Model != nil {
|
||||||
|
watchdog[mkWatchdogModel] = *vmConfig.WatchdogDevice.Model
|
||||||
|
} else {
|
||||||
|
watchdog[mkWatchdogModel] = dvWatchdogModel
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
watchdog[mkWatchdogEnabled] = false
|
||||||
|
watchdog[mkWatchdogAction] = dvWatchdogAction
|
||||||
|
watchdog[mkWatchdogModel] = dvWatchdogModel
|
||||||
|
}
|
||||||
|
|
||||||
|
currentWatchdog := d.Get(mkWatchdog).([]interface{})
|
||||||
|
currentWatchdogEnabled := len(currentWatchdog) > 0 &&
|
||||||
|
currentWatchdog[0] != nil && currentWatchdog[0].(map[string]interface{})[mkWatchdogEnabled].(bool)
|
||||||
|
currentWatchdogDisabled := len(currentWatchdog) > 0 &&
|
||||||
|
currentWatchdog[0] != nil && !currentWatchdog[0].(map[string]interface{})[mkWatchdogEnabled].(bool)
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case len(clone) > 0 && len(currentWatchdog) > 0:
|
||||||
|
err := d.Set(mkWatchdog, []interface{}{watchdog})
|
||||||
|
diags = append(diags, diag.FromErr(err)...)
|
||||||
|
case currentWatchdogEnabled ||
|
||||||
|
watchdog[mkWatchdogEnabled] != false ||
|
||||||
|
watchdog[mkWatchdogAction] != dvWatchdogAction ||
|
||||||
|
watchdog[mkWatchdogModel] != dvWatchdogModel:
|
||||||
|
err := d.Set(mkWatchdog, []interface{}{watchdog})
|
||||||
|
diags = append(diags, diag.FromErr(err)...)
|
||||||
|
case currentWatchdogDisabled && vmConfig.WatchdogDevice == nil:
|
||||||
|
// do nothing
|
||||||
|
default:
|
||||||
|
err := d.Set(mkWatchdog, []interface{}{})
|
||||||
|
diags = append(diags, diag.FromErr(err)...)
|
||||||
|
}
|
||||||
|
|
||||||
vmAPI := client.Node(nodeName).VM(vmID)
|
vmAPI := client.Node(nodeName).VM(vmID)
|
||||||
started := d.Get(mkStarted).(bool)
|
started := d.Get(mkStarted).(bool)
|
||||||
|
|
||||||
@ -5160,6 +5307,37 @@ func vmUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.D
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Prepare the new watchdog configuration.
|
||||||
|
if d.HasChange(mkWatchdog) {
|
||||||
|
watchdogBlock, err := structure.GetSchemaBlock(
|
||||||
|
resource,
|
||||||
|
d,
|
||||||
|
[]string{mkWatchdog},
|
||||||
|
0,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return diag.FromErr(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
watchdogEnabled := types.CustomBool(
|
||||||
|
watchdogBlock[mkWatchdogEnabled].(bool),
|
||||||
|
)
|
||||||
|
if watchdogEnabled {
|
||||||
|
watchdogAction := watchdogBlock[mkWatchdogAction].(string)
|
||||||
|
watchdogModel := watchdogBlock[mkWatchdogModel].(string)
|
||||||
|
|
||||||
|
updateBody.WatchdogDevice = &vms.CustomWatchdogDevice{
|
||||||
|
Action: &watchdogAction,
|
||||||
|
Model: &watchdogModel,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
del = append(del, "watchdog")
|
||||||
|
}
|
||||||
|
|
||||||
|
rebootRequired = true
|
||||||
|
}
|
||||||
|
|
||||||
// Update the configuration now that everything has been prepared.
|
// Update the configuration now that everything has been prepared.
|
||||||
updateBody.Delete = del
|
updateBody.Delete = del
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user