Skip to content

Commit

Permalink
add memory tests and change memory_max to memory_dynamic_max
Browse files Browse the repository at this point in the history
* change vm set/create params memoryMax to memoryStaticMax

* add state migrations
  • Loading branch information
aslak11 committed Mar 8, 2024
1 parent 59420cf commit 8c8f9f3
Show file tree
Hide file tree
Showing 5 changed files with 355 additions and 43 deletions.
28 changes: 18 additions & 10 deletions client/vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,6 @@ func (c *Client) CreateVm(vmReq Vm, createTime time.Duration) (*Vm, error) {
"cpuCap": nil,
"cpuWeight": nil,
"CPUs": vmReq.CPUs.Number,
"memoryMax": vmReq.Memory.Static[1],
"existingDisks": existingDisks,
// TODO: (#145) Uncomment this once issues with secure_boot have been figured out
// "secureBoot": vmReq.SecureBoot,
Expand All @@ -283,12 +282,14 @@ func (c *Client) CreateVm(vmReq Vm, createTime time.Duration) (*Vm, error) {
"auto_poweron": vmReq.AutoPoweron,
"high_availability": vmReq.HA,
}
if len(vmReq.Memory.Dynamic) == 2 && vmReq.Memory.Dynamic[0] != 0 && vmReq.Memory.Dynamic[1] != 0 {
if len(vmReq.Memory.Dynamic) == 2 && vmReq.Memory.Dynamic[0] != 0 {
params["memoryMin"] = vmReq.Memory.Dynamic[0]
}
if len(vmReq.Memory.Dynamic) == 2 && vmReq.Memory.Dynamic[1] != 0 {
params["memoryMax"] = vmReq.Memory.Dynamic[1]
if len(vmReq.Memory.Static) == 2 {
params["memoryStaticMax"] = vmReq.Memory.Static[1]
}
}
if len(vmReq.Memory.Static) == 2 && vmReq.Memory.Static[1] != 0 {
params["memoryStaticMax"] = vmReq.Memory.Static[1]
}

if !params["clone"].(bool) && vmReq.CloneType == CloneTypeFastClone {
Expand Down Expand Up @@ -372,6 +373,12 @@ func (c *Client) CreateVm(vmReq Vm, createTime time.Duration) (*Vm, error) {
if params["memoryStaticMax"] != nil {
xsParams["memoryStaticMax"] = params["memoryStaticMax"]
}
if params["memoryMin"] != nil {
xsParams["memoryMin"] = params["memoryMin"]
}
if params["memoryMax"] != nil {
xsParams["memoryMax"] = params["memoryMax"]
}
var success bool
err = c.Call("vm.set", xsParams, &success)

Expand Down Expand Up @@ -419,7 +426,6 @@ func (c *Client) UpdateVm(vmReq Vm) (*Vm, error) {
"auto_poweron": vmReq.AutoPoweron,
"high_availability": vmReq.HA, // valid options are best-effort, restart, ''
"CPUs": vmReq.CPUs.Number,
"memoryMax": vmReq.Memory.Static[1],
"expNestedHvm": vmReq.ExpNestedHvm,
"startDelay": vmReq.StartDelay,
// TODO: These need more investigation before they are implemented
Expand All @@ -434,12 +440,14 @@ func (c *Client) UpdateVm(vmReq Vm) (*Vm, error) {
// cpusMask, cpuWeight and cpuCap can be changed at runtime to an integer value or null
// coresPerSocket is null or a number of cores per socket. Putting an invalid value doesn't seem to cause an error :(
}
if len(vmReq.Memory.Dynamic) == 2 && vmReq.Memory.Dynamic[0] != 0 && vmReq.Memory.Dynamic[1] != 0 {
if len(vmReq.Memory.Dynamic) == 2 && vmReq.Memory.Dynamic[0] != 0 {
params["memoryMin"] = vmReq.Memory.Dynamic[0]
}
if len(vmReq.Memory.Dynamic) == 2 && vmReq.Memory.Dynamic[1] != 0 {
params["memoryMax"] = vmReq.Memory.Dynamic[1]
if len(vmReq.Memory.Static) == 2 {
params["memoryStaticMax"] = vmReq.Memory.Static[1]
}
}
if len(vmReq.Memory.Static) == 2 && vmReq.Memory.Static[1] != 0 {
params["memoryStaticMax"] = vmReq.Memory.Static[1]
}

affinityHost := vmReq.AffinityHost
Expand Down
13 changes: 12 additions & 1 deletion docs/resources/vm.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,18 @@ $ xo-cli xo.getAllObjects filter='json:{"id": "cf7b5d7d-3cd5-6b7c-5025-5c935c8cd
# Updating the VM to use 5 CPUs would stop/start the VM
```
- `disk` (Block List, Min: 1) The disk the VM will have access to. (see [below for nested schema](#nestedblock--disk))
- `memory_max` (Number) The amount of memory in bytes the VM will have. Updates to this field will case a stop and start of the VM.
- `memory_max` (Number) The amount of static memory in bytes the VM will have. Updates to this field will case a stop and start of the VM if the new value is greater than the dynamic memory max. This can be determined with the following command:
```
$ xo-cli xo.getAllObjects filter='json:{"id": "cf7b5d7d-3cd5-6b7c-5025-5c935c8cd0b8"}' | jq '.[].memory.dynamic'
[
2147483648, # memory dynamic min
4294967296 # memory dynamic max (4GB)
]
# Updating the VM to use 3GB of memory would happen without stopping/starting the VM
# Updating the VM to use 5GB of memory would stop/start the VM
```
- `name_label` (String) The name of the VM.
- `network` (Block List, Min: 1) The network for the VM. (see [below for nested schema](#nestedblock--network))
- `template` (String) The ID of the VM template to create the new VM from.
Expand Down
38 changes: 38 additions & 0 deletions xoa/internal/state/migrate.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ func VmStateUpgradeV0(ctx context.Context, rawState map[string]interface{}, meta
rawState["destroy_cloud_config_vdi_after_boot"] = false
return rawState, nil
}
func VmStateUpgradeV1(ctx context.Context, rawState map[string]interface{}, meta interface{}) (map[string]interface{}, error) {
rawState["memory_dynamic_max"] = rawState["memory_max"]
delete(rawState, "memory_max")
return rawState, nil
}

func ResourceVmResourceV0() *schema.Resource {
return &schema.Resource{
Expand Down Expand Up @@ -352,6 +357,39 @@ $ xo-cli xo.getAllObjects filter='json:{"id": "cf7b5d7d-3cd5-6b7c-5025-5c935c8cd
}
}

func ResourceVmResourceV1() *schema.Resource {
return &schema.Resource{
Schema: map[string]*schema.Schema{
"memory_max": &schema.Schema{
Type: schema.TypeInt,
Required: true,
Description: `The amount of memory in bytes the VM will have. Updates to this field will case a stop and start of the VM if the new value is greater than the dynamic memory max. This can be determined with the following command:
$ xo-cli xo.getAllObjects filter='json:{"id": "cf7b5d7d-3cd5-6b7c-5025-5c935c8cd0b8"}' | jq '.[].memory.dynamic'
[
2147483648, # memory dynamic min
4294967296 # memory dynamic max (4GB)
]
# Updating the VM to use 3GB of memory would happen without stopping/starting the VM
# Updating the VM to use 5GB of memory would stop/start the VM
`,
},
"memory_dynamic_min": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
Description: `Dynamic minimum (bytes)`,
Computed: true,
},
"memory_dynamic_max": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
Description: `Dynamic maximum (bytes)`,
Computed: true,
},
},
}
}

func suppressAttachedDiffWhenHalted(k, old, new string, d *schema.ResourceData) (suppress bool) {
powerState := d.Get("power_state").(string)
suppress = true
Expand Down
83 changes: 59 additions & 24 deletions xoa/resource_xenorchestra_vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,15 +215,28 @@ $ xo-cli xo.getAllObjects filter='json:{"id": "cf7b5d7d-3cd5-6b7c-5025-5c935c8cd
`,
},
"memory_max": &schema.Schema{
Type: schema.TypeInt,
Required: true,
Description: `The amount of memory in bytes the VM will have. Updates to this field will case a stop and start of the VM.`,
Type: schema.TypeInt,
Required: true,
Description: `The amount of static memory in bytes the VM will have. Updates to this field will case a stop and start of the VM if the new value is greater than the dynamic memory max. This can be determined with the following command:
` + "```" + `
$ xo-cli xo.getAllObjects filter='json:{"id": "cf7b5d7d-3cd5-6b7c-5025-5c935c8cd0b8"}' | jq '.[].memory.dynamic'
[
2147483648, # memory dynamic min
4294967296 # memory dynamic max (4GB)
]
# Updating the VM to use 3GB of memory would happen without stopping/starting the VM
# Updating the VM to use 5GB of memory would stop/start the VM
` + "```" + `
`,
},
"memory_dynamic_min": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
Description: `Dynamic minimum (bytes)`,
Default: 2147483648,
Computed: true,
},
"memory_dynamic_max": &schema.Schema{
Type: schema.TypeInt,
Expand Down Expand Up @@ -441,13 +454,18 @@ This does not work in terraform since that is applied on Xen Orchestra's client
Read: resourceVmRead,
Update: resourceVmUpdate,
Delete: resourceVmDelete,
SchemaVersion: 1,
SchemaVersion: 2,
StateUpgraders: []schema.StateUpgrader{
{
Type: state.ResourceVmResourceV0().CoreConfigSchema().ImpliedType(),
Upgrade: state.VmStateUpgradeV0,
Version: 0,
},
{
Type: state.ResourceVmResourceV1().CoreConfigSchema().ImpliedType(),
Upgrade: state.VmStateUpgradeV1,
Version: 1,
},
},
Importer: &schema.ResourceImporter{
State: RecordImport,
Expand Down Expand Up @@ -539,6 +557,10 @@ func resourceVmCreate(d *schema.ResourceData, m interface{}) error {
}
}

memoryObject, err := memory(d)
if err != nil {
return err
}
createVmParams := client.Vm{
BlockedOperations: blockedOperations,
Boot: client.Boot{
Expand All @@ -559,7 +581,7 @@ func resourceVmCreate(d *schema.ResourceData, m interface{}) error {
Number: d.Get("cpus").(int),
},
CloudNetworkConfig: d.Get("cloud_network_config").(string),
Memory: memory(d),
Memory: *memoryObject,
Tags: vmTags,
Disks: ds,
Installation: installation,
Expand Down Expand Up @@ -880,9 +902,6 @@ func resourceVmUpdate(d *schema.ResourceData, m interface{}) error {
if d.HasChange("memory_max") {
haltForUpdates = true
}
if d.Get("memory_max").(int) < d.Get("memory_dynamic_max").(int) {
haltForUpdates = true
}

blockOperations := map[string]string{}
if d.HasChange("blocked_operations") {
Expand All @@ -902,12 +921,16 @@ func resourceVmUpdate(d *schema.ResourceData, m interface{}) error {

}

memoryObject, err := memory(d)
if err != nil {
return err
}
vmReq := client.Vm{
Id: id,
CPUs: client.CPUs{
Number: cpus,
},
Memory: memory(d),
Memory: *memoryObject,
NameLabel: nameLabel,
NameDescription: nameDescription,
HA: ha,
Expand All @@ -927,6 +950,10 @@ func resourceVmUpdate(d *schema.ResourceData, m interface{}) error {
},
}

if len(vmReq.Memory.Dynamic) == 2 && vmReq.Memory.Dynamic[1] > vm.Memory.Static[1] {
haltForUpdates = true
}

if d.HasChange("affinity_host") {
vmReq.AffinityHost = &affinityHost
}
Expand Down Expand Up @@ -1024,20 +1051,35 @@ func resourceVmUpdate(d *schema.ResourceData, m interface{}) error {
return resourceVmRead(d, m)
}

func memory(d *schema.ResourceData) client.MemoryObject {
memory := client.MemoryObject{
func memory(d *schema.ResourceData) (*client.MemoryObject, error) {
memory := &client.MemoryObject{
Dynamic: []int{},
Static: []int{
0, d.Get("memory_max").(int),
},
}
if d.Get("memory_dynamic_min") != nil && d.Get("memory_dynamic_max") != nil {
memory.Dynamic = []int{
d.Get("memory_dynamic_min").(int),
d.Get("memory_dynamic_max").(int),
if !d.GetRawConfig().GetAttr("memory_dynamic_min").IsNull() {
if memoryDynamicMin, ok := d.GetOk("memory_dynamic_min"); ok {
if len(memory.Dynamic) == 0 {
memory.Dynamic = []int{memoryDynamicMin.(int), 0}
} else {
memory.Dynamic[0] = memoryDynamicMin.(int)
}
}
}
return memory
if !d.GetRawConfig().GetAttr("memory_dynamic_max").IsNull() {
if memoryDynamicMax, ok := d.GetOk("memory_dynamic_max"); ok {
if len(memory.Dynamic) == 0 {
memory.Dynamic = []int{0, memoryDynamicMax.(int)}
} else {
memory.Dynamic[1] = memoryDynamicMax.(int)
}
}
if memory.Dynamic[1] > memory.Static[1] {
return nil, errors.New(fmt.Sprintf("memory_dynamic_max cannot be more than memory_max"))
}
}
return memory, nil
}

func resourceVmDelete(d *schema.ResourceData, m interface{}) error {
Expand Down Expand Up @@ -1129,13 +1171,6 @@ func RecordImport(d *schema.ResourceData, m interface{}) ([]*schema.ResourceData
func recordToData(resource client.Vm, vifs []client.VIF, disks []client.Disk, cdroms []client.Disk, d *schema.ResourceData) error {
d.SetId(resource.Id)
// d.Set("cloud_config", resource.CloudConfig)
if len(resource.Memory.Dynamic) == 2 {
if err := d.Set("memory_max", resource.Memory.Dynamic[1]); err != nil {
return err
}
} else {
log.Printf("[WARN] Expected the VM's static memory limits to have two values, %v found instead\n", resource.Memory.Dynamic)
}
d.Set("memory_max", resource.Memory.Static[1])
d.Set("memory_dynamic_min", resource.Memory.Dynamic[0])
d.Set("memory_dynamic_max", resource.Memory.Dynamic[1])
Expand Down
Loading

0 comments on commit 8c8f9f3

Please sign in to comment.