0
0
mirror of https://github.com/bpg/terraform-provider-proxmox.git synced 2025-08-27 05:45:38 +00:00

more disk management refactoring

Signed-off-by: Pavel Boldyrev <627562+bpg@users.noreply.github.com>
This commit is contained in:
Pavel Boldyrev 2024-02-03 16:57:46 -05:00
parent 98f16fc97c
commit 87ff97ca58
No known key found for this signature in database
GPG Key ID: 02A24794ADAC7455
5 changed files with 396 additions and 342 deletions

View File

@ -262,44 +262,75 @@ func (c *Client) RebootVMAsync(ctx context.Context, d *RebootRequestBody) (*stri
return resBody.Data, nil return resBody.Data, nil
} }
// // ResizeVMDisk resizes a virtual machine disk.
// func (c *Client) ResizeVMDisk(ctx context.Context, d *ResizeDiskRequestBody) error {
// var err error
// tflog.Debug(ctx, "resize disk", map[string]interface{}{
// "disk": d.Disk,
// "size": d.Size,
// })
// for i := 0; i < 5; i++ {
// err = c.DoRequest(
// ctx,
// http.MethodPut,
// c.ExpandPath("resize"),
// d,
// nil,
// )
// if err == nil {
// return nil
// }
// tflog.Debug(ctx, "resize disk failed", map[string]interface{}{
// "retry": i,
// })
// time.Sleep(5 * time.Second)
// if ctx.Err() != nil {
// return fmt.Errorf("error resizing VM disk: %w", ctx.Err())
// }
// }
// if err != nil {
// return fmt.Errorf("error resizing VM disk: %w", err)
// }
// return nil
// }
// ResizeVMDisk resizes a virtual machine disk. // ResizeVMDisk resizes a virtual machine disk.
func (c *Client) ResizeVMDisk(ctx context.Context, d *ResizeDiskRequestBody) error { func (c *Client) ResizeVMDisk(ctx context.Context, d *ResizeDiskRequestBody, timeout int) error {
var err error taskID, err := c.ResizeVMDiskAsync(ctx, d)
tflog.Debug(ctx, "resize disk", map[string]interface{}{
"disk": d.Disk,
"size": d.Size,
})
for i := 0; i < 5; i++ {
err = c.DoRequest(
ctx,
http.MethodPut,
c.ExpandPath("resize"),
d,
nil,
)
if err == nil {
return nil
}
tflog.Debug(ctx, "resize disk failed", map[string]interface{}{
"retry": i,
})
time.Sleep(5 * time.Second)
if ctx.Err() != nil {
return fmt.Errorf("error resizing VM disk: %w", ctx.Err())
}
}
if err != nil { if err != nil {
return fmt.Errorf("error resizing VM disk: %w", err) return err
}
err = c.Tasks().WaitForTask(ctx, *taskID, timeout, 5)
if err != nil {
return fmt.Errorf("error waiting for VM disk resize: %w", err)
} }
return nil return nil
} }
// ResizeVMDiskAsync resizes a virtual machine disk asynchronously.
func (c *Client) ResizeVMDiskAsync(ctx context.Context, d *ResizeDiskRequestBody) (*string, error) {
resBody := &MoveDiskResponseBody{}
err := c.DoRequest(ctx, http.MethodPut, c.ExpandPath("resize"), d, resBody)
if err != nil {
return nil, fmt.Errorf("error moving VM disk: %w", err)
}
if resBody.Data == nil {
return nil, api.ErrNoDataObjectInResponse
}
return resBody.Data, nil
}
// ShutdownVM shuts down a virtual machine. // ShutdownVM shuts down a virtual machine.
func (c *Client) ShutdownVM(ctx context.Context, d *ShutdownRequestBody, timeout int) error { func (c *Client) ShutdownVM(ctx context.Context, d *ShutdownRequestBody, timeout int) error {
taskID, err := c.ShutdownVMAsync(ctx, d) taskID, err := c.ShutdownVMAsync(ctx, d)

View File

@ -14,6 +14,7 @@ import (
"path/filepath" "path/filepath"
"strconv" "strconv"
"strings" "strings"
"unicode"
"github.com/bpg/terraform-provider-proxmox/proxmox/types" "github.com/bpg/terraform-provider-proxmox/proxmox/types"
) )
@ -242,8 +243,32 @@ func (d CustomStorageDevice) IsCloudInitDrive(vmID int) bool {
strings.Contains(d.FileVolume, fmt.Sprintf("vm-%d-cloudinit", vmID)) strings.Contains(d.FileVolume, fmt.Sprintf("vm-%d-cloudinit", vmID))
} }
// CustomStorageDevices handles QEMU SATA device parameters. // StorageInterface returns the storage interface of the CustomStorageDevice, e.g. "virtio" or "scsi" for "virtio0" or "scsi2".
type CustomStorageDevices map[string]CustomStorageDevice func (d CustomStorageDevice) StorageInterface() string {
for i, r := range *d.Interface {
if unicode.IsDigit(r) {
return (*d.Interface)[:i]
}
}
panic(fmt.Sprintf("cannot determine storage interface for disk interface '%s'", *d.Interface))
}
// CustomStorageDevices handles map of QEMU storage device per disk interface.
type CustomStorageDevices map[string]*CustomStorageDevice
// ByStorageInterface returns a map of CustomStorageDevices filtered by the given storage interface.
func (d CustomStorageDevices) ByStorageInterface(storageInterface string) CustomStorageDevices {
result := make(CustomStorageDevices)
for k, v := range d {
if v.StorageInterface() == storageInterface {
result[k] = v
}
}
return result
}
// CustomTPMState handles QEMU TPM state parameters. // CustomTPMState handles QEMU TPM state parameters.
type CustomTPMState struct { type CustomTPMState struct {

View File

@ -120,6 +120,102 @@ func TestCustomStorageDevice_IsCloudInitDrive(t *testing.T) {
} }
} }
func TestCustomStorageDevice_StorageInterface(t *testing.T) {
t.Parallel()
tests := []struct {
name string
device CustomStorageDevice
want string
}{
{
name: "virtio0",
device: CustomStorageDevice{
Interface: types.StrPtr("virtio0"),
},
want: "virtio",
}, {
name: "scsi13",
device: CustomStorageDevice{
Interface: types.StrPtr("scsi13"),
},
want: "scsi",
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
got := tt.device.StorageInterface()
assert.Equal(t, tt.want, got)
})
}
}
func TestCustomStorageDevices_ByStorageInterface(t *testing.T) {
t.Parallel()
tests := []struct {
name string
iface string
devices CustomStorageDevices
want CustomStorageDevices
}{
{
name: "empty",
iface: "virtio",
devices: CustomStorageDevices{},
want: CustomStorageDevices{},
},
{
name: "not in the list",
iface: "sata",
devices: CustomStorageDevices{
"virtio0": CustomStorageDevice{
Interface: types.StrPtr("virtio0"),
},
"scsi13": CustomStorageDevice{
Interface: types.StrPtr("scsi13"),
},
},
want: CustomStorageDevices{},
},
{
name: "not in the list",
iface: "virtio",
devices: CustomStorageDevices{
"virtio0": CustomStorageDevice{
Interface: types.StrPtr("virtio0"),
},
"scsi13": CustomStorageDevice{
Interface: types.StrPtr("scsi13"),
},
"virtio1": CustomStorageDevice{
Interface: types.StrPtr("virtio1"),
},
},
want: CustomStorageDevices{
"virtio0": CustomStorageDevice{
Interface: types.StrPtr("virtio0"),
},
"virtio1": CustomStorageDevice{
Interface: types.StrPtr("virtio1"),
},
},
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
got := tt.devices.ByStorageInterface(tt.iface)
assert.Equal(t, tt.want, got)
})
}
}
func TestCustomPCIDevice_UnmarshalJSON(t *testing.T) { func TestCustomPCIDevice_UnmarshalJSON(t *testing.T) {
t.Parallel() t.Parallel()

View File

@ -2,7 +2,6 @@ package vm
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"strconv" "strconv"
"strings" "strings"
@ -17,7 +16,6 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
"golang.org/x/exp/maps"
) )
const ( const (
@ -203,143 +201,100 @@ func createDisks(
ctx context.Context, vmConfig *vms.GetResponseData, d *schema.ResourceData, vmAPI *vms.Client, ctx context.Context, vmConfig *vms.GetResponseData, d *schema.ResourceData, vmAPI *vms.Client,
) (map[string]*vms.CustomStorageDevice, error) { ) (map[string]*vms.CustomStorageDevice, error) {
// this is what VM has at the moment: map of interface name (virtio1) -> disk object // this is what VM has at the moment: map of interface name (virtio1) -> disk object
currentStorageDevices := populateFileIDs(mapStorageDevices(vmConfig), d) currentDisks := populateFileIDs(mapStorageDevices(vmConfig), d)
// map of interface type (virtio|sata|scsi|...) -> map of interface name (virtio1) -> disk object // map of interface name (virtio1) -> disk object
planStorageDevices, e := getStorageDevicesFromResource(d) planDisks, e := getStorageDevicesFromResource(d)
if e != nil { if e != nil {
return nil, e return nil, e
} }
for diskInterface, planDisk := range planDisks {
currentDisk := currentDisks[diskInterface]
if currentDisk == nil {
// create disks that are not present in the current configuration // create disks that are not present in the current configuration
for prefix, disks := range planStorageDevices { err := createDisk(ctx, planDisk, vmAPI)
for diskInterface, disk := range disks { if err != nil {
if currentStorageDevices[diskInterface] == nil { return nil, err
diskUpdateBody := &vms.UpdateRequestBody{}
switch prefix {
case "virtio":
if diskUpdateBody.VirtualIODevices == nil {
diskUpdateBody.VirtualIODevices = vms.CustomStorageDevices{}
} }
diskUpdateBody.VirtualIODevices[diskInterface] = disk } else {
case "sata": // disk is present, i.e. when cloned a template, but we need to check if it needs to be resized
if diskUpdateBody.SATADevices == nil { if planDisk.Size.InGigabytes() < currentDisk.Size.InGigabytes() {
diskUpdateBody.SATADevices = vms.CustomStorageDevices{}
}
diskUpdateBody.SATADevices[diskInterface] = disk
case "scsi":
if diskUpdateBody.SCSIDevices == nil {
diskUpdateBody.SCSIDevices = vms.CustomStorageDevices{}
}
diskUpdateBody.SCSIDevices[diskInterface] = disk
}
e = vmAPI.UpdateVM(ctx, diskUpdateBody)
if e != nil {
return nil, e
}
}
}
}
disk := d.Get(mkDisk).([]interface{})
for i := range disk {
diskBlock := disk[i].(map[string]interface{})
diskInterface := diskBlock[mkDiskInterface].(string)
dataStoreID := diskBlock[mkDiskDatastoreID].(string)
diskSize := int64(diskBlock[mkDiskSize].(int))
prefix := diskDigitPrefix(diskInterface)
currentDiskInfo := currentStorageDevices[diskInterface]
configuredDiskInfo := planStorageDevices[prefix][diskInterface]
if currentDiskInfo == nil {
diskUpdateBody := &vms.UpdateRequestBody{}
switch prefix {
case "virtio":
if diskUpdateBody.VirtualIODevices == nil {
diskUpdateBody.VirtualIODevices = vms.CustomStorageDevices{}
}
diskUpdateBody.VirtualIODevices[diskInterface] = configuredDiskInfo
case "sata":
if diskUpdateBody.SATADevices == nil {
diskUpdateBody.SATADevices = vms.CustomStorageDevices{}
}
diskUpdateBody.SATADevices[diskInterface] = configuredDiskInfo
case "scsi":
if diskUpdateBody.SCSIDevices == nil {
diskUpdateBody.SCSIDevices = vms.CustomStorageDevices{}
}
diskUpdateBody.SCSIDevices[diskInterface] = configuredDiskInfo
}
e = vmAPI.UpdateVM(ctx, diskUpdateBody)
if e != nil {
return nil, e
}
continue
}
if diskSize < currentDiskInfo.Size.InGigabytes() {
return nil, fmt.Errorf("disk resize fails requests size (%dG) is lower than current size (%s)", return nil, fmt.Errorf("disk resize fails requests size (%dG) is lower than current size (%s)",
diskSize, planDisk.Size.InGigabytes(),
*currentDiskInfo.Size, *currentDisk.Size,
) )
} }
moveDisk := false
if *planDisk.ID != "" {
fileIDParts := strings.Split(currentDisk.FileVolume, ":")
moveDisk = *planDisk.ID != fileIDParts[0]
}
if moveDisk {
moveDiskTimeout := d.Get(mkTimeoutMoveDisk).(int)
deleteOriginalDisk := types.CustomBool(true) deleteOriginalDisk := types.CustomBool(true)
diskMoveBody := &vms.MoveDiskRequestBody{ diskMoveBody := &vms.MoveDiskRequestBody{
DeleteOriginalDisk: &deleteOriginalDisk, DeleteOriginalDisk: &deleteOriginalDisk,
Disk: diskInterface, Disk: diskInterface,
TargetStorage: dataStoreID, TargetStorage: *planDisk.ID,
} }
err := vmAPI.MoveVMDisk(ctx, diskMoveBody, moveDiskTimeout)
if err != nil {
return nil, err
}
}
if planDisk.Size.InGigabytes() > currentDisk.Size.InGigabytes() {
moveDiskTimeout := d.Get(mkTimeoutMoveDisk).(int)
diskResizeBody := &vms.ResizeDiskRequestBody{ diskResizeBody := &vms.ResizeDiskRequestBody{
Disk: diskInterface, Disk: diskInterface,
Size: *types.DiskSizeFromGigabytes(diskSize), Size: *types.DiskSizeFromGigabytes(planDisk.Size.InGigabytes()),
} }
moveDisk := false err := vmAPI.ResizeVMDisk(ctx, diskResizeBody, moveDiskTimeout)
if err != nil {
if dataStoreID != "" { return nil, err
moveDisk = true
if currentStorageDevices[diskInterface] != nil {
fileIDParts := strings.Split(currentStorageDevices[diskInterface].FileVolume, ":")
moveDisk = dataStoreID != fileIDParts[0]
} }
} }
if moveDisk {
moveDiskTimeout := d.Get(mkTimeoutMoveDisk).(int)
e = vmAPI.MoveVMDisk(ctx, diskMoveBody, moveDiskTimeout)
if e != nil {
return nil, e
}
}
if diskSize > currentDiskInfo.Size.InGigabytes() {
e = vmAPI.ResizeVMDisk(ctx, diskResizeBody)
if e != nil {
return nil, e
}
} }
} }
return currentStorageDevices, nil return currentDisks, nil
}
func createDisk(ctx context.Context, disk *vms.CustomStorageDevice, vmAPI *vms.Client) error {
addToDevices := func(ds vms.CustomStorageDevices, disk *vms.CustomStorageDevice) vms.CustomStorageDevices {
if ds == nil {
ds = vms.CustomStorageDevices{}
}
ds[*disk.Interface] = disk
return ds
}
diskUpdateBody := &vms.UpdateRequestBody{}
switch disk.StorageInterface() {
case "virtio":
diskUpdateBody.VirtualIODevices = addToDevices(diskUpdateBody.VirtualIODevices, disk)
case "sata":
diskUpdateBody.SATADevices = addToDevices(diskUpdateBody.SATADevices, disk)
case "scsi":
diskUpdateBody.SCSIDevices = addToDevices(diskUpdateBody.SCSIDevices, disk)
}
err := vmAPI.UpdateVM(ctx, diskUpdateBody)
if err != nil {
return err
}
return nil
} }
func vmCreateCustomDisks(ctx context.Context, d *schema.ResourceData, m interface{}) error { func vmCreateCustomDisks(ctx context.Context, d *schema.ResourceData, m interface{}) error {
@ -503,15 +458,12 @@ func vmCreateCustomDisks(ctx context.Context, d *schema.ResourceData, m interfac
return nil return nil
} }
func getStorageDevicesFromResource(d *schema.ResourceData) (map[string]map[string]vms.CustomStorageDevice, error) { func getStorageDevicesFromResource(d *schema.ResourceData) (vms.CustomStorageDevices, error) {
return getDiskDeviceObjects1(d, d.Get(mkDisk).([]interface{})) return getDiskDeviceObjects1(d, d.Get(mkDisk).([]interface{}))
} }
func getDiskDeviceObjects1( func getDiskDeviceObjects1(d *schema.ResourceData, disks []interface{}) (vms.CustomStorageDevices, error) {
d *schema.ResourceData, diskDeviceObjects := vms.CustomStorageDevices{}
disks []interface{},
) (map[string]map[string]vms.CustomStorageDevice, error) {
diskDeviceObjects := map[string]map[string]vms.CustomStorageDevice{}
resource := VM() resource := VM()
for _, diskEntry := range disks { for _, diskEntry := range disks {
@ -601,23 +553,16 @@ func getDiskDeviceObjects1(
} }
} }
baseDiskInterface := diskDigitPrefix(diskInterface) storageInterface := diskDevice.StorageInterface()
if baseDiskInterface != "virtio" && baseDiskInterface != "scsi" && if storageInterface != "virtio" && storageInterface != "scsi" && storageInterface != "sata" {
baseDiskInterface != "sata" { return diskDeviceObjects, fmt.Errorf(
errorMsg := fmt.Sprintf( "Defined disk interface not supported. Interface was '%s', but only 'virtio', 'sata' and 'scsi' are supported",
"Defined disk interface not supported. Interface was %s, but only virtio, sata and scsi are supported",
diskInterface, diskInterface,
) )
return diskDeviceObjects, errors.New(errorMsg)
} }
if _, present := diskDeviceObjects[baseDiskInterface]; !present { diskDeviceObjects[diskInterface] = &diskDevice
diskDeviceObjects[baseDiskInterface] = map[string]vms.CustomStorageDevice{}
}
diskDeviceObjects[baseDiskInterface][diskInterface] = diskDevice
} }
return diskDeviceObjects, nil return diskDeviceObjects, nil
@ -756,63 +701,53 @@ func updateDisk(d *schema.ResourceData, vmConfig *vms.GetResponseData, updateBod
return nil return nil
} }
diskDeviceObjects, err := getStorageDevicesFromResource(d) currentDisks := populateFileIDs(mapStorageDevices(vmConfig), d)
planDisks, err := getStorageDevicesFromResource(d)
if err != nil { if err != nil {
return err return err
} }
diskDeviceInfo := populateFileIDs(mapStorageDevices(vmConfig), d) addToDevices := func(ds vms.CustomStorageDevices, disk *vms.CustomStorageDevice) vms.CustomStorageDevices {
if ds == nil {
for prefix, diskMap := range diskDeviceObjects { ds = vms.CustomStorageDevices{}
if diskMap == nil {
continue
} }
for key, value := range diskMap { ds[*disk.Interface] = disk
if diskDeviceInfo[key] == nil {
return ds
}
for diskInterface, disk := range planDisks {
if currentDisks[diskInterface] == nil {
// TODO: create a new disk here // TODO: create a new disk here
return fmt.Errorf("missing %s device %s", prefix, key) return fmt.Errorf("missing device %s", diskInterface)
} }
tmp := *diskDeviceInfo[key] // copy the current disk and update the fields
tmp.BurstableReadSpeedMbps = value.BurstableReadSpeedMbps tmp := *currentDisks[diskInterface]
tmp.BurstableWriteSpeedMbps = value.BurstableWriteSpeedMbps tmp.BurstableReadSpeedMbps = disk.BurstableReadSpeedMbps
tmp.MaxReadSpeedMbps = value.MaxReadSpeedMbps tmp.BurstableWriteSpeedMbps = disk.BurstableWriteSpeedMbps
tmp.MaxWriteSpeedMbps = value.MaxWriteSpeedMbps tmp.MaxReadSpeedMbps = disk.MaxReadSpeedMbps
tmp.Cache = value.Cache tmp.MaxWriteSpeedMbps = disk.MaxWriteSpeedMbps
tmp.Cache = disk.Cache
tmp.Discard = disk.Discard
tmp.IOThread = disk.IOThread
tmp.SSD = disk.SSD
switch prefix { switch disk.StorageInterface() {
case "virtio": case "virtio":
{ updateBody.VirtualIODevices = addToDevices(updateBody.VirtualIODevices, &tmp)
if updateBody.VirtualIODevices == nil {
updateBody.VirtualIODevices = vms.CustomStorageDevices{}
}
updateBody.VirtualIODevices[key] = tmp
}
case "sata": case "sata":
{ updateBody.SATADevices = addToDevices(updateBody.SATADevices, &tmp)
if updateBody.SATADevices == nil {
updateBody.SATADevices = vms.CustomStorageDevices{}
}
updateBody.SATADevices[key] = tmp
}
case "scsi": case "scsi":
{ updateBody.SCSIDevices = addToDevices(updateBody.SCSIDevices, &tmp)
if updateBody.SCSIDevices == nil {
updateBody.SCSIDevices = vms.CustomStorageDevices{}
}
updateBody.SCSIDevices[key] = tmp
}
case "ide": case "ide":
{ {
// Investigate whether to support IDE mapping. // Investigate whether to support IDE mapping.
} }
default: default:
return fmt.Errorf("device prefix %s not supported", prefix) return fmt.Errorf("device storage interface %s not supported", disk.StorageInterface())
}
} }
} }
@ -942,22 +877,3 @@ func getDiskDatastores(vm *vms.GetResponseData, d *schema.ResourceData) []string
return datastores return datastores
} }
type customStorageDeviceMap struct {
// map of interface type (virtio|sata|scsi|...) -> map of interface name (virtio1) -> disk object
devices map[string]map[string]vms.CustomStorageDevice
}
func (c *customStorageDeviceMap) byInterfaceType(interfaceType string) []vms.CustomStorageDevice {
return maps.Values[map[string]vms.CustomStorageDevice](c.devices[interfaceType])
}
func (c *customStorageDeviceMap) byInterfaceName(interfaceName string) (*vms.CustomStorageDevice, bool) {
for _, devices := range c.devices {
if device, ok := devices[interfaceName]; ok {
return &device, true
}
}
return nil, false
}

View File

@ -15,7 +15,6 @@ import (
"strconv" "strconv"
"strings" "strings"
"time" "time"
"unicode"
"github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp"
"github.com/google/uuid" "github.com/google/uuid"
@ -357,16 +356,16 @@ func vmCreateClone(ctx context.Context, d *schema.ResourceData, m interface{}) d
if len(cdrom) > 0 || len(initialization) > 0 { if len(cdrom) > 0 || len(initialization) > 0 {
ideDevices = vms.CustomStorageDevices{ ideDevices = vms.CustomStorageDevices{
"ide0": vms.CustomStorageDevice{ "ide0": &vms.CustomStorageDevice{
Enabled: false, Enabled: false,
}, },
"ide1": vms.CustomStorageDevice{ "ide1": &vms.CustomStorageDevice{
Enabled: false, Enabled: false,
}, },
"ide2": vms.CustomStorageDevice{ "ide2": &vms.CustomStorageDevice{
Enabled: false, Enabled: false,
}, },
"ide3": vms.CustomStorageDevice{ "ide3": &vms.CustomStorageDevice{
Enabled: false, Enabled: false,
}, },
} }
@ -385,7 +384,7 @@ func vmCreateClone(ctx context.Context, d *schema.ResourceData, m interface{}) d
cdromMedia := "cdrom" cdromMedia := "cdrom"
ideDevices[cdromInterface] = vms.CustomStorageDevice{ ideDevices[cdromInterface] = &vms.CustomStorageDevice{
Enabled: cdromEnabled, Enabled: cdromEnabled,
FileVolume: cdromFileID, FileVolume: cdromFileID,
Media: &cdromMedia, Media: &cdromMedia,
@ -458,7 +457,7 @@ func vmCreateClone(ctx context.Context, d *schema.ResourceData, m interface{}) d
cdromCloudInitFileID := fmt.Sprintf("%s:cloudinit", initializationDatastoreID) cdromCloudInitFileID := fmt.Sprintf("%s:cloudinit", initializationDatastoreID)
cdromCloudInitMedia := "cdrom" cdromCloudInitMedia := "cdrom"
ideDevices[initializationInterface] = vms.CustomStorageDevice{ ideDevices[initializationInterface] = &vms.CustomStorageDevice{
Enabled: cdromCloudInitEnabled, Enabled: cdromCloudInitEnabled,
FileVolume: cdromCloudInitFileID, FileVolume: cdromCloudInitFileID,
Media: &cdromCloudInitMedia, Media: &cdromCloudInitMedia,
@ -784,11 +783,6 @@ func vmCreateCustom(ctx context.Context, d *schema.ResourceData, m interface{})
description := d.Get(mkDescription).(string) description := d.Get(mkDescription).(string)
diskDeviceObjects, err := getStorageDevicesFromResource(d)
if err != nil {
return diag.FromErr(err)
}
var efiDisk *vms.CustomEFIDisk var efiDisk *vms.CustomEFIDisk
efiDiskBlock := d.Get(mkEFIDisk).([]interface{}) efiDiskBlock := d.Get(mkEFIDisk).([]interface{})
@ -831,11 +825,6 @@ func vmCreateCustom(ctx context.Context, d *schema.ResourceData, m interface{})
} }
} }
virtioDeviceObjects := diskDeviceObjects["virtio"]
scsiDeviceObjects := diskDeviceObjects["scsi"]
// ideDeviceObjects := getOrderedDiskDeviceList(diskDeviceObjects, "ide")
sataDeviceObjects := diskDeviceObjects["sata"]
initializationConfig := vmGetCloudInitConfig(d) initializationConfig := vmGetCloudInitConfig(d)
if initializationConfig != nil { if initializationConfig != nil {
@ -935,20 +924,26 @@ func vmCreateCustom(ctx context.Context, d *schema.ResourceData, m interface{})
bootOrderConverted = []string{cdromInterface} bootOrderConverted = []string{cdromInterface}
} }
planDisks, err := getStorageDevicesFromResource(d)
if err != nil {
return diag.FromErr(err)
}
bootOrder := d.Get(mkBootOrder).([]interface{}) bootOrder := d.Get(mkBootOrder).([]interface{})
//nolint:nestif
if len(bootOrder) == 0 { if len(bootOrder) == 0 {
if sataDeviceObjects != nil { for _, disk := range planDisks {
if *disk.Interface == "sata0" {
bootOrderConverted = append(bootOrderConverted, "sata0") bootOrderConverted = append(bootOrderConverted, "sata0")
} }
if scsiDeviceObjects != nil { if *disk.Interface == "scsi0" {
bootOrderConverted = append(bootOrderConverted, "scsi0") bootOrderConverted = append(bootOrderConverted, "scsi0")
} }
if virtioDeviceObjects != nil { if *disk.Interface == "virtio0" {
bootOrderConverted = append(bootOrderConverted, "virtio0") bootOrderConverted = append(bootOrderConverted, "virtio0")
} }
}
if networkDeviceObjects != nil { if networkDeviceObjects != nil {
bootOrderConverted = append(bootOrderConverted, "net0") bootOrderConverted = append(bootOrderConverted, "net0")
@ -967,12 +962,12 @@ func vmCreateCustom(ctx context.Context, d *schema.ResourceData, m interface{})
ideDevice2Media := "cdrom" ideDevice2Media := "cdrom"
ideDevices := vms.CustomStorageDevices{ ideDevices := vms.CustomStorageDevices{
cdromCloudInitInterface: vms.CustomStorageDevice{ cdromCloudInitInterface: &vms.CustomStorageDevice{
Enabled: cdromCloudInitEnabled, Enabled: cdromCloudInitEnabled,
FileVolume: cdromCloudInitFileID, FileVolume: cdromCloudInitFileID,
Media: &ideDevice2Media, Media: &ideDevice2Media,
}, },
cdromInterface: vms.CustomStorageDevice{ cdromInterface: &vms.CustomStorageDevice{
Enabled: cdromEnabled, Enabled: cdromEnabled,
FileVolume: cdromFileID, FileVolume: cdromFileID,
Media: &ideDevice2Media, Media: &ideDevice2Media,
@ -1028,15 +1023,18 @@ func vmCreateCustom(ctx context.Context, d *schema.ResourceData, m interface{})
VMID: &vmID, VMID: &vmID,
} }
if sataDeviceObjects != nil { sataDeviceObjects := planDisks.ByStorageInterface("sata")
if len(sataDeviceObjects) > 0 {
createBody.SATADevices = sataDeviceObjects createBody.SATADevices = sataDeviceObjects
} }
if scsiDeviceObjects != nil { scsiDeviceObjects := planDisks.ByStorageInterface("scsi")
if len(scsiDeviceObjects) > 0 {
createBody.SCSIDevices = scsiDeviceObjects createBody.SCSIDevices = scsiDeviceObjects
} }
if virtioDeviceObjects != nil { virtioDeviceObjects := planDisks.ByStorageInterface("virtio")
if len(virtioDeviceObjects) > 0 {
createBody.VirtualIODevices = virtioDeviceObjects createBody.VirtualIODevices = virtioDeviceObjects
} }
@ -3266,16 +3264,16 @@ func vmUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.D
updateBody := &vms.UpdateRequestBody{ updateBody := &vms.UpdateRequestBody{
IDEDevices: vms.CustomStorageDevices{ IDEDevices: vms.CustomStorageDevices{
"ide0": vms.CustomStorageDevice{ "ide0": &vms.CustomStorageDevice{
Enabled: false, Enabled: false,
}, },
"ide1": vms.CustomStorageDevice{ "ide1": &vms.CustomStorageDevice{
Enabled: false, Enabled: false,
}, },
"ide2": vms.CustomStorageDevice{ "ide2": &vms.CustomStorageDevice{
Enabled: false, Enabled: false,
}, },
"ide3": vms.CustomStorageDevice{ "ide3": &vms.CustomStorageDevice{
Enabled: false, Enabled: false,
}, },
}, },
@ -3462,7 +3460,7 @@ func vmUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.D
cdromMedia := "cdrom" cdromMedia := "cdrom"
updateBody.IDEDevices[cdromInterface] = vms.CustomStorageDevice{ updateBody.IDEDevices[cdromInterface] = &vms.CustomStorageDevice{
Enabled: cdromEnabled, Enabled: cdromEnabled,
FileVolume: cdromFileID, FileVolume: cdromFileID,
Media: &cdromMedia, Media: &cdromMedia,
@ -3609,7 +3607,7 @@ func vmUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.D
fileVolume = ideDevice.FileVolume fileVolume = ideDevice.FileVolume
} }
updateBody.IDEDevices[initializationInterface] = vms.CustomStorageDevice{ updateBody.IDEDevices[initializationInterface] = &vms.CustomStorageDevice{
Enabled: true, Enabled: true,
FileVolume: fileVolume, FileVolume: fileVolume,
Media: &cdromMedia, Media: &cdromMedia,
@ -3854,13 +3852,11 @@ func vmUpdateDiskLocationAndSize(
} }
if oldEfiDisk != nil { if oldEfiDisk != nil {
baseDiskInterface := diskDigitPrefix(*oldEfiDisk.Interface) diskOldEntries[*oldEfiDisk.Interface] = oldEfiDisk
diskOldEntries[baseDiskInterface][*oldEfiDisk.Interface] = *oldEfiDisk
} }
if newEfiDisk != nil { if newEfiDisk != nil {
baseDiskInterface := diskDigitPrefix(*newEfiDisk.Interface) diskNewEntries[*newEfiDisk.Interface] = newEfiDisk
diskNewEntries[baseDiskInterface][*newEfiDisk.Interface] = *newEfiDisk
} }
if oldEfiDisk != nil && newEfiDisk != nil && oldEfiDisk.Size != newEfiDisk.Size { if oldEfiDisk != nil && newEfiDisk != nil && oldEfiDisk.Size != newEfiDisk.Size {
@ -3878,13 +3874,11 @@ func vmUpdateDiskLocationAndSize(
newTPMState := vmGetTPMStateAsStorageDevice(d, diskNew.([]interface{})) newTPMState := vmGetTPMStateAsStorageDevice(d, diskNew.([]interface{}))
if oldTPMState != nil { if oldTPMState != nil {
baseDiskInterface := diskDigitPrefix(*oldTPMState.Interface) diskOldEntries[*oldTPMState.Interface] = oldTPMState
diskOldEntries[baseDiskInterface][*oldTPMState.Interface] = *oldTPMState
} }
if newTPMState != nil { if newTPMState != nil {
baseDiskInterface := diskDigitPrefix(*newTPMState.Interface) diskNewEntries[*newTPMState.Interface] = newTPMState
diskNewEntries[baseDiskInterface][*newTPMState.Interface] = *newTPMState
} }
if oldTPMState != nil && newTPMState != nil && oldTPMState.Size != newTPMState.Size { if oldTPMState != nil && newTPMState != nil && oldTPMState.Size != newTPMState.Size {
@ -3894,22 +3888,23 @@ func vmUpdateDiskLocationAndSize(
} }
} }
// TODO: move to disks.go
var diskMoveBodies []*vms.MoveDiskRequestBody var diskMoveBodies []*vms.MoveDiskRequestBody
var diskResizeBodies []*vms.ResizeDiskRequestBody var diskResizeBodies []*vms.ResizeDiskRequestBody
shutdownForDisksRequired := false shutdownForDisksRequired := false
for prefix, diskMap := range diskOldEntries { for oldKey, oldDisk := range diskOldEntries {
for oldKey, oldDisk := range diskMap { if _, present := diskNewEntries[oldKey]; !present {
if _, present := diskNewEntries[prefix][oldKey]; !present {
return diag.Errorf( return diag.Errorf(
"deletion of disks not supported. Please delete disk by hand. Old Interface was %s", "deletion of disks not supported. Please delete disk by hand. Old Interface was %s",
*oldDisk.Interface, *oldDisk.Interface,
) )
} }
if *oldDisk.ID != *diskNewEntries[prefix][oldKey].ID { if *oldDisk.ID != *diskNewEntries[oldKey].ID {
if oldDisk.IsOwnedBy(vmID) { if oldDisk.IsOwnedBy(vmID) {
deleteOriginalDisk := types.CustomBool(true) deleteOriginalDisk := types.CustomBool(true)
@ -3918,7 +3913,7 @@ func vmUpdateDiskLocationAndSize(
&vms.MoveDiskRequestBody{ &vms.MoveDiskRequestBody{
DeleteOriginalDisk: &deleteOriginalDisk, DeleteOriginalDisk: &deleteOriginalDisk,
Disk: *oldDisk.Interface, Disk: *oldDisk.Interface,
TargetStorage: *diskNewEntries[prefix][oldKey].ID, TargetStorage: *diskNewEntries[oldKey].ID,
}, },
) )
@ -3929,19 +3924,19 @@ func vmUpdateDiskLocationAndSize(
"Cannot move %s:%s to datastore %s in VM %d configuration, it is not owned by this VM!", "Cannot move %s:%s to datastore %s in VM %d configuration, it is not owned by this VM!",
*oldDisk.ID, *oldDisk.ID,
*oldDisk.PathInDatastore(), *oldDisk.PathInDatastore(),
*diskNewEntries[prefix][oldKey].ID, *diskNewEntries[oldKey].ID,
vmID, vmID,
) )
} }
} }
if *oldDisk.Size < *diskNewEntries[prefix][oldKey].Size { if *oldDisk.Size < *diskNewEntries[oldKey].Size {
if oldDisk.IsOwnedBy(vmID) { if oldDisk.IsOwnedBy(vmID) {
diskResizeBodies = append( diskResizeBodies = append(
diskResizeBodies, diskResizeBodies,
&vms.ResizeDiskRequestBody{ &vms.ResizeDiskRequestBody{
Disk: *oldDisk.Interface, Disk: *oldDisk.Interface,
Size: *diskNewEntries[prefix][oldKey].Size, Size: *diskNewEntries[oldKey].Size,
}, },
) )
} else { } else {
@ -3954,7 +3949,6 @@ func vmUpdateDiskLocationAndSize(
} }
} }
} }
}
if shutdownForDisksRequired && !template { if shutdownForDisksRequired && !template {
if e := vmShutdown(ctx, vmAPI, d); e != nil { if e := vmShutdown(ctx, vmAPI, d); e != nil {
@ -3972,7 +3966,9 @@ func vmUpdateDiskLocationAndSize(
} }
for _, reqBody := range diskResizeBodies { for _, reqBody := range diskResizeBodies {
err = vmAPI.ResizeVMDisk(ctx, reqBody) moveDiskTimeout := d.Get(mkTimeoutMoveDisk).(int)
err = vmAPI.ResizeVMDisk(ctx, reqBody, moveDiskTimeout)
if err != nil { if err != nil {
return diag.FromErr(err) return diag.FromErr(err)
} }
@ -4075,16 +4071,6 @@ func vmDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.D
return nil return nil
} }
func diskDigitPrefix(s string) string {
for i, r := range s {
if unicode.IsDigit(r) {
return s[:i]
}
}
return s
}
func getPCIInfo(resp *vms.GetResponseData, _ *schema.ResourceData) map[string]*vms.CustomPCIDevice { func getPCIInfo(resp *vms.GetResponseData, _ *schema.ResourceData) map[string]*vms.CustomPCIDevice {
pciDevices := map[string]*vms.CustomPCIDevice{} pciDevices := map[string]*vms.CustomPCIDevice{}