Skip to content

Commit

Permalink
feat: memory mapping
Browse files Browse the repository at this point in the history
  • Loading branch information
Tinyblargon committed Jul 26, 2024
1 parent f1d62ee commit 4098fd3
Show file tree
Hide file tree
Showing 3 changed files with 187 additions and 39 deletions.
73 changes: 34 additions & 39 deletions proxmox/config_qemu.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ type (
type ConfigQemu struct {
Agent *QemuGuestAgent `json:"agent,omitempty"`
Args string `json:"args,omitempty"`
Balloon int `json:"balloon,omitempty"` // TODO should probably be a bool
Bios string `json:"bios,omitempty"`
Boot string `json:"boot,omitempty"` // TODO should be an array of custom enums
BootDisk string `json:"bootdisk,omitempty"` // TODO discuss deprecation? Only returned as it's deprecated in the proxmox api
Expand All @@ -48,9 +47,9 @@ type ConfigQemu struct {
Iso *IsoFile `json:"iso,omitempty"` // Same as Disks.Ide.Disk_2.CdRom.Iso
LinkedVmId uint `json:"linked_id,omitempty"` // Only returned setting it has no effect
Machine string `json:"machine,omitempty"` // TODO should be custom type with enum
Memory int `json:"memory,omitempty"` // TODO should be uint
Name string `json:"name,omitempty"` // TODO should be custom type as there are character and length limitations
Node string `json:"node,omitempty"` // Only returned setting it has no effect, set node in the VmRef instead
Memory *QemuMemory `json:"memory,omitempty"`
Name string `json:"name,omitempty"` // TODO should be custom type as there are character and length limitations
Node string `json:"node,omitempty"` // Only returned setting it has no effect, set node in the VmRef instead
Onboot *bool `json:"onboot,omitempty"`
Pool *PoolName `json:"pool,omitempty"`
Protection *bool `json:"protection,omitempty"`
Expand Down Expand Up @@ -82,6 +81,7 @@ type ConfigQemu struct {

const (
ConfigQemu_Error_UnableToUpdateWithoutReboot string = "unable to update vm without rebooting"
ConfigQemu_Error_MemoryRequired string = "memory is required during creation"
)

// Create - Tell Proxmox API to make the VM
Expand Down Expand Up @@ -174,9 +174,6 @@ func (config ConfigQemu) mapToAPI(currentConfig ConfigQemu, version Version) (re
if config.Agent != nil {
params["agent"] = config.Agent.mapToAPI(currentConfig.Agent)
}
if config.Balloon >= 1 {
params["balloon"] = config.Balloon
}
if config.Bios != "" {
params["bios"] = config.Bios
}
Expand Down Expand Up @@ -208,9 +205,6 @@ func (config ConfigQemu) mapToAPI(currentConfig ConfigQemu, version Version) (re
if config.Machine != "" {
params["machine"] = config.Machine
}
if config.Memory != 0 {
params["memory"] = config.Memory
}
if config.Name != "" {
params["name"] = config.Name
}
Expand Down Expand Up @@ -286,6 +280,9 @@ func (config ConfigQemu) mapToAPI(currentConfig ConfigQemu, version Version) (re
if config.CloudInit != nil {
itemsToDelete += config.CloudInit.mapToAPI(currentConfig.CloudInit, params, version)
}
if config.Memory != nil {
itemsToDelete += config.Memory.mapToAPI(currentConfig.Memory, params)
}

// Create EFI disk
config.CreateQemuEfiParams(params)
Expand Down Expand Up @@ -328,6 +325,7 @@ func (ConfigQemu) mapToStruct(vmr *VmRef, params map[string]interface{}) (*Confi

config := ConfigQemu{
CloudInit: CloudInit{}.mapToSDK(params),
Memory: QemuMemory{}.mapToSDK(params),
}

if vmr != nil {
Expand All @@ -343,12 +341,6 @@ func (ConfigQemu) mapToStruct(vmr *VmRef, params map[string]interface{}) (*Confi
if _, isSet := params["args"]; isSet {
config.Args = strings.TrimSpace(params["args"].(string))
}
if _, isSet := params["balloon"]; isSet {
balloon := int(params["balloon"].(float64))
if balloon > 0 {
config.Balloon = balloon
}
}
//boot by default from hard disk (c), CD-ROM (d), network (n).
if _, isSet := params["boot"]; isSet {
config.Boot = params["boot"].(string)
Expand All @@ -373,18 +365,6 @@ func (ConfigQemu) mapToStruct(vmr *VmRef, params map[string]interface{}) (*Confi
if _, isSet := params["machine"]; isSet {
config.Machine = params["machine"].(string)
}
if _, isSet := params["memory"]; isSet {
switch params["memory"].(type) {
case float64:
config.Memory = int(params["memory"].(float64))
case string:
mem, err := strconv.ParseFloat(params["memory"].(string), 64)
if err != nil {
return nil, err
}
config.Memory = int(mem)
}
}
if _, isSet := params["name"]; isSet {
config.Name = params["name"].(string)
}
Expand Down Expand Up @@ -832,6 +812,32 @@ func (newConfig ConfigQemu) setAdvanced(currentConfig *ConfigQemu, rebootIfNeede
func (config ConfigQemu) Validate(current *ConfigQemu, version Version) (err error) {
// TODO test all other use cases
// TODO has no context about changes caused by updating the vm
if current == nil { // Create
if config.Memory == nil {
return errors.New(ConfigQemu_Error_MemoryRequired)
} else {
if err = config.Memory.Validate(nil); err != nil {
return
}
}
if config.TPM != nil {
if err = config.TPM.Validate(nil); err != nil {
return
}
}
} else { // Update
if config.Memory != nil {
if err = config.Memory.Validate(current.Memory); err != nil {
return
}
}
if config.TPM != nil {
if err = config.TPM.Validate(current.TPM); err != nil {
return
}
}
}
// Shared
if config.Agent != nil {
if err = config.Agent.Validate(); err != nil {
return
Expand All @@ -853,17 +859,6 @@ func (config ConfigQemu) Validate(current *ConfigQemu, version Version) (err err
return
}
}
if config.TPM != nil {
if current == nil {
if err = config.TPM.Validate(nil); err != nil {
return
}
} else {
if err = config.TPM.Validate(current.TPM); err != nil {
return
}
}
}
if config.Tags != nil {
if err := Tag("").validate(*config.Tags); err != nil {
return err
Expand Down
78 changes: 78 additions & 0 deletions proxmox/config_qemu_memory.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package proxmox

import (
"github.com/Telmate/proxmox-api-go/internal/parse"
)

type QemuMemory struct {
CapacityMiB *QemuMemoryCapacity `json:"capacity,omitempty"` // min 1, max 4178944
MinimumCapacityMiB *QemuMemoryBalloonCapacity `json:"balloon,omitempty"` // 0 to clear (balloon), max 4178944
Shares *QemuMemoryShares `json:"shares,omitempty"` // 0 to clear, max 50000
}

func (config QemuMemory) mapToAPI(current *QemuMemory, params map[string]interface{}) string {
if current == nil { // create
if config.CapacityMiB != nil {
params["memory"] = *config.CapacityMiB
}
if config.MinimumCapacityMiB != nil {
params["balloon"] = *config.MinimumCapacityMiB
if config.CapacityMiB == nil {
params["memory"] = *config.MinimumCapacityMiB
}
}
if config.Shares != nil {
if *config.Shares > 0 {
params["shares"] = *config.Shares
}
}
return ""
}
// update
if config.CapacityMiB != nil {
params["memory"] = *config.CapacityMiB
if config.MinimumCapacityMiB == nil && current.MinimumCapacityMiB != nil && uint32(*current.MinimumCapacityMiB) > uint32(*config.CapacityMiB) {
params["balloon"] = *config.CapacityMiB
return ",shares"
}
}
if config.MinimumCapacityMiB != nil {
params["balloon"] = *config.MinimumCapacityMiB
if *config.MinimumCapacityMiB == 0 {
return ",shares"
}
}
if config.Shares != nil {
if *config.Shares == 0 {
return ",shares"
}
params["shares"] = *config.Shares
}
return ""
}

func (QemuMemory) mapToSDK(params map[string]interface{}) *QemuMemory {
config := QemuMemory{}
if v, isSet := params["memory"]; isSet {
tmp, _ := parse.Uint(v)
tmpIntermediate := QemuMemoryCapacity(tmp)
config.CapacityMiB = &tmpIntermediate
}
if v, isSet := params["balloon"]; isSet {
tmp, _ := parse.Uint(v)
tmpIntermediate := QemuMemoryBalloonCapacity(tmp)
config.MinimumCapacityMiB = &tmpIntermediate
}
if v, isSet := params["shares"]; isSet {
tmp, _ := parse.Uint(v)
tmpIntermediate := QemuMemoryShares(tmp)
config.Shares = &tmpIntermediate
}
return &config
}

type QemuMemoryBalloonCapacity uint32 // max 4178944

type QemuMemoryCapacity uint32 // max 4178944

type QemuMemoryShares uint16 // max 50000
75 changes: 75 additions & 0 deletions proxmox/config_qemu_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3187,6 +3187,49 @@ func Test_ConfigQemu_mapToAPI(t *testing.T) {
currentConfig: ConfigQemu{Iso: &IsoFile{Storage: "test", File: "file.iso"}},
config: &ConfigQemu{Iso: &IsoFile{Storage: "NewStorage", File: "file.iso"}},
output: map[string]interface{}{"ide2": "NewStorage:iso/file.iso,media=cdrom"}}}},
{category: `Memory`,
create: []test{
{name: `MinimumCapacityMiB`,
config: &ConfigQemu{Memory: &QemuMemory{MinimumCapacityMiB: util.Pointer(QemuMemoryBalloonCapacity(1024))}},
output: map[string]interface{}{
"memory": QemuMemoryBalloonCapacity(1024),
"balloon": QemuMemoryBalloonCapacity(1024)}},
{name: `Shares`,
config: &ConfigQemu{Memory: &QemuMemory{Shares: util.Pointer(QemuMemoryShares(40000))}},
output: map[string]interface{}{"shares": QemuMemoryShares(40000)}},
{name: `Shares 0`,
config: &ConfigQemu{Memory: &QemuMemory{Shares: util.Pointer(QemuMemoryShares(0))}},
output: map[string]interface{}{}}},
createUpdate: []test{
{name: `CapacityMiB`,
config: &ConfigQemu{Memory: &QemuMemory{CapacityMiB: util.Pointer(QemuMemoryCapacity(2048))}},
currentConfig: ConfigQemu{Memory: &QemuMemory{CapacityMiB: util.Pointer(QemuMemoryCapacity(1024))}},
output: map[string]interface{}{"memory": QemuMemoryCapacity(2048)}}},
update: []test{
{name: `CapacityMiB smaller then current MinimumCapacityMiB`,
config: &ConfigQemu{Memory: &QemuMemory{CapacityMiB: util.Pointer(QemuMemoryCapacity(1024))}},
currentConfig: ConfigQemu{Memory: &QemuMemory{CapacityMiB: util.Pointer(QemuMemoryCapacity(2048)), MinimumCapacityMiB: util.Pointer(QemuMemoryBalloonCapacity(2048))}},
output: map[string]interface{}{"memory": QemuMemoryCapacity(1024), "balloon": QemuMemoryCapacity(1024), "delete": "shares"}},
{name: `CapacityMiB smaller then current MinimumCapacityMiB and MinimumCapacityMiB lowered`,
config: &ConfigQemu{Memory: &QemuMemory{CapacityMiB: util.Pointer(QemuMemoryCapacity(1024)), MinimumCapacityMiB: util.Pointer(QemuMemoryBalloonCapacity(512))}},
currentConfig: ConfigQemu{Memory: &QemuMemory{CapacityMiB: util.Pointer(QemuMemoryCapacity(2048)), MinimumCapacityMiB: util.Pointer(QemuMemoryBalloonCapacity(2048))}},
output: map[string]interface{}{"memory": QemuMemoryCapacity(1024), "balloon": QemuMemoryBalloonCapacity(512)}},
{name: `MinimumCapacityMiB`,
config: &ConfigQemu{Memory: &QemuMemory{MinimumCapacityMiB: util.Pointer(QemuMemoryBalloonCapacity(1024))}},
currentConfig: ConfigQemu{Memory: &QemuMemory{MinimumCapacityMiB: util.Pointer(QemuMemoryBalloonCapacity(2048))}},
output: map[string]interface{}{"balloon": QemuMemoryBalloonCapacity(1024)}},
{name: `MinimumCapacityMiB 0`,
config: &ConfigQemu{Memory: &QemuMemory{MinimumCapacityMiB: util.Pointer(QemuMemoryBalloonCapacity(0))}},
currentConfig: ConfigQemu{Memory: &QemuMemory{MinimumCapacityMiB: util.Pointer(QemuMemoryBalloonCapacity(1024))}},
output: map[string]interface{}{"balloon": QemuMemoryBalloonCapacity(0), "delete": "shares"}},
{name: `Shares`,
config: &ConfigQemu{Memory: &QemuMemory{Shares: util.Pointer(QemuMemoryShares(40000))}},
currentConfig: ConfigQemu{Memory: &QemuMemory{Shares: util.Pointer(QemuMemoryShares(20000))}},
output: map[string]interface{}{"shares": QemuMemoryShares(40000)}},
{name: `Shares 0`,
config: &ConfigQemu{Memory: &QemuMemory{Shares: util.Pointer(QemuMemoryShares(0))}},
currentConfig: ConfigQemu{Memory: &QemuMemory{Shares: util.Pointer(QemuMemoryShares(20000))}},
output: map[string]interface{}{"delete": "shares"}}}},
{category: `Tags`,
createUpdate: []test{
{name: `Tags Empty`,
Expand Down Expand Up @@ -3236,6 +3279,9 @@ func Test_ConfigQemu_mapToAPI(t *testing.T) {

func Test_ConfigQemu_mapToStruct(t *testing.T) {
baseConfig := func(config ConfigQemu) *ConfigQemu {
if config.Memory == nil {
config.Memory = &QemuMemory{}
}
return &config
}
parseIP := func(rawIP string) (ip netip.Addr) {
Expand Down Expand Up @@ -5415,6 +5461,35 @@ func Test_ConfigQemu_mapToStruct(t *testing.T) {
File: "debian-11.0.0-amd64-netinst.iso",
Storage: "local",
SizeInKibibytes: "377M"}}}}}})}}},
{category: `Memory`,
tests: []test{
{name: `All float64`,
input: map[string]interface{}{
"memory": float64(1024),
"balloon": float64(512),
"shares": float64(50)},
output: baseConfig(ConfigQemu{Memory: &QemuMemory{
CapacityMiB: util.Pointer(QemuMemoryCapacity(1024)),
MinimumCapacityMiB: util.Pointer(QemuMemoryBalloonCapacity(512)),
Shares: util.Pointer(QemuMemoryShares(50))}})},
{name: `All string`,
input: map[string]interface{}{
"memory": "1024",
"balloon": "512",
"shares": "50"},
output: baseConfig(ConfigQemu{Memory: &QemuMemory{
CapacityMiB: util.Pointer(QemuMemoryCapacity(1024)),
MinimumCapacityMiB: util.Pointer(QemuMemoryBalloonCapacity(512)),
Shares: util.Pointer(QemuMemoryShares(50))}})},
{name: `memory`,
input: map[string]interface{}{"memory": float64(2000)},
output: baseConfig(ConfigQemu{Memory: &QemuMemory{CapacityMiB: util.Pointer(QemuMemoryCapacity(2000))}})},
{name: `balloon`,
input: map[string]interface{}{"balloon": float64(1000)},
output: baseConfig(ConfigQemu{Memory: &QemuMemory{MinimumCapacityMiB: util.Pointer(QemuMemoryBalloonCapacity(1000))}})},
{name: `shares`,
input: map[string]interface{}{"shares": float64(100)},
output: baseConfig(ConfigQemu{Memory: &QemuMemory{Shares: util.Pointer(QemuMemoryShares(100))}})}}},
{category: `Node`,
tests: []test{
{name: `vmr nil`,
Expand Down

0 comments on commit 4098fd3

Please sign in to comment.