mirror of
https://github.com/bpg/terraform-provider-proxmox.git
synced 2025-08-22 19:38:35 +00:00
fet(vm): allow scsi
and sata
interfaces for CloudInit Drive (#598)
* fet(vm): allow `scsi` and `sata` interfaces for CloudInit Drive --------- Signed-off-by: Pavel Boldyrev <627562+bpg@users.noreply.github.com>
This commit is contained in:
parent
fa590ed9e9
commit
0b8f2e2c6f
@ -300,8 +300,9 @@ output "ubuntu_vm_public_key" {
|
||||
- `datastore_id` - (Optional) The identifier for the datastore to create the
|
||||
cloud-init disk in (defaults to `local-lvm`).
|
||||
- `interface` - (Optional) The hardware interface to connect the cloud-init
|
||||
image to. Must be `ideN`. Will be detected if the setting is missing but a
|
||||
cloud-init image is present, otherwise defaults to `ide2`.
|
||||
image to. Must be one of `ide0..3`, `sata0..5`, `scsi0..30`. Will be
|
||||
detected if the setting is missing but a cloud-init image is present,
|
||||
otherwise defaults to `ide2`.
|
||||
- `dns` - (Optional) The DNS configuration.
|
||||
- `domain` - (Optional) The DNS search domain.
|
||||
- `server` - (Optional) The DNS server.
|
||||
|
@ -7,6 +7,7 @@ resource "proxmox_virtual_environment_vm" "example_template" {
|
||||
enabled = true
|
||||
}
|
||||
|
||||
bios = "ovmf"
|
||||
description = "Managed by Terraform"
|
||||
|
||||
cpu {
|
||||
@ -56,7 +57,7 @@ resource "proxmox_virtual_environment_vm" "example_template" {
|
||||
|
||||
initialization {
|
||||
datastore_id = local.datastore_id
|
||||
# interface = "ide2"
|
||||
interface = "scsi4"
|
||||
|
||||
dns {
|
||||
server = "1.1.1.1"
|
||||
@ -76,6 +77,7 @@ resource "proxmox_virtual_environment_vm" "example_template" {
|
||||
meta_data_file_id = proxmox_virtual_environment_file.meta_config.id
|
||||
}
|
||||
|
||||
machine = "q35"
|
||||
name = "terraform-provider-proxmox-example-template"
|
||||
|
||||
network_device {
|
||||
@ -113,6 +115,8 @@ resource "proxmox_virtual_environment_vm" "example" {
|
||||
vm_id = proxmox_virtual_environment_vm.example_template.id
|
||||
}
|
||||
|
||||
machine = "q35"
|
||||
|
||||
memory {
|
||||
dedicated = 768
|
||||
}
|
||||
@ -135,7 +139,7 @@ resource "proxmox_virtual_environment_vm" "example" {
|
||||
// if unspecified:
|
||||
// - autodetected if there is a cloud-init device on the template
|
||||
// - otherwise defaults to ide2
|
||||
interface = "ide0"
|
||||
interface = "scsi4"
|
||||
|
||||
dns {
|
||||
server = "8.8.8.8"
|
||||
|
@ -43,6 +43,40 @@ func (c *Client) GetTaskStatus(ctx context.Context, upid string) (*GetTaskStatus
|
||||
return resBody.Data, nil
|
||||
}
|
||||
|
||||
// GetTaskLog retrieves the log of a task. The log is returned as an array of
|
||||
// lines. Each line is an object with a line number and the text of the line.
|
||||
// Reads first 50 lines by default.
|
||||
func (c *Client) GetTaskLog(ctx context.Context, upid string) ([]string, error) {
|
||||
resBody := &GetTaskLogResponseBody{}
|
||||
lines := []string{}
|
||||
|
||||
path, err := c.BuildPath(upid, "log")
|
||||
if err != nil {
|
||||
return lines, fmt.Errorf("error building path for task status: %w", err)
|
||||
}
|
||||
|
||||
err = c.DoRequest(
|
||||
ctx,
|
||||
http.MethodGet,
|
||||
path,
|
||||
nil,
|
||||
resBody,
|
||||
)
|
||||
if err != nil {
|
||||
return lines, fmt.Errorf("error retrieving task status: %w", err)
|
||||
}
|
||||
|
||||
if resBody.Data == nil {
|
||||
return lines, api.ErrNoDataObjectInResponse
|
||||
}
|
||||
|
||||
for _, line := range resBody.Data {
|
||||
lines = append(lines, line.LineText)
|
||||
}
|
||||
|
||||
return lines, nil
|
||||
}
|
||||
|
||||
// WaitForTask waits for a specific task to complete.
|
||||
func (c *Client) WaitForTask(ctx context.Context, upid string, timeoutSec, delaySec int) error {
|
||||
timeDelay := int64(delaySec)
|
||||
|
@ -25,6 +25,17 @@ type GetTaskStatusResponseData struct {
|
||||
ExitCode string `json:"exitstatus,omitempty"`
|
||||
}
|
||||
|
||||
// GetTaskLogResponseBody contains the body from a node get task log response.
|
||||
type GetTaskLogResponseBody struct {
|
||||
Data []*GetTaskLogResponseData `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
// GetTaskLogResponseData contains the data from a node get task log response.
|
||||
type GetTaskLogResponseData struct {
|
||||
LineNumber int `json:"n,omitempty"`
|
||||
LineText string `json:"t,omitempty"`
|
||||
}
|
||||
|
||||
// TaskID contains the components of a PVE task ID.
|
||||
type TaskID struct {
|
||||
NodeName string
|
||||
|
@ -330,18 +330,33 @@ func (c *Client) ShutdownVMAsync(ctx context.Context, d *ShutdownRequestBody) (*
|
||||
}
|
||||
|
||||
// StartVM starts a virtual machine.
|
||||
func (c *Client) StartVM(ctx context.Context, timeout int) error {
|
||||
// Returns the task log if the VM had warnings at startup, or fails to start.
|
||||
func (c *Client) StartVM(ctx context.Context, timeout int) ([]string, error) {
|
||||
taskID, err := c.StartVMAsync(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = c.Tasks().WaitForTask(ctx, *taskID, timeout, 5)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error waiting for VM start: %w", err)
|
||||
log, e := c.Tasks().GetTaskLog(ctx, *taskID)
|
||||
if e != nil {
|
||||
tflog.Error(ctx, "error retrieving task log", map[string]interface{}{
|
||||
"task_id": *taskID,
|
||||
"error": e.Error(),
|
||||
})
|
||||
|
||||
log = []string{}
|
||||
}
|
||||
|
||||
return nil
|
||||
if strings.Contains(err.Error(), "WARNING") && len(log) > 0 {
|
||||
return log, nil
|
||||
}
|
||||
|
||||
return log, fmt.Errorf("error waiting for VM start: %w", err)
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// StartVMAsync starts a virtual machine asynchronously.
|
||||
|
@ -256,13 +256,12 @@ func IDEInterface() schema.SchemaValidateDiagFunc {
|
||||
// CloudInitInterface is a schema validation function that accepts either an IDE interface identifier or an
|
||||
// empty string, which is used as the default and means "detect which interface should be used automatically".
|
||||
func CloudInitInterface() schema.SchemaValidateDiagFunc {
|
||||
return validation.ToDiagFunc(validation.StringInSlice([]string{
|
||||
"",
|
||||
"ide0",
|
||||
"ide1",
|
||||
"ide2",
|
||||
"ide3",
|
||||
}, false))
|
||||
r := regexp.MustCompile(`^ide[0-3]|sata[0-5]|scsi(?:30|[12][0-9]|[0-9])$`)
|
||||
|
||||
return validation.ToDiagFunc(validation.Any(
|
||||
validation.StringIsEmpty,
|
||||
validation.StringMatch(r, "one of ide0..3|sata0..5|scsi0..30"),
|
||||
))
|
||||
}
|
||||
|
||||
// CloudInitType is a schema validation function for cloud-init types.
|
||||
|
@ -1602,16 +1602,26 @@ func deleteIdeDrives(ctx context.Context, vmAPI *vms.Client, itf1 string, itf2 s
|
||||
|
||||
// Start the VM, then wait for it to actually start; it may not be started immediately if running in HA mode.
|
||||
func vmStart(ctx context.Context, vmAPI *vms.Client, d *schema.ResourceData) diag.Diagnostics {
|
||||
var diags diag.Diagnostics
|
||||
|
||||
tflog.Debug(ctx, "Starting VM")
|
||||
|
||||
startVMTimeout := d.Get(mkResourceVirtualEnvironmentVMTimeoutStartVM).(int)
|
||||
|
||||
e := vmAPI.StartVM(ctx, startVMTimeout)
|
||||
log, e := vmAPI.StartVM(ctx, startVMTimeout)
|
||||
if e != nil {
|
||||
return diag.FromErr(e)
|
||||
return append(diags, diag.FromErr(e)...)
|
||||
}
|
||||
|
||||
return diag.FromErr(vmAPI.WaitForVMState(ctx, "running", startVMTimeout, 1))
|
||||
if len(log) > 0 {
|
||||
lines := "\n\t| " + strings.Join(log, "\n\t| ")
|
||||
diags = append(diags, diag.Diagnostic{
|
||||
Severity: diag.Warning,
|
||||
Summary: fmt.Sprintf("the VM startup task finished with a warning, task log:\n%s", lines),
|
||||
})
|
||||
}
|
||||
|
||||
return append(diags, diag.FromErr(vmAPI.WaitForVMState(ctx, "running", startVMTimeout, 1))...)
|
||||
}
|
||||
|
||||
// Shutdown the VM, then wait for it to actually shut down (it may not be shut down immediately if
|
||||
@ -3779,12 +3789,16 @@ func vmReadCustom(
|
||||
diskObjects := getDiskInfo(vmConfig, d)
|
||||
|
||||
for di, dd := range diskObjects {
|
||||
disk := map[string]interface{}{}
|
||||
|
||||
if dd == nil || dd.FileVolume == "none" || strings.HasPrefix(di, "ide") {
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasSuffix(dd.FileVolume, fmt.Sprintf("vm-%d-cloudinit", vmID)) {
|
||||
continue
|
||||
}
|
||||
|
||||
disk := map[string]interface{}{}
|
||||
|
||||
fileIDParts := strings.Split(dd.FileVolume, ":")
|
||||
|
||||
disk[mkResourceVirtualEnvironmentVMDiskDatastoreID] = fileIDParts[0]
|
||||
|
Loading…
Reference in New Issue
Block a user