0
0
mirror of https://github.com/bpg/terraform-provider-proxmox.git synced 2025-07-04 21:14:05 +00:00

chore(vm): refactor storage devices handling from/to API (#1394)

Signed-off-by: Pavel Boldyrev <627562+bpg@users.noreply.github.com>
This commit is contained in:
Pavel Boldyrev 2024-06-16 21:23:48 -04:00 committed by GitHub
parent b7d48f8716
commit cc7fc63ec1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 305 additions and 728 deletions

View File

@ -25,10 +25,9 @@ func attributeTypes() map[string]attr.Type {
}
}
func (m *Model) exportToCustomStorageDevice(iface string) vms.CustomStorageDevice {
func (m *Model) exportToCustomStorageDevice() vms.CustomStorageDevice {
return vms.CustomStorageDevice{
FileVolume: m.FileID.ValueString(),
Interface: &iface,
Media: ptr.Ptr("cdrom"),
}
}

View File

@ -22,7 +22,7 @@ type Value = types.Map
// NewValue returns a new Value with the given CD-ROM settings from the PVE API.
func NewValue(ctx context.Context, config *vms.GetResponseData, diags *diag.Diagnostics) Value {
// find storage devices with media=cdrom
cdroms := vms.MapCustomStorageDevices(*config).Filter(func(device *vms.CustomStorageDevice) bool {
cdroms := config.CustomStorageDevices.Filter(func(device *vms.CustomStorageDevice) bool {
return device.Media != nil && *device.Media == "cdrom"
})
@ -57,10 +57,7 @@ func FillCreateBody(ctx context.Context, planValue Value, body *vms.CreateReques
}
for iface, cdrom := range plan {
err := body.AddCustomStorageDevice(cdrom.exportToCustomStorageDevice(iface))
if err != nil {
diags.AddError(err.Error(), "")
}
body.AddCustomStorageDevice(iface, cdrom.exportToCustomStorageDevice())
}
}
@ -91,18 +88,12 @@ func FillUpdateBody(
toCreate, toUpdate, toDelete := utils.MapDiff(plan, state)
for iface, dev := range toCreate {
err := updateBody.AddCustomStorageDevice(dev.exportToCustomStorageDevice(iface))
if err != nil {
diags.AddError(err.Error(), "")
}
updateBody.AddCustomStorageDevice(iface, dev.exportToCustomStorageDevice())
}
for iface, dev := range toUpdate {
// for CD-ROMs, the update fully override the existing device, we don't do per-attribute check
err := updateBody.AddCustomStorageDevice(dev.exportToCustomStorageDevice(iface))
if err != nil {
diags.AddError(err.Error(), "")
}
updateBody.AddCustomStorageDevice(iface, dev.exportToCustomStorageDevice())
}
for iface := range toDelete {

View File

@ -11,14 +11,17 @@ import (
"fmt"
"net/url"
"path/filepath"
"reflect"
"strconv"
"strings"
"unicode"
"github.com/bpg/terraform-provider-proxmox/proxmox/types"
)
// StorageInterfaces is a list of supported storage interfaces.
//
//nolint:gochecknoglobals
var StorageInterfaces = []string{"ide", "sata", "scsi", "virtio"}
// CustomStorageDevice handles QEMU SATA device parameters.
type CustomStorageDevice struct {
AIO *string `json:"aio,omitempty" url:"aio,omitempty"`
@ -44,7 +47,6 @@ type CustomStorageDevice struct {
DatastoreID *string `json:"-" url:"-"`
Enabled bool `json:"-" url:"-"`
FileID *string `json:"-" url:"-"`
Interface *string `json:"-" url:"-"`
}
// CustomStorageDevices handles map of QEMU storage device per disk interface.
@ -108,19 +110,6 @@ func (d *CustomStorageDevice) IsCloudInitDrive(vmID int) bool {
strings.Contains(d.FileVolume, fmt.Sprintf("vm-%d-cloudinit", vmID))
}
// StorageInterface returns the storage interface of the CustomStorageDevice,
// e.g. "virtio" or "scsi" for "virtio0" or "scsi2".
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))
return ""
}
// EncodeOptions converts a CustomStorageDevice's common options a URL value.
func (d *CustomStorageDevice) EncodeOptions() string {
values := []string{}
@ -374,13 +363,6 @@ func (d *CustomStorageDevice) UnmarshalJSON(b []byte) error {
return nil
}
// ByStorageInterface returns a map of CustomStorageDevices filtered by the given storage interface.
func (d CustomStorageDevices) ByStorageInterface(storageInterface string) CustomStorageDevices {
return d.Filter(func(d *CustomStorageDevice) bool {
return d.StorageInterface() == storageInterface
})
}
// Filter returns a map of CustomStorageDevices filtered by the given function.
func (d CustomStorageDevices) Filter(fn func(*CustomStorageDevice) bool) CustomStorageDevices {
result := make(CustomStorageDevices)
@ -406,29 +388,3 @@ func (d CustomStorageDevices) EncodeValues(_ string, v *url.Values) error {
return nil
}
// MapCustomStorageDevices maps the custom storage devices from the API response.
//
// NOTE: CustomStorageDevice.FileID and CustomStorageDevice.DatastoreID are not set in this function.
func MapCustomStorageDevices(resp GetResponseData) CustomStorageDevices {
csd := CustomStorageDevices{}
mapDevice(csd, resp, "ide", "IDE", 3)
mapDevice(csd, resp, "sata", "SATA", 5)
mapDevice(csd, resp, "scsi", "SCSI", 13)
mapDevice(csd, resp, "virtio", "VirtualIO", 15)
return csd
}
func mapDevice(csds CustomStorageDevices, resp GetResponseData, keyPrefix, fieldPrefix string, end int) {
for i := 0; i <= end; i++ {
field := reflect.ValueOf(resp).FieldByName(fieldPrefix + "Device" + strconv.Itoa(i))
if !field.IsZero() {
val := field.Interface()
if val != nil {
csds[keyPrefix+strconv.Itoa(i)] = val.(*CustomStorageDevice)
}
}
}
}

View File

@ -121,154 +121,3 @@ 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: ptr.Ptr("virtio0"),
},
want: "virtio",
}, {
name: "scsi13",
device: CustomStorageDevice{
Interface: ptr.Ptr("scsi13"),
},
want: "scsi",
},
}
for _, tt := range tests {
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: "nothing matches",
iface: "sata",
devices: CustomStorageDevices{
"virtio0": &CustomStorageDevice{
Interface: ptr.Ptr("virtio0"),
},
"scsi13": &CustomStorageDevice{
Interface: ptr.Ptr("scsi13"),
},
},
want: CustomStorageDevices{},
},
{
name: "partially matches",
iface: "virtio",
devices: CustomStorageDevices{
"virtio0": &CustomStorageDevice{
Interface: ptr.Ptr("virtio0"),
},
"scsi13": &CustomStorageDevice{
Interface: ptr.Ptr("scsi13"),
},
"virtio1": &CustomStorageDevice{
Interface: ptr.Ptr("virtio1"),
},
},
want: CustomStorageDevices{
"virtio0": &CustomStorageDevice{
Interface: ptr.Ptr("virtio0"),
},
"virtio1": &CustomStorageDevice{
Interface: ptr.Ptr("virtio1"),
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
got := tt.devices.ByStorageInterface(tt.iface)
assert.Equal(t, tt.want, got)
})
}
}
func TestMapCustomStorageDevices(t *testing.T) {
t.Parallel()
type args struct {
resp GetResponseData
}
tests := []struct {
name string
args args
want CustomStorageDevices
}{
{"no storage devices", args{GetResponseData{}}, CustomStorageDevices{}},
{
"ide0 storage devices",
args{GetResponseData{IDEDevice0: &CustomStorageDevice{}}},
map[string]*CustomStorageDevice{"ide0": {}},
},
{
"multiple ide storage devices",
args{GetResponseData{
IDEDevice1: &CustomStorageDevice{},
IDEDevice3: &CustomStorageDevice{},
}},
map[string]*CustomStorageDevice{"ide1": {}, "ide3": {}},
},
{
"mixed storage devices",
args{GetResponseData{
IDEDevice1: &CustomStorageDevice{},
VirtualIODevice5: &CustomStorageDevice{},
SATADevice0: &CustomStorageDevice{},
IDEDevice3: &CustomStorageDevice{},
SCSIDevice10: &CustomStorageDevice{},
}},
map[string]*CustomStorageDevice{
"ide1": {},
"virtio5": {},
"sata0": {},
"ide3": {},
"scsi10": {},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
devices := MapCustomStorageDevices(tt.args.resp)
assert.Equalf(t, tt.want, devices, "MapCustomStorageDevices(%v)", tt.args.resp)
})
}
}

View File

@ -7,9 +7,11 @@
package vms
import (
"encoding/json"
"errors"
"fmt"
"reflect"
"regexp"
"strings"
"github.com/bpg/terraform-provider-proxmox/proxmox/types"
@ -60,7 +62,6 @@ type CreateRequestBody struct {
HookScript *string `json:"hookscript,omitempty" url:"hookscript,omitempty"`
Hotplug types.CustomCommaSeparatedList `json:"hotplug,omitempty" url:"hotplug,omitempty,comma"`
Hugepages *string `json:"hugepages,omitempty" url:"hugepages,omitempty"`
IDEDevices CustomStorageDevices `json:"ide,omitempty" url:",omitempty"`
KeepHugepages *types.CustomBool `json:"keephugepages,omitempty" url:"keephugepages,omitempty,int"`
KeyboardLayout *string `json:"keyboard,omitempty" url:"keyboard,omitempty"`
KVMArguments *string `json:"args,omitempty" url:"args,omitempty,space"`
@ -79,8 +80,6 @@ type CreateRequestBody struct {
PCIDevices CustomPCIDevices `json:"hostpci,omitempty" url:"hostpci,omitempty"`
PoolID *string `json:"pool,omitempty" url:"pool,omitempty"`
Revert *string `json:"revert,omitempty" url:"revert,omitempty"`
SATADevices CustomStorageDevices `json:"sata,omitempty" url:"sata,omitempty"`
SCSIDevices CustomStorageDevices `json:"scsi,omitempty" url:"scsi,omitempty"`
SCSIHardware *string `json:"scsihw,omitempty" url:"scsihw,omitempty"`
SerialDevices CustomSerialDevices `json:"serial,omitempty" url:"serial,omitempty"`
SharedMemory *CustomSharedMemory `json:"ivshmem,omitempty" url:"ivshmem,omitempty"`
@ -98,51 +97,22 @@ type CreateRequestBody struct {
USBDevices CustomUSBDevices `json:"usb,omitempty" url:"usb,omitempty"`
VGADevice *CustomVGADevice `json:"vga,omitempty" url:"vga,omitempty"`
VirtualCPUCount *int64 `json:"vcpus,omitempty" url:"vcpus,omitempty"`
VirtualIODevices CustomStorageDevices `json:"virtio,omitempty" url:"virtio,omitempty"`
VMGenerationID *string `json:"vmgenid,omitempty" url:"vmgenid,omitempty"`
VMID int `json:"vmid,omitempty" url:"vmid,omitempty"`
VMStateDatastoreID *string `json:"vmstatestorage,omitempty" url:"vmstatestorage,omitempty"`
WatchdogDevice *CustomWatchdogDevice `json:"watchdog,omitempty" url:"watchdog,omitempty"`
CustomStorageDevices CustomStorageDevices `json:"-"`
}
// AddCustomStorageDevice adds a custom storage device to the create request body.
func (b *CreateRequestBody) AddCustomStorageDevice(device CustomStorageDevice) error {
func (b *CreateRequestBody) AddCustomStorageDevice(iface string, device CustomStorageDevice) {
device.Enabled = true
switch device.StorageInterface() {
case "ide":
if b.IDEDevices == nil {
b.IDEDevices = make(CustomStorageDevices, 1)
}
b.IDEDevices[*device.Interface] = &device
case "sata":
if b.SATADevices == nil {
b.SATADevices = make(CustomStorageDevices, 1)
}
b.SATADevices[*device.Interface] = &device
case "scsi":
if b.SCSIDevices == nil {
b.SCSIDevices = make(CustomStorageDevices, 1)
}
b.SCSIDevices[*device.Interface] = &device
case "virtio":
if b.VirtualIODevices == nil {
b.VirtualIODevices = make(CustomStorageDevices, 1)
}
b.VirtualIODevices[*device.Interface] = &device
default:
return fmt.Errorf("unsupported storage interface: %s", device.StorageInterface())
if b.CustomStorageDevices == nil {
b.CustomStorageDevices = make(CustomStorageDevices, 1)
}
return nil
b.CustomStorageDevices[iface] = &device
}
// CreateResponseBody contains the body from a create response.
@ -235,10 +205,6 @@ type GetResponseData struct {
HookScript *string `json:"hookscript,omitempty"`
Hotplug *types.CustomCommaSeparatedList `json:"hotplug,omitempty"`
Hugepages *string `json:"hugepages,omitempty"`
IDEDevice0 *CustomStorageDevice `json:"ide0,omitempty"`
IDEDevice1 *CustomStorageDevice `json:"ide1,omitempty"`
IDEDevice2 *CustomStorageDevice `json:"ide2,omitempty"`
IDEDevice3 *CustomStorageDevice `json:"ide3,omitempty"`
IPConfig0 *CustomCloudInitIPConfig `json:"ipconfig0,omitempty"`
IPConfig1 *CustomCloudInitIPConfig `json:"ipconfig1,omitempty"`
IPConfig2 *CustomCloudInitIPConfig `json:"ipconfig2,omitempty"`
@ -330,26 +296,6 @@ type GetResponseData struct {
PCIDevice3 *CustomPCIDevice `json:"hostpci3,omitempty"`
PoolID *string `json:"pool,omitempty"`
Revert *string `json:"revert,omitempty"`
SATADevice0 *CustomStorageDevice `json:"sata0,omitempty"`
SATADevice1 *CustomStorageDevice `json:"sata1,omitempty"`
SATADevice2 *CustomStorageDevice `json:"sata2,omitempty"`
SATADevice3 *CustomStorageDevice `json:"sata3,omitempty"`
SATADevice4 *CustomStorageDevice `json:"sata4,omitempty"`
SATADevice5 *CustomStorageDevice `json:"sata5,omitempty"`
SCSIDevice0 *CustomStorageDevice `json:"scsi0,omitempty"`
SCSIDevice1 *CustomStorageDevice `json:"scsi1,omitempty"`
SCSIDevice2 *CustomStorageDevice `json:"scsi2,omitempty"`
SCSIDevice3 *CustomStorageDevice `json:"scsi3,omitempty"`
SCSIDevice4 *CustomStorageDevice `json:"scsi4,omitempty"`
SCSIDevice5 *CustomStorageDevice `json:"scsi5,omitempty"`
SCSIDevice6 *CustomStorageDevice `json:"scsi6,omitempty"`
SCSIDevice7 *CustomStorageDevice `json:"scsi7,omitempty"`
SCSIDevice8 *CustomStorageDevice `json:"scsi8,omitempty"`
SCSIDevice9 *CustomStorageDevice `json:"scsi9,omitempty"`
SCSIDevice10 *CustomStorageDevice `json:"scsi10,omitempty"`
SCSIDevice11 *CustomStorageDevice `json:"scsi11,omitempty"`
SCSIDevice12 *CustomStorageDevice `json:"scsi12,omitempty"`
SCSIDevice13 *CustomStorageDevice `json:"scsi13,omitempty"`
SCSIHardware *string `json:"scsihw,omitempty"`
SerialDevice0 *string `json:"serial0,omitempty"`
SerialDevice1 *string `json:"serial1,omitempty"`
@ -373,25 +319,10 @@ type GetResponseData struct {
USBDevice3 *CustomUSBDevice `json:"usb3,omitempty"`
VGADevice *CustomVGADevice `json:"vga,omitempty"`
VirtualCPUCount *int64 `json:"vcpus,omitempty"`
VirtualIODevice0 *CustomStorageDevice `json:"virtio0,omitempty"`
VirtualIODevice1 *CustomStorageDevice `json:"virtio1,omitempty"`
VirtualIODevice2 *CustomStorageDevice `json:"virtio2,omitempty"`
VirtualIODevice3 *CustomStorageDevice `json:"virtio3,omitempty"`
VirtualIODevice4 *CustomStorageDevice `json:"virtio4,omitempty"`
VirtualIODevice5 *CustomStorageDevice `json:"virtio5,omitempty"`
VirtualIODevice6 *CustomStorageDevice `json:"virtio6,omitempty"`
VirtualIODevice7 *CustomStorageDevice `json:"virtio7,omitempty"`
VirtualIODevice8 *CustomStorageDevice `json:"virtio8,omitempty"`
VirtualIODevice9 *CustomStorageDevice `json:"virtio9,omitempty"`
VirtualIODevice10 *CustomStorageDevice `json:"virtio10,omitempty"`
VirtualIODevice11 *CustomStorageDevice `json:"virtio11,omitempty"`
VirtualIODevice12 *CustomStorageDevice `json:"virtio12,omitempty"`
VirtualIODevice13 *CustomStorageDevice `json:"virtio13,omitempty"`
VirtualIODevice14 *CustomStorageDevice `json:"virtio14,omitempty"`
VirtualIODevice15 *CustomStorageDevice `json:"virtio15,omitempty"`
VMGenerationID *string `json:"vmgenid,omitempty"`
VMStateDatastoreID *string `json:"vmstatestorage,omitempty"`
WatchdogDevice *CustomWatchdogDevice `json:"watchdog,omitempty"`
CustomStorageDevices CustomStorageDevices `json:"-"`
}
// GetStatusResponseBody contains the body from a VM get status response.
@ -518,6 +449,48 @@ type UpdateAsyncResponseBody struct {
// UpdateRequestBody contains the data for an virtual machine update request.
type UpdateRequestBody = CreateRequestBody
// UnmarshalJSON unmarshals the custom storage devices from the JSON data.
func (d *GetResponseData) UnmarshalJSON(b []byte) error {
type Alias GetResponseData
var data Alias
// get original struct
if err := json.Unmarshal(b, &data); err != nil {
return fmt.Errorf("failed to unmarshal data: %w", err)
}
var byAttr map[string]interface{}
// now get map by attribute name
err := json.Unmarshal(b, &byAttr)
if err != nil {
return fmt.Errorf("failed to unmarshal data: %w", err)
}
allDevices := make(CustomStorageDevices)
for key, value := range byAttr {
for _, prefix := range StorageInterfaces {
r := regexp.MustCompile(`^` + prefix + `\d+$`)
if r.MatchString(key) {
var device CustomStorageDevice
if err := json.Unmarshal([]byte(`"`+value.(string)+`"`), &device); err != nil {
return fmt.Errorf("failed to unmarshal %s: %w", key, err)
}
allDevices[key] = &device
}
}
}
data.CustomStorageDevices = allDevices
*d = GetResponseData(data)
return nil
}
// ToDelete adds a field to the delete list. The field name should be the **actual** field name in the struct.
func (b *UpdateRequestBody) ToDelete(fieldName string) error {
if b == nil {

View File

@ -0,0 +1,64 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package vms
import (
"encoding/json"
"fmt"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestUnmarshalGetResponseData(t *testing.T) {
t.Parallel()
jsonData := fmt.Sprintf(`{
"archive": "test",
"ide0": "%[1]s",
"ide1": "%[1]s",
"ide2": "%[1]s",
"ide3": "%[1]s",
"virtio13": "%[1]s",
"scsi22": "%[1]s"
}`, "local-lvm:vm-100-disk-0,aio=io_uring,backup=1,cache=none,discard=ignore,replicate=1,size=8G,ssd=1")
var data GetResponseData
err := json.Unmarshal([]byte(jsonData), &data)
require.NoError(t, err)
assert.Equal(t, "test", *data.BackupFile)
assert.NotNil(t, data.CustomStorageDevices)
assert.Len(t, data.CustomStorageDevices, 6)
assert.NotNil(t, data.CustomStorageDevices["ide0"])
assertDevice(t, data.CustomStorageDevices["ide0"])
assert.NotNil(t, data.CustomStorageDevices["ide1"])
assertDevice(t, data.CustomStorageDevices["ide1"])
assert.NotNil(t, data.CustomStorageDevices["ide2"])
assertDevice(t, data.CustomStorageDevices["ide2"])
assert.NotNil(t, data.CustomStorageDevices["ide3"])
assertDevice(t, data.CustomStorageDevices["ide3"])
assert.NotNil(t, data.CustomStorageDevices["virtio13"])
assertDevice(t, data.CustomStorageDevices["virtio13"])
assert.NotNil(t, data.CustomStorageDevices["scsi22"])
assertDevice(t, data.CustomStorageDevices["scsi22"])
}
func assertDevice(t *testing.T, dev *CustomStorageDevice) {
t.Helper()
assert.Equal(t, "io_uring", *dev.AIO)
assert.True(t, bool(*dev.Backup))
assert.Equal(t, "none", *dev.Cache)
assert.Equal(t, "ignore", *dev.Discard)
assert.Equal(t, "local-lvm:vm-100-disk-0", dev.FileVolume)
assert.True(t, bool(*dev.Replicate))
assert.Equal(t, "8G", dev.Size.String())
assert.True(t, bool(*dev.SSD))
}

View File

@ -11,6 +11,7 @@ import (
"errors"
"fmt"
"regexp"
"slices"
"strings"
"unicode"
@ -26,56 +27,10 @@ import (
"github.com/bpg/terraform-provider-proxmox/utils"
)
const supportedDiskInterfaces = "virtio, sata, scsi, ide"
// GetInfo returns the disk information for a VM.
// Deprecated: use vms.MapCustomStorageDevices from proxmox/nodes/vms instead.
func GetInfo(resp *vms.GetResponseData, d *schema.ResourceData) vms.CustomStorageDevices {
storageDevices := vms.CustomStorageDevices{}
storageDevices["ide0"] = resp.IDEDevice0
storageDevices["ide1"] = resp.IDEDevice1
storageDevices["ide2"] = resp.IDEDevice2
storageDevices["ide3"] = resp.IDEDevice3
storageDevices["sata0"] = resp.SATADevice0
storageDevices["sata1"] = resp.SATADevice1
storageDevices["sata2"] = resp.SATADevice2
storageDevices["sata3"] = resp.SATADevice3
storageDevices["sata4"] = resp.SATADevice4
storageDevices["sata5"] = resp.SATADevice5
storageDevices["scsi0"] = resp.SCSIDevice0
storageDevices["scsi1"] = resp.SCSIDevice1
storageDevices["scsi2"] = resp.SCSIDevice2
storageDevices["scsi3"] = resp.SCSIDevice3
storageDevices["scsi4"] = resp.SCSIDevice4
storageDevices["scsi5"] = resp.SCSIDevice5
storageDevices["scsi6"] = resp.SCSIDevice6
storageDevices["scsi7"] = resp.SCSIDevice7
storageDevices["scsi8"] = resp.SCSIDevice8
storageDevices["scsi9"] = resp.SCSIDevice9
storageDevices["scsi10"] = resp.SCSIDevice10
storageDevices["scsi11"] = resp.SCSIDevice11
storageDevices["scsi12"] = resp.SCSIDevice12
storageDevices["scsi13"] = resp.SCSIDevice13
storageDevices["virtio0"] = resp.VirtualIODevice0
storageDevices["virtio1"] = resp.VirtualIODevice1
storageDevices["virtio2"] = resp.VirtualIODevice2
storageDevices["virtio3"] = resp.VirtualIODevice3
storageDevices["virtio4"] = resp.VirtualIODevice4
storageDevices["virtio5"] = resp.VirtualIODevice5
storageDevices["virtio6"] = resp.VirtualIODevice6
storageDevices["virtio7"] = resp.VirtualIODevice7
storageDevices["virtio8"] = resp.VirtualIODevice8
storageDevices["virtio9"] = resp.VirtualIODevice9
storageDevices["virtio10"] = resp.VirtualIODevice10
storageDevices["virtio11"] = resp.VirtualIODevice11
storageDevices["virtio12"] = resp.VirtualIODevice12
storageDevices["virtio13"] = resp.VirtualIODevice13
storageDevices["virtio14"] = resp.VirtualIODevice14
storageDevices["virtio15"] = resp.VirtualIODevice15
storageDevices := resp.CustomStorageDevices
currentDisk := d.Get(MkDisk)
@ -92,10 +47,6 @@ func GetInfo(resp *vms.GetResponseData, d *schema.ResourceData) vms.CustomStorag
if v.Size == nil {
v.Size = new(types.DiskSize)
}
// defensive copy of the loop variable
iface := k
v.Interface = &iface
}
}
@ -106,7 +57,7 @@ func GetInfo(resp *vms.GetResponseData, d *schema.ResourceData) vms.CustomStorag
func CreateClone(
ctx context.Context,
d *schema.ResourceData,
planDisks map[string]vms.CustomStorageDevices,
planDisks vms.CustomStorageDevices,
allDiskInfo vms.CustomStorageDevices,
vmAPI *vms.Client,
) error {
@ -116,45 +67,18 @@ func CreateClone(
diskInterface := diskBlock[mkDiskInterface].(string)
dataStoreID := diskBlock[mkDiskDatastoreID].(string)
diskSize := int64(diskBlock[mkDiskSize].(int))
prefix := DigitPrefix(diskInterface)
currentDiskInfo := allDiskInfo[diskInterface]
configuredDiskInfo := planDisks[prefix][diskInterface]
configuredDiskInfo := planDisks[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
case "ide":
if diskUpdateBody.IDEDevices == nil {
diskUpdateBody.IDEDevices = vms.CustomStorageDevices{}
}
diskUpdateBody.IDEDevices[diskInterface] = configuredDiskInfo
}
diskUpdateBody.AddCustomStorageDevice(diskInterface, *configuredDiskInfo)
err := vmAPI.UpdateVM(ctx, diskUpdateBody)
if err != nil {
return fmt.Errorf("disk create fails: %w", err)
return fmt.Errorf("disk update fails: %w", err)
}
continue
@ -225,7 +149,7 @@ func GetDiskDeviceObjects(
d *schema.ResourceData,
resource *schema.Resource,
disks []interface{},
) (map[string]vms.CustomStorageDevices, error) {
) (vms.CustomStorageDevices, error) {
var diskDevices []interface{}
if disks != nil {
@ -234,7 +158,7 @@ func GetDiskDeviceObjects(
diskDevices = d.Get(MkDisk).([]interface{})
}
diskDeviceObjects := map[string]vms.CustomStorageDevices{}
diskDeviceObjects := vms.CustomStorageDevices{}
for _, diskEntry := range diskDevices {
diskDevice := &vms.CustomStorageDevice{
@ -300,7 +224,6 @@ func GetDiskDeviceObjects(
diskDevice.Discard = &discard
diskDevice.FileID = &fileID
diskDevice.Format = &fileFormat
diskDevice.Interface = &diskInterface
diskDevice.Replicate = &replicate
diskDevice.Serial = &serial
diskDevice.Size = types.DiskSizeFromGigabytes(int64(size))
@ -356,21 +279,16 @@ func GetDiskDeviceObjects(
}
}
baseDiskInterface := DigitPrefix(diskInterface)
if !strings.Contains(supportedDiskInterfaces, baseDiskInterface) {
if !slices.Contains(vms.StorageInterfaces, DigitPrefix(diskInterface)) {
errorMsg := fmt.Sprintf(
"Defined disk interface not supported. Interface was %s, but only %s are supported",
diskInterface, supportedDiskInterfaces,
"Defined disk interface not supported. Interface was %s, but only %v are supported",
diskInterface, vms.StorageInterfaces,
)
return diskDeviceObjects, errors.New(errorMsg)
}
if _, present := diskDeviceObjects[baseDiskInterface]; !present {
diskDeviceObjects[baseDiskInterface] = vms.CustomStorageDevices{}
}
diskDeviceObjects[baseDiskInterface][diskInterface] = diskDevice
diskDeviceObjects[diskInterface] = diskDevice
}
return diskDeviceObjects, nil
@ -382,16 +300,14 @@ func CreateCustomDisks(
client proxmox.Client,
nodeName string,
vmID int,
storageDevices map[string]vms.CustomStorageDevices,
storageDevices vms.CustomStorageDevices,
) diag.Diagnostics {
for _, diskDevice := range storageDevices {
for _, disk := range diskDevice {
if disk != nil && disk.FileID != nil && *disk.FileID != "" {
// only custom disks with defined file ID
err := createCustomDisk(ctx, client, nodeName, vmID, *disk)
if err != nil {
return diag.FromErr(err)
}
for iface, disk := range storageDevices {
if disk != nil && disk.FileID != nil && *disk.FileID != "" {
// only custom disks with defined file ID
err := createCustomDisk(ctx, client, nodeName, vmID, iface, *disk)
if err != nil {
return diag.FromErr(err)
}
}
}
@ -404,23 +320,24 @@ func createCustomDisk(
client proxmox.Client,
nodeName string,
vmID int,
d vms.CustomStorageDevice,
iface string,
disk vms.CustomStorageDevice,
) error {
fileFormat := dvDiskFileFormat
if d.Format != nil && *d.Format != "" {
fileFormat = *d.Format
if disk.Format != nil && *disk.Format != "" {
fileFormat = *disk.Format
}
//nolint:lll
commands := []string{
`set -e`,
ssh.TrySudo,
fmt.Sprintf(`file_id="%s"`, *d.FileID),
fmt.Sprintf(`file_id="%s"`, *disk.FileID),
fmt.Sprintf(`file_format="%s"`, fileFormat),
fmt.Sprintf(`datastore_id_target="%s"`, *d.DatastoreID),
fmt.Sprintf(`datastore_id_target="%s"`, *disk.DatastoreID),
fmt.Sprintf(`vm_id="%d"`, vmID),
fmt.Sprintf(`disk_options="%s"`, d.EncodeOptions()),
fmt.Sprintf(`disk_interface="%s"`, *d.Interface),
fmt.Sprintf(`disk_options="%s"`, disk.EncodeOptions()),
fmt.Sprintf(`disk_interface="%s"`, iface),
`source_image=$(try_sudo "pvesm path $file_id")`,
`imported_disk="$(try_sudo "qm importdisk $vm_id $source_image $datastore_id_target -format $file_format" | grep "unused0" | cut -d ":" -f 3 | cut -d "'" -f 1)"`,
`disk_id="${datastore_id_target}:$imported_disk,${disk_options}"`,
@ -441,8 +358,8 @@ func createCustomDisk(
})
err = client.Node(nodeName).VM(vmID).ResizeVMDisk(ctx, &vms.ResizeDiskRequestBody{
Disk: *d.Interface,
Size: *d.Size,
Disk: iface,
Size: *disk.Size,
})
if err != nil {
return fmt.Errorf("resizing disk: %w", err)
@ -651,103 +568,62 @@ func Update(
nodeName string,
vmID int,
d *schema.ResourceData,
planDisks map[string]vms.CustomStorageDevices,
planDisks vms.CustomStorageDevices,
currentDisks vms.CustomStorageDevices,
updateBody *vms.UpdateRequestBody,
) (bool, error) {
rebootRequired := false
if d.HasChange(MkDisk) {
for prefix, diskMap := range planDisks {
if diskMap == nil {
for iface, disk := range planDisks {
var tmp *vms.CustomStorageDevice
switch {
case currentDisks[iface] == nil && disk != nil:
if disk.FileID != nil && *disk.FileID != "" {
// only disks with defined file ID are custom image disks that need to be created via import.
err := createCustomDisk(ctx, client, nodeName, vmID, iface, *disk)
if err != nil {
return false, fmt.Errorf("creating custom disk: %w", err)
}
} else {
// otherwise this is a blank disk that can be added directly via update API
tmp = disk
}
case currentDisks[iface] != nil:
// update existing disk
tmp = currentDisks[iface]
default:
// something went wrong
return false, fmt.Errorf("missing device %s", iface)
}
if tmp == nil || disk == nil {
continue
}
for key, disk := range diskMap {
var tmp *vms.CustomStorageDevice
switch {
case currentDisks[key] == nil && disk != nil:
if disk.FileID != nil && *disk.FileID != "" {
// only disks with defined file ID are custom image disks that need to be created via import.
err := createCustomDisk(ctx, client, nodeName, vmID, *disk)
if err != nil {
return false, fmt.Errorf("creating custom disk: %w", err)
}
} else {
// otherwise this is a blank disk that can be added directly via update API
tmp = disk
}
case currentDisks[key] != nil:
// update existing disk
tmp = currentDisks[key]
default:
// something went wrong
return false, fmt.Errorf("missing %s device %s", prefix, key)
}
if tmp == nil || disk == nil {
continue
}
if tmp.AIO != disk.AIO {
rebootRequired = true
tmp.AIO = disk.AIO
}
tmp.Backup = disk.Backup
tmp.BurstableReadSpeedMbps = disk.BurstableReadSpeedMbps
tmp.BurstableWriteSpeedMbps = disk.BurstableWriteSpeedMbps
tmp.Cache = disk.Cache
tmp.Discard = disk.Discard
tmp.IOThread = disk.IOThread
tmp.IopsRead = disk.IopsRead
tmp.IopsWrite = disk.IopsWrite
tmp.MaxIopsRead = disk.MaxIopsRead
tmp.MaxIopsWrite = disk.MaxIopsWrite
tmp.MaxReadSpeedMbps = disk.MaxReadSpeedMbps
tmp.MaxWriteSpeedMbps = disk.MaxWriteSpeedMbps
tmp.Replicate = disk.Replicate
tmp.Serial = disk.Serial
tmp.SSD = disk.SSD
switch prefix {
case "virtio":
{
if updateBody.VirtualIODevices == nil {
updateBody.VirtualIODevices = vms.CustomStorageDevices{}
}
updateBody.VirtualIODevices[key] = tmp
}
case "sata":
{
if updateBody.SATADevices == nil {
updateBody.SATADevices = vms.CustomStorageDevices{}
}
updateBody.SATADevices[key] = tmp
}
case "scsi":
{
if updateBody.SCSIDevices == nil {
updateBody.SCSIDevices = vms.CustomStorageDevices{}
}
updateBody.SCSIDevices[key] = tmp
}
case "ide":
{
if updateBody.IDEDevices == nil {
updateBody.IDEDevices = vms.CustomStorageDevices{}
}
updateBody.IDEDevices[key] = tmp
}
default:
return false, fmt.Errorf("device prefix %s not supported", prefix)
}
if tmp.AIO != disk.AIO {
rebootRequired = true
tmp.AIO = disk.AIO
}
tmp.Backup = disk.Backup
tmp.BurstableReadSpeedMbps = disk.BurstableReadSpeedMbps
tmp.BurstableWriteSpeedMbps = disk.BurstableWriteSpeedMbps
tmp.Cache = disk.Cache
tmp.Discard = disk.Discard
tmp.IOThread = disk.IOThread
tmp.IopsRead = disk.IopsRead
tmp.IopsWrite = disk.IopsWrite
tmp.MaxIopsRead = disk.MaxIopsRead
tmp.MaxIopsWrite = disk.MaxIopsWrite
tmp.MaxReadSpeedMbps = disk.MaxReadSpeedMbps
tmp.MaxWriteSpeedMbps = disk.MaxWriteSpeedMbps
tmp.Replicate = disk.Replicate
tmp.Serial = disk.Serial
tmp.SSD = disk.SSD
updateBody.AddCustomStorageDevice(iface, *tmp)
}
}

View File

@ -1524,52 +1524,12 @@ func vmCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.D
// Check for an existing CloudInit IDE drive. If no such drive is found, return the specified `defaultValue`.
func findExistingCloudInitDrive(vmConfig *vms.GetResponseData, vmID int, defaultValue string) string {
ideDevices := []*vms.CustomStorageDevice{
vmConfig.IDEDevice0,
vmConfig.IDEDevice1,
vmConfig.IDEDevice2,
vmConfig.IDEDevice3,
}
for i, device := range ideDevices {
if device != nil && device.Enabled && device.IsCloudInitDrive(vmID) {
return fmt.Sprintf("ide%d", i)
}
}
devs := vmConfig.CustomStorageDevices.Filter(func(device *vms.CustomStorageDevice) bool {
return device.IsCloudInitDrive(vmID)
})
sataDevices := []*vms.CustomStorageDevice{
vmConfig.SATADevice0,
vmConfig.SATADevice1,
vmConfig.SATADevice2,
vmConfig.SATADevice3,
vmConfig.SATADevice4,
vmConfig.SATADevice5,
}
for i, device := range sataDevices {
if device != nil && device.Enabled && device.IsCloudInitDrive(vmID) {
return fmt.Sprintf("sata%d", i)
}
}
scsiDevices := []*vms.CustomStorageDevice{
vmConfig.SCSIDevice0,
vmConfig.SCSIDevice1,
vmConfig.SCSIDevice2,
vmConfig.SCSIDevice3,
vmConfig.SCSIDevice4,
vmConfig.SCSIDevice5,
vmConfig.SCSIDevice6,
vmConfig.SCSIDevice7,
vmConfig.SCSIDevice8,
vmConfig.SCSIDevice9,
vmConfig.SCSIDevice10,
vmConfig.SCSIDevice11,
vmConfig.SCSIDevice12,
vmConfig.SCSIDevice13,
}
for i, device := range scsiDevices {
if device != nil && device.Enabled && device.IsCloudInitDrive(vmID) {
return fmt.Sprintf("scsi%d", i)
}
for iface := range devs {
return iface
}
return defaultValue
@ -1578,61 +1538,11 @@ func findExistingCloudInitDrive(vmConfig *vms.GetResponseData, vmID int, default
// Return a pointer to the storage device configuration based on a name. The device name is assumed to be a
// valid ide, sata, or scsi interface name.
func getStorageDevice(vmConfig *vms.GetResponseData, deviceName string) *vms.CustomStorageDevice {
switch deviceName {
case "ide0":
return vmConfig.IDEDevice0
case "ide1":
return vmConfig.IDEDevice1
case "ide2":
return vmConfig.IDEDevice2
case "ide3":
return vmConfig.IDEDevice3
case "sata0":
return vmConfig.SATADevice0
case "sata1":
return vmConfig.SATADevice1
case "sata2":
return vmConfig.SATADevice2
case "sata3":
return vmConfig.SATADevice3
case "sata4":
return vmConfig.SATADevice4
case "sata5":
return vmConfig.SATADevice5
case "scsi0":
return vmConfig.SCSIDevice0
case "scsi1":
return vmConfig.SCSIDevice1
case "scsi2":
return vmConfig.SCSIDevice2
case "scsi3":
return vmConfig.SCSIDevice3
case "scsi4":
return vmConfig.SCSIDevice4
case "scsi5":
return vmConfig.SCSIDevice5
case "scsi6":
return vmConfig.SCSIDevice6
case "scsi7":
return vmConfig.SCSIDevice7
case "scsi8":
return vmConfig.SCSIDevice8
case "scsi9":
return vmConfig.SCSIDevice9
case "scsi10":
return vmConfig.SCSIDevice10
case "scsi11":
return vmConfig.SCSIDevice11
case "scsi12":
return vmConfig.SCSIDevice12
case "scsi13":
return vmConfig.SCSIDevice13
default:
return nil
if dev, ok := vmConfig.CustomStorageDevices[deviceName]; ok {
return dev
}
return nil
}
// Delete IDE interfaces that can then be used for CloudInit. The first interface will always
@ -2073,7 +1983,9 @@ func vmCreateClone(ctx context.Context, d *schema.ResourceData, m interface{}) d
}
if len(cdrom) > 0 || len(initialization) > 0 {
updateBody.IDEDevices = ideDevices
for iface, dev := range ideDevices {
updateBody.AddCustomStorageDevice(iface, *dev)
}
}
if keyboardLayout != dvKeyboardLayout {
@ -2564,11 +2476,6 @@ func vmCreateCustom(ctx context.Context, d *schema.ResourceData, m interface{})
return diag.FromErr(err)
}
virtioDeviceObjects := diskDeviceObjects["virtio"]
scsiDeviceObjects := diskDeviceObjects["scsi"]
ideDeviceObjects := diskDeviceObjects["ide"]
sataDeviceObjects := diskDeviceObjects["sata"]
var bootOrderConverted []string
if cdromEnabled {
bootOrderConverted = []string{cdromInterface}
@ -2577,19 +2484,19 @@ func vmCreateCustom(ctx context.Context, d *schema.ResourceData, m interface{})
bootOrder := d.Get(mkBootOrder).([]interface{})
if len(bootOrder) == 0 {
if ideDeviceObjects != nil {
if _, ok := diskDeviceObjects["ide0"]; ok {
bootOrderConverted = append(bootOrderConverted, "ide0")
}
if sataDeviceObjects != nil {
if _, ok := diskDeviceObjects["sata0"]; ok {
bootOrderConverted = append(bootOrderConverted, "sata0")
}
if scsiDeviceObjects != nil {
if _, ok := diskDeviceObjects["scsi0"]; ok {
bootOrderConverted = append(bootOrderConverted, "scsi0")
}
if virtioDeviceObjects != nil {
if _, ok := diskDeviceObjects["virtio0"]; ok {
bootOrderConverted = append(bootOrderConverted, "virtio0")
}
@ -2609,10 +2516,9 @@ func vmCreateCustom(ctx context.Context, d *schema.ResourceData, m interface{})
}
ideDevice2Media := "cdrom"
ideDevices := vms.CustomStorageDevices{}
if cdromCloudInitInterface != "" {
ideDevices[cdromCloudInitInterface] = &vms.CustomStorageDevice{
diskDeviceObjects[cdromCloudInitInterface] = &vms.CustomStorageDevice{
Enabled: cdromCloudInitEnabled,
FileVolume: cdromCloudInitFileID,
Media: &ideDevice2Media,
@ -2620,7 +2526,7 @@ func vmCreateCustom(ctx context.Context, d *schema.ResourceData, m interface{})
}
if cdromInterface != "" {
ideDevices[cdromInterface] = &vms.CustomStorageDevice{
diskDeviceObjects[cdromInterface] = &vms.CustomStorageDevice{
Enabled: cdromEnabled,
FileVolume: cdromFileID,
Media: &ideDevice2Media,
@ -2657,47 +2563,31 @@ func vmCreateCustom(ctx context.Context, d *schema.ResourceData, m interface{})
Flags: &cpuFlagsConverted,
Type: cpuType,
},
CPUSockets: ptr.Ptr(int64(cpuSockets)),
CPUUnits: ptr.Ptr(int64(cpuUnits)),
DedicatedMemory: &memoryDedicated,
DeletionProtection: &protection,
EFIDisk: efiDisk,
TPMState: tpmState,
FloatingMemory: &memoryFloating,
IDEDevices: ideDevices,
KeyboardLayout: &keyboardLayout,
NetworkDevices: networkDeviceObjects,
NUMAEnabled: &cpuNUMA,
NUMADevices: numaDeviceObjects,
OSType: &operatingSystemType,
PCIDevices: pciDeviceObjects,
SCSIHardware: &scsiHardware,
SerialDevices: serialDevices,
SharedMemory: memorySharedObject,
StartOnBoot: &onBoot,
SMBIOS: smbios,
StartupOrder: startupOrder,
TabletDeviceEnabled: &tabletDevice,
Template: &template,
USBDevices: usbDeviceObjects,
VGADevice: vgaDevice,
VMID: vmID,
}
if ideDeviceObjects != nil {
createBody.IDEDevices = ideDeviceObjects
}
if sataDeviceObjects != nil {
createBody.SATADevices = sataDeviceObjects
}
if scsiDeviceObjects != nil {
createBody.SCSIDevices = scsiDeviceObjects
}
if virtioDeviceObjects != nil {
createBody.VirtualIODevices = virtioDeviceObjects
CPUSockets: ptr.Ptr(int64(cpuSockets)),
CPUUnits: ptr.Ptr(int64(cpuUnits)),
DedicatedMemory: &memoryDedicated,
DeletionProtection: &protection,
EFIDisk: efiDisk,
TPMState: tpmState,
FloatingMemory: &memoryFloating,
KeyboardLayout: &keyboardLayout,
NetworkDevices: networkDeviceObjects,
NUMAEnabled: &cpuNUMA,
NUMADevices: numaDeviceObjects,
OSType: &operatingSystemType,
PCIDevices: pciDeviceObjects,
SCSIHardware: &scsiHardware,
SerialDevices: serialDevices,
SharedMemory: memorySharedObject,
StartOnBoot: &onBoot,
SMBIOS: smbios,
StartupOrder: startupOrder,
TabletDeviceEnabled: &tabletDevice,
Template: &template,
USBDevices: usbDeviceObjects,
VGADevice: vgaDevice,
VMID: vmID,
CustomStorageDevices: diskDeviceObjects,
}
// Only the root account is allowed to change the CPU architecture, which makes this check necessary.
@ -3017,14 +2907,11 @@ func vmGetEfiDiskAsStorageDevice(d *schema.ResourceData, disk []interface{}) (*v
if efiDisk != nil {
id := "0"
baseDiskInterface := "efidisk"
diskInterface := fmt.Sprint(baseDiskInterface, id)
storageDevice = &vms.CustomStorageDevice{
Enabled: true,
FileVolume: efiDisk.FileVolume,
Format: efiDisk.Format,
Interface: &diskInterface,
DatastoreID: &id,
}
@ -3075,13 +2962,9 @@ func vmGetTPMStateAsStorageDevice(d *schema.ResourceData, disk []interface{}) *v
if tpmState != nil {
id := "0"
baseDiskInterface := "tpmstate"
diskInterface := fmt.Sprint(baseDiskInterface, id)
storageDevice = &vms.CustomStorageDevice{
Enabled: true,
FileVolume: tpmState.FileVolume,
Interface: &diskInterface,
DatastoreID: &id,
}
}
@ -4757,22 +4640,7 @@ func vmUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.D
vmAPI := client.Node(nodeName).VM(vmID)
updateBody := &vms.UpdateRequestBody{
IDEDevices: vms.CustomStorageDevices{
"ide0": &vms.CustomStorageDevice{
Enabled: false,
},
"ide1": &vms.CustomStorageDevice{
Enabled: false,
},
"ide2": &vms.CustomStorageDevice{
Enabled: false,
},
"ide3": &vms.CustomStorageDevice{
Enabled: false,
},
},
}
updateBody := &vms.UpdateRequestBody{}
var del []string
@ -4961,11 +4829,11 @@ func vmUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.D
cdromMedia := "cdrom"
updateBody.IDEDevices[cdromInterface] = &vms.CustomStorageDevice{
updateBody.AddCustomStorageDevice(cdromInterface, vms.CustomStorageDevice{
Enabled: cdromEnabled,
FileVolume: cdromFileID,
Media: &cdromMedia,
}
})
}
// Prepare the new CPU configuration.
@ -5132,11 +5000,11 @@ func vmUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.D
fileVolume = ideDevice.FileVolume
}
updateBody.IDEDevices[initializationInterface] = &vms.CustomStorageDevice{
updateBody.AddCustomStorageDevice(initializationInterface, vms.CustomStorageDevice{
Enabled: true,
FileVolume: fileVolume,
Media: &cdromMedia,
}
})
}
rebootRequired = true
@ -5416,13 +5284,11 @@ func vmUpdateDiskLocationAndSize(
}
if oldEfiDisk != nil {
baseDiskInterface := disk.DigitPrefix(*oldEfiDisk.Interface)
diskOldEntries[baseDiskInterface][*oldEfiDisk.Interface] = oldEfiDisk
diskOldEntries["efidisk0"] = oldEfiDisk
}
if newEfiDisk != nil {
baseDiskInterface := disk.DigitPrefix(*newEfiDisk.Interface)
diskNewEntries[baseDiskInterface][*newEfiDisk.Interface] = newEfiDisk
diskNewEntries["efidisk0"] = newEfiDisk
}
if oldEfiDisk != nil && newEfiDisk != nil && oldEfiDisk.Size != newEfiDisk.Size {
@ -5440,13 +5306,11 @@ func vmUpdateDiskLocationAndSize(
newTPMState := vmGetTPMStateAsStorageDevice(d, diskNew.([]interface{}))
if oldTPMState != nil {
baseDiskInterface := disk.DigitPrefix(*oldTPMState.Interface)
diskOldEntries[baseDiskInterface][*oldTPMState.Interface] = oldTPMState
diskOldEntries["tpmstate0"] = oldTPMState
}
if newTPMState != nil {
baseDiskInterface := disk.DigitPrefix(*newTPMState.Interface)
diskNewEntries[baseDiskInterface][*newTPMState.Interface] = newTPMState
diskNewEntries["tpmstate0"] = newTPMState
}
if oldTPMState != nil && newTPMState != nil && oldTPMState.Size != newTPMState.Size {
@ -5462,58 +5326,56 @@ func vmUpdateDiskLocationAndSize(
shutdownForDisksRequired := false
for prefix, diskMap := range diskOldEntries {
for oldKey, oldDisk := range diskMap {
if _, present := diskNewEntries[prefix][oldKey]; !present {
for oldIface, oldDisk := range diskOldEntries {
if _, present := diskNewEntries[oldIface]; !present {
return diag.Errorf(
"deletion of disks not supported. Please delete disk by hand. Old interface was %q",
oldIface,
)
}
if *oldDisk.DatastoreID != *diskNewEntries[oldIface].DatastoreID {
if oldDisk.IsOwnedBy(vmID) {
deleteOriginalDisk := types.CustomBool(true)
diskMoveBodies = append(
diskMoveBodies,
&vms.MoveDiskRequestBody{
DeleteOriginalDisk: &deleteOriginalDisk,
Disk: oldIface,
TargetStorage: *diskNewEntries[oldIface].DatastoreID,
},
)
// Cannot be done while VM is running.
shutdownForDisksRequired = true
} else {
return diag.Errorf(
"deletion of disks not supported. Please delete disk by hand. Old interface was %q",
*oldDisk.Interface,
"Cannot move %s:%s to datastore %s in VM %d configuration, it is not owned by this VM!",
*oldDisk.DatastoreID,
*oldDisk.PathInDatastore(),
*diskNewEntries[oldIface].DatastoreID,
vmID,
)
}
}
if *oldDisk.DatastoreID != *diskNewEntries[prefix][oldKey].DatastoreID {
if oldDisk.IsOwnedBy(vmID) {
deleteOriginalDisk := types.CustomBool(true)
diskMoveBodies = append(
diskMoveBodies,
&vms.MoveDiskRequestBody{
DeleteOriginalDisk: &deleteOriginalDisk,
Disk: *oldDisk.Interface,
TargetStorage: *diskNewEntries[prefix][oldKey].DatastoreID,
},
)
// Cannot be done while VM is running.
shutdownForDisksRequired = true
} else {
return diag.Errorf(
"Cannot move %s:%s to datastore %s in VM %d configuration, it is not owned by this VM!",
*oldDisk.DatastoreID,
*oldDisk.PathInDatastore(),
*diskNewEntries[prefix][oldKey].DatastoreID,
vmID,
)
}
}
if *oldDisk.Size < *diskNewEntries[prefix][oldKey].Size {
if oldDisk.IsOwnedBy(vmID) {
diskResizeBodies = append(
diskResizeBodies,
&vms.ResizeDiskRequestBody{
Disk: *oldDisk.Interface,
Size: *diskNewEntries[prefix][oldKey].Size,
},
)
} else {
return diag.Errorf(
"Cannot resize %s:%s in VM %d configuration, it is not owned by this VM!",
*oldDisk.DatastoreID,
*oldDisk.PathInDatastore(),
vmID,
)
}
if *oldDisk.Size < *diskNewEntries[oldIface].Size {
if oldDisk.IsOwnedBy(vmID) {
diskResizeBodies = append(
diskResizeBodies,
&vms.ResizeDiskRequestBody{
Disk: oldIface,
Size: *diskNewEntries[oldIface].Size,
},
)
} else {
return diag.Errorf(
"Cannot resize %s:%s in VM %d configuration, it is not owned by this VM!",
*oldDisk.DatastoreID,
*oldDisk.PathInDatastore(),
vmID,
)
}
}
}

View File

@ -1,3 +1,10 @@
#!/bin/sh
TF_ACC=1 env $(cat testacc.env | xargs) go test -v -timeout 360s -run "$1" github.com/bpg/terraform-provider-proxmox/fwprovider/tests $2
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
#
# shellcheck disable=SC2046
TF_ACC=1 env $(xargs < testacc.env) go test -v -count 1 -timeout 360s -run "$1" github.com/bpg/terraform-provider-proxmox/fwprovider/... $2