mirror of
https://github.com/bpg/terraform-provider-proxmox.git
synced 2025-08-23 03:48:35 +00:00
feat(vm): efi disk, cpu numa (#384)
* efi disks support * make size a string * add cpu numa support * add docs * fix description * string size fixes (cherry picked from commit 254724d1d10c5191d294f0377adac49ad41dc880) * fix lints * fix(vm): replace `size` with `type` for efi disk also - add support for `pre-enrolled-keys` - add example - fix handling of `numa` flag when it's not in the template - cleanup linter errors --------- Co-authored-by: Pavel Boldyrev <627562+bpg@users.noreply.github.com>
This commit is contained in:
parent
23a519475d
commit
e9a74e9037
@ -165,6 +165,7 @@ output "ubuntu_vm_public_key" {
|
|||||||
protection for AMD models.
|
protection for AMD models.
|
||||||
- `hotplugged` - (Optional) The number of hotplugged vCPUs (defaults
|
- `hotplugged` - (Optional) The number of hotplugged vCPUs (defaults
|
||||||
to `0`).
|
to `0`).
|
||||||
|
- `numa` - (Boolean) Enable/disable NUMA. (default to `false`)
|
||||||
- `sockets` - (Optional) The number of CPU sockets (defaults to `1`).
|
- `sockets` - (Optional) The number of CPU sockets (defaults to `1`).
|
||||||
- `type` - (Optional) The emulated CPU type (defaults to `qemu64`).
|
- `type` - (Optional) The emulated CPU type (defaults to `qemu64`).
|
||||||
- `486` - Intel 486.
|
- `486` - Intel 486.
|
||||||
@ -241,6 +242,19 @@ output "ubuntu_vm_public_key" {
|
|||||||
- `ssd` - (Optional) Whether to use an SSD emulation option for this disk (
|
- `ssd` - (Optional) Whether to use an SSD emulation option for this disk (
|
||||||
defaults to `false`). Note that SSD emulation is not supported on VirtIO
|
defaults to `false`). Note that SSD emulation is not supported on VirtIO
|
||||||
Block drives.
|
Block drives.
|
||||||
|
- `efi_disk` - (Optional) The efi disk device (required if `bios` is set
|
||||||
|
to `ovmf`)
|
||||||
|
- `datastore_id` (Optional) The identifier for the datastore to create
|
||||||
|
the disk in (defaults to `local-lvm`).
|
||||||
|
- `file_format` (Optional) The file format.
|
||||||
|
- `type` (Optional) Size and type of the OVMF EFI disk. `4m` is newer and
|
||||||
|
recommended, and required for Secure Boot. For backwards compatibility
|
||||||
|
use `2m`. Ignored for VMs with cpu.architecture=`aarch64` (defaults
|
||||||
|
to `2m`).
|
||||||
|
- `pre_enrolled_keys` (Optional) Use am EFI vars template with
|
||||||
|
distribution-specific and Microsoft Standard keys enrolled, if used with
|
||||||
|
EFI type=`4m`. Ignored for VMs with cpu.architecture=`aarch64` (defaults
|
||||||
|
to `false`).
|
||||||
- `hostpci` - (Optional) A host PCI device mapping (multiple blocks supported).
|
- `hostpci` - (Optional) A host PCI device mapping (multiple blocks supported).
|
||||||
- `device` - (Required) The PCI device name for Proxmox, in form
|
- `device` - (Required) The PCI device name for Proxmox, in form
|
||||||
of `hostpciX` where `X` is a sequential number from 0 to 3.
|
of `hostpciX` where `X` is a sequential number from 0 to 3.
|
||||||
|
@ -9,6 +9,16 @@ resource "proxmox_virtual_environment_vm" "example_template" {
|
|||||||
|
|
||||||
description = "Managed by Terraform"
|
description = "Managed by Terraform"
|
||||||
|
|
||||||
|
cpu {
|
||||||
|
numa = true
|
||||||
|
}
|
||||||
|
|
||||||
|
efi_disk {
|
||||||
|
datastore_id = local.datastore_id
|
||||||
|
file_format = "raw"
|
||||||
|
type = "4m"
|
||||||
|
}
|
||||||
|
|
||||||
# disk {
|
# disk {
|
||||||
# datastore_id = local.datastore_id
|
# datastore_id = local.datastore_id
|
||||||
# file_id = proxmox_virtual_environment_file.ubuntu_cloud_image.id
|
# file_id = proxmox_virtual_environment_file.ubuntu_cloud_image.id
|
||||||
|
@ -82,9 +82,10 @@ type CustomCPUEmulation struct {
|
|||||||
|
|
||||||
// CustomEFIDisk handles QEMU EFI disk parameters.
|
// CustomEFIDisk handles QEMU EFI disk parameters.
|
||||||
type CustomEFIDisk struct {
|
type CustomEFIDisk struct {
|
||||||
Size *types.DiskSize `json:"size,omitempty" url:"size,omitempty"`
|
FileVolume string `json:"file" url:"file"`
|
||||||
FileVolume string `json:"file" url:"file"`
|
Format *string `json:"format,omitempty" url:"format,omitempty"`
|
||||||
Format *string `json:"format,omitempty" url:"format,omitempty"`
|
Type *string `json:"efitype,omitempty" url:"efitype,omitempty"`
|
||||||
|
PreEnrolledKeys *types2.CustomBool `json:"pre-enrolled-keys,omitempty" url:"pre-enrolled-keys,omitempty,int"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// CustomNetworkDevice handles QEMU network device parameters.
|
// CustomNetworkDevice handles QEMU network device parameters.
|
||||||
@ -784,8 +785,16 @@ func (r CustomEFIDisk) EncodeValues(key string, v *url.Values) error {
|
|||||||
values = append(values, fmt.Sprintf("format=%s", *r.Format))
|
values = append(values, fmt.Sprintf("format=%s", *r.Format))
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.Size != nil {
|
if r.Type != nil {
|
||||||
values = append(values, fmt.Sprintf("size=%s", *r.Size))
|
values = append(values, fmt.Sprintf("efitype=%s", *r.Type))
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.PreEnrolledKeys != nil {
|
||||||
|
if *r.PreEnrolledKeys {
|
||||||
|
values = append(values, "pre-enrolled-keys=1")
|
||||||
|
} else {
|
||||||
|
values = append(values, "pre-enrolled-keys=0")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
v.Add(key, strings.Join(values, ","))
|
v.Add(key, strings.Join(values, ","))
|
||||||
@ -1472,22 +1481,25 @@ func (r *CustomEFIDisk) UnmarshalJSON(b []byte) error {
|
|||||||
|
|
||||||
pairs := strings.Split(s, ",")
|
pairs := strings.Split(s, ",")
|
||||||
|
|
||||||
for _, p := range pairs {
|
for i, p := range pairs {
|
||||||
v := strings.Split(strings.TrimSpace(p), "=")
|
v := strings.Split(strings.TrimSpace(p), "=")
|
||||||
|
|
||||||
|
if len(v) == 1 && i == 0 {
|
||||||
|
r.FileVolume = v[0]
|
||||||
|
}
|
||||||
|
|
||||||
if len(v) == 2 {
|
if len(v) == 2 {
|
||||||
switch v[0] {
|
switch v[0] {
|
||||||
case "format":
|
|
||||||
r.Format = &v[1]
|
|
||||||
case "file":
|
case "file":
|
||||||
r.FileVolume = v[1]
|
r.FileVolume = v[1]
|
||||||
case "size":
|
case "format":
|
||||||
r.Size = new(types.DiskSize)
|
r.Format = &v[1]
|
||||||
|
case "efitype":
|
||||||
err := r.Size.UnmarshalJSON([]byte(v[1]))
|
t := strings.ToLower(v[1])
|
||||||
if err != nil {
|
r.Type = &t
|
||||||
return fmt.Errorf("failed to unmarshal disk size: %w", err)
|
case "pre-enrolled-keys":
|
||||||
}
|
bv := types2.CustomBool(v[1] == "1")
|
||||||
|
r.PreEnrolledKeys = &bv
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,12 @@ type DiskSize int64
|
|||||||
|
|
||||||
// String returns the string representation of the disk size.
|
// String returns the string representation of the disk size.
|
||||||
func (r DiskSize) String() string {
|
func (r DiskSize) String() string {
|
||||||
return formatDiskSize(int64(r))
|
return FormatDiskSize(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// InMegabytes returns the disk size in megabytes.
|
||||||
|
func (r DiskSize) InMegabytes() int {
|
||||||
|
return int(int64(r) / 1024 / 1024)
|
||||||
}
|
}
|
||||||
|
|
||||||
// InGigabytes returns the disk size in gigabytes.
|
// InGigabytes returns the disk size in gigabytes.
|
||||||
@ -39,7 +44,7 @@ func DiskSizeFromGigabytes(size int) DiskSize {
|
|||||||
|
|
||||||
// MarshalJSON marshals a disk size into a Proxmox API `<DiskSize>` string.
|
// MarshalJSON marshals a disk size into a Proxmox API `<DiskSize>` string.
|
||||||
func (r DiskSize) MarshalJSON() ([]byte, error) {
|
func (r DiskSize) MarshalJSON() ([]byte, error) {
|
||||||
bytes, err := json.Marshal(formatDiskSize(int64(r)))
|
bytes, err := json.Marshal(FormatDiskSize(r))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("cannot marshal disk size: %w", err)
|
return nil, fmt.Errorf("cannot marshal disk size: %w", err)
|
||||||
}
|
}
|
||||||
@ -51,27 +56,23 @@ func (r DiskSize) MarshalJSON() ([]byte, error) {
|
|||||||
func (r *DiskSize) UnmarshalJSON(b []byte) error {
|
func (r *DiskSize) UnmarshalJSON(b []byte) error {
|
||||||
s := string(b)
|
s := string(b)
|
||||||
|
|
||||||
size, err := parseDiskSize(&s)
|
size, err := ParseDiskSize(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
*r = DiskSize(size)
|
*r = size
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseDiskSize parses a disk size string into a number of bytes.
|
// ParseDiskSize parses a disk size string into a number of bytes.
|
||||||
func parseDiskSize(size *string) (int64, error) {
|
func ParseDiskSize(size string) (DiskSize, error) {
|
||||||
if size == nil {
|
matches := sizeRegex.FindStringSubmatch(size)
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
matches := sizeRegex.FindStringSubmatch(*size)
|
|
||||||
if len(matches) > 0 {
|
if len(matches) > 0 {
|
||||||
fsize, err := strconv.ParseFloat(matches[1], 64)
|
fsize, err := strconv.ParseFloat(matches[1], 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return -1, fmt.Errorf("cannot parse disk size \"%s\": %w", *size, err)
|
return -1, fmt.Errorf("cannot parse disk size \"%s\": %w", size, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
switch strings.ToLower(matches[3]) {
|
switch strings.ToLower(matches[3]) {
|
||||||
@ -85,13 +86,14 @@ func parseDiskSize(size *string) (int64, error) {
|
|||||||
fsize = fsize * 1024 * 1024 * 1024 * 1024
|
fsize = fsize * 1024 * 1024 * 1024 * 1024
|
||||||
}
|
}
|
||||||
|
|
||||||
return int64(math.Ceil(fsize)), nil
|
return DiskSize(math.Ceil(fsize)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1, fmt.Errorf("cannot parse disk size \"%s\"", *size)
|
return -1, fmt.Errorf("cannot parse disk size \"%s\"", size)
|
||||||
}
|
}
|
||||||
|
|
||||||
func formatDiskSize(size int64) string {
|
// FormatDiskSize turns a number of bytes into a disk size string.
|
||||||
|
func FormatDiskSize(size DiskSize) string {
|
||||||
if size < 0 {
|
if size < 0 {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
@ -17,37 +17,36 @@ func TestParseDiskSize(t *testing.T) {
|
|||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
size *string
|
size string
|
||||||
want int64
|
want int64
|
||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
{"handle null size", nil, 0, false},
|
{"parse TB", "2TB", 2199023255552, false},
|
||||||
{"parse TB", StrPtr("2TB"), 2199023255552, false},
|
{"parse T", "2T", 2199023255552, false},
|
||||||
{"parse T", StrPtr("2T"), 2199023255552, false},
|
{"parse fraction T", "2.2T", 2418925581108, false},
|
||||||
{"parse fraction T", StrPtr("2.2T"), 2418925581108, false},
|
{"parse GB", "2GB", 2147483648, false},
|
||||||
{"parse GB", StrPtr("2GB"), 2147483648, false},
|
{"parse G", "2G", 2147483648, false},
|
||||||
{"parse G", StrPtr("2G"), 2147483648, false},
|
{"parse M", "2048M", 2147483648, false},
|
||||||
{"parse M", StrPtr("2048M"), 2147483648, false},
|
{"parse MB", "2048MB", 2147483648, false},
|
||||||
{"parse MB", StrPtr("2048MB"), 2147483648, false},
|
{"parse MiB", "2048MiB", 2147483648, false},
|
||||||
{"parse MiB", StrPtr("2048MiB"), 2147483648, false},
|
{"parse K", "1K", 1024, false},
|
||||||
{"parse K", StrPtr("1K"), 1024, false},
|
{"parse KB", "2KB", 2048, false},
|
||||||
{"parse KB", StrPtr("2KB"), 2048, false},
|
{"parse KiB", "4KiB", 4096, false},
|
||||||
{"parse KiB", StrPtr("4KiB"), 4096, false},
|
{"parse no units as bytes", "12345", 12345, false},
|
||||||
{"parse no units as bytes", StrPtr("12345"), 12345, false},
|
{"error on bad format string", "20l8G", -1, true},
|
||||||
{"error on bad format string", StrPtr("20l8G"), -1, true},
|
{"error on unknown unit string", "2048W", -1, true},
|
||||||
{"error on unknown unit string", StrPtr("2048W"), -1, true},
|
{"error on arbitrary string", "something", -1, true},
|
||||||
{"error on arbitrary string", StrPtr("something"), -1, true},
|
|
||||||
}
|
}
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
tt := test
|
tt := test
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
got, err := parseDiskSize(tt.size)
|
got, err := ParseDiskSize(tt.size)
|
||||||
if (err != nil) != tt.wantErr {
|
if (err != nil) != tt.wantErr {
|
||||||
t.Errorf("parseDiskSize() error = %v, wantErr %v", err, tt.wantErr)
|
t.Errorf("parseDiskSize() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if got != tt.want {
|
if int64(got) != tt.want {
|
||||||
t.Errorf("parseDiskSize() got = %v, want %v", got, tt.want)
|
t.Errorf("parseDiskSize() got = %v, want %v", got, tt.want)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -73,7 +72,7 @@ func TestFormatDiskSize(t *testing.T) {
|
|||||||
tt := test
|
tt := test
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
if got := formatDiskSize(tt.size); got != tt.want {
|
if got := FormatDiskSize(DiskSize(tt.size)); got != tt.want {
|
||||||
t.Errorf("formatDiskSize() = %v, want %v", got, tt.want)
|
t.Errorf("formatDiskSize() = %v, want %v", got, tt.want)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -23,6 +23,7 @@ import (
|
|||||||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
|
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
|
||||||
|
|
||||||
"github.com/bpg/terraform-provider-proxmox/proxmox/nodes/vms"
|
"github.com/bpg/terraform-provider-proxmox/proxmox/nodes/vms"
|
||||||
|
"github.com/bpg/terraform-provider-proxmox/proxmox/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getBIOSValidator() schema.SchemaValidateDiagFunc {
|
func getBIOSValidator() schema.SchemaValidateDiagFunc {
|
||||||
@ -186,6 +187,29 @@ func getFileIDValidator() schema.SchemaValidateDiagFunc {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//nolint:unused
|
||||||
|
func getFileSizeValidator() schema.SchemaValidateDiagFunc {
|
||||||
|
return validation.ToDiagFunc(func(i interface{}, k string) ([]string, []error) {
|
||||||
|
v, ok := i.(string)
|
||||||
|
var es []error
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
es = append(es, fmt.Errorf("expected type of %s to be string", k))
|
||||||
|
return nil, es
|
||||||
|
}
|
||||||
|
|
||||||
|
if v != "" {
|
||||||
|
_, err := types.ParseDiskSize(v)
|
||||||
|
if err != nil {
|
||||||
|
es = append(es, fmt.Errorf("expected %s to be a valid file size (100, 1M, 1G), got %s", k, v))
|
||||||
|
return nil, es
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return []string{}, es
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func getKeyboardLayoutValidator() schema.SchemaValidateDiagFunc {
|
func getKeyboardLayoutValidator() schema.SchemaValidateDiagFunc {
|
||||||
return validation.ToDiagFunc(validation.StringInSlice([]string{
|
return validation.ToDiagFunc(validation.StringInSlice([]string{
|
||||||
"da",
|
"da",
|
||||||
@ -506,6 +530,11 @@ func getDiskDatastores(vm *vms.GetResponseData, d *schema.ResourceData) []string
|
|||||||
datastoresSet[fileIDParts[0]] = 1
|
datastoresSet[fileIDParts[0]] = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if vm.EFIDisk != nil {
|
||||||
|
fileIDParts := strings.Split(vm.EFIDisk.FileVolume, ":")
|
||||||
|
datastoresSet[fileIDParts[0]] = 1
|
||||||
|
}
|
||||||
|
|
||||||
datastores := []string{}
|
datastores := []string{}
|
||||||
for datastore := range datastoresSet {
|
for datastore := range datastoresSet {
|
||||||
datastores = append(datastores, datastore)
|
datastores = append(datastores, datastore)
|
||||||
|
@ -47,6 +47,7 @@ const (
|
|||||||
dvResourceVirtualEnvironmentVMCPUArchitecture = "x86_64"
|
dvResourceVirtualEnvironmentVMCPUArchitecture = "x86_64"
|
||||||
dvResourceVirtualEnvironmentVMCPUCores = 1
|
dvResourceVirtualEnvironmentVMCPUCores = 1
|
||||||
dvResourceVirtualEnvironmentVMCPUHotplugged = 0
|
dvResourceVirtualEnvironmentVMCPUHotplugged = 0
|
||||||
|
dvResourceVirtualEnvironmentVMCPUNUMA = false
|
||||||
dvResourceVirtualEnvironmentVMCPUSockets = 1
|
dvResourceVirtualEnvironmentVMCPUSockets = 1
|
||||||
dvResourceVirtualEnvironmentVMCPUType = "qemu64"
|
dvResourceVirtualEnvironmentVMCPUType = "qemu64"
|
||||||
dvResourceVirtualEnvironmentVMCPUUnits = 1024
|
dvResourceVirtualEnvironmentVMCPUUnits = 1024
|
||||||
@ -63,6 +64,10 @@ const (
|
|||||||
dvResourceVirtualEnvironmentVMDiskSpeedReadBurstable = 0
|
dvResourceVirtualEnvironmentVMDiskSpeedReadBurstable = 0
|
||||||
dvResourceVirtualEnvironmentVMDiskSpeedWrite = 0
|
dvResourceVirtualEnvironmentVMDiskSpeedWrite = 0
|
||||||
dvResourceVirtualEnvironmentVMDiskSpeedWriteBurstable = 0
|
dvResourceVirtualEnvironmentVMDiskSpeedWriteBurstable = 0
|
||||||
|
dvResourceVirtualEnvironmentVMEFIDiskDatastoreID = "local-lvm"
|
||||||
|
dvResourceVirtualEnvironmentVMEFIDiskFileFormat = "qcow2"
|
||||||
|
dvResourceVirtualEnvironmentVMEFIDiskType = "2m"
|
||||||
|
dvResourceVirtualEnvironmentVMEFIDiskPreEnrolledKeys = false
|
||||||
dvResourceVirtualEnvironmentVMInitializationDatastoreID = "local-lvm"
|
dvResourceVirtualEnvironmentVMInitializationDatastoreID = "local-lvm"
|
||||||
dvResourceVirtualEnvironmentVMInitializationDNSDomain = ""
|
dvResourceVirtualEnvironmentVMInitializationDNSDomain = ""
|
||||||
dvResourceVirtualEnvironmentVMInitializationDNSServer = ""
|
dvResourceVirtualEnvironmentVMInitializationDNSServer = ""
|
||||||
@ -139,6 +144,7 @@ const (
|
|||||||
mkResourceVirtualEnvironmentVMCPUCores = "cores"
|
mkResourceVirtualEnvironmentVMCPUCores = "cores"
|
||||||
mkResourceVirtualEnvironmentVMCPUFlags = "flags"
|
mkResourceVirtualEnvironmentVMCPUFlags = "flags"
|
||||||
mkResourceVirtualEnvironmentVMCPUHotplugged = "hotplugged"
|
mkResourceVirtualEnvironmentVMCPUHotplugged = "hotplugged"
|
||||||
|
mkResourceVirtualEnvironmentVMCPUNUMA = "numa"
|
||||||
mkResourceVirtualEnvironmentVMCPUSockets = "sockets"
|
mkResourceVirtualEnvironmentVMCPUSockets = "sockets"
|
||||||
mkResourceVirtualEnvironmentVMCPUType = "type"
|
mkResourceVirtualEnvironmentVMCPUType = "type"
|
||||||
mkResourceVirtualEnvironmentVMCPUUnits = "units"
|
mkResourceVirtualEnvironmentVMCPUUnits = "units"
|
||||||
@ -157,6 +163,11 @@ const (
|
|||||||
mkResourceVirtualEnvironmentVMDiskSpeedReadBurstable = "read_burstable"
|
mkResourceVirtualEnvironmentVMDiskSpeedReadBurstable = "read_burstable"
|
||||||
mkResourceVirtualEnvironmentVMDiskSpeedWrite = "write"
|
mkResourceVirtualEnvironmentVMDiskSpeedWrite = "write"
|
||||||
mkResourceVirtualEnvironmentVMDiskSpeedWriteBurstable = "write_burstable"
|
mkResourceVirtualEnvironmentVMDiskSpeedWriteBurstable = "write_burstable"
|
||||||
|
mkResourceVirtualEnvironmentVMEFIDisk = "efi_disk"
|
||||||
|
mkResourceVirtualEnvironmentVMEFIDiskDatastoreID = "datastore_id"
|
||||||
|
mkResourceVirtualEnvironmentVMEFIDiskFileFormat = "file_format"
|
||||||
|
mkResourceVirtualEnvironmentVMEFIDiskType = "type"
|
||||||
|
mkResourceVirtualEnvironmentVMEFIDiskPreEnrolledKeys = "pre_enrolled_keys"
|
||||||
mkResourceVirtualEnvironmentVMHostPCI = "hostpci"
|
mkResourceVirtualEnvironmentVMHostPCI = "hostpci"
|
||||||
mkResourceVirtualEnvironmentVMHostPCIDevice = "device"
|
mkResourceVirtualEnvironmentVMHostPCIDevice = "device"
|
||||||
mkResourceVirtualEnvironmentVMHostPCIDeviceID = "id"
|
mkResourceVirtualEnvironmentVMHostPCIDeviceID = "id"
|
||||||
@ -449,6 +460,7 @@ func VM() *schema.Resource {
|
|||||||
mkResourceVirtualEnvironmentVMCPUArchitecture: dvResourceVirtualEnvironmentVMCPUArchitecture,
|
mkResourceVirtualEnvironmentVMCPUArchitecture: dvResourceVirtualEnvironmentVMCPUArchitecture,
|
||||||
mkResourceVirtualEnvironmentVMCPUCores: dvResourceVirtualEnvironmentVMCPUCores,
|
mkResourceVirtualEnvironmentVMCPUCores: dvResourceVirtualEnvironmentVMCPUCores,
|
||||||
mkResourceVirtualEnvironmentVMCPUFlags: []interface{}{},
|
mkResourceVirtualEnvironmentVMCPUFlags: []interface{}{},
|
||||||
|
mkResourceVirtualEnvironmentVMCPUNUMA: dvResourceVirtualEnvironmentVMCPUNUMA,
|
||||||
mkResourceVirtualEnvironmentVMCPUHotplugged: dvResourceVirtualEnvironmentVMCPUHotplugged,
|
mkResourceVirtualEnvironmentVMCPUHotplugged: dvResourceVirtualEnvironmentVMCPUHotplugged,
|
||||||
mkResourceVirtualEnvironmentVMCPUSockets: dvResourceVirtualEnvironmentVMCPUSockets,
|
mkResourceVirtualEnvironmentVMCPUSockets: dvResourceVirtualEnvironmentVMCPUSockets,
|
||||||
mkResourceVirtualEnvironmentVMCPUType: dvResourceVirtualEnvironmentVMCPUType,
|
mkResourceVirtualEnvironmentVMCPUType: dvResourceVirtualEnvironmentVMCPUType,
|
||||||
@ -488,6 +500,12 @@ func VM() *schema.Resource {
|
|||||||
Default: dvResourceVirtualEnvironmentVMCPUHotplugged,
|
Default: dvResourceVirtualEnvironmentVMCPUHotplugged,
|
||||||
ValidateDiagFunc: validation.ToDiagFunc(validation.IntBetween(0, 2304)),
|
ValidateDiagFunc: validation.ToDiagFunc(validation.IntBetween(0, 2304)),
|
||||||
},
|
},
|
||||||
|
mkResourceVirtualEnvironmentVMCPUNUMA: {
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
Description: "Enable/disable NUMA.",
|
||||||
|
Optional: true,
|
||||||
|
Default: dvResourceVirtualEnvironmentVMCPUNUMA,
|
||||||
|
},
|
||||||
mkResourceVirtualEnvironmentVMCPUSockets: {
|
mkResourceVirtualEnvironmentVMCPUSockets: {
|
||||||
Type: schema.TypeInt,
|
Type: schema.TypeInt,
|
||||||
Description: "The number of CPU sockets",
|
Description: "The number of CPU sockets",
|
||||||
@ -644,6 +662,61 @@ func VM() *schema.Resource {
|
|||||||
MaxItems: 14,
|
MaxItems: 14,
|
||||||
MinItems: 0,
|
MinItems: 0,
|
||||||
},
|
},
|
||||||
|
mkResourceVirtualEnvironmentVMEFIDisk: {
|
||||||
|
Type: schema.TypeList,
|
||||||
|
Description: "The efidisk device",
|
||||||
|
Optional: true,
|
||||||
|
ForceNew: true,
|
||||||
|
DefaultFunc: func() (interface{}, error) {
|
||||||
|
return []interface{}{
|
||||||
|
map[string]interface{}{
|
||||||
|
mkResourceVirtualEnvironmentVMEFIDiskDatastoreID: dvResourceVirtualEnvironmentVMEFIDiskDatastoreID,
|
||||||
|
mkResourceVirtualEnvironmentVMEFIDiskType: dvResourceVirtualEnvironmentVMEFIDiskType,
|
||||||
|
mkResourceVirtualEnvironmentVMEFIDiskFileFormat: dvResourceVirtualEnvironmentVMEFIDiskFileFormat,
|
||||||
|
mkResourceVirtualEnvironmentVMEFIDiskPreEnrolledKeys: dvResourceVirtualEnvironmentVMEFIDiskPreEnrolledKeys,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
Elem: &schema.Resource{
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
mkResourceVirtualEnvironmentVMEFIDiskDatastoreID: {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Description: "The datastore id",
|
||||||
|
Optional: true,
|
||||||
|
Default: dvResourceVirtualEnvironmentVMEFIDiskDatastoreID,
|
||||||
|
},
|
||||||
|
mkResourceVirtualEnvironmentVMEFIDiskFileFormat: {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Description: "The file format",
|
||||||
|
Optional: true,
|
||||||
|
ForceNew: true,
|
||||||
|
Computed: true,
|
||||||
|
ValidateDiagFunc: getFileFormatValidator(),
|
||||||
|
},
|
||||||
|
mkResourceVirtualEnvironmentVMEFIDiskType: {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Description: "Size and type of the OVMF EFI disk",
|
||||||
|
Optional: true,
|
||||||
|
ForceNew: true,
|
||||||
|
Default: dvResourceVirtualEnvironmentVMEFIDiskType,
|
||||||
|
ValidateDiagFunc: validation.ToDiagFunc(validation.StringInSlice([]string{
|
||||||
|
"2m",
|
||||||
|
"4m",
|
||||||
|
}, true)),
|
||||||
|
},
|
||||||
|
mkResourceVirtualEnvironmentVMEFIDiskPreEnrolledKeys: {
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
Description: "Use an EFI vars template with distribution-specific and Microsoft Standard " +
|
||||||
|
"keys enrolled, if used with efi type=`4m`.",
|
||||||
|
Optional: true,
|
||||||
|
ForceNew: true,
|
||||||
|
Default: dvResourceVirtualEnvironmentVMEFIDiskPreEnrolledKeys,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MaxItems: 1,
|
||||||
|
MinItems: 0,
|
||||||
|
},
|
||||||
mkResourceVirtualEnvironmentVMInitialization: {
|
mkResourceVirtualEnvironmentVMInitialization: {
|
||||||
Type: schema.TypeList,
|
Type: schema.TypeList,
|
||||||
Description: "The cloud-init configuration",
|
Description: "The cloud-init configuration",
|
||||||
@ -1544,6 +1617,7 @@ func vmCreateClone(ctx context.Context, d *schema.ResourceData, m interface{}) d
|
|||||||
cpuCores := cpuBlock[mkResourceVirtualEnvironmentVMCPUCores].(int)
|
cpuCores := cpuBlock[mkResourceVirtualEnvironmentVMCPUCores].(int)
|
||||||
cpuFlags := cpuBlock[mkResourceVirtualEnvironmentVMCPUFlags].([]interface{})
|
cpuFlags := cpuBlock[mkResourceVirtualEnvironmentVMCPUFlags].([]interface{})
|
||||||
cpuHotplugged := cpuBlock[mkResourceVirtualEnvironmentVMCPUHotplugged].(int)
|
cpuHotplugged := cpuBlock[mkResourceVirtualEnvironmentVMCPUHotplugged].(int)
|
||||||
|
cpuNUMA := types2.CustomBool(cpuBlock[mkResourceVirtualEnvironmentVMCPUNUMA].(bool))
|
||||||
cpuSockets := cpuBlock[mkResourceVirtualEnvironmentVMCPUSockets].(int)
|
cpuSockets := cpuBlock[mkResourceVirtualEnvironmentVMCPUSockets].(int)
|
||||||
cpuType := cpuBlock[mkResourceVirtualEnvironmentVMCPUType].(string)
|
cpuType := cpuBlock[mkResourceVirtualEnvironmentVMCPUType].(string)
|
||||||
cpuUnits := cpuBlock[mkResourceVirtualEnvironmentVMCPUUnits].(int)
|
cpuUnits := cpuBlock[mkResourceVirtualEnvironmentVMCPUUnits].(int)
|
||||||
@ -1565,6 +1639,7 @@ func vmCreateClone(ctx context.Context, d *schema.ResourceData, m interface{}) d
|
|||||||
Flags: &cpuFlagsConverted,
|
Flags: &cpuFlagsConverted,
|
||||||
Type: cpuType,
|
Type: cpuType,
|
||||||
}
|
}
|
||||||
|
updateBody.NUMAEnabled = &cpuNUMA
|
||||||
updateBody.CPUSockets = &cpuSockets
|
updateBody.CPUSockets = &cpuSockets
|
||||||
updateBody.CPUUnits = &cpuUnits
|
updateBody.CPUUnits = &cpuUnits
|
||||||
|
|
||||||
@ -1694,6 +1769,7 @@ func vmCreateClone(ctx context.Context, d *schema.ResourceData, m interface{}) d
|
|||||||
}
|
}
|
||||||
|
|
||||||
disk := d.Get(mkResourceVirtualEnvironmentVMDisk).([]interface{})
|
disk := d.Get(mkResourceVirtualEnvironmentVMDisk).([]interface{})
|
||||||
|
efiDisk := d.Get(mkResourceVirtualEnvironmentVMEFIDisk).([]interface{})
|
||||||
|
|
||||||
vmConfig, e := vmAPI.GetVM(ctx)
|
vmConfig, e := vmAPI.GetVM(ctx)
|
||||||
if e != nil {
|
if e != nil {
|
||||||
@ -1805,6 +1881,65 @@ func vmCreateClone(ctx context.Context, d *schema.ResourceData, m interface{}) d
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
efiDiskInfo := vmGetEfiDisk(d, nil) // from the resource config
|
||||||
|
|
||||||
|
for i := range efiDisk {
|
||||||
|
diskBlock := efiDisk[i].(map[string]interface{})
|
||||||
|
diskInterface := "efidisk0"
|
||||||
|
dataStoreID := diskBlock[mkResourceVirtualEnvironmentVMEFIDiskDatastoreID].(string)
|
||||||
|
efiType := diskBlock[mkResourceVirtualEnvironmentVMEFIDiskType].(string)
|
||||||
|
|
||||||
|
currentDiskInfo := vmConfig.EFIDisk
|
||||||
|
configuredDiskInfo := efiDiskInfo
|
||||||
|
|
||||||
|
if currentDiskInfo == nil {
|
||||||
|
diskUpdateBody := &vms.UpdateRequestBody{}
|
||||||
|
|
||||||
|
diskUpdateBody.EFIDisk = configuredDiskInfo
|
||||||
|
|
||||||
|
e = vmAPI.UpdateVM(ctx, diskUpdateBody)
|
||||||
|
if e != nil {
|
||||||
|
return diag.FromErr(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if &efiType != currentDiskInfo.Type {
|
||||||
|
return diag.Errorf(
|
||||||
|
"resizing of efidisks is not supported.",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteOriginalDisk := types2.CustomBool(true)
|
||||||
|
|
||||||
|
diskMoveBody := &vms.MoveDiskRequestBody{
|
||||||
|
DeleteOriginalDisk: &deleteOriginalDisk,
|
||||||
|
Disk: diskInterface,
|
||||||
|
TargetStorage: dataStoreID,
|
||||||
|
}
|
||||||
|
|
||||||
|
moveDisk := false
|
||||||
|
|
||||||
|
if dataStoreID != "" {
|
||||||
|
moveDisk = true
|
||||||
|
|
||||||
|
if allDiskInfo[diskInterface] != nil {
|
||||||
|
fileIDParts := strings.Split(allDiskInfo[diskInterface].FileVolume, ":")
|
||||||
|
moveDisk = dataStoreID != fileIDParts[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if moveDisk {
|
||||||
|
moveDiskTimeout := d.Get(mkResourceVirtualEnvironmentVMTimeoutMoveDisk).(int)
|
||||||
|
|
||||||
|
e = vmAPI.MoveVMDisk(ctx, diskMoveBody, moveDiskTimeout)
|
||||||
|
if e != nil {
|
||||||
|
return diag.FromErr(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return vmCreateStart(ctx, d, m)
|
return vmCreateStart(ctx, d, m)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1879,6 +2014,7 @@ func vmCreateCustom(ctx context.Context, d *schema.ResourceData, m interface{})
|
|||||||
cpuFlags := cpuBlock[mkResourceVirtualEnvironmentVMCPUFlags].([]interface{})
|
cpuFlags := cpuBlock[mkResourceVirtualEnvironmentVMCPUFlags].([]interface{})
|
||||||
cpuHotplugged := cpuBlock[mkResourceVirtualEnvironmentVMCPUHotplugged].(int)
|
cpuHotplugged := cpuBlock[mkResourceVirtualEnvironmentVMCPUHotplugged].(int)
|
||||||
cpuSockets := cpuBlock[mkResourceVirtualEnvironmentVMCPUSockets].(int)
|
cpuSockets := cpuBlock[mkResourceVirtualEnvironmentVMCPUSockets].(int)
|
||||||
|
cpuNUMA := types2.CustomBool(cpuBlock[mkResourceVirtualEnvironmentVMCPUNUMA].(bool))
|
||||||
cpuType := cpuBlock[mkResourceVirtualEnvironmentVMCPUType].(string)
|
cpuType := cpuBlock[mkResourceVirtualEnvironmentVMCPUType].(string)
|
||||||
cpuUnits := cpuBlock[mkResourceVirtualEnvironmentVMCPUUnits].(int)
|
cpuUnits := cpuBlock[mkResourceVirtualEnvironmentVMCPUUnits].(int)
|
||||||
|
|
||||||
@ -1888,6 +2024,29 @@ func vmCreateCustom(ctx context.Context, d *schema.ResourceData, m interface{})
|
|||||||
return diag.FromErr(err)
|
return diag.FromErr(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var efiDisk *vms.CustomEFIDisk
|
||||||
|
|
||||||
|
efiDiskBlock := d.Get(mkResourceVirtualEnvironmentVMEFIDisk).([]interface{})
|
||||||
|
if len(efiDiskBlock) > 0 {
|
||||||
|
block := efiDiskBlock[0].(map[string]interface{})
|
||||||
|
|
||||||
|
datastoreID, _ := block[mkResourceVirtualEnvironmentVMEFIDiskDatastoreID].(string)
|
||||||
|
fileFormat, _ := block[mkResourceVirtualEnvironmentVMEFIDiskFileFormat].(string)
|
||||||
|
efiType, _ := block[mkResourceVirtualEnvironmentVMEFIDiskType].(string)
|
||||||
|
preEnrolledKeys := types2.CustomBool(block[mkResourceVirtualEnvironmentVMEFIDiskPreEnrolledKeys].(bool))
|
||||||
|
|
||||||
|
if fileFormat == "" {
|
||||||
|
fileFormat = dvResourceVirtualEnvironmentVMEFIDiskFileFormat
|
||||||
|
}
|
||||||
|
|
||||||
|
efiDisk = &vms.CustomEFIDisk{
|
||||||
|
Type: &efiType,
|
||||||
|
FileVolume: fmt.Sprintf("%s:1", datastoreID),
|
||||||
|
Format: &fileFormat,
|
||||||
|
PreEnrolledKeys: &preEnrolledKeys,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
virtioDeviceObjects := diskDeviceObjects["virtio"]
|
virtioDeviceObjects := diskDeviceObjects["virtio"]
|
||||||
scsiDeviceObjects := diskDeviceObjects["scsi"]
|
scsiDeviceObjects := diskDeviceObjects["scsi"]
|
||||||
// ideDeviceObjects := getOrderedDiskDeviceList(diskDeviceObjects, "ide")
|
// ideDeviceObjects := getOrderedDiskDeviceList(diskDeviceObjects, "ide")
|
||||||
@ -2055,10 +2214,12 @@ func vmCreateCustom(ctx context.Context, d *schema.ResourceData, m interface{})
|
|||||||
CPUSockets: &cpuSockets,
|
CPUSockets: &cpuSockets,
|
||||||
CPUUnits: &cpuUnits,
|
CPUUnits: &cpuUnits,
|
||||||
DedicatedMemory: &memoryDedicated,
|
DedicatedMemory: &memoryDedicated,
|
||||||
|
EFIDisk: efiDisk,
|
||||||
FloatingMemory: &memoryFloating,
|
FloatingMemory: &memoryFloating,
|
||||||
IDEDevices: ideDevices,
|
IDEDevices: ideDevices,
|
||||||
KeyboardLayout: &keyboardLayout,
|
KeyboardLayout: &keyboardLayout,
|
||||||
NetworkDevices: networkDeviceObjects,
|
NetworkDevices: networkDeviceObjects,
|
||||||
|
NUMAEnabled: &cpuNUMA,
|
||||||
OSType: &operatingSystemType,
|
OSType: &operatingSystemType,
|
||||||
PCIDevices: pciDeviceObjects,
|
PCIDevices: pciDeviceObjects,
|
||||||
SCSIHardware: &scsiHardware,
|
SCSIHardware: &scsiHardware,
|
||||||
@ -2616,6 +2777,69 @@ func vmGetDiskDeviceObjects(
|
|||||||
return diskDeviceObjects, nil
|
return diskDeviceObjects, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func vmGetEfiDisk(d *schema.ResourceData, disk []interface{}) *vms.CustomEFIDisk {
|
||||||
|
var efiDisk []interface{}
|
||||||
|
|
||||||
|
if disk != nil {
|
||||||
|
efiDisk = disk
|
||||||
|
} else {
|
||||||
|
efiDisk = d.Get(mkResourceVirtualEnvironmentVMEFIDisk).([]interface{})
|
||||||
|
}
|
||||||
|
|
||||||
|
var efiDiskConfig *vms.CustomEFIDisk
|
||||||
|
|
||||||
|
if len(efiDisk) > 0 {
|
||||||
|
efiDiskConfig = &vms.CustomEFIDisk{}
|
||||||
|
|
||||||
|
block := efiDisk[0].(map[string]interface{})
|
||||||
|
datastoreID, _ := block[mkResourceVirtualEnvironmentVMEFIDiskDatastoreID].(string)
|
||||||
|
fileFormat, _ := block[mkResourceVirtualEnvironmentVMEFIDiskFileFormat].(string)
|
||||||
|
efiType, _ := block[mkResourceVirtualEnvironmentVMEFIDiskType].(string)
|
||||||
|
preEnrolledKeys := types2.CustomBool(block[mkResourceVirtualEnvironmentVMEFIDiskPreEnrolledKeys].(bool))
|
||||||
|
|
||||||
|
// special case for efi disk, the size is ignored, see docs for more info
|
||||||
|
efiDiskConfig.FileVolume = fmt.Sprintf("%s:1", datastoreID)
|
||||||
|
efiDiskConfig.Format = &fileFormat
|
||||||
|
efiDiskConfig.Type = &efiType
|
||||||
|
efiDiskConfig.PreEnrolledKeys = &preEnrolledKeys
|
||||||
|
}
|
||||||
|
|
||||||
|
return efiDiskConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
func vmGetEfiDiskAsStorageDevice(d *schema.ResourceData, disk []interface{}) (*vms.CustomStorageDevice, error) {
|
||||||
|
efiDisk := vmGetEfiDisk(d, disk)
|
||||||
|
|
||||||
|
var storageDevice *vms.CustomStorageDevice
|
||||||
|
|
||||||
|
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,
|
||||||
|
ID: &id,
|
||||||
|
}
|
||||||
|
|
||||||
|
if efiDisk.Type != nil {
|
||||||
|
ds, err := types.ParseDiskSize(*efiDisk.Type)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid efi disk type: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
sizeInt := ds.InMegabytes()
|
||||||
|
storageDevice.Size = &ds
|
||||||
|
storageDevice.SizeInt = &sizeInt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return storageDevice, nil
|
||||||
|
}
|
||||||
|
|
||||||
func vmGetHostPCIDeviceObjects(d *schema.ResourceData) vms.CustomPCIDevices {
|
func vmGetHostPCIDeviceObjects(d *schema.ResourceData) vms.CustomPCIDevices {
|
||||||
pciDevice := d.Get(mkResourceVirtualEnvironmentVMHostPCI).([]interface{})
|
pciDevice := d.Get(mkResourceVirtualEnvironmentVMHostPCI).([]interface{})
|
||||||
pciDeviceObjects := make(vms.CustomPCIDevices, len(pciDevice))
|
pciDeviceObjects := make(vms.CustomPCIDevices, len(pciDevice))
|
||||||
@ -3038,6 +3262,13 @@ func vmReadCustom(
|
|||||||
cpu[mkResourceVirtualEnvironmentVMCPUHotplugged] = 0
|
cpu[mkResourceVirtualEnvironmentVMCPUHotplugged] = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if vmConfig.NUMAEnabled != nil {
|
||||||
|
cpu[mkResourceVirtualEnvironmentVMCPUNUMA] = *vmConfig.NUMAEnabled
|
||||||
|
} else {
|
||||||
|
// Default value of "numa" is "false" according to the API documentation.
|
||||||
|
cpu[mkResourceVirtualEnvironmentVMCPUNUMA] = false
|
||||||
|
}
|
||||||
|
|
||||||
if vmConfig.CPUSockets != nil {
|
if vmConfig.CPUSockets != nil {
|
||||||
cpu[mkResourceVirtualEnvironmentVMCPUSockets] = *vmConfig.CPUSockets
|
cpu[mkResourceVirtualEnvironmentVMCPUSockets] = *vmConfig.CPUSockets
|
||||||
} else {
|
} else {
|
||||||
@ -3212,6 +3443,60 @@ func vmReadCustom(
|
|||||||
diags = append(diags, diag.FromErr(err)...)
|
diags = append(diags, diag.FromErr(err)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//nolint:nestif
|
||||||
|
if vmConfig.EFIDisk != nil {
|
||||||
|
efiDisk := map[string]interface{}{}
|
||||||
|
|
||||||
|
fileIDParts := strings.Split(vmConfig.EFIDisk.FileVolume, ":")
|
||||||
|
|
||||||
|
efiDisk[mkResourceVirtualEnvironmentVMEFIDiskDatastoreID] = fileIDParts[0]
|
||||||
|
|
||||||
|
if vmConfig.EFIDisk.Format != nil {
|
||||||
|
efiDisk[mkResourceVirtualEnvironmentVMEFIDiskFileFormat] = *vmConfig.EFIDisk.Format
|
||||||
|
} else {
|
||||||
|
// disk format may not be returned by config API if it is default for the storage, and that may be different
|
||||||
|
// from the default qcow2, so we need to read it from the storage API to make sure we have the correct value
|
||||||
|
files, err := api.Node(nodeName).ListDatastoreFiles(ctx, fileIDParts[0])
|
||||||
|
if err != nil {
|
||||||
|
diags = append(diags, diag.FromErr(err)...)
|
||||||
|
} else {
|
||||||
|
efiDisk[mkResourceVirtualEnvironmentVMEFIDiskFileFormat] = ""
|
||||||
|
for _, v := range files {
|
||||||
|
if v.VolumeID == vmConfig.EFIDisk.FileVolume {
|
||||||
|
efiDisk[mkResourceVirtualEnvironmentVMEFIDiskFileFormat] = v.FileFormat
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if vmConfig.EFIDisk.Type != nil {
|
||||||
|
efiDisk[mkResourceVirtualEnvironmentVMEFIDiskType] = *vmConfig.EFIDisk.Type
|
||||||
|
} else {
|
||||||
|
efiDisk[mkResourceVirtualEnvironmentVMEFIDiskType] = dvResourceVirtualEnvironmentVMEFIDiskType
|
||||||
|
}
|
||||||
|
|
||||||
|
if vmConfig.EFIDisk.PreEnrolledKeys != nil {
|
||||||
|
efiDisk[mkResourceVirtualEnvironmentVMEFIDiskPreEnrolledKeys] = *vmConfig.EFIDisk.PreEnrolledKeys
|
||||||
|
} else {
|
||||||
|
efiDisk[mkResourceVirtualEnvironmentVMEFIDiskPreEnrolledKeys] = false
|
||||||
|
}
|
||||||
|
|
||||||
|
currentEfiDisk := d.Get(mkResourceVirtualEnvironmentVMEFIDisk).([]interface{})
|
||||||
|
|
||||||
|
if len(clone) > 0 && len(currentEfiDisk) > 0 {
|
||||||
|
err := d.Set(mkResourceVirtualEnvironmentVMEFIDisk, []interface{}{efiDisk})
|
||||||
|
diags = append(diags, diag.FromErr(err)...)
|
||||||
|
} else if len(currentEfiDisk) > 0 ||
|
||||||
|
efiDisk[mkResourceVirtualEnvironmentVMEFIDiskDatastoreID] != dvResourceVirtualEnvironmentVMEFIDiskDatastoreID ||
|
||||||
|
efiDisk[mkResourceVirtualEnvironmentVMEFIDiskType] != dvResourceVirtualEnvironmentVMEFIDiskType ||
|
||||||
|
efiDisk[mkResourceVirtualEnvironmentVMEFIDiskPreEnrolledKeys] != dvResourceVirtualEnvironmentVMEFIDiskPreEnrolledKeys || //nolint:lll
|
||||||
|
efiDisk[mkResourceVirtualEnvironmentVMEFIDiskFileFormat] != dvResourceVirtualEnvironmentVMEFIDiskFileFormat {
|
||||||
|
err := d.Set(mkResourceVirtualEnvironmentVMEFIDisk, []interface{}{efiDisk})
|
||||||
|
diags = append(diags, diag.FromErr(err)...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
currentPCIList := d.Get(mkResourceVirtualEnvironmentVMHostPCI).([]interface{})
|
currentPCIList := d.Get(mkResourceVirtualEnvironmentVMHostPCI).([]interface{})
|
||||||
pciMap := map[string]interface{}{}
|
pciMap := map[string]interface{}{}
|
||||||
var orderedPCIList []interface{}
|
var orderedPCIList []interface{}
|
||||||
@ -4216,6 +4501,7 @@ func vmUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.D
|
|||||||
cpuCores := cpuBlock[mkResourceVirtualEnvironmentVMCPUCores].(int)
|
cpuCores := cpuBlock[mkResourceVirtualEnvironmentVMCPUCores].(int)
|
||||||
cpuFlags := cpuBlock[mkResourceVirtualEnvironmentVMCPUFlags].([]interface{})
|
cpuFlags := cpuBlock[mkResourceVirtualEnvironmentVMCPUFlags].([]interface{})
|
||||||
cpuHotplugged := cpuBlock[mkResourceVirtualEnvironmentVMCPUHotplugged].(int)
|
cpuHotplugged := cpuBlock[mkResourceVirtualEnvironmentVMCPUHotplugged].(int)
|
||||||
|
cpuNUMA := types2.CustomBool(cpuBlock[mkResourceVirtualEnvironmentVMCPUNUMA].(bool))
|
||||||
cpuSockets := cpuBlock[mkResourceVirtualEnvironmentVMCPUSockets].(int)
|
cpuSockets := cpuBlock[mkResourceVirtualEnvironmentVMCPUSockets].(int)
|
||||||
cpuType := cpuBlock[mkResourceVirtualEnvironmentVMCPUType].(string)
|
cpuType := cpuBlock[mkResourceVirtualEnvironmentVMCPUType].(string)
|
||||||
cpuUnits := cpuBlock[mkResourceVirtualEnvironmentVMCPUUnits].(int)
|
cpuUnits := cpuBlock[mkResourceVirtualEnvironmentVMCPUUnits].(int)
|
||||||
@ -4229,6 +4515,7 @@ func vmUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.D
|
|||||||
updateBody.CPUCores = &cpuCores
|
updateBody.CPUCores = &cpuCores
|
||||||
updateBody.CPUSockets = &cpuSockets
|
updateBody.CPUSockets = &cpuSockets
|
||||||
updateBody.CPUUnits = &cpuUnits
|
updateBody.CPUUnits = &cpuUnits
|
||||||
|
updateBody.NUMAEnabled = &cpuNUMA
|
||||||
|
|
||||||
if cpuHotplugged > 0 {
|
if cpuHotplugged > 0 {
|
||||||
updateBody.VirtualCPUCount = &cpuHotplugged
|
updateBody.VirtualCPUCount = &cpuHotplugged
|
||||||
@ -4311,6 +4598,15 @@ func vmUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.D
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Prepare the new efi disk configuration.
|
||||||
|
if d.HasChange(mkResourceVirtualEnvironmentVMEFIDisk) {
|
||||||
|
efiDisk := vmGetEfiDisk(d, nil)
|
||||||
|
|
||||||
|
updateBody.EFIDisk = efiDisk
|
||||||
|
|
||||||
|
rebootRequired = true
|
||||||
|
}
|
||||||
|
|
||||||
// Prepare the new cloud-init configuration.
|
// Prepare the new cloud-init configuration.
|
||||||
if d.HasChange(mkResourceVirtualEnvironmentVMInitialization) {
|
if d.HasChange(mkResourceVirtualEnvironmentVMInitialization) {
|
||||||
initializationConfig := vmGetCloudInitConfig(d)
|
initializationConfig := vmGetCloudInitConfig(d)
|
||||||
@ -4330,15 +4626,14 @@ func vmUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.D
|
|||||||
Media: &cdromMedia,
|
Media: &cdromMedia,
|
||||||
}
|
}
|
||||||
|
|
||||||
if vmConfig.IDEDevice2 != nil {
|
if vmConfig.IDEDevice2 != nil &&
|
||||||
if strings.Contains(
|
strings.Contains(
|
||||||
vmConfig.IDEDevice2.FileVolume,
|
vmConfig.IDEDevice2.FileVolume,
|
||||||
fmt.Sprintf("vm-%d-cloudinit", vmID),
|
fmt.Sprintf("vm-%d-cloudinit", vmID),
|
||||||
) {
|
) {
|
||||||
tmp := updateBody.IDEDevices["ide2"]
|
tmp := updateBody.IDEDevices["ide2"]
|
||||||
tmp.Enabled = true
|
tmp.Enabled = true
|
||||||
updateBody.IDEDevices["ide2"] = tmp
|
updateBody.IDEDevices["ide2"] = tmp
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4535,6 +4830,37 @@ func vmUpdateDiskLocationAndSize(
|
|||||||
return diag.FromErr(err)
|
return diag.FromErr(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add efidisk if it has changes
|
||||||
|
if d.HasChange(mkResourceVirtualEnvironmentVMEFIDisk) {
|
||||||
|
diskOld, diskNew := d.GetChange(mkResourceVirtualEnvironmentVMEFIDisk)
|
||||||
|
|
||||||
|
oldEfiDisk, e := vmGetEfiDiskAsStorageDevice(d, diskOld.([]interface{}))
|
||||||
|
if e != nil {
|
||||||
|
return diag.FromErr(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
newEfiDisk, e := vmGetEfiDiskAsStorageDevice(d, diskNew.([]interface{}))
|
||||||
|
if e != nil {
|
||||||
|
return diag.FromErr(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
if oldEfiDisk != nil {
|
||||||
|
baseDiskInterface := diskDigitPrefix(*oldEfiDisk.Interface)
|
||||||
|
diskOldEntries[baseDiskInterface][*oldEfiDisk.Interface] = *oldEfiDisk
|
||||||
|
}
|
||||||
|
|
||||||
|
if newEfiDisk != nil {
|
||||||
|
baseDiskInterface := diskDigitPrefix(*newEfiDisk.Interface)
|
||||||
|
diskNewEntries[baseDiskInterface][*newEfiDisk.Interface] = *newEfiDisk
|
||||||
|
}
|
||||||
|
|
||||||
|
if oldEfiDisk != nil && newEfiDisk != nil && oldEfiDisk.Size != newEfiDisk.Size {
|
||||||
|
return diag.Errorf(
|
||||||
|
"resizing of efidisks is not supported.",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var diskMoveBodies []*vms.MoveDiskRequestBody
|
var diskMoveBodies []*vms.MoveDiskRequestBody
|
||||||
|
|
||||||
var diskResizeBodies []*vms.ResizeDiskRequestBody
|
var diskResizeBodies []*vms.ResizeDiskRequestBody
|
||||||
@ -4566,7 +4892,7 @@ func vmUpdateDiskLocationAndSize(
|
|||||||
shutdownForDisksRequired = true
|
shutdownForDisksRequired = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if *oldDisk.SizeInt <= *diskNewEntries[prefix][oldKey].SizeInt {
|
if *oldDisk.SizeInt < *diskNewEntries[prefix][oldKey].SizeInt {
|
||||||
diskResizeBodies = append(
|
diskResizeBodies = append(
|
||||||
diskResizeBodies,
|
diskResizeBodies,
|
||||||
&vms.ResizeDiskRequestBody{
|
&vms.ResizeDiskRequestBody{
|
||||||
|
@ -45,6 +45,7 @@ func TestVMSchema(t *testing.T) {
|
|||||||
mkResourceVirtualEnvironmentVMCPU,
|
mkResourceVirtualEnvironmentVMCPU,
|
||||||
mkResourceVirtualEnvironmentVMDescription,
|
mkResourceVirtualEnvironmentVMDescription,
|
||||||
mkResourceVirtualEnvironmentVMDisk,
|
mkResourceVirtualEnvironmentVMDisk,
|
||||||
|
mkResourceVirtualEnvironmentVMEFIDisk,
|
||||||
mkResourceVirtualEnvironmentVMInitialization,
|
mkResourceVirtualEnvironmentVMInitialization,
|
||||||
mkResourceVirtualEnvironmentVMHostPCI,
|
mkResourceVirtualEnvironmentVMHostPCI,
|
||||||
mkResourceVirtualEnvironmentVMKeyboardLayout,
|
mkResourceVirtualEnvironmentVMKeyboardLayout,
|
||||||
@ -80,6 +81,7 @@ func TestVMSchema(t *testing.T) {
|
|||||||
mkResourceVirtualEnvironmentVMCPU: schema.TypeList,
|
mkResourceVirtualEnvironmentVMCPU: schema.TypeList,
|
||||||
mkResourceVirtualEnvironmentVMDescription: schema.TypeString,
|
mkResourceVirtualEnvironmentVMDescription: schema.TypeString,
|
||||||
mkResourceVirtualEnvironmentVMDisk: schema.TypeList,
|
mkResourceVirtualEnvironmentVMDisk: schema.TypeList,
|
||||||
|
mkResourceVirtualEnvironmentVMEFIDisk: schema.TypeList,
|
||||||
mkResourceVirtualEnvironmentVMHostPCI: schema.TypeList,
|
mkResourceVirtualEnvironmentVMHostPCI: schema.TypeList,
|
||||||
mkResourceVirtualEnvironmentVMInitialization: schema.TypeList,
|
mkResourceVirtualEnvironmentVMInitialization: schema.TypeList,
|
||||||
mkResourceVirtualEnvironmentVMIPv4Addresses: schema.TypeList,
|
mkResourceVirtualEnvironmentVMIPv4Addresses: schema.TypeList,
|
||||||
@ -165,6 +167,7 @@ func TestVMSchema(t *testing.T) {
|
|||||||
mkResourceVirtualEnvironmentVMCPUCores,
|
mkResourceVirtualEnvironmentVMCPUCores,
|
||||||
mkResourceVirtualEnvironmentVMCPUFlags,
|
mkResourceVirtualEnvironmentVMCPUFlags,
|
||||||
mkResourceVirtualEnvironmentVMCPUHotplugged,
|
mkResourceVirtualEnvironmentVMCPUHotplugged,
|
||||||
|
mkResourceVirtualEnvironmentVMCPUNUMA,
|
||||||
mkResourceVirtualEnvironmentVMCPUSockets,
|
mkResourceVirtualEnvironmentVMCPUSockets,
|
||||||
mkResourceVirtualEnvironmentVMCPUType,
|
mkResourceVirtualEnvironmentVMCPUType,
|
||||||
mkResourceVirtualEnvironmentVMCPUUnits,
|
mkResourceVirtualEnvironmentVMCPUUnits,
|
||||||
@ -175,6 +178,7 @@ func TestVMSchema(t *testing.T) {
|
|||||||
mkResourceVirtualEnvironmentVMCPUCores: schema.TypeInt,
|
mkResourceVirtualEnvironmentVMCPUCores: schema.TypeInt,
|
||||||
mkResourceVirtualEnvironmentVMCPUFlags: schema.TypeList,
|
mkResourceVirtualEnvironmentVMCPUFlags: schema.TypeList,
|
||||||
mkResourceVirtualEnvironmentVMCPUHotplugged: schema.TypeInt,
|
mkResourceVirtualEnvironmentVMCPUHotplugged: schema.TypeInt,
|
||||||
|
mkResourceVirtualEnvironmentVMCPUNUMA: schema.TypeBool,
|
||||||
mkResourceVirtualEnvironmentVMCPUSockets: schema.TypeInt,
|
mkResourceVirtualEnvironmentVMCPUSockets: schema.TypeInt,
|
||||||
mkResourceVirtualEnvironmentVMCPUType: schema.TypeString,
|
mkResourceVirtualEnvironmentVMCPUType: schema.TypeString,
|
||||||
mkResourceVirtualEnvironmentVMCPUUnits: schema.TypeInt,
|
mkResourceVirtualEnvironmentVMCPUUnits: schema.TypeInt,
|
||||||
@ -216,6 +220,20 @@ func TestVMSchema(t *testing.T) {
|
|||||||
mkResourceVirtualEnvironmentVMDiskSpeedWriteBurstable: schema.TypeInt,
|
mkResourceVirtualEnvironmentVMDiskSpeedWriteBurstable: schema.TypeInt,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
efiDiskSchema := test.AssertNestedSchemaExistence(t, s, mkResourceVirtualEnvironmentVMEFIDisk)
|
||||||
|
|
||||||
|
test.AssertOptionalArguments(t, efiDiskSchema, []string{
|
||||||
|
mkResourceVirtualEnvironmentVMEFIDiskDatastoreID,
|
||||||
|
mkResourceVirtualEnvironmentVMEFIDiskFileFormat,
|
||||||
|
mkResourceVirtualEnvironmentVMEFIDiskType,
|
||||||
|
})
|
||||||
|
|
||||||
|
test.AssertValueTypes(t, efiDiskSchema, map[string]schema.ValueType{
|
||||||
|
mkResourceVirtualEnvironmentVMEFIDiskDatastoreID: schema.TypeString,
|
||||||
|
mkResourceVirtualEnvironmentVMEFIDiskFileFormat: schema.TypeString,
|
||||||
|
mkResourceVirtualEnvironmentVMEFIDiskType: schema.TypeString,
|
||||||
|
})
|
||||||
|
|
||||||
initializationSchema := test.AssertNestedSchemaExistence(
|
initializationSchema := test.AssertNestedSchemaExistence(
|
||||||
t,
|
t,
|
||||||
s,
|
s,
|
||||||
|
Loading…
Reference in New Issue
Block a user