Skip to content

Commit 61498fe

Browse files
avorimamcbenjemaa
andauthored
Make CPU and RAM configurable (#62)
* Make CPU cores, sockets and RAM configurable * Update template * Introduce constants for VM config options * ⚠️ Change integer types to int32 * Invert and document bool return value meanings --------- Co-authored-by: Mohamed Chiheb Ben Jemaa <mc.benjemaa@gmail.com>
1 parent 6cca696 commit 61498fe

File tree

11 files changed

+156
-23
lines changed

11 files changed

+156
-23
lines changed

api/v1alpha1/proxmoxmachine_types.go

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,24 @@ type ProxmoxMachineSpec struct {
5050
// +optional
5151
VirtualMachineID *int64 `json:"virtualMachineID,omitempty"`
5252

53+
// NumSockets is the number of CPU sockets in a virtual machine.
54+
// Defaults to the property value in the template from which the virtual machine is cloned.
55+
// +kubebuilder:validation:Minimum=1
56+
// +optional
57+
NumSockets int32 `json:"numSockets,omitempty"`
58+
59+
// NumCores is the number of cores per CPU socket in a virtual machine.
60+
// Defaults to the property value in the template from which the virtual machine is cloned.
61+
// +kubebuilder:validation:Minimum=1
62+
// +optional
63+
NumCores int32 `json:"numCores,omitempty"`
64+
65+
// MemoryMiB is the size of a virtual machine's memory, in MiB.
66+
// Defaults to the property value in the template from which the virtual machine is cloned.
67+
// +kubebuilder:validation:MultipleOf=8
68+
// +optional
69+
MemoryMiB int32 `json:"memoryMiB,omitempty"`
70+
5371
// Disks contains a set of disk configuration options,
5472
// which will be applied before the first startup.
5573
//
@@ -84,7 +102,7 @@ type DiskSize struct {
84102
// template.
85103
//
86104
// +kubebuilder:validation:Minimum=5
87-
SizeGB int `json:"sizeGb"`
105+
SizeGB int32 `json:"sizeGb"`
88106
}
89107

90108
// TargetFileStorageFormat the target format of the cloned disk.
@@ -117,7 +135,7 @@ type VirtualMachineCloneSpec struct {
117135

118136
// TemplateID the vm_template vmid used for cloning a new VM.
119137
// +optional
120-
TemplateID *int `json:"templateID,omitempty"`
138+
TemplateID *int32 `json:"templateID,omitempty"`
121139

122140
// Description for the new VM.
123141
// +optional
@@ -285,7 +303,7 @@ func (r *ProxmoxMachine) GetVirtualMachineID() int64 {
285303
}
286304

287305
// GetTemplateID get the proxmox template "vmid" used to provision this machine.
288-
func (r *ProxmoxMachine) GetTemplateID() int {
306+
func (r *ProxmoxMachine) GetTemplateID() int32 {
289307
if r.Spec.TemplateID != nil {
290308
return *r.Spec.TemplateID
291309
}

api/v1alpha1/zz_generated.deepcopy.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config/crd/bases/infrastructure.cluster.x-k8s.io_proxmoxmachines.yaml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ spec:
8181
description: "Size defines the size in gigabyte. \n As Proxmox
8282
does not support shrinking, the size must be bigger than
8383
the already configured size in the template."
84+
format: int32
8485
minimum: 5
8586
type: integer
8687
required:
@@ -104,6 +105,27 @@ spec:
104105
description: Full Create a full copy of all disks. This is always
105106
done when you clone a normal VM. Create a Full clone by default.
106107
type: boolean
108+
memoryMiB:
109+
description: MemoryMiB is the size of a virtual machine's memory,
110+
in MiB. Defaults to the property value in the template from which
111+
the virtual machine is cloned.
112+
format: int32
113+
multipleOf: 8
114+
type: integer
115+
numCores:
116+
description: NumCores is the number of cores per CPU socket in a virtual
117+
machine. Defaults to the property value in the template from which
118+
the virtual machine is cloned.
119+
format: int32
120+
minimum: 1
121+
type: integer
122+
numSockets:
123+
description: NumSockets is the number of CPU sockets in a virtual
124+
machine. Defaults to the property value in the template from which
125+
the virtual machine is cloned.
126+
format: int32
127+
minimum: 1
128+
type: integer
107129
pool:
108130
description: Pool Add the new VM to the specified pool.
109131
type: string
@@ -135,6 +157,7 @@ spec:
135157
templateID:
136158
description: TemplateID the vm_template vmid used for cloning a new
137159
VM.
160+
format: int32
138161
type: integer
139162
virtualMachineID:
140163
description: VirtualMachineID is the Proxmox identifier for the ProxmoxMachine

config/crd/bases/infrastructure.cluster.x-k8s.io_proxmoxmachinetemplates.yaml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ spec:
8686
As Proxmox does not support shrinking, the size
8787
must be bigger than the already configured size
8888
in the template."
89+
format: int32
8990
minimum: 5
9091
type: integer
9192
required:
@@ -111,6 +112,27 @@ spec:
111112
always done when you clone a normal VM. Create a Full clone
112113
by default.
113114
type: boolean
115+
memoryMiB:
116+
description: MemoryMiB is the size of a virtual machine's
117+
memory, in MiB. Defaults to the property value in the template
118+
from which the virtual machine is cloned.
119+
format: int32
120+
multipleOf: 8
121+
type: integer
122+
numCores:
123+
description: NumCores is the number of cores per CPU socket
124+
in a virtual machine. Defaults to the property value in
125+
the template from which the virtual machine is cloned.
126+
format: int32
127+
minimum: 1
128+
type: integer
129+
numSockets:
130+
description: NumSockets is the number of CPU sockets in a
131+
virtual machine. Defaults to the property value in the template
132+
from which the virtual machine is cloned.
133+
format: int32
134+
minimum: 1
135+
type: integer
114136
pool:
115137
description: Pool Add the new VM to the specified pool.
116138
type: string
@@ -143,6 +165,7 @@ spec:
143165
templateID:
144166
description: TemplateID the vm_template vmid used for cloning
145167
a new VM.
168+
format: int32
146169
type: integer
147170
virtualMachineID:
148171
description: VirtualMachineID is the Proxmox identifier for

envfile.example

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,6 @@ export DNS_SERVERS="[8.8.8.8,8.8.4.4]"
1212
export ALLOWED_NODES="[pve1,pve2,pve3]"
1313
export BOOT_VOLUME_DEVICE=scsi0
1414
export BOOT_VOLUME_SIZE=100
15+
export NUM_SOCKETS=2
16+
export NUM_CORES=4
17+
export MEMORY_MIB=16384

internal/service/vmservice/bootstrap.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,16 @@ import (
1616
"github.com/ionos-cloud/cluster-api-provider-proxmox/pkg/scope"
1717
)
1818

19-
func reconcileBootstrapData(ctx context.Context, machineScope *scope.MachineScope) (bool, error) {
19+
func reconcileBootstrapData(ctx context.Context, machineScope *scope.MachineScope) (requeue bool, err error) {
2020
if pointer.BoolDeref(machineScope.ProxmoxMachine.Status.BootstrapDataProvided, false) {
2121
// skip machine already have the bootstrap data.
22-
return true, nil
22+
return false, nil
2323
}
2424

2525
if machineScope.ProxmoxMachine.Status.IPAddr == nil {
2626
// skip machine doesn't have an IpAddress yet.
2727
conditions.MarkFalse(machineScope.ProxmoxMachine, infrav1alpha1.VMProvisionedCondition, infrav1alpha1.WaitingForStaticIPAllocationReason, clusterv1.ConditionSeverityWarning, "no ip address")
28-
return false, nil
28+
return true, nil
2929
}
3030
machineScope.Logger.V(4).Info("reconciling BootstrapData.")
3131

@@ -66,7 +66,7 @@ func reconcileBootstrapData(ctx context.Context, machineScope *scope.MachineScop
6666

6767
machineScope.ProxmoxMachine.Status.BootstrapDataProvided = pointer.Bool(true)
6868

69-
return true, nil
69+
return false, nil
7070
}
7171

7272
// getBootstrapData obtains a machine's bootstrap data from the relevant k8s secret and returns the

internal/service/vmservice/vm.go

Lines changed: 61 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,15 @@ import (
3535
"github.com/ionos-cloud/cluster-api-provider-proxmox/pkg/scope"
3636
)
3737

38+
const (
39+
// See following link for a list of available config options:
40+
// https://pve.proxmox.com/pve-docs/api-viewer/index.html#/nodes/{node}/qemu/{vmid}/config
41+
42+
optionSockets = "sockets"
43+
optionCores = "cores"
44+
optionMemory = "memory"
45+
)
46+
3847
// ReconcileVM makes sure that the VM is in the desired state by:
3948
// 1. Creating the VM if it does not exist, then...
4049
// 2. Updating the VM with the bootstrap data, such as the cloud-init meta and user data, before...
@@ -98,19 +107,23 @@ func ReconcileVM(ctx context.Context, scope *scope.MachineScope) (infrav1alpha1.
98107
return vm, err
99108
}
100109

101-
if ok, err := reconcileIPAddresses(ctx, scope); err != nil || !ok {
110+
if requeue, err := reconcileIPAddresses(ctx, scope); err != nil || requeue {
102111
return vm, err
103112
}
104113

105-
if ok, err := reconcileBootstrapData(ctx, scope); err != nil || !ok {
114+
if requeue, err := reconcileBootstrapData(ctx, scope); err != nil || requeue {
106115
return vm, err
107116
}
108117

109118
if err := reconcileDisks(scope); err != nil {
110119
return vm, err
111120
}
112121

113-
if ok, err := reconcilePowerState(scope); err != nil || !ok {
122+
if requeue, err := reconcileVirtualMachineConfig(scope); err != nil || requeue {
123+
return vm, err
124+
}
125+
126+
if requeue, err := reconcilePowerState(scope); err != nil || requeue {
114127
return vm, err
115128
}
116129

@@ -145,14 +158,47 @@ func reconcileDisks(machineScope *scope.MachineScope) error {
145158
return nil
146159
}
147160

148-
func reconcilePowerState(machineScope *scope.MachineScope) (bool, error) {
161+
func reconcileVirtualMachineConfig(machineScope *scope.MachineScope) (requeue bool, err error) {
162+
if machineScope.VirtualMachine.IsRunning() || machineScope.ProxmoxMachine.Status.Ready {
163+
// We only want to do this before the machine was started or is ready
164+
return false, nil
165+
}
166+
167+
vmConfig := machineScope.VirtualMachine.VirtualMachineConfig
168+
169+
var vmOptions []proxmox.VirtualMachineOption
170+
if value := machineScope.ProxmoxMachine.Spec.NumSockets; value > 0 && vmConfig.Sockets != int(value) {
171+
vmOptions = append(vmOptions, proxmox.VirtualMachineOption{Name: optionSockets, Value: value})
172+
}
173+
if value := machineScope.ProxmoxMachine.Spec.NumCores; value > 0 && vmConfig.Cores != int(value) {
174+
vmOptions = append(vmOptions, proxmox.VirtualMachineOption{Name: optionCores, Value: value})
175+
}
176+
if value := machineScope.ProxmoxMachine.Spec.MemoryMiB; value > 0 && vmConfig.Memory != int(value) {
177+
vmOptions = append(vmOptions, proxmox.VirtualMachineOption{Name: optionMemory, Value: value})
178+
}
179+
if len(vmOptions) == 0 {
180+
return false, nil
181+
}
182+
183+
machineScope.V(4).Info("reconciling virtual machine config")
184+
185+
task, err := machineScope.InfraCluster.ProxmoxClient.ConfigureVM(machineScope.VirtualMachine, vmOptions...)
186+
if err != nil {
187+
return false, errors.Wrapf(err, "failed to configure VM %s", machineScope.Name())
188+
}
189+
190+
machineScope.ProxmoxMachine.Status.TaskRef = pointer.String(string(task.UPID))
191+
return true, nil
192+
}
193+
194+
func reconcilePowerState(machineScope *scope.MachineScope) (requeue bool, err error) {
149195
ipAddr := machineScope.ProxmoxMachine.Status.IPAddr
150196

151197
if ipAddr == nil || *ipAddr == "" {
152198
machineScope.V(4).Info("ip address not set for machine")
153199
// machine doesn't have an ip address yet
154200
// needs to reconcile again
155-
return false, nil
201+
return true, nil
156202
}
157203

158204
machineScope.V(4).Info("ensuring machine is started")
@@ -166,10 +212,10 @@ func reconcilePowerState(machineScope *scope.MachineScope) (bool, error) {
166212

167213
if t != nil {
168214
machineScope.ProxmoxMachine.Status.TaskRef = pointer.String(string(t.UPID))
169-
return false, nil
215+
return true, nil
170216
}
171217

172-
return true, nil
218+
return false, nil
173219
}
174220

175221
//nolint:unparam
@@ -180,10 +226,10 @@ func reconcileNetworkStatus(machineScope *scope.MachineScope) error {
180226
return nil
181227
}
182228

183-
func reconcileIPAddresses(ctx context.Context, machineScope *scope.MachineScope) (bool, error) {
229+
func reconcileIPAddresses(ctx context.Context, machineScope *scope.MachineScope) (requeue bool, err error) {
184230
if machineScope.ProxmoxMachine.Status.IPAddr != nil {
185231
// skip machine has an IpAddress already.
186-
return true, nil
232+
return false, nil
187233
}
188234
machineScope.Logger.V(4).Info("reconciling IPAddresses.")
189235
conditions.MarkFalse(machineScope.ProxmoxMachine, infrav1alpha1.VMProvisionedCondition, infrav1alpha1.WaitingForStaticIPAllocationReason, clusterv1.ConditionSeverityInfo, "")
@@ -197,7 +243,7 @@ func reconcileIPAddresses(ctx context.Context, machineScope *scope.MachineScope)
197243
if err != nil {
198244
return false, errors.Wrapf(err, "unable to create Ip address claim for machine %s", machineScope.Name())
199245
}
200-
return false, nil
246+
return true, nil
201247
}
202248
}
203249

@@ -214,17 +260,17 @@ func reconcileIPAddresses(ctx context.Context, machineScope *scope.MachineScope)
214260
machineScope.Logger.V(4).Info("adding virtual machine ip tag.")
215261
t, err := machineScope.VirtualMachine.AddTag(ipTag)
216262
if err != nil {
217-
return false, errors.Wrapf(err, "unable to add Ip tag to VirtualMachine %s", machineScope.VirtualMachine.Name)
263+
return false, errors.Wrapf(err, "unable to add Ip tag to VirtualMachine %s", machineScope.Name())
218264
}
219265
machineScope.ProxmoxMachine.Status.TaskRef = pointer.String(string(t.UPID))
220-
return false, nil
266+
return true, nil
221267
}
222268

223269
// update the status.IpAddr.
224270
machineScope.Logger.V(4).Info("updating ProxmoxMachine.status.ipAddr.")
225271
machineScope.ProxmoxMachine.Status.IPAddr = pointer.String(ip)
226272

227-
return true, nil
273+
return false, nil
228274
}
229275

230276
func reconcileMachineAddresses(scope *scope.MachineScope) error {
@@ -303,8 +349,8 @@ func createVM(scope *scope.MachineScope) (proxmox.VMCloneResponse, error) {
303349
options.Target = selectNextNode(scope)
304350
}
305351

306-
templateID := *scope.ProxmoxMachine.Spec.TemplateID
307-
res, err := scope.InfraCluster.ProxmoxClient.CloneVM(templateID, options)
352+
templateID := scope.ProxmoxMachine.GetTemplateID()
353+
res, err := scope.InfraCluster.ProxmoxClient.CloneVM(int(templateID), options)
308354
if err != nil {
309355
return res, err
310356
}

pkg/proxmox/client.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ import "github.com/luthermonson/go-proxmox"
2323
type Client interface {
2424
CloneVM(vmID int, clone VMCloneRequest) (VMCloneResponse, error)
2525

26+
ConfigureVM(vm *proxmox.VirtualMachine, options ...VirtualMachineOption) (*proxmox.Task, error)
27+
2628
GetVM(nodeName string, vmID int64) (*proxmox.VirtualMachine, error)
2729

2830
DeleteVM(nodeName string, vmID int64) (*proxmox.Task, error)

pkg/proxmox/goproxmox/api_client.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,15 @@ func (c *ProxmoxAPIClient) CloneVM(vmID int, clone capmox.VMCloneRequest) (capmo
6565
return capmox.VMCloneResponse{NewID: int64(newID), Task: task}, nil
6666
}
6767

68+
// ConfigureVM updates a VMs settings.
69+
func (c *ProxmoxAPIClient) ConfigureVM(vm *proxmox.VirtualMachine, options ...capmox.VirtualMachineOption) (*proxmox.Task, error) {
70+
task, err := vm.Config(options...)
71+
if err != nil {
72+
return nil, fmt.Errorf("unable to configure vm: %w", err)
73+
}
74+
return task, nil
75+
}
76+
6877
// GetVM returns a VM based on nodeName and vmID.
6978
func (c *ProxmoxAPIClient) GetVM(nodeName string, vmID int64) (*proxmox.VirtualMachine, error) {
7079
node, err := c.Client.Node(nodeName)

pkg/proxmox/types.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,6 @@ type VMCloneResponse struct {
2121
NewID int64 `json:"newId,omitempty"`
2222
Task *proxmox.Task `json:"task,omitempty"`
2323
}
24+
25+
// VirtualMachineOption is an alias for VirtualMachineOption to prevent import conflicts.
26+
type VirtualMachineOption = proxmox.VirtualMachineOption

0 commit comments

Comments
 (0)