mirror of
https://github.com/bpg/terraform-provider-proxmox.git
synced 2025-06-30 02:31:10 +00:00
fix(vm): do not overwrite cpu
attributes with defaults when cloning
Signed-off-by: Pavel Boldyrev <627562+bpg@users.noreply.github.com>
This commit is contained in:
parent
163a773088
commit
9c72e584de
122
fwprovider/tests/resource_vm_clone_test.go
Normal file
122
fwprovider/tests/resource_vm_clone_test.go
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
/*
|
||||||
|
* 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 tests
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAccResourceVMCloneCPU(t *testing.T) {
|
||||||
|
te := initTestEnvironment(t)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
step []resource.TestStep
|
||||||
|
}{
|
||||||
|
{"copy cpu from template in full", []resource.TestStep{{
|
||||||
|
Config: te.renderConfig(`
|
||||||
|
resource "proxmox_virtual_environment_vm" "test_cpu_clone_template" {
|
||||||
|
node_name = "{{.NodeName}}"
|
||||||
|
started = false
|
||||||
|
name = "test-cpu-clone-template"
|
||||||
|
template = "true"
|
||||||
|
|
||||||
|
cpu {
|
||||||
|
cores = 2
|
||||||
|
type = "host"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resource "proxmox_virtual_environment_vm" "test_cpu_clone" {
|
||||||
|
node_name = "{{.NodeName}}"
|
||||||
|
started = false
|
||||||
|
name = "test-cpu-clone"
|
||||||
|
|
||||||
|
clone {
|
||||||
|
vm_id = proxmox_virtual_environment_vm.test_cpu_clone_template.id
|
||||||
|
}
|
||||||
|
}`),
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testResourceAttributes("proxmox_virtual_environment_vm.test_cpu_clone", map[string]string{
|
||||||
|
"cpu.0.cores": "2",
|
||||||
|
"cpu.0.type": "host",
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
}}},
|
||||||
|
{"merge cpu attributes", []resource.TestStep{{
|
||||||
|
Config: te.renderConfig(`
|
||||||
|
resource "proxmox_virtual_environment_vm" "test_cpu_clone_template" {
|
||||||
|
node_name = "{{.NodeName}}"
|
||||||
|
started = false
|
||||||
|
name = "test-cpu-clone-template"
|
||||||
|
template = "true"
|
||||||
|
|
||||||
|
cpu {
|
||||||
|
cores = 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resource "proxmox_virtual_environment_vm" "test_cpu_clone" {
|
||||||
|
node_name = "{{.NodeName}}"
|
||||||
|
started = false
|
||||||
|
name = "test-cpu-clone"
|
||||||
|
|
||||||
|
cpu {
|
||||||
|
type = "host"
|
||||||
|
}
|
||||||
|
|
||||||
|
clone {
|
||||||
|
vm_id = proxmox_virtual_environment_vm.test_cpu_clone_template.id
|
||||||
|
}
|
||||||
|
}`),
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testResourceAttributes("proxmox_virtual_environment_vm.test_cpu_clone", map[string]string{
|
||||||
|
"cpu.0.cores": "2",
|
||||||
|
"cpu.0.type": "host",
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
}}},
|
||||||
|
{"overwrite cpu attributes in full", []resource.TestStep{{
|
||||||
|
Config: te.renderConfig(`
|
||||||
|
resource "proxmox_virtual_environment_vm" "test_cpu_clone_template" {
|
||||||
|
node_name = "{{.NodeName}}"
|
||||||
|
started = false
|
||||||
|
name = "test-cpu-clone-template"
|
||||||
|
template = "true"
|
||||||
|
}
|
||||||
|
resource "proxmox_virtual_environment_vm" "test_cpu_clone" {
|
||||||
|
node_name = "{{.NodeName}}"
|
||||||
|
started = false
|
||||||
|
name = "test-cpu-clone"
|
||||||
|
|
||||||
|
cpu {
|
||||||
|
cores = 2
|
||||||
|
type = "host"
|
||||||
|
}
|
||||||
|
|
||||||
|
clone {
|
||||||
|
vm_id = proxmox_virtual_environment_vm.test_cpu_clone_template.id
|
||||||
|
}
|
||||||
|
}`),
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testResourceAttributes("proxmox_virtual_environment_vm.test_cpu_clone", map[string]string{
|
||||||
|
"cpu.0.cores": "2",
|
||||||
|
"cpu.0.type": "host",
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
}}},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
resource.ParallelTest(t, resource.TestCase{
|
||||||
|
ProtoV6ProviderFactories: te.accProviders,
|
||||||
|
Steps: tt.step,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -77,7 +77,7 @@ type CustomCPUEmulation struct {
|
|||||||
Flags *[]string `json:"flags,omitempty" url:"flags,omitempty,semicolon"`
|
Flags *[]string `json:"flags,omitempty" url:"flags,omitempty,semicolon"`
|
||||||
Hidden *types.CustomBool `json:"hidden,omitempty" url:"hidden,omitempty,int"`
|
Hidden *types.CustomBool `json:"hidden,omitempty" url:"hidden,omitempty,int"`
|
||||||
HVVendorID *string `json:"hv-vendor-id,omitempty" url:"hv-vendor-id,omitempty"`
|
HVVendorID *string `json:"hv-vendor-id,omitempty" url:"hv-vendor-id,omitempty"`
|
||||||
Type string `json:"cputype,omitempty" url:"cputype,omitempty"`
|
Type *string `json:"cputype,omitempty" url:"cputype,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// CustomEFIDisk handles QEMU EFI disk parameters.
|
// CustomEFIDisk handles QEMU EFI disk parameters.
|
||||||
@ -820,7 +820,7 @@ func (r CustomCloudInitConfig) EncodeValues(_ string, v *url.Values) error {
|
|||||||
// EncodeValues converts a CustomCPUEmulation struct to a URL vlaue.
|
// EncodeValues converts a CustomCPUEmulation struct to a URL vlaue.
|
||||||
func (r CustomCPUEmulation) EncodeValues(key string, v *url.Values) error {
|
func (r CustomCPUEmulation) EncodeValues(key string, v *url.Values) error {
|
||||||
values := []string{
|
values := []string{
|
||||||
fmt.Sprintf("cputype=%s", r.Type),
|
fmt.Sprintf("cputype=%s", *r.Type),
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.Flags != nil && len(*r.Flags) > 0 {
|
if r.Flags != nil && len(*r.Flags) > 0 {
|
||||||
@ -1464,11 +1464,11 @@ func (r *CustomCPUEmulation) UnmarshalJSON(b []byte) error {
|
|||||||
v := strings.Split(strings.TrimSpace(p), "=")
|
v := strings.Split(strings.TrimSpace(p), "=")
|
||||||
|
|
||||||
if len(v) == 1 {
|
if len(v) == 1 {
|
||||||
r.Type = v[0]
|
r.Type = &v[0]
|
||||||
} else if len(v) == 2 {
|
} else if len(v) == 2 {
|
||||||
switch v[0] {
|
switch v[0] {
|
||||||
case "cputype":
|
case "cputype":
|
||||||
r.Type = v[1]
|
r.Type = &v[1]
|
||||||
case "flags":
|
case "flags":
|
||||||
if v[1] != "" {
|
if v[1] != "" {
|
||||||
f := strings.Split(v[1], ";")
|
f := strings.Split(v[1], ";")
|
||||||
|
107
proxmoxtf/resource/validators/cpu.go
Normal file
107
proxmoxtf/resource/validators/cpu.go
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
package validators
|
||||||
|
|
||||||
|
import (
|
||||||
|
"regexp"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
|
||||||
|
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CPUArchitectureValidator returns a schema validation function for a CPU architecture.
|
||||||
|
func CPUArchitectureValidator() schema.SchemaValidateDiagFunc {
|
||||||
|
return validation.ToDiagFunc(validation.StringInSlice([]string{
|
||||||
|
"aarch64",
|
||||||
|
"x86_64",
|
||||||
|
}, false))
|
||||||
|
}
|
||||||
|
|
||||||
|
// CPUTypeValidator returns a schema validation function for a CPU type.
|
||||||
|
func CPUTypeValidator() schema.SchemaValidateDiagFunc {
|
||||||
|
standardTypes := []string{
|
||||||
|
"486",
|
||||||
|
"Broadwell",
|
||||||
|
"Broadwell-IBRS",
|
||||||
|
"Broadwell-noTSX",
|
||||||
|
"Broadwell-noTSX-IBRS",
|
||||||
|
"Cascadelake-Server",
|
||||||
|
"Cascadelake-Server-noTSX",
|
||||||
|
"Cascadelake-Server-v2",
|
||||||
|
"Cascadelake-Server-v4",
|
||||||
|
"Cascadelake-Server-v5",
|
||||||
|
"Conroe",
|
||||||
|
"Cooperlake",
|
||||||
|
"Cooperlake-v2",
|
||||||
|
"EPYC",
|
||||||
|
"EPYC-IBPB",
|
||||||
|
"EPYC-Milan",
|
||||||
|
"EPYC-Rome",
|
||||||
|
"EPYC-Rome-v2",
|
||||||
|
"EPYC-v3",
|
||||||
|
"Haswell",
|
||||||
|
"Haswell-IBRS",
|
||||||
|
"Haswell-noTSX",
|
||||||
|
"Haswell-noTSX-IBRS",
|
||||||
|
"Icelake-Client",
|
||||||
|
"Icelake-Client-noTSX",
|
||||||
|
"Icelake-Server",
|
||||||
|
"Icelake-Server-noTSX",
|
||||||
|
"Icelake-Server-v3",
|
||||||
|
"Icelake-Server-v4",
|
||||||
|
"Icelake-Server-v5",
|
||||||
|
"Icelake-Server-v6",
|
||||||
|
"IvyBridge",
|
||||||
|
"IvyBridge-IBRS",
|
||||||
|
"KnightsMill",
|
||||||
|
"Nehalem",
|
||||||
|
"Nehalem-IBRS",
|
||||||
|
"Opteron_G1",
|
||||||
|
"Opteron_G2",
|
||||||
|
"Opteron_G3",
|
||||||
|
"Opteron_G4",
|
||||||
|
"Opteron_G5",
|
||||||
|
"Penryn",
|
||||||
|
"SandyBridge",
|
||||||
|
"SandyBridge-IBRS",
|
||||||
|
"SapphireRapids",
|
||||||
|
"Skylake-Client",
|
||||||
|
"Skylake-Client-IBRS",
|
||||||
|
"Skylake-Client-noTSX-IBRS",
|
||||||
|
"Skylake-Client-v4",
|
||||||
|
"Skylake-Server",
|
||||||
|
"Skylake-Server-IBRS",
|
||||||
|
"Skylake-Server-noTSX-IBRS",
|
||||||
|
"Skylake-Server-v4",
|
||||||
|
"Skylake-Server-v5",
|
||||||
|
"Westmere",
|
||||||
|
"Westmere-IBRS",
|
||||||
|
"athlon",
|
||||||
|
"core2duo",
|
||||||
|
"coreduo",
|
||||||
|
"host",
|
||||||
|
"kvm32",
|
||||||
|
"kvm64",
|
||||||
|
"max",
|
||||||
|
"pentium",
|
||||||
|
"pentium2",
|
||||||
|
"pentium3",
|
||||||
|
"phenom",
|
||||||
|
"qemu32",
|
||||||
|
"qemu64",
|
||||||
|
"x86-64-v2",
|
||||||
|
"x86-64-v2-AES",
|
||||||
|
"x86-64-v3",
|
||||||
|
"x86-64-v4",
|
||||||
|
}
|
||||||
|
|
||||||
|
return validation.ToDiagFunc(validation.Any(
|
||||||
|
validation.StringInSlice(standardTypes, false),
|
||||||
|
validation.StringMatch(regexp.MustCompile(`^custom-.+$`), "must be a valid custom CPU type"),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
// CPUAffinityValidator returns a schema validation function for a CPU affinity.
|
||||||
|
func CPUAffinityValidator() schema.SchemaValidateDiagFunc {
|
||||||
|
return validation.ToDiagFunc(
|
||||||
|
validation.StringMatch(regexp.MustCompile(`^\d+[\d-,]*$`), "must contain numbers or number ranges separated by ','"),
|
||||||
|
)
|
||||||
|
}
|
38
proxmoxtf/resource/validators/cpu_test.go
Normal file
38
proxmoxtf/resource/validators/cpu_test.go
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package validators
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCPUType(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
value string
|
||||||
|
valid bool
|
||||||
|
}{
|
||||||
|
{"empty", "", false},
|
||||||
|
{"invalid", "invalid", false},
|
||||||
|
{"valid", "host", true},
|
||||||
|
{"valid", "qemu64", true},
|
||||||
|
{"valid", "custom-abc", true},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
f := CPUTypeValidator()
|
||||||
|
res := f(tt.value, nil)
|
||||||
|
|
||||||
|
if tt.valid {
|
||||||
|
require.Empty(t, res, "validate: '%s'", tt.value)
|
||||||
|
} else {
|
||||||
|
require.NotEmpty(t, res, "validate: '%s'", tt.value)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
386
proxmoxtf/resource/vm/cpu/cpu.go
Normal file
386
proxmoxtf/resource/vm/cpu/cpu.go
Normal file
@ -0,0 +1,386 @@
|
|||||||
|
package cpu
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
|
||||||
|
|
||||||
|
"github.com/bpg/terraform-provider-proxmox/proxmox"
|
||||||
|
"github.com/bpg/terraform-provider-proxmox/proxmox/nodes/vms"
|
||||||
|
"github.com/bpg/terraform-provider-proxmox/proxmox/types"
|
||||||
|
"github.com/bpg/terraform-provider-proxmox/proxmoxtf/structure"
|
||||||
|
)
|
||||||
|
|
||||||
|
func UpdateWhenClone(d *schema.ResourceData, api proxmox.Client, updateBody *vms.UpdateRequestBody) {
|
||||||
|
cpu := d.Get(MkCPU).([]interface{})
|
||||||
|
if len(cpu) > 0 && cpu[0] != nil {
|
||||||
|
cpuBlock := cpu[0].(map[string]interface{})
|
||||||
|
|
||||||
|
cpuArchitecture := cpuBlock[mkCPUArchitecture].(string)
|
||||||
|
cpuCores := cpuBlock[mkCPUCores].(int)
|
||||||
|
cpuFlags := cpuBlock[mkCPUFlags].([]interface{})
|
||||||
|
cpuHotplugged := cpuBlock[mkCPUHotplugged].(int)
|
||||||
|
cpuLimit := cpuBlock[mkCPULimit].(int)
|
||||||
|
cpuNUMA := types.CustomBool(cpuBlock[mkCPUNUMA].(bool))
|
||||||
|
cpuSockets := cpuBlock[mkCPUSockets].(int)
|
||||||
|
cpuType := cpuBlock[mkCPUType].(string)
|
||||||
|
cpuUnits := cpuBlock[mkCPUUnits].(int)
|
||||||
|
cpuAffinity := cpuBlock[mkCPUAffinity].(string)
|
||||||
|
|
||||||
|
cpuFlagsConverted := make([]string, len(cpuFlags))
|
||||||
|
|
||||||
|
for fi, flag := range cpuFlags {
|
||||||
|
cpuFlagsConverted[fi] = flag.(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only the root account is allowed to change the CPU architecture, which makes this check necessary.
|
||||||
|
if api.API().IsRootTicket() ||
|
||||||
|
cpuArchitecture != dvCPUArchitecture {
|
||||||
|
updateBody.CPUArchitecture = &cpuArchitecture
|
||||||
|
}
|
||||||
|
|
||||||
|
if cpuCores != dvCPUCores && cpuCores > 0 {
|
||||||
|
// update only if we have non-default & non-empty value
|
||||||
|
updateBody.CPUCores = &cpuCores
|
||||||
|
}
|
||||||
|
|
||||||
|
updateBody.NUMAEnabled = &cpuNUMA
|
||||||
|
updateBody.CPUSockets = &cpuSockets
|
||||||
|
updateBody.CPUUnits = &cpuUnits
|
||||||
|
|
||||||
|
if cpuType != dvCPUType && cpuType != "" {
|
||||||
|
// update only if we have non-default & non-empty value
|
||||||
|
if updateBody.CPUEmulation == nil {
|
||||||
|
updateBody.CPUEmulation = &vms.CustomCPUEmulation{}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateBody.CPUEmulation.Type = &cpuType
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(cpuFlagsConverted) > 0 {
|
||||||
|
if updateBody.CPUEmulation == nil {
|
||||||
|
updateBody.CPUEmulation = &vms.CustomCPUEmulation{}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateBody.CPUEmulation.Flags = &cpuFlagsConverted
|
||||||
|
}
|
||||||
|
|
||||||
|
if cpuAffinity != "" {
|
||||||
|
updateBody.CPUAffinity = &cpuAffinity
|
||||||
|
}
|
||||||
|
|
||||||
|
if cpuHotplugged > 0 {
|
||||||
|
updateBody.VirtualCPUCount = &cpuHotplugged
|
||||||
|
}
|
||||||
|
|
||||||
|
if cpuLimit > 0 {
|
||||||
|
updateBody.CPULimit = &cpuLimit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Create(resource *schema.Resource, d *schema.ResourceData, api proxmox.Client, createBody *vms.CreateRequestBody) error {
|
||||||
|
cpuBlock, err := structure.GetSchemaBlock(
|
||||||
|
resource,
|
||||||
|
d,
|
||||||
|
[]string{MkCPU},
|
||||||
|
0,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error reading CPU block: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cpuArchitecture := cpuBlock[mkCPUArchitecture].(string)
|
||||||
|
cpuCores := cpuBlock[mkCPUCores].(int)
|
||||||
|
cpuFlags := cpuBlock[mkCPUFlags].([]interface{})
|
||||||
|
cpuHotplugged := cpuBlock[mkCPUHotplugged].(int)
|
||||||
|
cpuLimit := cpuBlock[mkCPULimit].(int)
|
||||||
|
cpuSockets := cpuBlock[mkCPUSockets].(int)
|
||||||
|
cpuNUMA := types.CustomBool(cpuBlock[mkCPUNUMA].(bool))
|
||||||
|
cpuType := cpuBlock[mkCPUType].(string)
|
||||||
|
cpuUnits := cpuBlock[mkCPUUnits].(int)
|
||||||
|
cpuAffinity := cpuBlock[mkCPUAffinity].(string)
|
||||||
|
|
||||||
|
if cpuCores != dvCPUCores && cpuCores > 0 {
|
||||||
|
// set only if we have non-default & non-empty value
|
||||||
|
createBody.CPUCores = &cpuCores
|
||||||
|
}
|
||||||
|
|
||||||
|
if cpuSockets != dvCPUSockets && cpuSockets > 0 {
|
||||||
|
// set only if we have non-default & non-empty value
|
||||||
|
createBody.CPUSockets = &cpuSockets
|
||||||
|
}
|
||||||
|
|
||||||
|
if cpuUnits != dvCPUUnits && cpuUnits > 0 {
|
||||||
|
// set only if we have non-default & non-empty value
|
||||||
|
createBody.CPUUnits = &cpuUnits
|
||||||
|
}
|
||||||
|
|
||||||
|
if cpuNUMA != dvCPUNUMA && cpuNUMA {
|
||||||
|
// set only if we have non-default & non-empty value
|
||||||
|
createBody.NUMAEnabled = &cpuNUMA
|
||||||
|
}
|
||||||
|
|
||||||
|
if cpuLimit > 0 {
|
||||||
|
createBody.CPULimit = &cpuLimit
|
||||||
|
}
|
||||||
|
|
||||||
|
if cpuAffinity != "" {
|
||||||
|
createBody.CPUAffinity = &cpuAffinity
|
||||||
|
}
|
||||||
|
|
||||||
|
if cpuHotplugged > 0 {
|
||||||
|
createBody.VirtualCPUCount = &cpuHotplugged
|
||||||
|
}
|
||||||
|
|
||||||
|
if cpuType != dvCPUType && cpuType != "" {
|
||||||
|
// set only if we have non-default & non-empty value
|
||||||
|
if createBody.CPUEmulation == nil {
|
||||||
|
createBody.CPUEmulation = &vms.CustomCPUEmulation{}
|
||||||
|
}
|
||||||
|
|
||||||
|
createBody.CPUEmulation.Type = &cpuType
|
||||||
|
}
|
||||||
|
|
||||||
|
cpuFlagsConverted := make([]string, len(cpuFlags))
|
||||||
|
for fi, flag := range cpuFlags {
|
||||||
|
cpuFlagsConverted[fi] = flag.(string)
|
||||||
|
}
|
||||||
|
if len(cpuFlagsConverted) > 0 {
|
||||||
|
// set only if we have non-default & non-empty value
|
||||||
|
if createBody.CPUEmulation == nil {
|
||||||
|
createBody.CPUEmulation = &vms.CustomCPUEmulation{}
|
||||||
|
}
|
||||||
|
|
||||||
|
createBody.CPUEmulation.Flags = &cpuFlagsConverted
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only the root account is allowed to change the CPU architecture, which makes this check necessary.
|
||||||
|
if api.API().IsRootTicket() ||
|
||||||
|
cpuArchitecture != dvCPUArchitecture {
|
||||||
|
createBody.CPUArchitecture = &cpuArchitecture
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Read(vmConfig *vms.GetResponseData, api proxmox.Client, d *schema.ResourceData, clone bool) error {
|
||||||
|
// Compare the CPU configuration to the one stored in the state.
|
||||||
|
cpu := map[string]interface{}{}
|
||||||
|
|
||||||
|
if vmConfig.CPUArchitecture != nil {
|
||||||
|
cpu[mkCPUArchitecture] = *vmConfig.CPUArchitecture
|
||||||
|
} else {
|
||||||
|
// Default value of "arch" is "" according to the API documentation.
|
||||||
|
// However, assume the provider's default value as a workaround when the root account is not being used.
|
||||||
|
if !api.API().IsRootTicket() {
|
||||||
|
cpu[mkCPUArchitecture] = dvCPUArchitecture
|
||||||
|
} else {
|
||||||
|
cpu[mkCPUArchitecture] = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if vmConfig.CPUCores != nil {
|
||||||
|
cpu[mkCPUCores] = *vmConfig.CPUCores
|
||||||
|
//} else {
|
||||||
|
// // Default value of "cores" is "1" according to the API documentation.
|
||||||
|
// cpu[mkCPUCores] = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if vmConfig.VirtualCPUCount != nil {
|
||||||
|
cpu[mkCPUHotplugged] = *vmConfig.VirtualCPUCount
|
||||||
|
} else {
|
||||||
|
// Default value of "vcpus" is "1" according to the API documentation.
|
||||||
|
cpu[mkCPUHotplugged] = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if vmConfig.CPULimit != nil {
|
||||||
|
cpu[mkCPULimit] = *vmConfig.CPULimit
|
||||||
|
} else {
|
||||||
|
// Default value of "cpulimit" is "0" according to the API documentation.
|
||||||
|
cpu[mkCPULimit] = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if vmConfig.NUMAEnabled != nil {
|
||||||
|
cpu[mkCPUNUMA] = *vmConfig.NUMAEnabled
|
||||||
|
} else {
|
||||||
|
// Default value of "numa" is "false" according to the API documentation.
|
||||||
|
cpu[mkCPUNUMA] = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if vmConfig.CPUSockets != nil {
|
||||||
|
cpu[mkCPUSockets] = *vmConfig.CPUSockets
|
||||||
|
} else {
|
||||||
|
// Default value of "sockets" is "1" according to the API documentation.
|
||||||
|
cpu[mkCPUSockets] = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if vmConfig.CPUEmulation != nil {
|
||||||
|
if vmConfig.CPUEmulation.Flags != nil {
|
||||||
|
convertedFlags := make([]interface{}, len(*vmConfig.CPUEmulation.Flags))
|
||||||
|
|
||||||
|
for fi, fv := range *vmConfig.CPUEmulation.Flags {
|
||||||
|
convertedFlags[fi] = fv
|
||||||
|
}
|
||||||
|
|
||||||
|
cpu[mkCPUFlags] = convertedFlags
|
||||||
|
//} else {
|
||||||
|
// cpu[mkCPUFlags] = []interface{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
cpu[mkCPUType] = vmConfig.CPUEmulation.Type
|
||||||
|
//} else {
|
||||||
|
// cpu[mkCPUFlags] = []interface{}{}
|
||||||
|
// // Default value of "cputype" is "qemu64" according to the QEMU documentation.
|
||||||
|
// cpu[mkCPUType] = dvCPUType
|
||||||
|
}
|
||||||
|
|
||||||
|
if vmConfig.CPUUnits != nil {
|
||||||
|
cpu[mkCPUUnits] = *vmConfig.CPUUnits
|
||||||
|
} else {
|
||||||
|
// Default value of "cpuunits" is "1024" according to the API documentation.
|
||||||
|
cpu[mkCPUUnits] = 1024
|
||||||
|
}
|
||||||
|
|
||||||
|
if vmConfig.CPUAffinity != nil {
|
||||||
|
cpu[mkCPUAffinity] = *vmConfig.CPUAffinity
|
||||||
|
} else {
|
||||||
|
cpu[mkCPUAffinity] = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
currentCPU := d.Get(MkCPU).([]interface{})
|
||||||
|
|
||||||
|
//if len(clone) > 0 {
|
||||||
|
// if len(currentCPU) > 0 {
|
||||||
|
// err := d.Set(mkCPU, []interface{}{cpu})
|
||||||
|
// diags = append(diags, diag.FromErr(err)...)
|
||||||
|
// }
|
||||||
|
//} else if len(currentCPU) > 0 ||
|
||||||
|
// cpu[mkCPUArchitecture] != dvCPUArchitecture ||
|
||||||
|
// cpu[mkCPUCores] != dvCPUCores ||
|
||||||
|
// len(cpu[mkCPUFlags].([]interface{})) > 0 ||
|
||||||
|
// cpu[mkCPUHotplugged] != dvCPUHotplugged ||
|
||||||
|
// cpu[mkCPULimit] != dvCPULimit ||
|
||||||
|
// cpu[mkCPUSockets] != dvCPUSockets ||
|
||||||
|
// cpu[mkCPUType] != dvCPUType ||
|
||||||
|
// cpu[mkCPUUnits] != dvCPUUnits {
|
||||||
|
// err := d.Set(mkCPU, []interface{}{cpu})
|
||||||
|
// diags = append(diags, diag.FromErr(err)...)
|
||||||
|
//}
|
||||||
|
|
||||||
|
if clone || len(currentCPU) > 0 ||
|
||||||
|
cpu[mkCPUArchitecture] != dvCPUArchitecture ||
|
||||||
|
//cpu[mkCPUCores] != dvCPUCores ||
|
||||||
|
//len(cpu[mkCPUFlags].([]interface{})) > 0 ||
|
||||||
|
cpu[mkCPUHotplugged] != dvCPUHotplugged ||
|
||||||
|
cpu[mkCPULimit] != dvCPULimit ||
|
||||||
|
cpu[mkCPUSockets] != dvCPUSockets ||
|
||||||
|
//cpu[mkCPUType] != dvCPUType ||
|
||||||
|
cpu[mkCPUUnits] != dvCPUUnits {
|
||||||
|
err := d.Set(MkCPU, []interface{}{cpu})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error setting CPU: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Update(d *schema.ResourceData, resource *schema.Resource, api proxmox.Client, updateBody *vms.UpdateRequestBody) ([]string, bool, error) {
|
||||||
|
// Prepare the new CPU configuration.
|
||||||
|
|
||||||
|
if !d.HasChange(MkCPU) {
|
||||||
|
return nil, false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
del := []string{}
|
||||||
|
cpuBlock, err := structure.GetSchemaBlock(
|
||||||
|
resource,
|
||||||
|
d,
|
||||||
|
[]string{MkCPU},
|
||||||
|
0,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cpuArchitecture := cpuBlock[mkCPUArchitecture].(string)
|
||||||
|
cpuCores := cpuBlock[mkCPUCores].(int)
|
||||||
|
cpuFlags := cpuBlock[mkCPUFlags].([]interface{})
|
||||||
|
cpuHotplugged := cpuBlock[mkCPUHotplugged].(int)
|
||||||
|
cpuLimit := cpuBlock[mkCPULimit].(int)
|
||||||
|
cpuNUMA := types.CustomBool(cpuBlock[mkCPUNUMA].(bool))
|
||||||
|
cpuSockets := cpuBlock[mkCPUSockets].(int)
|
||||||
|
cpuType := cpuBlock[mkCPUType].(string)
|
||||||
|
cpuUnits := cpuBlock[mkCPUUnits].(int)
|
||||||
|
cpuAffinity := cpuBlock[mkCPUAffinity].(string)
|
||||||
|
|
||||||
|
// Only the root account is allowed to change the CPU architecture, which makes this check necessary.
|
||||||
|
if api.API().IsRootTicket() ||
|
||||||
|
cpuArchitecture != dvCPUArchitecture {
|
||||||
|
updateBody.CPUArchitecture = &cpuArchitecture
|
||||||
|
}
|
||||||
|
|
||||||
|
if cpuCores != dvCPUCores && cpuCores > 0 {
|
||||||
|
// set only if we have non-default & non-empty value
|
||||||
|
updateBody.CPUCores = &cpuCores
|
||||||
|
}
|
||||||
|
|
||||||
|
updateBody.CPUSockets = &cpuSockets
|
||||||
|
updateBody.CPUUnits = &cpuUnits
|
||||||
|
updateBody.NUMAEnabled = &cpuNUMA
|
||||||
|
|
||||||
|
// CPU affinity is a special case, only root can change it.
|
||||||
|
// we can't even have it in the delete list, as PVE will return an error for non-root.
|
||||||
|
// Hence, checking explicitly if it has changed.
|
||||||
|
if d.HasChange(MkCPU + ".0." + mkCPUAffinity) {
|
||||||
|
if cpuAffinity != "" {
|
||||||
|
updateBody.CPUAffinity = &cpuAffinity
|
||||||
|
} else {
|
||||||
|
del = append(del, "affinity")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if cpuHotplugged > 0 {
|
||||||
|
updateBody.VirtualCPUCount = &cpuHotplugged
|
||||||
|
} else {
|
||||||
|
del = append(del, "vcpus")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cpuLimit > 0 {
|
||||||
|
updateBody.CPULimit = &cpuLimit
|
||||||
|
} else {
|
||||||
|
del = append(del, "cpulimit")
|
||||||
|
}
|
||||||
|
|
||||||
|
cpuFlagsConverted := make([]string, len(cpuFlags))
|
||||||
|
|
||||||
|
for fi, flag := range cpuFlags {
|
||||||
|
cpuFlagsConverted[fi] = flag.(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cpuType != dvCPUType && cpuType != "" {
|
||||||
|
// set only if we have non-default & non-empty value
|
||||||
|
if updateBody.CPUEmulation == nil {
|
||||||
|
updateBody.CPUEmulation = &vms.CustomCPUEmulation{}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateBody.CPUEmulation.Type = &cpuType
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(cpuFlagsConverted) > 0 {
|
||||||
|
// set only if we have non-default & non-empty value
|
||||||
|
if updateBody.CPUEmulation == nil {
|
||||||
|
updateBody.CPUEmulation = &vms.CustomCPUEmulation{}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateBody.CPUEmulation.Flags = &cpuFlagsConverted
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: this may not be true
|
||||||
|
rebootRequired := true
|
||||||
|
|
||||||
|
return del, rebootRequired, nil
|
||||||
|
}
|
141
proxmoxtf/resource/vm/cpu/schema.go
Normal file
141
proxmoxtf/resource/vm/cpu/schema.go
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
package cpu
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
|
||||||
|
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
|
||||||
|
|
||||||
|
"github.com/bpg/terraform-provider-proxmox/proxmoxtf/resource/validators"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
dvCPUArchitecture = "x86_64"
|
||||||
|
dvCPUCores = 1
|
||||||
|
dvCPUHotplugged = 0
|
||||||
|
dvCPULimit = 0
|
||||||
|
dvCPUNUMA = false
|
||||||
|
dvCPUSockets = 1
|
||||||
|
dvCPUType = "qemu64"
|
||||||
|
dvCPUUnits = 1024
|
||||||
|
dvCPUAffinity = ""
|
||||||
|
|
||||||
|
MkCPU = "cpu"
|
||||||
|
mkCPUArchitecture = "architecture"
|
||||||
|
mkCPUCores = "cores"
|
||||||
|
mkCPUFlags = "flags"
|
||||||
|
mkCPUHotplugged = "hotplugged"
|
||||||
|
mkCPULimit = "limit"
|
||||||
|
mkCPUNUMA = "numa"
|
||||||
|
mkCPUSockets = "sockets"
|
||||||
|
mkCPUType = "type"
|
||||||
|
mkCPUUnits = "units"
|
||||||
|
mkCPUAffinity = "affinity"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Schema() map[string]*schema.Schema {
|
||||||
|
return map[string]*schema.Schema{
|
||||||
|
MkCPU: {
|
||||||
|
Type: schema.TypeList,
|
||||||
|
Description: "The CPU allocation",
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
DefaultFunc: func() (interface{}, error) {
|
||||||
|
return []interface{}{
|
||||||
|
map[string]interface{}{
|
||||||
|
mkCPUArchitecture: dvCPUArchitecture,
|
||||||
|
mkCPUCores: dvCPUCores,
|
||||||
|
mkCPUFlags: []interface{}{},
|
||||||
|
mkCPUHotplugged: dvCPUHotplugged,
|
||||||
|
mkCPULimit: dvCPULimit,
|
||||||
|
mkCPUNUMA: dvCPUNUMA,
|
||||||
|
mkCPUSockets: dvCPUSockets,
|
||||||
|
mkCPUType: dvCPUType,
|
||||||
|
mkCPUUnits: dvCPUUnits,
|
||||||
|
mkCPUAffinity: dvCPUAffinity,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
Elem: &schema.Resource{
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
mkCPUArchitecture: {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Description: "The CPU architecture",
|
||||||
|
Optional: true,
|
||||||
|
Default: dvCPUArchitecture,
|
||||||
|
ValidateDiagFunc: validators.CPUArchitectureValidator(),
|
||||||
|
},
|
||||||
|
mkCPUCores: {
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
Description: "The number of CPU cores",
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
ValidateDiagFunc: validation.ToDiagFunc(validation.IntBetween(1, 2304)),
|
||||||
|
},
|
||||||
|
mkCPUFlags: {
|
||||||
|
Type: schema.TypeList,
|
||||||
|
Description: "The CPU flags",
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
//DefaultFunc: func() (interface{}, error) {
|
||||||
|
// return []interface{}{}, nil
|
||||||
|
//},
|
||||||
|
Elem: &schema.Schema{Type: schema.TypeString},
|
||||||
|
},
|
||||||
|
mkCPUHotplugged: {
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
Description: "The number of hotplugged vCPUs",
|
||||||
|
Optional: true,
|
||||||
|
Default: dvCPUHotplugged,
|
||||||
|
ValidateDiagFunc: validation.ToDiagFunc(validation.IntBetween(0, 2304)),
|
||||||
|
},
|
||||||
|
mkCPULimit: {
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
Description: "Limit of CPU usage",
|
||||||
|
Optional: true,
|
||||||
|
Default: dvCPULimit,
|
||||||
|
ValidateDiagFunc: validation.ToDiagFunc(
|
||||||
|
validation.IntBetween(0, 128),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
mkCPUNUMA: {
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
Description: "Enable/disable NUMA.",
|
||||||
|
Optional: true,
|
||||||
|
Default: dvCPUNUMA,
|
||||||
|
},
|
||||||
|
mkCPUSockets: {
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
Description: "The number of CPU sockets",
|
||||||
|
Optional: true,
|
||||||
|
Default: dvCPUSockets,
|
||||||
|
ValidateDiagFunc: validation.ToDiagFunc(validation.IntBetween(1, 16)),
|
||||||
|
},
|
||||||
|
mkCPUType: {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Description: "The emulated CPU type",
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
ValidateDiagFunc: validators.CPUTypeValidator(),
|
||||||
|
},
|
||||||
|
mkCPUUnits: {
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
Description: "The CPU units",
|
||||||
|
Optional: true,
|
||||||
|
Default: dvCPUUnits,
|
||||||
|
ValidateDiagFunc: validation.ToDiagFunc(
|
||||||
|
validation.IntBetween(2, 262144),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
mkCPUAffinity: {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Description: "The CPU affinity",
|
||||||
|
Optional: true,
|
||||||
|
Default: dvCPUAffinity,
|
||||||
|
ValidateDiagFunc: validators.CPUAffinityValidator(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MaxItems: 1,
|
||||||
|
MinItems: 0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
40
proxmoxtf/resource/vm/cpu/schema_test.go
Normal file
40
proxmoxtf/resource/vm/cpu/schema_test.go
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package cpu
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
|
||||||
|
|
||||||
|
"github.com/bpg/terraform-provider-proxmox/proxmoxtf/test"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCPUSchema(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
s := Schema()
|
||||||
|
|
||||||
|
cpuSchema := test.AssertNestedSchemaExistence(t, s, MkCPU)
|
||||||
|
|
||||||
|
test.AssertOptionalArguments(t, cpuSchema, []string{
|
||||||
|
mkCPUArchitecture,
|
||||||
|
mkCPUCores,
|
||||||
|
mkCPUFlags,
|
||||||
|
mkCPUHotplugged,
|
||||||
|
mkCPUNUMA,
|
||||||
|
mkCPUSockets,
|
||||||
|
mkCPUType,
|
||||||
|
mkCPUUnits,
|
||||||
|
})
|
||||||
|
|
||||||
|
test.AssertValueTypes(t, cpuSchema, map[string]schema.ValueType{
|
||||||
|
mkCPUArchitecture: schema.TypeString,
|
||||||
|
mkCPUCores: schema.TypeInt,
|
||||||
|
mkCPUFlags: schema.TypeList,
|
||||||
|
mkCPUHotplugged: schema.TypeInt,
|
||||||
|
mkCPUNUMA: schema.TypeBool,
|
||||||
|
mkCPUSockets: schema.TypeInt,
|
||||||
|
mkCPUType: schema.TypeString,
|
||||||
|
mkCPUUnits: schema.TypeInt,
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
@ -8,7 +8,7 @@ import (
|
|||||||
"github.com/bpg/terraform-provider-proxmox/proxmoxtf/test"
|
"github.com/bpg/terraform-provider-proxmox/proxmoxtf/test"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestVMSchema(t *testing.T) {
|
func TestDiskSchema(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
s := Schema()
|
s := Schema()
|
||||||
|
@ -52,105 +52,6 @@ func BIOSValidator() schema.SchemaValidateDiagFunc {
|
|||||||
}, false))
|
}, false))
|
||||||
}
|
}
|
||||||
|
|
||||||
// CPUArchitectureValidator returns a schema validation function for a CPU architecture.
|
|
||||||
func CPUArchitectureValidator() schema.SchemaValidateDiagFunc {
|
|
||||||
return validation.ToDiagFunc(validation.StringInSlice([]string{
|
|
||||||
"aarch64",
|
|
||||||
"x86_64",
|
|
||||||
}, false))
|
|
||||||
}
|
|
||||||
|
|
||||||
// CPUTypeValidator returns a schema validation function for a CPU type.
|
|
||||||
func CPUTypeValidator() schema.SchemaValidateDiagFunc {
|
|
||||||
standardTypes := []string{
|
|
||||||
"486",
|
|
||||||
"Broadwell",
|
|
||||||
"Broadwell-IBRS",
|
|
||||||
"Broadwell-noTSX",
|
|
||||||
"Broadwell-noTSX-IBRS",
|
|
||||||
"Cascadelake-Server",
|
|
||||||
"Cascadelake-Server-noTSX",
|
|
||||||
"Cascadelake-Server-v2",
|
|
||||||
"Cascadelake-Server-v4",
|
|
||||||
"Cascadelake-Server-v5",
|
|
||||||
"Conroe",
|
|
||||||
"Cooperlake",
|
|
||||||
"Cooperlake-v2",
|
|
||||||
"EPYC",
|
|
||||||
"EPYC-IBPB",
|
|
||||||
"EPYC-Milan",
|
|
||||||
"EPYC-Rome",
|
|
||||||
"EPYC-Rome-v2",
|
|
||||||
"EPYC-v3",
|
|
||||||
"Haswell",
|
|
||||||
"Haswell-IBRS",
|
|
||||||
"Haswell-noTSX",
|
|
||||||
"Haswell-noTSX-IBRS",
|
|
||||||
"Icelake-Client",
|
|
||||||
"Icelake-Client-noTSX",
|
|
||||||
"Icelake-Server",
|
|
||||||
"Icelake-Server-noTSX",
|
|
||||||
"Icelake-Server-v3",
|
|
||||||
"Icelake-Server-v4",
|
|
||||||
"Icelake-Server-v5",
|
|
||||||
"Icelake-Server-v6",
|
|
||||||
"IvyBridge",
|
|
||||||
"IvyBridge-IBRS",
|
|
||||||
"KnightsMill",
|
|
||||||
"Nehalem",
|
|
||||||
"Nehalem-IBRS",
|
|
||||||
"Opteron_G1",
|
|
||||||
"Opteron_G2",
|
|
||||||
"Opteron_G3",
|
|
||||||
"Opteron_G4",
|
|
||||||
"Opteron_G5",
|
|
||||||
"Penryn",
|
|
||||||
"SandyBridge",
|
|
||||||
"SandyBridge-IBRS",
|
|
||||||
"SapphireRapids",
|
|
||||||
"Skylake-Client",
|
|
||||||
"Skylake-Client-IBRS",
|
|
||||||
"Skylake-Client-noTSX-IBRS",
|
|
||||||
"Skylake-Client-v4",
|
|
||||||
"Skylake-Server",
|
|
||||||
"Skylake-Server-IBRS",
|
|
||||||
"Skylake-Server-noTSX-IBRS",
|
|
||||||
"Skylake-Server-v4",
|
|
||||||
"Skylake-Server-v5",
|
|
||||||
"Westmere",
|
|
||||||
"Westmere-IBRS",
|
|
||||||
"athlon",
|
|
||||||
"core2duo",
|
|
||||||
"coreduo",
|
|
||||||
"host",
|
|
||||||
"kvm32",
|
|
||||||
"kvm64",
|
|
||||||
"max",
|
|
||||||
"pentium",
|
|
||||||
"pentium2",
|
|
||||||
"pentium3",
|
|
||||||
"phenom",
|
|
||||||
"qemu32",
|
|
||||||
"qemu64",
|
|
||||||
"x86-64-v2",
|
|
||||||
"x86-64-v2-AES",
|
|
||||||
"x86-64-v3",
|
|
||||||
"x86-64-v4",
|
|
||||||
}
|
|
||||||
|
|
||||||
return validation.ToDiagFunc(validation.Any(
|
|
||||||
validation.StringInSlice(standardTypes, false),
|
|
||||||
validation.StringMatch(regexp.MustCompile(`^custom-.+$`), "must be a valid custom CPU type"),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
// CPUAffinityValidator returns a schema validation function for a CPU affinity.
|
|
||||||
func CPUAffinityValidator() schema.SchemaValidateDiagFunc {
|
|
||||||
return validation.ToDiagFunc(
|
|
||||||
validation.StringMatch(regexp.MustCompile(`^\d+[\d-,]*$`), "must contain numbers or number ranges separated by ','"),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// QEMUAgentTypeValidator is a schema validation function for QEMU agent types.
|
// QEMUAgentTypeValidator is a schema validation function for QEMU agent types.
|
||||||
func QEMUAgentTypeValidator() schema.SchemaValidateDiagFunc {
|
func QEMUAgentTypeValidator() schema.SchemaValidateDiagFunc {
|
||||||
return validation.ToDiagFunc(validation.StringInSlice([]string{"isa", "virtio"}, false))
|
return validation.ToDiagFunc(validation.StringInSlice([]string{"isa", "virtio"}, false))
|
||||||
|
@ -12,37 +12,6 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCPUType(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
value string
|
|
||||||
valid bool
|
|
||||||
}{
|
|
||||||
{"empty", "", false},
|
|
||||||
{"invalid", "invalid", false},
|
|
||||||
{"valid", "host", true},
|
|
||||||
{"valid", "qemu64", true},
|
|
||||||
{"valid", "custom-abc", true},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
f := CPUTypeValidator()
|
|
||||||
res := f(tt.value, nil)
|
|
||||||
|
|
||||||
if tt.valid {
|
|
||||||
require.Empty(t, res, "validate: '%s'", tt.value)
|
|
||||||
} else {
|
|
||||||
require.NotEmpty(t, res, "validate: '%s'", tt.value)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMachineType(t *testing.T) {
|
func TestMachineType(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@ import (
|
|||||||
|
|
||||||
"golang.org/x/exp/maps"
|
"golang.org/x/exp/maps"
|
||||||
|
|
||||||
|
"github.com/bpg/terraform-provider-proxmox/proxmoxtf/resource/vm/cpu"
|
||||||
"github.com/bpg/terraform-provider-proxmox/proxmoxtf/resource/vm/disk"
|
"github.com/bpg/terraform-provider-proxmox/proxmoxtf/resource/vm/disk"
|
||||||
"github.com/bpg/terraform-provider-proxmox/proxmoxtf/resource/vm/network"
|
"github.com/bpg/terraform-provider-proxmox/proxmoxtf/resource/vm/network"
|
||||||
"github.com/bpg/terraform-provider-proxmox/utils"
|
"github.com/bpg/terraform-provider-proxmox/utils"
|
||||||
@ -59,15 +60,6 @@ const (
|
|||||||
dvCloneNodeName = ""
|
dvCloneNodeName = ""
|
||||||
dvCloneFull = true
|
dvCloneFull = true
|
||||||
dvCloneRetries = 1
|
dvCloneRetries = 1
|
||||||
dvCPUArchitecture = "x86_64"
|
|
||||||
dvCPUCores = 1
|
|
||||||
dvCPUHotplugged = 0
|
|
||||||
dvCPULimit = 0
|
|
||||||
dvCPUNUMA = false
|
|
||||||
dvCPUSockets = 1
|
|
||||||
dvCPUType = "qemu64"
|
|
||||||
dvCPUUnits = 1024
|
|
||||||
dvCPUAffinity = ""
|
|
||||||
dvDescription = ""
|
dvDescription = ""
|
||||||
|
|
||||||
dvEFIDiskDatastoreID = "local-lvm"
|
dvEFIDiskDatastoreID = "local-lvm"
|
||||||
@ -164,17 +156,6 @@ const (
|
|||||||
mkCloneNodeName = "node_name"
|
mkCloneNodeName = "node_name"
|
||||||
mkCloneVMID = "vm_id"
|
mkCloneVMID = "vm_id"
|
||||||
mkCloneFull = "full"
|
mkCloneFull = "full"
|
||||||
mkCPU = "cpu"
|
|
||||||
mkCPUArchitecture = "architecture"
|
|
||||||
mkCPUCores = "cores"
|
|
||||||
mkCPUFlags = "flags"
|
|
||||||
mkCPUHotplugged = "hotplugged"
|
|
||||||
mkCPULimit = "limit"
|
|
||||||
mkCPUNUMA = "numa"
|
|
||||||
mkCPUSockets = "sockets"
|
|
||||||
mkCPUType = "type"
|
|
||||||
mkCPUUnits = "units"
|
|
||||||
mkCPUAffinity = "affinity"
|
|
||||||
mkDescription = "description"
|
mkDescription = "description"
|
||||||
|
|
||||||
mkNUMA = "numa"
|
mkNUMA = "numa"
|
||||||
@ -495,108 +476,6 @@ func VM() *schema.Resource {
|
|||||||
MaxItems: 1,
|
MaxItems: 1,
|
||||||
MinItems: 0,
|
MinItems: 0,
|
||||||
},
|
},
|
||||||
mkCPU: {
|
|
||||||
Type: schema.TypeList,
|
|
||||||
Description: "The CPU allocation",
|
|
||||||
Optional: true,
|
|
||||||
DefaultFunc: func() (interface{}, error) {
|
|
||||||
return []interface{}{
|
|
||||||
map[string]interface{}{
|
|
||||||
mkCPUArchitecture: dvCPUArchitecture,
|
|
||||||
mkCPUCores: dvCPUCores,
|
|
||||||
mkCPUFlags: []interface{}{},
|
|
||||||
mkCPUHotplugged: dvCPUHotplugged,
|
|
||||||
mkCPULimit: dvCPULimit,
|
|
||||||
mkCPUNUMA: dvCPUNUMA,
|
|
||||||
mkCPUSockets: dvCPUSockets,
|
|
||||||
mkCPUType: dvCPUType,
|
|
||||||
mkCPUUnits: dvCPUUnits,
|
|
||||||
mkCPUAffinity: dvCPUAffinity,
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
},
|
|
||||||
Elem: &schema.Resource{
|
|
||||||
Schema: map[string]*schema.Schema{
|
|
||||||
mkCPUArchitecture: {
|
|
||||||
Type: schema.TypeString,
|
|
||||||
Description: "The CPU architecture",
|
|
||||||
Optional: true,
|
|
||||||
Default: dvCPUArchitecture,
|
|
||||||
ValidateDiagFunc: CPUArchitectureValidator(),
|
|
||||||
},
|
|
||||||
mkCPUCores: {
|
|
||||||
Type: schema.TypeInt,
|
|
||||||
Description: "The number of CPU cores",
|
|
||||||
Optional: true,
|
|
||||||
Default: dvCPUCores,
|
|
||||||
ValidateDiagFunc: validation.ToDiagFunc(validation.IntBetween(1, 2304)),
|
|
||||||
},
|
|
||||||
mkCPUFlags: {
|
|
||||||
Type: schema.TypeList,
|
|
||||||
Description: "The CPU flags",
|
|
||||||
Optional: true,
|
|
||||||
DefaultFunc: func() (interface{}, error) {
|
|
||||||
return []interface{}{}, nil
|
|
||||||
},
|
|
||||||
Elem: &schema.Schema{Type: schema.TypeString},
|
|
||||||
},
|
|
||||||
mkCPUHotplugged: {
|
|
||||||
Type: schema.TypeInt,
|
|
||||||
Description: "The number of hotplugged vCPUs",
|
|
||||||
Optional: true,
|
|
||||||
Default: dvCPUHotplugged,
|
|
||||||
ValidateDiagFunc: validation.ToDiagFunc(validation.IntBetween(0, 2304)),
|
|
||||||
},
|
|
||||||
mkCPULimit: {
|
|
||||||
Type: schema.TypeInt,
|
|
||||||
Description: "Limit of CPU usage",
|
|
||||||
Optional: true,
|
|
||||||
Default: dvCPULimit,
|
|
||||||
ValidateDiagFunc: validation.ToDiagFunc(
|
|
||||||
validation.IntBetween(0, 128),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
mkCPUNUMA: {
|
|
||||||
Type: schema.TypeBool,
|
|
||||||
Description: "Enable/disable NUMA.",
|
|
||||||
Optional: true,
|
|
||||||
Default: dvCPUNUMA,
|
|
||||||
},
|
|
||||||
mkCPUSockets: {
|
|
||||||
Type: schema.TypeInt,
|
|
||||||
Description: "The number of CPU sockets",
|
|
||||||
Optional: true,
|
|
||||||
Default: dvCPUSockets,
|
|
||||||
ValidateDiagFunc: validation.ToDiagFunc(validation.IntBetween(1, 16)),
|
|
||||||
},
|
|
||||||
mkCPUType: {
|
|
||||||
Type: schema.TypeString,
|
|
||||||
Description: "The emulated CPU type",
|
|
||||||
Optional: true,
|
|
||||||
Default: dvCPUType,
|
|
||||||
ValidateDiagFunc: CPUTypeValidator(),
|
|
||||||
},
|
|
||||||
mkCPUUnits: {
|
|
||||||
Type: schema.TypeInt,
|
|
||||||
Description: "The CPU units",
|
|
||||||
Optional: true,
|
|
||||||
Default: dvCPUUnits,
|
|
||||||
ValidateDiagFunc: validation.ToDiagFunc(
|
|
||||||
validation.IntBetween(2, 262144),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
mkCPUAffinity: {
|
|
||||||
Type: schema.TypeString,
|
|
||||||
Description: "The CPU affinity",
|
|
||||||
Optional: true,
|
|
||||||
Default: dvCPUAffinity,
|
|
||||||
ValidateDiagFunc: CPUAffinityValidator(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
MaxItems: 1,
|
|
||||||
MinItems: 0,
|
|
||||||
},
|
|
||||||
mkDescription: {
|
mkDescription: {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Description: "The description",
|
Description: "The description",
|
||||||
@ -1450,6 +1329,7 @@ func VM() *schema.Resource {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
structure.MergeSchema(s, cpu.Schema())
|
||||||
structure.MergeSchema(s, disk.Schema())
|
structure.MergeSchema(s, disk.Schema())
|
||||||
structure.MergeSchema(s, network.Schema())
|
structure.MergeSchema(s, network.Schema())
|
||||||
|
|
||||||
@ -1871,7 +1751,6 @@ func vmCreateClone(ctx context.Context, d *schema.ResourceData, m interface{}) d
|
|||||||
kvmArguments := d.Get(mkKVMArguments).(string)
|
kvmArguments := d.Get(mkKVMArguments).(string)
|
||||||
scsiHardware := d.Get(mkSCSIHardware).(string)
|
scsiHardware := d.Get(mkSCSIHardware).(string)
|
||||||
cdrom := d.Get(mkCDROM).([]interface{})
|
cdrom := d.Get(mkCDROM).([]interface{})
|
||||||
cpu := d.Get(mkCPU).([]interface{})
|
|
||||||
initialization := d.Get(mkInitialization).([]interface{})
|
initialization := d.Get(mkInitialization).([]interface{})
|
||||||
hostPCI := d.Get(mkHostPCI).([]interface{})
|
hostPCI := d.Get(mkHostPCI).([]interface{})
|
||||||
hostUSB := d.Get(mkHostUSB).([]interface{})
|
hostUSB := d.Get(mkHostUSB).([]interface{})
|
||||||
@ -1964,53 +1843,7 @@ func vmCreateClone(ctx context.Context, d *schema.ResourceData, m interface{}) d
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(cpu) > 0 && cpu[0] != nil {
|
cpu.UpdateWhenClone(d, api, updateBody)
|
||||||
cpuBlock := cpu[0].(map[string]interface{})
|
|
||||||
|
|
||||||
cpuArchitecture := cpuBlock[mkCPUArchitecture].(string)
|
|
||||||
cpuCores := cpuBlock[mkCPUCores].(int)
|
|
||||||
cpuFlags := cpuBlock[mkCPUFlags].([]interface{})
|
|
||||||
cpuHotplugged := cpuBlock[mkCPUHotplugged].(int)
|
|
||||||
cpuLimit := cpuBlock[mkCPULimit].(int)
|
|
||||||
cpuNUMA := types.CustomBool(cpuBlock[mkCPUNUMA].(bool))
|
|
||||||
cpuSockets := cpuBlock[mkCPUSockets].(int)
|
|
||||||
cpuType := cpuBlock[mkCPUType].(string)
|
|
||||||
cpuUnits := cpuBlock[mkCPUUnits].(int)
|
|
||||||
cpuAffinity := cpuBlock[mkCPUAffinity].(string)
|
|
||||||
|
|
||||||
cpuFlagsConverted := make([]string, len(cpuFlags))
|
|
||||||
|
|
||||||
for fi, flag := range cpuFlags {
|
|
||||||
cpuFlagsConverted[fi] = flag.(string)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only the root account is allowed to change the CPU architecture, which makes this check necessary.
|
|
||||||
if api.API().IsRootTicket() ||
|
|
||||||
cpuArchitecture != dvCPUArchitecture {
|
|
||||||
updateBody.CPUArchitecture = &cpuArchitecture
|
|
||||||
}
|
|
||||||
|
|
||||||
updateBody.CPUCores = &cpuCores
|
|
||||||
updateBody.CPUEmulation = &vms.CustomCPUEmulation{
|
|
||||||
Flags: &cpuFlagsConverted,
|
|
||||||
Type: cpuType,
|
|
||||||
}
|
|
||||||
updateBody.NUMAEnabled = &cpuNUMA
|
|
||||||
updateBody.CPUSockets = &cpuSockets
|
|
||||||
updateBody.CPUUnits = &cpuUnits
|
|
||||||
|
|
||||||
if cpuAffinity != "" {
|
|
||||||
updateBody.CPUAffinity = &cpuAffinity
|
|
||||||
}
|
|
||||||
|
|
||||||
if cpuHotplugged > 0 {
|
|
||||||
updateBody.VirtualCPUCount = &cpuHotplugged
|
|
||||||
}
|
|
||||||
|
|
||||||
if cpuLimit > 0 {
|
|
||||||
updateBody.CPULimit = &cpuLimit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
vmConfig, err := vmAPI.GetVM(ctx)
|
vmConfig, err := vmAPI.GetVM(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -2194,8 +2027,6 @@ func vmCreateClone(ctx context.Context, d *schema.ResourceData, m interface{}) d
|
|||||||
return diag.FromErr(e)
|
return diag.FromErr(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////////
|
|
||||||
|
|
||||||
allDiskInfo := disk.GetInfo(vmConfig, d) // from the cloned VM
|
allDiskInfo := disk.GetInfo(vmConfig, d) // from the cloned VM
|
||||||
|
|
||||||
planDisks, e := disk.GetDiskDeviceObjects(d, VM(), nil) // from the resource config
|
planDisks, e := disk.GetDiskDeviceObjects(d, VM(), nil) // from the resource config
|
||||||
@ -2382,28 +2213,6 @@ func vmCreateCustom(ctx context.Context, d *schema.ResourceData, m interface{})
|
|||||||
cdromFileID = "cdrom"
|
cdromFileID = "cdrom"
|
||||||
}
|
}
|
||||||
|
|
||||||
cpuBlock, err := structure.GetSchemaBlock(
|
|
||||||
resource,
|
|
||||||
d,
|
|
||||||
[]string{mkCPU},
|
|
||||||
0,
|
|
||||||
true,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return diag.FromErr(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
cpuArchitecture := cpuBlock[mkCPUArchitecture].(string)
|
|
||||||
cpuCores := cpuBlock[mkCPUCores].(int)
|
|
||||||
cpuFlags := cpuBlock[mkCPUFlags].([]interface{})
|
|
||||||
cpuHotplugged := cpuBlock[mkCPUHotplugged].(int)
|
|
||||||
cpuLimit := cpuBlock[mkCPULimit].(int)
|
|
||||||
cpuSockets := cpuBlock[mkCPUSockets].(int)
|
|
||||||
cpuNUMA := types.CustomBool(cpuBlock[mkCPUNUMA].(bool))
|
|
||||||
cpuType := cpuBlock[mkCPUType].(string)
|
|
||||||
cpuUnits := cpuBlock[mkCPUUnits].(int)
|
|
||||||
cpuAffinity := cpuBlock[mkCPUAffinity].(string)
|
|
||||||
|
|
||||||
description := d.Get(mkDescription).(string)
|
description := d.Get(mkDescription).(string)
|
||||||
|
|
||||||
var efiDisk *vms.CustomEFIDisk
|
var efiDisk *vms.CustomEFIDisk
|
||||||
@ -2590,11 +2399,6 @@ func vmCreateCustom(ctx context.Context, d *schema.ResourceData, m interface{})
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cpuFlagsConverted := make([]string, len(cpuFlags))
|
|
||||||
for fi, flag := range cpuFlags {
|
|
||||||
cpuFlagsConverted[fi] = flag.(string)
|
|
||||||
}
|
|
||||||
|
|
||||||
ideDevice2Media := "cdrom"
|
ideDevice2Media := "cdrom"
|
||||||
ideDevices := vms.CustomStorageDevices{
|
ideDevices := vms.CustomStorageDevices{
|
||||||
cdromCloudInitInterface: &vms.CustomStorageDevice{
|
cdromCloudInitInterface: &vms.CustomStorageDevice{
|
||||||
@ -2631,14 +2435,7 @@ func vmCreateCustom(ctx context.Context, d *schema.ResourceData, m interface{})
|
|||||||
Boot: &vms.CustomBoot{
|
Boot: &vms.CustomBoot{
|
||||||
Order: &bootOrderConverted,
|
Order: &bootOrderConverted,
|
||||||
},
|
},
|
||||||
CloudInitConfig: initializationConfig,
|
CloudInitConfig: initializationConfig,
|
||||||
CPUCores: &cpuCores,
|
|
||||||
CPUEmulation: &vms.CustomCPUEmulation{
|
|
||||||
Flags: &cpuFlagsConverted,
|
|
||||||
Type: cpuType,
|
|
||||||
},
|
|
||||||
CPUSockets: &cpuSockets,
|
|
||||||
CPUUnits: &cpuUnits,
|
|
||||||
DedicatedMemory: &memoryDedicated,
|
DedicatedMemory: &memoryDedicated,
|
||||||
DeletionProtection: &protection,
|
DeletionProtection: &protection,
|
||||||
EFIDisk: efiDisk,
|
EFIDisk: efiDisk,
|
||||||
@ -2647,7 +2444,6 @@ func vmCreateCustom(ctx context.Context, d *schema.ResourceData, m interface{})
|
|||||||
IDEDevices: ideDevices,
|
IDEDevices: ideDevices,
|
||||||
KeyboardLayout: &keyboardLayout,
|
KeyboardLayout: &keyboardLayout,
|
||||||
NetworkDevices: networkDeviceObjects,
|
NetworkDevices: networkDeviceObjects,
|
||||||
NUMAEnabled: &cpuNUMA,
|
|
||||||
NUMADevices: numaDeviceObjects,
|
NUMADevices: numaDeviceObjects,
|
||||||
OSType: &operatingSystemType,
|
OSType: &operatingSystemType,
|
||||||
PCIDevices: pciDeviceObjects,
|
PCIDevices: pciDeviceObjects,
|
||||||
@ -2664,6 +2460,11 @@ func vmCreateCustom(ctx context.Context, d *schema.ResourceData, m interface{})
|
|||||||
VMID: &vmID,
|
VMID: &vmID,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = cpu.Create(resource, d, api, createBody)
|
||||||
|
if err != nil {
|
||||||
|
return diag.FromErr(err)
|
||||||
|
}
|
||||||
|
|
||||||
if sataDeviceObjects != nil {
|
if sataDeviceObjects != nil {
|
||||||
createBody.SATADevices = sataDeviceObjects
|
createBody.SATADevices = sataDeviceObjects
|
||||||
}
|
}
|
||||||
@ -2676,24 +2477,6 @@ func vmCreateCustom(ctx context.Context, d *schema.ResourceData, m interface{})
|
|||||||
createBody.VirtualIODevices = virtioDeviceObjects
|
createBody.VirtualIODevices = virtioDeviceObjects
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only the root account is allowed to change the CPU architecture, which makes this check necessary.
|
|
||||||
if api.API().IsRootTicket() ||
|
|
||||||
cpuArchitecture != dvCPUArchitecture {
|
|
||||||
createBody.CPUArchitecture = &cpuArchitecture
|
|
||||||
}
|
|
||||||
|
|
||||||
if cpuHotplugged > 0 {
|
|
||||||
createBody.VirtualCPUCount = &cpuHotplugged
|
|
||||||
}
|
|
||||||
|
|
||||||
if cpuLimit > 0 {
|
|
||||||
createBody.CPULimit = &cpuLimit
|
|
||||||
}
|
|
||||||
|
|
||||||
if cpuAffinity != "" {
|
|
||||||
createBody.CPUAffinity = &cpuAffinity
|
|
||||||
}
|
|
||||||
|
|
||||||
if description != "" {
|
if description != "" {
|
||||||
createBody.Description = &description
|
createBody.Description = &description
|
||||||
}
|
}
|
||||||
@ -3582,48 +3365,8 @@ func vmReadCustom(
|
|||||||
diags = append(diags, diag.FromErr(err)...)
|
diags = append(diags, diag.FromErr(err)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compare the CPU configuration to the one stored in the state.
|
err := cpu.Read(vmConfig, api, d, len(clone) > 0)
|
||||||
cpu := map[string]interface{}{}
|
diags = append(diags, diag.FromErr(err)...)
|
||||||
|
|
||||||
if vmConfig.CPUArchitecture != nil {
|
|
||||||
cpu[mkCPUArchitecture] = *vmConfig.CPUArchitecture
|
|
||||||
} else {
|
|
||||||
// Default value of "arch" is "" according to the API documentation.
|
|
||||||
// However, assume the provider's default value as a workaround when the root account is not being used.
|
|
||||||
if !api.API().IsRootTicket() {
|
|
||||||
cpu[mkCPUArchitecture] = dvCPUArchitecture
|
|
||||||
} else {
|
|
||||||
cpu[mkCPUArchitecture] = ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if vmConfig.CPUCores != nil {
|
|
||||||
cpu[mkCPUCores] = *vmConfig.CPUCores
|
|
||||||
} else {
|
|
||||||
// Default value of "cores" is "1" according to the API documentation.
|
|
||||||
cpu[mkCPUCores] = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
if vmConfig.VirtualCPUCount != nil {
|
|
||||||
cpu[mkCPUHotplugged] = *vmConfig.VirtualCPUCount
|
|
||||||
} else {
|
|
||||||
// Default value of "vcpus" is "1" according to the API documentation.
|
|
||||||
cpu[mkCPUHotplugged] = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
if vmConfig.CPULimit != nil {
|
|
||||||
cpu[mkCPULimit] = *vmConfig.CPULimit
|
|
||||||
} else {
|
|
||||||
// Default value of "cpulimit" is "0" according to the API documentation.
|
|
||||||
cpu[mkCPULimit] = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
if vmConfig.NUMAEnabled != nil {
|
|
||||||
cpu[mkCPUNUMA] = *vmConfig.NUMAEnabled
|
|
||||||
} else {
|
|
||||||
// Default value of "numa" is "false" according to the API documentation.
|
|
||||||
cpu[mkCPUNUMA] = false
|
|
||||||
}
|
|
||||||
|
|
||||||
currentNUMAList := d.Get(mkNUMA).([]interface{})
|
currentNUMAList := d.Get(mkNUMA).([]interface{})
|
||||||
numaMap := map[string]interface{}{}
|
numaMap := map[string]interface{}{}
|
||||||
@ -3663,66 +3406,6 @@ func vmReadCustom(
|
|||||||
diags = append(diags, diag.FromErr(err)...)
|
diags = append(diags, diag.FromErr(err)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
if vmConfig.CPUSockets != nil {
|
|
||||||
cpu[mkCPUSockets] = *vmConfig.CPUSockets
|
|
||||||
} else {
|
|
||||||
// Default value of "sockets" is "1" according to the API documentation.
|
|
||||||
cpu[mkCPUSockets] = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
if vmConfig.CPUEmulation != nil {
|
|
||||||
if vmConfig.CPUEmulation.Flags != nil {
|
|
||||||
convertedFlags := make([]interface{}, len(*vmConfig.CPUEmulation.Flags))
|
|
||||||
|
|
||||||
for fi, fv := range *vmConfig.CPUEmulation.Flags {
|
|
||||||
convertedFlags[fi] = fv
|
|
||||||
}
|
|
||||||
|
|
||||||
cpu[mkCPUFlags] = convertedFlags
|
|
||||||
} else {
|
|
||||||
cpu[mkCPUFlags] = []interface{}{}
|
|
||||||
}
|
|
||||||
|
|
||||||
cpu[mkCPUType] = vmConfig.CPUEmulation.Type
|
|
||||||
} else {
|
|
||||||
cpu[mkCPUFlags] = []interface{}{}
|
|
||||||
// Default value of "cputype" is "qemu64" according to the QEMU documentation.
|
|
||||||
cpu[mkCPUType] = "qemu64"
|
|
||||||
}
|
|
||||||
|
|
||||||
if vmConfig.CPUUnits != nil {
|
|
||||||
cpu[mkCPUUnits] = *vmConfig.CPUUnits
|
|
||||||
} else {
|
|
||||||
// Default value of "cpuunits" is "1024" according to the API documentation.
|
|
||||||
cpu[mkCPUUnits] = 1024
|
|
||||||
}
|
|
||||||
|
|
||||||
if vmConfig.CPUAffinity != nil {
|
|
||||||
cpu[mkCPUAffinity] = *vmConfig.CPUAffinity
|
|
||||||
} else {
|
|
||||||
cpu[mkCPUAffinity] = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
currentCPU := d.Get(mkCPU).([]interface{})
|
|
||||||
|
|
||||||
if len(clone) > 0 {
|
|
||||||
if len(currentCPU) > 0 {
|
|
||||||
err := d.Set(mkCPU, []interface{}{cpu})
|
|
||||||
diags = append(diags, diag.FromErr(err)...)
|
|
||||||
}
|
|
||||||
} else if len(currentCPU) > 0 ||
|
|
||||||
cpu[mkCPUArchitecture] != dvCPUArchitecture ||
|
|
||||||
cpu[mkCPUCores] != dvCPUCores ||
|
|
||||||
len(cpu[mkCPUFlags].([]interface{})) > 0 ||
|
|
||||||
cpu[mkCPUHotplugged] != dvCPUHotplugged ||
|
|
||||||
cpu[mkCPULimit] != dvCPULimit ||
|
|
||||||
cpu[mkCPUSockets] != dvCPUSockets ||
|
|
||||||
cpu[mkCPUType] != dvCPUType ||
|
|
||||||
cpu[mkCPUUnits] != dvCPUUnits {
|
|
||||||
err := d.Set(mkCPU, []interface{}{cpu})
|
|
||||||
diags = append(diags, diag.FromErr(err)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
allDiskInfo := disk.GetInfo(vmConfig, d)
|
allDiskInfo := disk.GetInfo(vmConfig, d)
|
||||||
|
|
||||||
diags = append(diags, disk.Read(ctx, d, allDiskInfo, vmID, api, nodeName, len(clone) > 0)...)
|
diags = append(diags, disk.Read(ctx, d, allDiskInfo, vmID, api, nodeName, len(clone) > 0)...)
|
||||||
@ -4961,79 +4644,14 @@ func vmUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.D
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare the new CPU configuration.
|
dd, rr, err := cpu.Update(d, resource, api, updateBody)
|
||||||
|
if err != nil {
|
||||||
if d.HasChange(mkCPU) {
|
return diag.FromErr(err)
|
||||||
cpuBlock, err := structure.GetSchemaBlock(
|
|
||||||
resource,
|
|
||||||
d,
|
|
||||||
[]string{mkCPU},
|
|
||||||
0,
|
|
||||||
true,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return diag.FromErr(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
cpuArchitecture := cpuBlock[mkCPUArchitecture].(string)
|
|
||||||
cpuCores := cpuBlock[mkCPUCores].(int)
|
|
||||||
cpuFlags := cpuBlock[mkCPUFlags].([]interface{})
|
|
||||||
cpuHotplugged := cpuBlock[mkCPUHotplugged].(int)
|
|
||||||
cpuLimit := cpuBlock[mkCPULimit].(int)
|
|
||||||
cpuNUMA := types.CustomBool(cpuBlock[mkCPUNUMA].(bool))
|
|
||||||
cpuSockets := cpuBlock[mkCPUSockets].(int)
|
|
||||||
cpuType := cpuBlock[mkCPUType].(string)
|
|
||||||
cpuUnits := cpuBlock[mkCPUUnits].(int)
|
|
||||||
cpuAffinity := cpuBlock[mkCPUAffinity].(string)
|
|
||||||
|
|
||||||
// Only the root account is allowed to change the CPU architecture, which makes this check necessary.
|
|
||||||
if api.API().IsRootTicket() ||
|
|
||||||
cpuArchitecture != dvCPUArchitecture {
|
|
||||||
updateBody.CPUArchitecture = &cpuArchitecture
|
|
||||||
}
|
|
||||||
|
|
||||||
updateBody.CPUCores = &cpuCores
|
|
||||||
updateBody.CPUSockets = &cpuSockets
|
|
||||||
updateBody.CPUUnits = &cpuUnits
|
|
||||||
updateBody.NUMAEnabled = &cpuNUMA
|
|
||||||
|
|
||||||
// CPU affinity is a special case, only root can change it.
|
|
||||||
// we can't even have it in the delete list, as PVE will return an error for non-root.
|
|
||||||
// Hence, checking explicitly if it has changed.
|
|
||||||
if d.HasChange(mkCPU + ".0." + mkCPUAffinity) {
|
|
||||||
if cpuAffinity != "" {
|
|
||||||
updateBody.CPUAffinity = &cpuAffinity
|
|
||||||
} else {
|
|
||||||
del = append(del, "affinity")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if cpuHotplugged > 0 {
|
|
||||||
updateBody.VirtualCPUCount = &cpuHotplugged
|
|
||||||
} else {
|
|
||||||
del = append(del, "vcpus")
|
|
||||||
}
|
|
||||||
|
|
||||||
if cpuLimit > 0 {
|
|
||||||
updateBody.CPULimit = &cpuLimit
|
|
||||||
} else {
|
|
||||||
del = append(del, "cpulimit")
|
|
||||||
}
|
|
||||||
|
|
||||||
cpuFlagsConverted := make([]string, len(cpuFlags))
|
|
||||||
|
|
||||||
for fi, flag := range cpuFlags {
|
|
||||||
cpuFlagsConverted[fi] = flag.(string)
|
|
||||||
}
|
|
||||||
|
|
||||||
updateBody.CPUEmulation = &vms.CustomCPUEmulation{
|
|
||||||
Flags: &cpuFlagsConverted,
|
|
||||||
Type: cpuType,
|
|
||||||
}
|
|
||||||
|
|
||||||
rebootRequired = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
del = append(del, dd...)
|
||||||
|
rebootRequired = rebootRequired || rr
|
||||||
|
|
||||||
// Prepare the new disk device configuration.
|
// Prepare the new disk device configuration.
|
||||||
allDiskInfo := disk.GetInfo(vmConfig, d)
|
allDiskInfo := disk.GetInfo(vmConfig, d)
|
||||||
|
|
||||||
@ -5042,7 +4660,7 @@ func vmUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.D
|
|||||||
return diag.FromErr(err)
|
return diag.FromErr(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
rr, err := disk.Update(d, planDisks, allDiskInfo, updateBody)
|
rr, err = disk.Update(d, planDisks, allDiskInfo, updateBody)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return diag.FromErr(err)
|
return diag.FromErr(err)
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ import (
|
|||||||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
|
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/bpg/terraform-provider-proxmox/proxmoxtf/resource/vm/cpu"
|
||||||
"github.com/bpg/terraform-provider-proxmox/proxmoxtf/resource/vm/disk"
|
"github.com/bpg/terraform-provider-proxmox/proxmoxtf/resource/vm/disk"
|
||||||
"github.com/bpg/terraform-provider-proxmox/proxmoxtf/resource/vm/network"
|
"github.com/bpg/terraform-provider-proxmox/proxmoxtf/resource/vm/network"
|
||||||
"github.com/bpg/terraform-provider-proxmox/proxmoxtf/test"
|
"github.com/bpg/terraform-provider-proxmox/proxmoxtf/test"
|
||||||
@ -45,7 +46,7 @@ func TestVMSchema(t *testing.T) {
|
|||||||
mkBootOrder,
|
mkBootOrder,
|
||||||
mkCDROM,
|
mkCDROM,
|
||||||
mkClone,
|
mkClone,
|
||||||
mkCPU,
|
cpu.MkCPU,
|
||||||
mkDescription,
|
mkDescription,
|
||||||
disk.MkDisk,
|
disk.MkDisk,
|
||||||
mkEFIDisk,
|
mkEFIDisk,
|
||||||
@ -75,7 +76,7 @@ func TestVMSchema(t *testing.T) {
|
|||||||
mkBIOS: schema.TypeString,
|
mkBIOS: schema.TypeString,
|
||||||
mkBootOrder: schema.TypeList,
|
mkBootOrder: schema.TypeList,
|
||||||
mkCDROM: schema.TypeList,
|
mkCDROM: schema.TypeList,
|
||||||
mkCPU: schema.TypeList,
|
cpu.MkCPU: schema.TypeList,
|
||||||
mkDescription: schema.TypeString,
|
mkDescription: schema.TypeString,
|
||||||
disk.MkDisk: schema.TypeList,
|
disk.MkDisk: schema.TypeList,
|
||||||
mkEFIDisk: schema.TypeList,
|
mkEFIDisk: schema.TypeList,
|
||||||
@ -153,30 +154,6 @@ func TestVMSchema(t *testing.T) {
|
|||||||
mkCloneVMID: schema.TypeInt,
|
mkCloneVMID: schema.TypeInt,
|
||||||
})
|
})
|
||||||
|
|
||||||
cpuSchema := test.AssertNestedSchemaExistence(t, s, mkCPU)
|
|
||||||
|
|
||||||
test.AssertOptionalArguments(t, cpuSchema, []string{
|
|
||||||
mkCPUArchitecture,
|
|
||||||
mkCPUCores,
|
|
||||||
mkCPUFlags,
|
|
||||||
mkCPUHotplugged,
|
|
||||||
mkCPUNUMA,
|
|
||||||
mkCPUSockets,
|
|
||||||
mkCPUType,
|
|
||||||
mkCPUUnits,
|
|
||||||
})
|
|
||||||
|
|
||||||
test.AssertValueTypes(t, cpuSchema, map[string]schema.ValueType{
|
|
||||||
mkCPUArchitecture: schema.TypeString,
|
|
||||||
mkCPUCores: schema.TypeInt,
|
|
||||||
mkCPUFlags: schema.TypeList,
|
|
||||||
mkCPUHotplugged: schema.TypeInt,
|
|
||||||
mkCPUNUMA: schema.TypeBool,
|
|
||||||
mkCPUSockets: schema.TypeInt,
|
|
||||||
mkCPUType: schema.TypeString,
|
|
||||||
mkCPUUnits: schema.TypeInt,
|
|
||||||
})
|
|
||||||
|
|
||||||
efiDiskSchema := test.AssertNestedSchemaExistence(t, s, mkEFIDisk)
|
efiDiskSchema := test.AssertNestedSchemaExistence(t, s, mkEFIDisk)
|
||||||
|
|
||||||
test.AssertOptionalArguments(t, efiDiskSchema, []string{
|
test.AssertOptionalArguments(t, efiDiskSchema, []string{
|
||||||
|
Loading…
Reference in New Issue
Block a user