mirror of
https://github.com/bpg/terraform-provider-proxmox.git
synced 2025-07-05 05:24:01 +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:
parent
b7d48f8716
commit
cc7fc63ec1
@ -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{
|
return vms.CustomStorageDevice{
|
||||||
FileVolume: m.FileID.ValueString(),
|
FileVolume: m.FileID.ValueString(),
|
||||||
Interface: &iface,
|
|
||||||
Media: ptr.Ptr("cdrom"),
|
Media: ptr.Ptr("cdrom"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ type Value = types.Map
|
|||||||
// NewValue returns a new Value with the given CD-ROM settings from the PVE API.
|
// 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 {
|
func NewValue(ctx context.Context, config *vms.GetResponseData, diags *diag.Diagnostics) Value {
|
||||||
// find storage devices with media=cdrom
|
// 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"
|
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 {
|
for iface, cdrom := range plan {
|
||||||
err := body.AddCustomStorageDevice(cdrom.exportToCustomStorageDevice(iface))
|
body.AddCustomStorageDevice(iface, cdrom.exportToCustomStorageDevice())
|
||||||
if err != nil {
|
|
||||||
diags.AddError(err.Error(), "")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,18 +88,12 @@ func FillUpdateBody(
|
|||||||
toCreate, toUpdate, toDelete := utils.MapDiff(plan, state)
|
toCreate, toUpdate, toDelete := utils.MapDiff(plan, state)
|
||||||
|
|
||||||
for iface, dev := range toCreate {
|
for iface, dev := range toCreate {
|
||||||
err := updateBody.AddCustomStorageDevice(dev.exportToCustomStorageDevice(iface))
|
updateBody.AddCustomStorageDevice(iface, dev.exportToCustomStorageDevice())
|
||||||
if err != nil {
|
|
||||||
diags.AddError(err.Error(), "")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for iface, dev := range toUpdate {
|
for iface, dev := range toUpdate {
|
||||||
// for CD-ROMs, the update fully override the existing device, we don't do per-attribute check
|
// for CD-ROMs, the update fully override the existing device, we don't do per-attribute check
|
||||||
err := updateBody.AddCustomStorageDevice(dev.exportToCustomStorageDevice(iface))
|
updateBody.AddCustomStorageDevice(iface, dev.exportToCustomStorageDevice())
|
||||||
if err != nil {
|
|
||||||
diags.AddError(err.Error(), "")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for iface := range toDelete {
|
for iface := range toDelete {
|
||||||
|
@ -11,14 +11,17 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"unicode"
|
|
||||||
|
|
||||||
"github.com/bpg/terraform-provider-proxmox/proxmox/types"
|
"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.
|
// CustomStorageDevice handles QEMU SATA device parameters.
|
||||||
type CustomStorageDevice struct {
|
type CustomStorageDevice struct {
|
||||||
AIO *string `json:"aio,omitempty" url:"aio,omitempty"`
|
AIO *string `json:"aio,omitempty" url:"aio,omitempty"`
|
||||||
@ -44,7 +47,6 @@ type CustomStorageDevice struct {
|
|||||||
DatastoreID *string `json:"-" url:"-"`
|
DatastoreID *string `json:"-" url:"-"`
|
||||||
Enabled bool `json:"-" url:"-"`
|
Enabled bool `json:"-" url:"-"`
|
||||||
FileID *string `json:"-" url:"-"`
|
FileID *string `json:"-" url:"-"`
|
||||||
Interface *string `json:"-" url:"-"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CustomStorageDevices handles map of QEMU storage device per disk interface.
|
// 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))
|
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.
|
// EncodeOptions converts a CustomStorageDevice's common options a URL value.
|
||||||
func (d *CustomStorageDevice) EncodeOptions() string {
|
func (d *CustomStorageDevice) EncodeOptions() string {
|
||||||
values := []string{}
|
values := []string{}
|
||||||
@ -374,13 +363,6 @@ func (d *CustomStorageDevice) UnmarshalJSON(b []byte) error {
|
|||||||
return nil
|
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.
|
// Filter returns a map of CustomStorageDevices filtered by the given function.
|
||||||
func (d CustomStorageDevices) Filter(fn func(*CustomStorageDevice) bool) CustomStorageDevices {
|
func (d CustomStorageDevices) Filter(fn func(*CustomStorageDevice) bool) CustomStorageDevices {
|
||||||
result := make(CustomStorageDevices)
|
result := make(CustomStorageDevices)
|
||||||
@ -406,29 +388,3 @@ func (d CustomStorageDevices) EncodeValues(_ string, v *url.Values) error {
|
|||||||
|
|
||||||
return nil
|
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -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)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -7,9 +7,11 @@
|
|||||||
package vms
|
package vms
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/bpg/terraform-provider-proxmox/proxmox/types"
|
"github.com/bpg/terraform-provider-proxmox/proxmox/types"
|
||||||
@ -60,7 +62,6 @@ type CreateRequestBody struct {
|
|||||||
HookScript *string `json:"hookscript,omitempty" url:"hookscript,omitempty"`
|
HookScript *string `json:"hookscript,omitempty" url:"hookscript,omitempty"`
|
||||||
Hotplug types.CustomCommaSeparatedList `json:"hotplug,omitempty" url:"hotplug,omitempty,comma"`
|
Hotplug types.CustomCommaSeparatedList `json:"hotplug,omitempty" url:"hotplug,omitempty,comma"`
|
||||||
Hugepages *string `json:"hugepages,omitempty" url:"hugepages,omitempty"`
|
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"`
|
KeepHugepages *types.CustomBool `json:"keephugepages,omitempty" url:"keephugepages,omitempty,int"`
|
||||||
KeyboardLayout *string `json:"keyboard,omitempty" url:"keyboard,omitempty"`
|
KeyboardLayout *string `json:"keyboard,omitempty" url:"keyboard,omitempty"`
|
||||||
KVMArguments *string `json:"args,omitempty" url:"args,omitempty,space"`
|
KVMArguments *string `json:"args,omitempty" url:"args,omitempty,space"`
|
||||||
@ -79,8 +80,6 @@ type CreateRequestBody struct {
|
|||||||
PCIDevices CustomPCIDevices `json:"hostpci,omitempty" url:"hostpci,omitempty"`
|
PCIDevices CustomPCIDevices `json:"hostpci,omitempty" url:"hostpci,omitempty"`
|
||||||
PoolID *string `json:"pool,omitempty" url:"pool,omitempty"`
|
PoolID *string `json:"pool,omitempty" url:"pool,omitempty"`
|
||||||
Revert *string `json:"revert,omitempty" url:"revert,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"`
|
SCSIHardware *string `json:"scsihw,omitempty" url:"scsihw,omitempty"`
|
||||||
SerialDevices CustomSerialDevices `json:"serial,omitempty" url:"serial,omitempty"`
|
SerialDevices CustomSerialDevices `json:"serial,omitempty" url:"serial,omitempty"`
|
||||||
SharedMemory *CustomSharedMemory `json:"ivshmem,omitempty" url:"ivshmem,omitempty"`
|
SharedMemory *CustomSharedMemory `json:"ivshmem,omitempty" url:"ivshmem,omitempty"`
|
||||||
@ -98,51 +97,22 @@ type CreateRequestBody struct {
|
|||||||
USBDevices CustomUSBDevices `json:"usb,omitempty" url:"usb,omitempty"`
|
USBDevices CustomUSBDevices `json:"usb,omitempty" url:"usb,omitempty"`
|
||||||
VGADevice *CustomVGADevice `json:"vga,omitempty" url:"vga,omitempty"`
|
VGADevice *CustomVGADevice `json:"vga,omitempty" url:"vga,omitempty"`
|
||||||
VirtualCPUCount *int64 `json:"vcpus,omitempty" url:"vcpus,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"`
|
VMGenerationID *string `json:"vmgenid,omitempty" url:"vmgenid,omitempty"`
|
||||||
VMID int `json:"vmid,omitempty" url:"vmid,omitempty"`
|
VMID int `json:"vmid,omitempty" url:"vmid,omitempty"`
|
||||||
VMStateDatastoreID *string `json:"vmstatestorage,omitempty" url:"vmstatestorage,omitempty"`
|
VMStateDatastoreID *string `json:"vmstatestorage,omitempty" url:"vmstatestorage,omitempty"`
|
||||||
WatchdogDevice *CustomWatchdogDevice `json:"watchdog,omitempty" url:"watchdog,omitempty"`
|
WatchdogDevice *CustomWatchdogDevice `json:"watchdog,omitempty" url:"watchdog,omitempty"`
|
||||||
|
CustomStorageDevices CustomStorageDevices `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddCustomStorageDevice adds a custom storage device to the create request body.
|
// 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
|
device.Enabled = true
|
||||||
|
|
||||||
switch device.StorageInterface() {
|
if b.CustomStorageDevices == nil {
|
||||||
case "ide":
|
b.CustomStorageDevices = make(CustomStorageDevices, 1)
|
||||||
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())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
b.CustomStorageDevices[iface] = &device
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateResponseBody contains the body from a create response.
|
// CreateResponseBody contains the body from a create response.
|
||||||
@ -235,10 +205,6 @@ type GetResponseData struct {
|
|||||||
HookScript *string `json:"hookscript,omitempty"`
|
HookScript *string `json:"hookscript,omitempty"`
|
||||||
Hotplug *types.CustomCommaSeparatedList `json:"hotplug,omitempty"`
|
Hotplug *types.CustomCommaSeparatedList `json:"hotplug,omitempty"`
|
||||||
Hugepages *string `json:"hugepages,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"`
|
IPConfig0 *CustomCloudInitIPConfig `json:"ipconfig0,omitempty"`
|
||||||
IPConfig1 *CustomCloudInitIPConfig `json:"ipconfig1,omitempty"`
|
IPConfig1 *CustomCloudInitIPConfig `json:"ipconfig1,omitempty"`
|
||||||
IPConfig2 *CustomCloudInitIPConfig `json:"ipconfig2,omitempty"`
|
IPConfig2 *CustomCloudInitIPConfig `json:"ipconfig2,omitempty"`
|
||||||
@ -330,26 +296,6 @@ type GetResponseData struct {
|
|||||||
PCIDevice3 *CustomPCIDevice `json:"hostpci3,omitempty"`
|
PCIDevice3 *CustomPCIDevice `json:"hostpci3,omitempty"`
|
||||||
PoolID *string `json:"pool,omitempty"`
|
PoolID *string `json:"pool,omitempty"`
|
||||||
Revert *string `json:"revert,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"`
|
SCSIHardware *string `json:"scsihw,omitempty"`
|
||||||
SerialDevice0 *string `json:"serial0,omitempty"`
|
SerialDevice0 *string `json:"serial0,omitempty"`
|
||||||
SerialDevice1 *string `json:"serial1,omitempty"`
|
SerialDevice1 *string `json:"serial1,omitempty"`
|
||||||
@ -373,25 +319,10 @@ type GetResponseData struct {
|
|||||||
USBDevice3 *CustomUSBDevice `json:"usb3,omitempty"`
|
USBDevice3 *CustomUSBDevice `json:"usb3,omitempty"`
|
||||||
VGADevice *CustomVGADevice `json:"vga,omitempty"`
|
VGADevice *CustomVGADevice `json:"vga,omitempty"`
|
||||||
VirtualCPUCount *int64 `json:"vcpus,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"`
|
VMGenerationID *string `json:"vmgenid,omitempty"`
|
||||||
VMStateDatastoreID *string `json:"vmstatestorage,omitempty"`
|
VMStateDatastoreID *string `json:"vmstatestorage,omitempty"`
|
||||||
WatchdogDevice *CustomWatchdogDevice `json:"watchdog,omitempty"`
|
WatchdogDevice *CustomWatchdogDevice `json:"watchdog,omitempty"`
|
||||||
|
CustomStorageDevices CustomStorageDevices `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetStatusResponseBody contains the body from a VM get status response.
|
// 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.
|
// UpdateRequestBody contains the data for an virtual machine update request.
|
||||||
type UpdateRequestBody = CreateRequestBody
|
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.
|
// 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 {
|
func (b *UpdateRequestBody) ToDelete(fieldName string) error {
|
||||||
if b == nil {
|
if b == nil {
|
||||||
|
64
proxmox/nodes/vms/vms_types_test.go
Normal file
64
proxmox/nodes/vms/vms_types_test.go
Normal 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))
|
||||||
|
}
|
@ -11,6 +11,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
|
||||||
@ -26,56 +27,10 @@ import (
|
|||||||
"github.com/bpg/terraform-provider-proxmox/utils"
|
"github.com/bpg/terraform-provider-proxmox/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
const supportedDiskInterfaces = "virtio, sata, scsi, ide"
|
|
||||||
|
|
||||||
// GetInfo returns the disk information for a VM.
|
// GetInfo returns the disk information for a VM.
|
||||||
// Deprecated: use vms.MapCustomStorageDevices from proxmox/nodes/vms instead.
|
// Deprecated: use vms.MapCustomStorageDevices from proxmox/nodes/vms instead.
|
||||||
func GetInfo(resp *vms.GetResponseData, d *schema.ResourceData) vms.CustomStorageDevices {
|
func GetInfo(resp *vms.GetResponseData, d *schema.ResourceData) vms.CustomStorageDevices {
|
||||||
storageDevices := vms.CustomStorageDevices{}
|
storageDevices := resp.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
|
|
||||||
|
|
||||||
currentDisk := d.Get(MkDisk)
|
currentDisk := d.Get(MkDisk)
|
||||||
|
|
||||||
@ -92,10 +47,6 @@ func GetInfo(resp *vms.GetResponseData, d *schema.ResourceData) vms.CustomStorag
|
|||||||
if v.Size == nil {
|
if v.Size == nil {
|
||||||
v.Size = new(types.DiskSize)
|
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(
|
func CreateClone(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
d *schema.ResourceData,
|
d *schema.ResourceData,
|
||||||
planDisks map[string]vms.CustomStorageDevices,
|
planDisks vms.CustomStorageDevices,
|
||||||
allDiskInfo vms.CustomStorageDevices,
|
allDiskInfo vms.CustomStorageDevices,
|
||||||
vmAPI *vms.Client,
|
vmAPI *vms.Client,
|
||||||
) error {
|
) error {
|
||||||
@ -116,45 +67,18 @@ func CreateClone(
|
|||||||
diskInterface := diskBlock[mkDiskInterface].(string)
|
diskInterface := diskBlock[mkDiskInterface].(string)
|
||||||
dataStoreID := diskBlock[mkDiskDatastoreID].(string)
|
dataStoreID := diskBlock[mkDiskDatastoreID].(string)
|
||||||
diskSize := int64(diskBlock[mkDiskSize].(int))
|
diskSize := int64(diskBlock[mkDiskSize].(int))
|
||||||
prefix := DigitPrefix(diskInterface)
|
|
||||||
|
|
||||||
currentDiskInfo := allDiskInfo[diskInterface]
|
currentDiskInfo := allDiskInfo[diskInterface]
|
||||||
configuredDiskInfo := planDisks[prefix][diskInterface]
|
configuredDiskInfo := planDisks[diskInterface]
|
||||||
|
|
||||||
if currentDiskInfo == nil {
|
if currentDiskInfo == nil {
|
||||||
diskUpdateBody := &vms.UpdateRequestBody{}
|
diskUpdateBody := &vms.UpdateRequestBody{}
|
||||||
|
|
||||||
switch prefix {
|
diskUpdateBody.AddCustomStorageDevice(diskInterface, *configuredDiskInfo)
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
err := vmAPI.UpdateVM(ctx, diskUpdateBody)
|
err := vmAPI.UpdateVM(ctx, diskUpdateBody)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("disk create fails: %w", err)
|
return fmt.Errorf("disk update fails: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
continue
|
continue
|
||||||
@ -225,7 +149,7 @@ func GetDiskDeviceObjects(
|
|||||||
d *schema.ResourceData,
|
d *schema.ResourceData,
|
||||||
resource *schema.Resource,
|
resource *schema.Resource,
|
||||||
disks []interface{},
|
disks []interface{},
|
||||||
) (map[string]vms.CustomStorageDevices, error) {
|
) (vms.CustomStorageDevices, error) {
|
||||||
var diskDevices []interface{}
|
var diskDevices []interface{}
|
||||||
|
|
||||||
if disks != nil {
|
if disks != nil {
|
||||||
@ -234,7 +158,7 @@ func GetDiskDeviceObjects(
|
|||||||
diskDevices = d.Get(MkDisk).([]interface{})
|
diskDevices = d.Get(MkDisk).([]interface{})
|
||||||
}
|
}
|
||||||
|
|
||||||
diskDeviceObjects := map[string]vms.CustomStorageDevices{}
|
diskDeviceObjects := vms.CustomStorageDevices{}
|
||||||
|
|
||||||
for _, diskEntry := range diskDevices {
|
for _, diskEntry := range diskDevices {
|
||||||
diskDevice := &vms.CustomStorageDevice{
|
diskDevice := &vms.CustomStorageDevice{
|
||||||
@ -300,7 +224,6 @@ func GetDiskDeviceObjects(
|
|||||||
diskDevice.Discard = &discard
|
diskDevice.Discard = &discard
|
||||||
diskDevice.FileID = &fileID
|
diskDevice.FileID = &fileID
|
||||||
diskDevice.Format = &fileFormat
|
diskDevice.Format = &fileFormat
|
||||||
diskDevice.Interface = &diskInterface
|
|
||||||
diskDevice.Replicate = &replicate
|
diskDevice.Replicate = &replicate
|
||||||
diskDevice.Serial = &serial
|
diskDevice.Serial = &serial
|
||||||
diskDevice.Size = types.DiskSizeFromGigabytes(int64(size))
|
diskDevice.Size = types.DiskSizeFromGigabytes(int64(size))
|
||||||
@ -356,21 +279,16 @@ func GetDiskDeviceObjects(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
baseDiskInterface := DigitPrefix(diskInterface)
|
if !slices.Contains(vms.StorageInterfaces, DigitPrefix(diskInterface)) {
|
||||||
if !strings.Contains(supportedDiskInterfaces, baseDiskInterface) {
|
|
||||||
errorMsg := fmt.Sprintf(
|
errorMsg := fmt.Sprintf(
|
||||||
"Defined disk interface not supported. Interface was %s, but only %s are supported",
|
"Defined disk interface not supported. Interface was %s, but only %v are supported",
|
||||||
diskInterface, supportedDiskInterfaces,
|
diskInterface, vms.StorageInterfaces,
|
||||||
)
|
)
|
||||||
|
|
||||||
return diskDeviceObjects, errors.New(errorMsg)
|
return diskDeviceObjects, errors.New(errorMsg)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, present := diskDeviceObjects[baseDiskInterface]; !present {
|
diskDeviceObjects[diskInterface] = diskDevice
|
||||||
diskDeviceObjects[baseDiskInterface] = vms.CustomStorageDevices{}
|
|
||||||
}
|
|
||||||
|
|
||||||
diskDeviceObjects[baseDiskInterface][diskInterface] = diskDevice
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return diskDeviceObjects, nil
|
return diskDeviceObjects, nil
|
||||||
@ -382,16 +300,14 @@ func CreateCustomDisks(
|
|||||||
client proxmox.Client,
|
client proxmox.Client,
|
||||||
nodeName string,
|
nodeName string,
|
||||||
vmID int,
|
vmID int,
|
||||||
storageDevices map[string]vms.CustomStorageDevices,
|
storageDevices vms.CustomStorageDevices,
|
||||||
) diag.Diagnostics {
|
) diag.Diagnostics {
|
||||||
for _, diskDevice := range storageDevices {
|
for iface, disk := range storageDevices {
|
||||||
for _, disk := range diskDevice {
|
if disk != nil && disk.FileID != nil && *disk.FileID != "" {
|
||||||
if disk != nil && disk.FileID != nil && *disk.FileID != "" {
|
// only custom disks with defined file ID
|
||||||
// only custom disks with defined file ID
|
err := createCustomDisk(ctx, client, nodeName, vmID, iface, *disk)
|
||||||
err := createCustomDisk(ctx, client, nodeName, vmID, *disk)
|
if err != nil {
|
||||||
if err != nil {
|
return diag.FromErr(err)
|
||||||
return diag.FromErr(err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -404,23 +320,24 @@ func createCustomDisk(
|
|||||||
client proxmox.Client,
|
client proxmox.Client,
|
||||||
nodeName string,
|
nodeName string,
|
||||||
vmID int,
|
vmID int,
|
||||||
d vms.CustomStorageDevice,
|
iface string,
|
||||||
|
disk vms.CustomStorageDevice,
|
||||||
) error {
|
) error {
|
||||||
fileFormat := dvDiskFileFormat
|
fileFormat := dvDiskFileFormat
|
||||||
if d.Format != nil && *d.Format != "" {
|
if disk.Format != nil && *disk.Format != "" {
|
||||||
fileFormat = *d.Format
|
fileFormat = *disk.Format
|
||||||
}
|
}
|
||||||
|
|
||||||
//nolint:lll
|
//nolint:lll
|
||||||
commands := []string{
|
commands := []string{
|
||||||
`set -e`,
|
`set -e`,
|
||||||
ssh.TrySudo,
|
ssh.TrySudo,
|
||||||
fmt.Sprintf(`file_id="%s"`, *d.FileID),
|
fmt.Sprintf(`file_id="%s"`, *disk.FileID),
|
||||||
fmt.Sprintf(`file_format="%s"`, fileFormat),
|
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(`vm_id="%d"`, vmID),
|
||||||
fmt.Sprintf(`disk_options="%s"`, d.EncodeOptions()),
|
fmt.Sprintf(`disk_options="%s"`, disk.EncodeOptions()),
|
||||||
fmt.Sprintf(`disk_interface="%s"`, *d.Interface),
|
fmt.Sprintf(`disk_interface="%s"`, iface),
|
||||||
`source_image=$(try_sudo "pvesm path $file_id")`,
|
`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)"`,
|
`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}"`,
|
`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{
|
err = client.Node(nodeName).VM(vmID).ResizeVMDisk(ctx, &vms.ResizeDiskRequestBody{
|
||||||
Disk: *d.Interface,
|
Disk: iface,
|
||||||
Size: *d.Size,
|
Size: *disk.Size,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("resizing disk: %w", err)
|
return fmt.Errorf("resizing disk: %w", err)
|
||||||
@ -651,103 +568,62 @@ func Update(
|
|||||||
nodeName string,
|
nodeName string,
|
||||||
vmID int,
|
vmID int,
|
||||||
d *schema.ResourceData,
|
d *schema.ResourceData,
|
||||||
planDisks map[string]vms.CustomStorageDevices,
|
planDisks vms.CustomStorageDevices,
|
||||||
currentDisks vms.CustomStorageDevices,
|
currentDisks vms.CustomStorageDevices,
|
||||||
updateBody *vms.UpdateRequestBody,
|
updateBody *vms.UpdateRequestBody,
|
||||||
) (bool, error) {
|
) (bool, error) {
|
||||||
rebootRequired := false
|
rebootRequired := false
|
||||||
|
|
||||||
if d.HasChange(MkDisk) {
|
if d.HasChange(MkDisk) {
|
||||||
for prefix, diskMap := range planDisks {
|
for iface, disk := range planDisks {
|
||||||
if diskMap == nil {
|
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
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
for key, disk := range diskMap {
|
if tmp.AIO != disk.AIO {
|
||||||
var tmp *vms.CustomStorageDevice
|
rebootRequired = true
|
||||||
|
tmp.AIO = disk.AIO
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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`.
|
// 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 {
|
func findExistingCloudInitDrive(vmConfig *vms.GetResponseData, vmID int, defaultValue string) string {
|
||||||
ideDevices := []*vms.CustomStorageDevice{
|
devs := vmConfig.CustomStorageDevices.Filter(func(device *vms.CustomStorageDevice) bool {
|
||||||
vmConfig.IDEDevice0,
|
return device.IsCloudInitDrive(vmID)
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sataDevices := []*vms.CustomStorageDevice{
|
for iface := range devs {
|
||||||
vmConfig.SATADevice0,
|
return iface
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return defaultValue
|
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
|
// 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.
|
// valid ide, sata, or scsi interface name.
|
||||||
func getStorageDevice(vmConfig *vms.GetResponseData, deviceName string) *vms.CustomStorageDevice {
|
func getStorageDevice(vmConfig *vms.GetResponseData, deviceName string) *vms.CustomStorageDevice {
|
||||||
switch deviceName {
|
if dev, ok := vmConfig.CustomStorageDevices[deviceName]; ok {
|
||||||
case "ide0":
|
return dev
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete IDE interfaces that can then be used for CloudInit. The first interface will always
|
// 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 {
|
if len(cdrom) > 0 || len(initialization) > 0 {
|
||||||
updateBody.IDEDevices = ideDevices
|
for iface, dev := range ideDevices {
|
||||||
|
updateBody.AddCustomStorageDevice(iface, *dev)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if keyboardLayout != dvKeyboardLayout {
|
if keyboardLayout != dvKeyboardLayout {
|
||||||
@ -2564,11 +2476,6 @@ func vmCreateCustom(ctx context.Context, d *schema.ResourceData, m interface{})
|
|||||||
return diag.FromErr(err)
|
return diag.FromErr(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
virtioDeviceObjects := diskDeviceObjects["virtio"]
|
|
||||||
scsiDeviceObjects := diskDeviceObjects["scsi"]
|
|
||||||
ideDeviceObjects := diskDeviceObjects["ide"]
|
|
||||||
sataDeviceObjects := diskDeviceObjects["sata"]
|
|
||||||
|
|
||||||
var bootOrderConverted []string
|
var bootOrderConverted []string
|
||||||
if cdromEnabled {
|
if cdromEnabled {
|
||||||
bootOrderConverted = []string{cdromInterface}
|
bootOrderConverted = []string{cdromInterface}
|
||||||
@ -2577,19 +2484,19 @@ func vmCreateCustom(ctx context.Context, d *schema.ResourceData, m interface{})
|
|||||||
bootOrder := d.Get(mkBootOrder).([]interface{})
|
bootOrder := d.Get(mkBootOrder).([]interface{})
|
||||||
|
|
||||||
if len(bootOrder) == 0 {
|
if len(bootOrder) == 0 {
|
||||||
if ideDeviceObjects != nil {
|
if _, ok := diskDeviceObjects["ide0"]; ok {
|
||||||
bootOrderConverted = append(bootOrderConverted, "ide0")
|
bootOrderConverted = append(bootOrderConverted, "ide0")
|
||||||
}
|
}
|
||||||
|
|
||||||
if sataDeviceObjects != nil {
|
if _, ok := diskDeviceObjects["sata0"]; ok {
|
||||||
bootOrderConverted = append(bootOrderConverted, "sata0")
|
bootOrderConverted = append(bootOrderConverted, "sata0")
|
||||||
}
|
}
|
||||||
|
|
||||||
if scsiDeviceObjects != nil {
|
if _, ok := diskDeviceObjects["scsi0"]; ok {
|
||||||
bootOrderConverted = append(bootOrderConverted, "scsi0")
|
bootOrderConverted = append(bootOrderConverted, "scsi0")
|
||||||
}
|
}
|
||||||
|
|
||||||
if virtioDeviceObjects != nil {
|
if _, ok := diskDeviceObjects["virtio0"]; ok {
|
||||||
bootOrderConverted = append(bootOrderConverted, "virtio0")
|
bootOrderConverted = append(bootOrderConverted, "virtio0")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2609,10 +2516,9 @@ func vmCreateCustom(ctx context.Context, d *schema.ResourceData, m interface{})
|
|||||||
}
|
}
|
||||||
|
|
||||||
ideDevice2Media := "cdrom"
|
ideDevice2Media := "cdrom"
|
||||||
ideDevices := vms.CustomStorageDevices{}
|
|
||||||
|
|
||||||
if cdromCloudInitInterface != "" {
|
if cdromCloudInitInterface != "" {
|
||||||
ideDevices[cdromCloudInitInterface] = &vms.CustomStorageDevice{
|
diskDeviceObjects[cdromCloudInitInterface] = &vms.CustomStorageDevice{
|
||||||
Enabled: cdromCloudInitEnabled,
|
Enabled: cdromCloudInitEnabled,
|
||||||
FileVolume: cdromCloudInitFileID,
|
FileVolume: cdromCloudInitFileID,
|
||||||
Media: &ideDevice2Media,
|
Media: &ideDevice2Media,
|
||||||
@ -2620,7 +2526,7 @@ func vmCreateCustom(ctx context.Context, d *schema.ResourceData, m interface{})
|
|||||||
}
|
}
|
||||||
|
|
||||||
if cdromInterface != "" {
|
if cdromInterface != "" {
|
||||||
ideDevices[cdromInterface] = &vms.CustomStorageDevice{
|
diskDeviceObjects[cdromInterface] = &vms.CustomStorageDevice{
|
||||||
Enabled: cdromEnabled,
|
Enabled: cdromEnabled,
|
||||||
FileVolume: cdromFileID,
|
FileVolume: cdromFileID,
|
||||||
Media: &ideDevice2Media,
|
Media: &ideDevice2Media,
|
||||||
@ -2657,47 +2563,31 @@ func vmCreateCustom(ctx context.Context, d *schema.ResourceData, m interface{})
|
|||||||
Flags: &cpuFlagsConverted,
|
Flags: &cpuFlagsConverted,
|
||||||
Type: cpuType,
|
Type: cpuType,
|
||||||
},
|
},
|
||||||
CPUSockets: ptr.Ptr(int64(cpuSockets)),
|
CPUSockets: ptr.Ptr(int64(cpuSockets)),
|
||||||
CPUUnits: ptr.Ptr(int64(cpuUnits)),
|
CPUUnits: ptr.Ptr(int64(cpuUnits)),
|
||||||
DedicatedMemory: &memoryDedicated,
|
DedicatedMemory: &memoryDedicated,
|
||||||
DeletionProtection: &protection,
|
DeletionProtection: &protection,
|
||||||
EFIDisk: efiDisk,
|
EFIDisk: efiDisk,
|
||||||
TPMState: tpmState,
|
TPMState: tpmState,
|
||||||
FloatingMemory: &memoryFloating,
|
FloatingMemory: &memoryFloating,
|
||||||
IDEDevices: ideDevices,
|
KeyboardLayout: &keyboardLayout,
|
||||||
KeyboardLayout: &keyboardLayout,
|
NetworkDevices: networkDeviceObjects,
|
||||||
NetworkDevices: networkDeviceObjects,
|
NUMAEnabled: &cpuNUMA,
|
||||||
NUMAEnabled: &cpuNUMA,
|
NUMADevices: numaDeviceObjects,
|
||||||
NUMADevices: numaDeviceObjects,
|
OSType: &operatingSystemType,
|
||||||
OSType: &operatingSystemType,
|
PCIDevices: pciDeviceObjects,
|
||||||
PCIDevices: pciDeviceObjects,
|
SCSIHardware: &scsiHardware,
|
||||||
SCSIHardware: &scsiHardware,
|
SerialDevices: serialDevices,
|
||||||
SerialDevices: serialDevices,
|
SharedMemory: memorySharedObject,
|
||||||
SharedMemory: memorySharedObject,
|
StartOnBoot: &onBoot,
|
||||||
StartOnBoot: &onBoot,
|
SMBIOS: smbios,
|
||||||
SMBIOS: smbios,
|
StartupOrder: startupOrder,
|
||||||
StartupOrder: startupOrder,
|
TabletDeviceEnabled: &tabletDevice,
|
||||||
TabletDeviceEnabled: &tabletDevice,
|
Template: &template,
|
||||||
Template: &template,
|
USBDevices: usbDeviceObjects,
|
||||||
USBDevices: usbDeviceObjects,
|
VGADevice: vgaDevice,
|
||||||
VGADevice: vgaDevice,
|
VMID: vmID,
|
||||||
VMID: vmID,
|
CustomStorageDevices: diskDeviceObjects,
|
||||||
}
|
|
||||||
|
|
||||||
if ideDeviceObjects != nil {
|
|
||||||
createBody.IDEDevices = ideDeviceObjects
|
|
||||||
}
|
|
||||||
|
|
||||||
if sataDeviceObjects != nil {
|
|
||||||
createBody.SATADevices = sataDeviceObjects
|
|
||||||
}
|
|
||||||
|
|
||||||
if scsiDeviceObjects != nil {
|
|
||||||
createBody.SCSIDevices = scsiDeviceObjects
|
|
||||||
}
|
|
||||||
|
|
||||||
if virtioDeviceObjects != nil {
|
|
||||||
createBody.VirtualIODevices = virtioDeviceObjects
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only the root account is allowed to change the CPU architecture, which makes this check necessary.
|
// 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 {
|
if efiDisk != nil {
|
||||||
id := "0"
|
id := "0"
|
||||||
baseDiskInterface := "efidisk"
|
|
||||||
diskInterface := fmt.Sprint(baseDiskInterface, id)
|
|
||||||
|
|
||||||
storageDevice = &vms.CustomStorageDevice{
|
storageDevice = &vms.CustomStorageDevice{
|
||||||
Enabled: true,
|
Enabled: true,
|
||||||
FileVolume: efiDisk.FileVolume,
|
FileVolume: efiDisk.FileVolume,
|
||||||
Format: efiDisk.Format,
|
Format: efiDisk.Format,
|
||||||
Interface: &diskInterface,
|
|
||||||
DatastoreID: &id,
|
DatastoreID: &id,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3075,13 +2962,9 @@ func vmGetTPMStateAsStorageDevice(d *schema.ResourceData, disk []interface{}) *v
|
|||||||
|
|
||||||
if tpmState != nil {
|
if tpmState != nil {
|
||||||
id := "0"
|
id := "0"
|
||||||
baseDiskInterface := "tpmstate"
|
|
||||||
diskInterface := fmt.Sprint(baseDiskInterface, id)
|
|
||||||
|
|
||||||
storageDevice = &vms.CustomStorageDevice{
|
storageDevice = &vms.CustomStorageDevice{
|
||||||
Enabled: true,
|
Enabled: true,
|
||||||
FileVolume: tpmState.FileVolume,
|
FileVolume: tpmState.FileVolume,
|
||||||
Interface: &diskInterface,
|
|
||||||
DatastoreID: &id,
|
DatastoreID: &id,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4757,22 +4640,7 @@ func vmUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.D
|
|||||||
|
|
||||||
vmAPI := client.Node(nodeName).VM(vmID)
|
vmAPI := client.Node(nodeName).VM(vmID)
|
||||||
|
|
||||||
updateBody := &vms.UpdateRequestBody{
|
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,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
var del []string
|
var del []string
|
||||||
|
|
||||||
@ -4961,11 +4829,11 @@ func vmUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.D
|
|||||||
|
|
||||||
cdromMedia := "cdrom"
|
cdromMedia := "cdrom"
|
||||||
|
|
||||||
updateBody.IDEDevices[cdromInterface] = &vms.CustomStorageDevice{
|
updateBody.AddCustomStorageDevice(cdromInterface, vms.CustomStorageDevice{
|
||||||
Enabled: cdromEnabled,
|
Enabled: cdromEnabled,
|
||||||
FileVolume: cdromFileID,
|
FileVolume: cdromFileID,
|
||||||
Media: &cdromMedia,
|
Media: &cdromMedia,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare the new CPU configuration.
|
// Prepare the new CPU configuration.
|
||||||
@ -5132,11 +5000,11 @@ func vmUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.D
|
|||||||
fileVolume = ideDevice.FileVolume
|
fileVolume = ideDevice.FileVolume
|
||||||
}
|
}
|
||||||
|
|
||||||
updateBody.IDEDevices[initializationInterface] = &vms.CustomStorageDevice{
|
updateBody.AddCustomStorageDevice(initializationInterface, vms.CustomStorageDevice{
|
||||||
Enabled: true,
|
Enabled: true,
|
||||||
FileVolume: fileVolume,
|
FileVolume: fileVolume,
|
||||||
Media: &cdromMedia,
|
Media: &cdromMedia,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
rebootRequired = true
|
rebootRequired = true
|
||||||
@ -5416,13 +5284,11 @@ func vmUpdateDiskLocationAndSize(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if oldEfiDisk != nil {
|
if oldEfiDisk != nil {
|
||||||
baseDiskInterface := disk.DigitPrefix(*oldEfiDisk.Interface)
|
diskOldEntries["efidisk0"] = oldEfiDisk
|
||||||
diskOldEntries[baseDiskInterface][*oldEfiDisk.Interface] = oldEfiDisk
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if newEfiDisk != nil {
|
if newEfiDisk != nil {
|
||||||
baseDiskInterface := disk.DigitPrefix(*newEfiDisk.Interface)
|
diskNewEntries["efidisk0"] = newEfiDisk
|
||||||
diskNewEntries[baseDiskInterface][*newEfiDisk.Interface] = newEfiDisk
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if oldEfiDisk != nil && newEfiDisk != nil && oldEfiDisk.Size != newEfiDisk.Size {
|
if oldEfiDisk != nil && newEfiDisk != nil && oldEfiDisk.Size != newEfiDisk.Size {
|
||||||
@ -5440,13 +5306,11 @@ func vmUpdateDiskLocationAndSize(
|
|||||||
newTPMState := vmGetTPMStateAsStorageDevice(d, diskNew.([]interface{}))
|
newTPMState := vmGetTPMStateAsStorageDevice(d, diskNew.([]interface{}))
|
||||||
|
|
||||||
if oldTPMState != nil {
|
if oldTPMState != nil {
|
||||||
baseDiskInterface := disk.DigitPrefix(*oldTPMState.Interface)
|
diskOldEntries["tpmstate0"] = oldTPMState
|
||||||
diskOldEntries[baseDiskInterface][*oldTPMState.Interface] = oldTPMState
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if newTPMState != nil {
|
if newTPMState != nil {
|
||||||
baseDiskInterface := disk.DigitPrefix(*newTPMState.Interface)
|
diskNewEntries["tpmstate0"] = newTPMState
|
||||||
diskNewEntries[baseDiskInterface][*newTPMState.Interface] = newTPMState
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if oldTPMState != nil && newTPMState != nil && oldTPMState.Size != newTPMState.Size {
|
if oldTPMState != nil && newTPMState != nil && oldTPMState.Size != newTPMState.Size {
|
||||||
@ -5462,58 +5326,56 @@ func vmUpdateDiskLocationAndSize(
|
|||||||
|
|
||||||
shutdownForDisksRequired := false
|
shutdownForDisksRequired := false
|
||||||
|
|
||||||
for prefix, diskMap := range diskOldEntries {
|
for oldIface, oldDisk := range diskOldEntries {
|
||||||
for oldKey, oldDisk := range diskMap {
|
if _, present := diskNewEntries[oldIface]; !present {
|
||||||
if _, present := diskNewEntries[prefix][oldKey]; !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(
|
return diag.Errorf(
|
||||||
"deletion of disks not supported. Please delete disk by hand. Old interface was %q",
|
"Cannot move %s:%s to datastore %s in VM %d configuration, it is not owned by this VM!",
|
||||||
*oldDisk.Interface,
|
*oldDisk.DatastoreID,
|
||||||
|
*oldDisk.PathInDatastore(),
|
||||||
|
*diskNewEntries[oldIface].DatastoreID,
|
||||||
|
vmID,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if *oldDisk.DatastoreID != *diskNewEntries[prefix][oldKey].DatastoreID {
|
if *oldDisk.Size < *diskNewEntries[oldIface].Size {
|
||||||
if oldDisk.IsOwnedBy(vmID) {
|
if oldDisk.IsOwnedBy(vmID) {
|
||||||
deleteOriginalDisk := types.CustomBool(true)
|
diskResizeBodies = append(
|
||||||
|
diskResizeBodies,
|
||||||
diskMoveBodies = append(
|
&vms.ResizeDiskRequestBody{
|
||||||
diskMoveBodies,
|
Disk: oldIface,
|
||||||
&vms.MoveDiskRequestBody{
|
Size: *diskNewEntries[oldIface].Size,
|
||||||
DeleteOriginalDisk: &deleteOriginalDisk,
|
},
|
||||||
Disk: *oldDisk.Interface,
|
)
|
||||||
TargetStorage: *diskNewEntries[prefix][oldKey].DatastoreID,
|
} else {
|
||||||
},
|
return diag.Errorf(
|
||||||
)
|
"Cannot resize %s:%s in VM %d configuration, it is not owned by this VM!",
|
||||||
|
*oldDisk.DatastoreID,
|
||||||
// Cannot be done while VM is running.
|
*oldDisk.PathInDatastore(),
|
||||||
shutdownForDisksRequired = true
|
vmID,
|
||||||
} 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,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
9
testacc
9
testacc
@ -1,3 +1,10 @@
|
|||||||
#!/bin/sh
|
#!/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
|
||||||
|
Loading…
Reference in New Issue
Block a user