0
0
mirror of https://github.com/bpg/terraform-provider-proxmox.git synced 2025-07-05 05:24:01 +00:00
terraform-provider-proxmox/proxmoxtf/resource/vm/cpu/cpu.go
Pavel Boldyrev 9c72e584de
fix(vm): do not overwrite cpu attributes with defaults when cloning
Signed-off-by: Pavel Boldyrev <627562+bpg@users.noreply.github.com>
2024-04-15 23:04:53 -04:00

387 lines
11 KiB
Go

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
}