Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions api/v1alpha1/proxmoxmachine_conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ func restoreProxmoxMachineSpec(src *ProxmoxMachineSpec, dst *v1alpha2.ProxmoxMac
if i < len(restored.Network.NetworkDevices) {
dst.Network.NetworkDevices[i].DefaultIPv4 = restored.Network.NetworkDevices[i].DefaultIPv4
dst.Network.NetworkDevices[i].DefaultIPv6 = restored.Network.NetworkDevices[i].DefaultIPv6
dst.Network.NetworkDevices[i].Queues = restored.Network.NetworkDevices[i].Queues
}
}
}
Expand Down
1 change: 1 addition & 0 deletions api/v1alpha1/zz_generated.conversion.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions api/v1alpha2/proxmoxmachine_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,13 @@
// +kubebuilder:validation:Maximum=4094
VLAN *int32 `json:"vlan,omitempty"`

// Queues is the number of queues assigned to the device.

Check failure on line 458 in api/v1alpha2/proxmoxmachine_types.go

View workflow job for this annotation

GitHub Actions / lint-api

commentstart: godoc for field NetworkDevice.Queues should start with 'queues ...' (kubeapilinter)
// This value is passed to the Multiqueue field in PROXMOX.
// +optional
// +kubebuilder:validation:Minimum=1
// +kubebuilder:validation:Maximum=65535
Queues *int32 `json:"queues,omitempty"`

// name is the network device name.
// +default="net0"
// +optional
Expand Down
14 changes: 14 additions & 0 deletions api/v1alpha2/proxmoxmachine_types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,20 @@ var _ = Describe("ProxmoxMachine Test", func() {
})
})

Context("Queues", func() {
It("Should not allow machine with network device queue equal to 0", func() {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a test for >65535, a test for 0<queues<65536, and a test for nil.

dm := defaultMachine()
dm.Spec.Network = &NetworkSpec{
NetworkDevices: []NetworkDevice{{
Bridge: ptr.To("vmbr0"),
Queues: ptr.To(int32(0)),
}},
}

Expect(k8sClient.Create(context.Background(), dm)).Should(MatchError(ContainSubstring("should be greater than or equal to 1")))
})
})

Context("VMIDRange", func() {
It("Should only allow spec.vmIDRange.start >= 100", func() {
dm := defaultMachine()
Expand Down
5 changes: 5 additions & 0 deletions api/v1alpha2/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -1114,6 +1114,14 @@ spec:
minLength: 4
pattern: ^net[0-9]+$
type: string
queues:
description: |-
Queues is the number of queues assigned to the device.
This value is passed to the Multiqueue field in PROXMOX.
format: int32
maximum: 65535
minimum: 1
type: integer
routes:
description: routes are the routes associated with this
interface.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -980,6 +980,14 @@ spec:
minLength: 4
pattern: ^net[0-9]+$
type: string
queues:
description: |-
Queues is the number of queues assigned to the device.
This value is passed to the Multiqueue field in PROXMOX.
format: int32
maximum: 65535
minimum: 1
type: integer
routes:
description: routes are the routes associated with
this interface.
Expand Down
28 changes: 26 additions & 2 deletions internal/service/vmservice/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,21 @@
return 0
}

// extractNetworkQueue returns the queue out of net device input e.g. virtio=A6:23:64:4D:84:CB,bridge=vmbr1,mtu=1500,tag=100,queues=4.
func extractNetworkQueue(input string) int32 {
re := regexp.MustCompile(`queues=(\d+)`)
match := re.FindStringSubmatch(input)
if len(match) > 1 {
queue, err := strconv.ParseUint(match[1], 10, 16)
if err != nil {
return 0
}
return int32(queue)
}

return 0
}

func shouldUpdateNetworkDevices(machineScope *scope.MachineScope) bool {
if machineScope.ProxmoxMachine.Spec.Network == nil {
// no network config needed
Expand Down Expand Up @@ -180,14 +195,19 @@
return true
}
}
queues := extractNetworkQueue(net)
if queues != ptr.Deref(v.Queues, 0) {
return true
}

}

Check failure on line 203 in internal/service/vmservice/utils.go

View workflow job for this annotation

GitHub Actions / lint

unnecessary trailing newline (whitespace)

return false
}

// formatNetworkDevice formats a network device config
// example 'virtio,bridge=vmbr0,tag=100'.
func formatNetworkDevice(model, bridge string, mtu *int32, vlan *int32) string {
// example 'virtio,bridge=vmbr0,tag=100,queues=4'.
func formatNetworkDevice(model, bridge string, mtu *int32, vlan *int32, queues int32) string {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function takes pointers to optional values, please follow that pattern.

var components = []string{model, fmt.Sprintf("bridge=%s", bridge)}

if mtu != nil {
Expand All @@ -198,6 +218,10 @@
components = append(components, fmt.Sprintf("tag=%d", *vlan))
}

if queues != 0 {
components = append(components, fmt.Sprintf("queues=%d", queues))
}

return strings.Join(components, ",")
}

Expand Down
2 changes: 1 addition & 1 deletion internal/service/vmservice/vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,7 @@ func reconcileVirtualMachineConfig(ctx context.Context, machineScope *scope.Mach
for _, v := range devices {
vmOptions = append(vmOptions, proxmox.VirtualMachineOption{
Name: string(v.Name),
Value: formatNetworkDevice(ptr.Deref(v.Model, "virtio"), ptr.Deref(v.Bridge, ""), v.MTU, v.VLAN),
Value: formatNetworkDevice(ptr.Deref(v.Model, "virtio"), ptr.Deref(v.Bridge, ""), v.MTU, v.VLAN, ptr.Deref(v.Queues, 0)),
})
}
}
Expand Down
39 changes: 35 additions & 4 deletions internal/service/vmservice/vm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -449,8 +449,8 @@ func TestReconcileVirtualMachineConfig_ApplyConfig(t *testing.T) {
proxmox.VirtualMachineOption{Name: optionCores, Value: *machineScope.ProxmoxMachine.Spec.NumCores},
proxmox.VirtualMachineOption{Name: optionMemory, Value: *machineScope.ProxmoxMachine.Spec.MemoryMiB},
proxmox.VirtualMachineOption{Name: optionDescription, Value: machineScope.ProxmoxMachine.Spec.Description},
proxmox.VirtualMachineOption{Name: "net0", Value: formatNetworkDevice("virtio", "vmbr0", ptr.To[int32](1500), nil)},
proxmox.VirtualMachineOption{Name: "net1", Value: formatNetworkDevice("virtio", "vmbr1", ptr.To[int32](1500), nil)},
proxmox.VirtualMachineOption{Name: "net0", Value: formatNetworkDevice("virtio", "vmbr0", ptr.To[int32](1500), nil, 0)},
proxmox.VirtualMachineOption{Name: "net1", Value: formatNetworkDevice("virtio", "vmbr1", ptr.To[int32](1500), nil, 0)},
}

proxmoxClient.EXPECT().ConfigureVM(context.Background(), vm, expectedOptions...).Return(task, nil).Once()
Expand Down Expand Up @@ -624,8 +624,39 @@ func TestReconcileVirtualMachineConfigVLAN(t *testing.T) {
proxmox.VirtualMachineOption{Name: optionSockets, Value: *machineScope.ProxmoxMachine.Spec.NumSockets},
proxmox.VirtualMachineOption{Name: optionCores, Value: *machineScope.ProxmoxMachine.Spec.NumCores},
proxmox.VirtualMachineOption{Name: optionMemory, Value: *machineScope.ProxmoxMachine.Spec.MemoryMiB},
proxmox.VirtualMachineOption{Name: "net0", Value: formatNetworkDevice("virtio", "vmbr0", nil, ptr.To(int32(100)))},
proxmox.VirtualMachineOption{Name: "net1", Value: formatNetworkDevice("virtio", "vmbr1", nil, ptr.To(int32(100)))},
proxmox.VirtualMachineOption{Name: "net0", Value: formatNetworkDevice("virtio", "vmbr0", nil, ptr.To(int32(100)), 0)},
proxmox.VirtualMachineOption{Name: "net1", Value: formatNetworkDevice("virtio", "vmbr1", nil, ptr.To(int32(100)), 0)},
}

proxmoxClient.EXPECT().ConfigureVM(context.TODO(), vm, expectedOptions...).Return(task, nil).Once()

requeue, err := reconcileVirtualMachineConfig(context.TODO(), machineScope)
require.NoError(t, err)
require.True(t, requeue)
require.EqualValues(t, task.UPID, *machineScope.ProxmoxMachine.Status.TaskRef)
}

func TestReconcileVirtualMachineConfigQueue(t *testing.T) {
machineScope, proxmoxClient, _ := setupReconcilerTestWithCondition(t, infrav1.ProxmoxMachineVirtualMachineProvisionedCloningReason)
machineScope.ProxmoxMachine.Spec.NumSockets = ptr.To(int32(4))
machineScope.ProxmoxMachine.Spec.NumCores = ptr.To(int32(4))
machineScope.ProxmoxMachine.Spec.MemoryMiB = ptr.To(int32(16 * 1024))
machineScope.ProxmoxMachine.Spec.Network = &infrav1.NetworkSpec{
NetworkDevices: []infrav1.NetworkDevice{
{Name: infrav1.NetName("net0"), Bridge: ptr.To("vmbr0"), Model: ptr.To("virtio"), VLAN: ptr.To(int32(100)), Queues: ptr.To(int32(4))},
{Name: infrav1.NetName("net1"), Bridge: ptr.To("vmbr1"), Model: ptr.To("virtio"), VLAN: ptr.To(int32(100)), Queues: ptr.To(int32(4))},
},
}

vm := newStoppedVM()
task := newTask()
machineScope.SetVirtualMachine(vm)
expectedOptions := []interface{}{
proxmox.VirtualMachineOption{Name: optionSockets, Value: *machineScope.ProxmoxMachine.Spec.NumSockets},
proxmox.VirtualMachineOption{Name: optionCores, Value: *machineScope.ProxmoxMachine.Spec.NumCores},
proxmox.VirtualMachineOption{Name: optionMemory, Value: *machineScope.ProxmoxMachine.Spec.MemoryMiB},
proxmox.VirtualMachineOption{Name: "net0", Value: formatNetworkDevice("virtio", "vmbr0", nil, ptr.To(int32(100)), 4)},
proxmox.VirtualMachineOption{Name: "net1", Value: formatNetworkDevice("virtio", "vmbr1", nil, ptr.To(int32(100)), 4)},
}

proxmoxClient.EXPECT().ConfigureVM(context.TODO(), vm, expectedOptions...).Return(task, nil).Once()
Expand Down
Loading