diff --git a/proxmox/virtual_environment_vm.go b/proxmox/virtual_environment_vm.go index 1b0cdbef0..ea2990838 100644 --- a/proxmox/virtual_environment_vm.go +++ b/proxmox/virtual_environment_vm.go @@ -77,8 +77,45 @@ func (c *VirtualEnvironmentClient) CreateVM( ctx context.Context, nodeName string, d *VirtualEnvironmentVMCreateRequestBody, + timeout int, ) error { - return c.DoRequest(ctx, http.MethodPost, fmt.Sprintf("nodes/%s/qemu", url.PathEscape(nodeName)), d, nil) + taskID, err := c.CreateVMAsync(ctx, nodeName, d) + if err != nil { + return err + } + + err = c.WaitForNodeTask(ctx, nodeName, *taskID, timeout, 1) + + if err != nil { + return fmt.Errorf("error waiting for VM creation: %w", err) + } + + return nil +} + +// CreateVMAsync creates a virtual machine asynchronously. +func (c *VirtualEnvironmentClient) CreateVMAsync( + ctx context.Context, + nodeName string, + d *VirtualEnvironmentVMCreateRequestBody, +) (*string, error) { + resBody := &VirtualEnvironmentVMCreateResponseBody{} + err := c.DoRequest( + ctx, + http.MethodPost, + fmt.Sprintf("nodes/%s/qemu", url.PathEscape(nodeName)), + d, + resBody, + ) + if err != nil { + return nil, err + } + + if resBody.Data == nil { + return nil, errors.New("the server did not include a data object in the response") + } + + return resBody.Data, nil } // DeleteVM deletes a virtual machine. diff --git a/proxmox/virtual_environment_vm_types.go b/proxmox/virtual_environment_vm_types.go index 24825906f..ab7d425b7 100644 --- a/proxmox/virtual_environment_vm_types.go +++ b/proxmox/virtual_environment_vm_types.go @@ -303,6 +303,10 @@ type VirtualEnvironmentVMCreateRequestBody struct { WatchdogDevice *CustomWatchdogDevice `json:"watchdog,omitempty" url:"watchdog,omitempty"` } +type VirtualEnvironmentVMCreateResponseBody struct { + Data *string `json:"data,omitempty"` +} + // VirtualEnvironmentVMGetQEMUNetworkInterfacesResponseBody contains the body from a QEMU get network interfaces response. type VirtualEnvironmentVMGetQEMUNetworkInterfacesResponseBody struct { Data *VirtualEnvironmentVMGetQEMUNetworkInterfacesResponseData `json:"data,omitempty"` diff --git a/proxmoxtf/resource/vm.go b/proxmoxtf/resource/vm.go index 58dfff528..8b4075092 100644 --- a/proxmoxtf/resource/vm.go +++ b/proxmoxtf/resource/vm.go @@ -228,6 +228,8 @@ const ( mkResourceVirtualEnvironmentVMVGAType = "type" mkResourceVirtualEnvironmentVMVMID = "vm_id" mkResourceVirtualEnvironmentVMSCSIHardware = "scsi_hardware" + + vmCreateTimeoutSeconds = 10 ) func VM() *schema.Resource { @@ -2072,21 +2074,13 @@ func vmCreateCustom(ctx context.Context, d *schema.ResourceData, m interface{}) createBody.PoolID = &poolID } - err = veClient.CreateVM(ctx, nodeName, createBody) + err = veClient.CreateVM(ctx, nodeName, createBody, vmCreateTimeoutSeconds) if err != nil { return diag.FromErr(err) } d.SetId(strconv.Itoa(vmID)) - // NOTE: The VM creation is not atomic, and not synchronous. This means that the VM might not be - // available immediately after the creation, or its state reported by the API might not be - // up to date. This is a problem for the following operations, which rely on the VM information - // returned by API calls, particularly read-back to populate the Terraform state. - // Would it be possible to wait for the VM to be fully available, or to wait for the API to report - // the correct state? - // time.Sleep(5 * time.Second) - return vmCreateCustomDisks(ctx, d, m) }