Skip to content

Commit 40a65bf

Browse files
committed
Add node affinity to VM
Signed-off-by: Justin Cinkelj <justin.cinkelj@xlab.si>
1 parent 9bbacad commit 40a65bf

File tree

4 files changed

+125
-15
lines changed

4 files changed

+125
-15
lines changed

internal/provider/hypercore_vm_resource.go

Lines changed: 62 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ import (
1111
"github.com/hashicorp/terraform-plugin-framework/path"
1212
"github.com/hashicorp/terraform-plugin-framework/resource"
1313
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
14+
"github.com/hashicorp/terraform-plugin-framework/resource/schema/objectdefault"
1415
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
16+
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault"
1517
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
1618
"github.com/hashicorp/terraform-plugin-framework/types"
1719
"github.com/hashicorp/terraform-plugin-log/tflog"
@@ -33,13 +35,14 @@ type HypercoreVMResource struct {
3335

3436
// HypercoreVMResourceModel describes the resource data model.
3537
type HypercoreVMResourceModel struct {
36-
Group types.String `tfsdk:"group"`
37-
Name types.String `tfsdk:"name"`
38-
Description types.String `tfsdk:"description"`
39-
VCPU types.Int32 `tfsdk:"vcpu"`
40-
Memory types.Int64 `tfsdk:"memory"`
41-
Clone CloneModel `tfsdk:"clone"`
42-
Id types.String `tfsdk:"id"`
38+
Group types.String `tfsdk:"group"`
39+
Name types.String `tfsdk:"name"`
40+
Description types.String `tfsdk:"description"`
41+
VCPU types.Int32 `tfsdk:"vcpu"`
42+
Memory types.Int64 `tfsdk:"memory"`
43+
Clone CloneModel `tfsdk:"clone"`
44+
AffinityStrategy AffinityStrategyModel `tfsdk:"affinity_strategy"`
45+
Id types.String `tfsdk:"id"`
4346
}
4447

4548
type CloneModel struct {
@@ -48,6 +51,12 @@ type CloneModel struct {
4851
MetaData types.String `tfsdk:"meta_data"`
4952
}
5053

54+
type AffinityStrategyModel struct {
55+
StrictAffinity types.Bool `tfsdk:"strict_affinity"`
56+
PreferredNodeUUID types.String `tfsdk:"preferred_node_uuid"`
57+
BackupNodeUUID types.String `tfsdk:"backup_node_uuid"`
58+
}
59+
5160
func (r *HypercoreVMResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
5261
resp.TypeName = req.ProviderTypeName + "_vm"
5362
}
@@ -93,6 +102,30 @@ func (r *HypercoreVMResource) Schema(ctx context.Context, req resource.SchemaReq
93102
"meta_data": types.StringType,
94103
},
95104
},
105+
"affinity_strategy": schema.ObjectAttribute{
106+
MarkdownDescription: "VM node affinity.",
107+
Optional: true,
108+
AttributeTypes: map[string]attr.Type{
109+
"strict_affinity": types.BoolType,
110+
"preferred_node_uuid": types.StringType,
111+
"backup_node_uuid": types.StringType,
112+
},
113+
Computed: true,
114+
Default: objectdefault.StaticValue(
115+
types.ObjectValueMust(
116+
map[string]attr.Type{
117+
"strict_affinity": types.BoolType,
118+
"preferred_node_uuid": types.StringType,
119+
"backup_node_uuid": types.StringType,
120+
},
121+
map[string]attr.Value{
122+
"strict_affinity": types.BoolValue(false),
123+
"preferred_node_uuid": types.StringValue(""),
124+
"backup_node_uuid": types.StringValue(""),
125+
},
126+
),
127+
),
128+
},
96129
"id": schema.StringAttribute{
97130
Computed: true,
98131
MarkdownDescription: "HypercoreVM identifier",
@@ -176,6 +209,9 @@ func (r *HypercoreVMResource) Create(ctx context.Context, req resource.CreateReq
176209
data.VCPU.ValueInt32Pointer(),
177210
data.Memory.ValueInt64Pointer(),
178211
nil,
212+
data.AffinityStrategy.StrictAffinity.ValueBool(),
213+
data.AffinityStrategy.PreferredNodeUUID.ValueString(),
214+
data.AffinityStrategy.BackupNodeUUID.ValueString(),
179215
)
180216
changed, msg := vmClone.Create(*r.client, ctx)
181217
tflog.Info(ctx, fmt.Sprintf("Changed: %t, Message: %s\n", changed, msg))
@@ -244,6 +280,11 @@ func (r *HypercoreVMResource) Read(ctx context.Context, req resource.ReadRequest
244280
data.VCPU = types.Int32Value(int32(utils.AnyToInteger64(hc3_vm["numVCPU"])))
245281
data.Memory = types.Int64Value(utils.AnyToInteger64(hc3_vm["mem"]) / 1024 / 1024)
246282

283+
affinityStrategy := utils.AnyToMap(hc3_vm["affinityStrategy"])
284+
data.AffinityStrategy.StrictAffinity = types.BoolValue(utils.AnyToBool(affinityStrategy["strictAffinity"]))
285+
data.AffinityStrategy.PreferredNodeUUID = types.StringValue(utils.AnyToString(affinityStrategy["preferredNodeUUID"]))
286+
data.AffinityStrategy.BackupNodeUUID = types.StringValue(utils.AnyToString(affinityStrategy["backupNodeUUID"]))
287+
247288
// ==============================================================================
248289

249290
// Save updated data into Terraform state
@@ -296,6 +337,20 @@ func (r *HypercoreVMResource) Update(ctx context.Context, req resource.UpdateReq
296337
updatePayload["numVCPU"] = data.VCPU.ValueInt32()
297338
}
298339

340+
affinityStrategy := map[string]any{}
341+
if data_state.AffinityStrategy.StrictAffinity != data.AffinityStrategy.StrictAffinity {
342+
affinityStrategy["strictAffinity"] = data.AffinityStrategy.StrictAffinity.ValueBool()
343+
}
344+
if data_state.AffinityStrategy.PreferredNodeUUID != data.AffinityStrategy.PreferredNodeUUID {
345+
affinityStrategy["preferredNodeUUID"] = data.AffinityStrategy.PreferredNodeUUID.ValueString()
346+
}
347+
if data_state.AffinityStrategy.BackupNodeUUID != data.AffinityStrategy.BackupNodeUUID {
348+
affinityStrategy["backupNodeUUID"] = data.AffinityStrategy.BackupNodeUUID.ValueString()
349+
}
350+
if len(affinityStrategy) > 0 {
351+
updatePayload["affinityStrategy"] = affinityStrategy
352+
}
353+
299354
taskTag, _ := restClient.UpdateRecord( /**/
300355
fmt.Sprintf("/rest/v1/VirDomain/%s", vm_uuid),
301356
updatePayload,

internal/utils/helper.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,15 @@ func AnyToString(str any) string {
9393
return stringifiedAny
9494
}
9595

96+
func AnyToBool(value any) bool {
97+
switch v := value.(type) {
98+
case bool:
99+
return v
100+
}
101+
102+
panic(fmt.Sprintf("Unexpected variable where an bool was expected: %v (type %T)", value, value))
103+
}
104+
96105
func AnyToInteger64(integer any) int64 {
97106
switch v := integer.(type) {
98107
case int:

internal/utils/vm.go

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ type VM struct {
6666
vcpu *int32
6767
memory *int64
6868
powerState *string
69+
strictAffinity bool
70+
preferredNodeUUID string
71+
backupNodeUUID string
6972

7073
_wasNiceShutdownTried bool
7174
_didNiceShutdownWork bool
@@ -85,6 +88,9 @@ func NewVM(
8588
_vcpu *int32,
8689
_memory *int64,
8790
_powerState *string,
91+
_strictAffinity bool,
92+
_preferredNodeUUID string,
93+
_backupNodeUUID string,
8894
) (*VM, error) {
8995
userDataB64 := base64.StdEncoding.EncodeToString([]byte(userData))
9096
metaDataB64 := base64.StdEncoding.EncodeToString([]byte(metaData))
@@ -98,11 +104,14 @@ func NewVM(
98104
"userData": userDataB64,
99105
"metaData": metaDataB64,
100106
},
101-
description: _description,
102-
tags: _tags,
103-
vcpu: _vcpu,
104-
memory: _memory,
105-
powerState: _powerState,
107+
description: _description,
108+
tags: _tags,
109+
vcpu: _vcpu,
110+
memory: _memory,
111+
powerState: _powerState,
112+
strictAffinity: _strictAffinity,
113+
preferredNodeUUID: _preferredNodeUUID,
114+
backupNodeUUID: _backupNodeUUID,
106115

107116
// helpers
108117
_wasNiceShutdownTried: false,
@@ -165,7 +174,7 @@ func (vc *VM) Create(restClient RestClient, ctx context.Context) (bool, string)
165174

166175
func (vc *VM) SetVMParams(restClient RestClient, ctx context.Context) (bool, bool, map[string]any) {
167176
vm := GetVMByName(vc.VMName, restClient, true)
168-
changed, changedParams := vc.GetChangedParams(*vm)
177+
changed, changedParams := vc.GetChangedParams(ctx, *vm)
169178

170179
if changed {
171180
updatePayload := vc.BuildUpdatePayload(changedParams)
@@ -404,10 +413,24 @@ func (vc *VM) BuildUpdatePayload(changedParams map[string]bool) map[string]any {
404413
updatePayload["numVCPU"] = *vc.vcpu
405414
}
406415

416+
affinityStrategy := map[string]any{}
417+
if changed, ok := changedParams["strictAffinity"]; ok && changed {
418+
affinityStrategy["strictAffinity"] = vc.strictAffinity
419+
}
420+
if changed, ok := changedParams["preferredNodeUUID"]; ok && changed {
421+
affinityStrategy["preferredNodeUUID"] = vc.preferredNodeUUID
422+
}
423+
if changed, ok := changedParams["backupNodeUUID"]; ok && changed {
424+
affinityStrategy["backupNodeUUID"] = vc.backupNodeUUID
425+
}
426+
if len(affinityStrategy) > 0 {
427+
updatePayload["affinityStrategy"] = affinityStrategy
428+
}
429+
407430
return updatePayload
408431
}
409432

410-
func (vc *VM) GetChangedParams(vmFromClient map[string]any) (bool, map[string]bool) {
433+
func (vc *VM) GetChangedParams(ctx context.Context, vmFromClient map[string]any) (bool, map[string]bool) {
411434
changedParams := map[string]bool{}
412435

413436
if vc.description != nil {
@@ -433,6 +456,11 @@ func (vc *VM) GetChangedParams(vmFromClient map[string]any) (bool, map[string]bo
433456
}
434457
}
435458

459+
hc3AffinityStrategy := AnyToMap(vmFromClient["affinityStrategy"])
460+
changedParams["strictAffinity"] = vc.strictAffinity != hc3AffinityStrategy["strictAffinity"]
461+
changedParams["preferredNodeUUID"] = vc.preferredNodeUUID != hc3AffinityStrategy["preferredNodeUUID"]
462+
changedParams["backupNodeUUID"] = vc.backupNodeUUID != hc3AffinityStrategy["backupNodeUUID"]
463+
436464
for _, changed := range changedParams {
437465
if changed {
438466
return true, changedParams

local/main.tf

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,21 +12,39 @@ terraform {
1212
provider "hypercore" {}
1313

1414
locals {
15-
vm_name = "testtf-disk-ana"
15+
vm_name = "testtf-disk-justin"
1616
empty_vm = "testtf-ana"
1717
clone_empty_vm = "testtf-clone-ana"
1818

1919
vm_meta_data_tmpl = "./assets/meta-data.ubuntu-22.04.yml.tftpl"
2020
vm_user_data_tmpl = "./assets/user-data.ubuntu-22.04.yml.tftpl"
2121
}
2222

23+
resource "hypercore_vm" "myvm" {
24+
name = local.vm_name
25+
clone = {
26+
source_vm_uuid = ""
27+
meta_data = ""
28+
user_data = ""
29+
}
30+
affinity_strategy = {
31+
strict_affinity = true
32+
preferred_node_uuid = data.hypercore_node.cluster0_peer1.nodes.0.uuid
33+
backup_node_uuid = data.hypercore_node.cluster0_peer1.nodes.0.uuid
34+
}
35+
}
36+
2337
data "hypercore_node" "cluster0_all" {
2438
}
2539

2640
data "hypercore_node" "cluster0_peer1" {
2741
peer_id = 1
2842
}
2943

44+
output "myvm" {
45+
value = hypercore_vm.myvm
46+
}
47+
3048
output "cluster_0_peer_1_uuid" {
3149
value = data.hypercore_node.cluster0_peer1.nodes.0.uuid
3250
}

0 commit comments

Comments
 (0)