From e96da2410a82ea87f6e2830dca00e8db7cc7773e Mon Sep 17 00:00:00 2001 From: Eli Elgaev Date: Mon, 18 May 2026 17:58:09 +0300 Subject: [PATCH 1/4] Added VFCount to the HTTP POST route /configure-host-vfs --- .../networkmanager/network_manager.go | 19 +++++++++++++------ .../hostagent/phase/network/hostnetwork.go | 6 +++--- .../hostagent/service/installation_service.go | 4 ++-- .../hostagent/service/types/types.go | 1 + 4 files changed, 19 insertions(+), 11 deletions(-) diff --git a/internal/provisioning/hostagent/networkmanager/network_manager.go b/internal/provisioning/hostagent/networkmanager/network_manager.go index 4b932137c..9c573b1d8 100644 --- a/internal/provisioning/hostagent/networkmanager/network_manager.go +++ b/internal/provisioning/hostagent/networkmanager/network_manager.go @@ -51,8 +51,9 @@ type Interface interface { Start() error // GetDevice returns the PCI device by serial number GetDevice(serialNumber string) (hostutil.Device, bool) - // AddNetworkRequest adds a network request for a DPU - AddNetworkRequest(dpu *provisioningv1.DPU) error + // AddNetworkRequest adds a network request for a DPU. + // If vfCount is non-nil it overrides the value derived from the DPUFlavor. + AddNetworkRequest(dpu *provisioningv1.DPU, vfCount *int) error } type NetworkManager struct { @@ -247,7 +248,7 @@ func (nm *NetworkManager) processNetworkRequest(nr NetworkRequest) error { return nil } -func (nm *NetworkManager) AddNetworkRequest(dpu *provisioningv1.DPU) error { +func (nm *NetworkManager) AddNetworkRequest(dpu *provisioningv1.DPU, vfCount *int) error { nm.Lock() defer nm.Unlock() if !nm.initialized { @@ -272,9 +273,15 @@ func (nm *NetworkManager) AddNetworkRequest(dpu *provisioningv1.DPU) error { } nr.PCIAddress = dev.Address - numOfVFs, err := nm.getNumOfVFs(dpu) - if err != nil { - return fmt.Errorf("failed to get number of VFs: %w", err) + var numOfVFs int + if vfCount != nil && *vfCount != 0 { + numOfVFs = *vfCount + } else { + var err error + numOfVFs, err = nm.getNumOfVFs(dpu) + if err != nil { + return fmt.Errorf("failed to get number of VFs: %w", err) + } } nr.NumOfVFs = numOfVFs diff --git a/internal/provisioning/hostagent/phase/network/hostnetwork.go b/internal/provisioning/hostagent/phase/network/hostnetwork.go index c0fc6e564..eb75d116b 100644 --- a/internal/provisioning/hostagent/phase/network/hostnetwork.go +++ b/internal/provisioning/hostagent/phase/network/hostnetwork.go @@ -31,10 +31,10 @@ const ( ) type Handler struct { - AddNetworkRequest func(dpu *provisioningv1.DPU) error + AddNetworkRequest func(dpu *provisioningv1.DPU, vfCount *int) error } -func NewHandler(addNetworkRequest func(dpu *provisioningv1.DPU) error) *Handler { +func NewHandler(addNetworkRequest func(dpu *provisioningv1.DPU, vfCount *int) error) *Handler { return &Handler{ AddNetworkRequest: addNetworkRequest, } @@ -42,7 +42,7 @@ func NewHandler(addNetworkRequest func(dpu *provisioningv1.DPU) error) *Handler func (h *Handler) Handle(ctx context.Context, dpu *provisioningv1.DPU) (provisioningv1.DPUStatus, ctrl.Result, error) { log := log.FromContext(ctx) - if err := h.AddNetworkRequest(dpu); err != nil { + if err := h.AddNetworkRequest(dpu, nil); err != nil { log.Error(err, "Failed to add network request") hostutil.NewCondition(condition).Failure(err, "FailedToSetupHostNetwork").Set(&dpu.Status.Conditions) return dpu.Status, ctrl.Result{}, err diff --git a/internal/provisioning/hostagent/service/installation_service.go b/internal/provisioning/hostagent/service/installation_service.go index 44922759b..e8d5199cf 100644 --- a/internal/provisioning/hostagent/service/installation_service.go +++ b/internal/provisioning/hostagent/service/installation_service.go @@ -67,7 +67,7 @@ const ( // NetworkConfigurator is an interface for triggering host network configuration. // It is satisfied by networkmanager.NetworkManager. type NetworkConfigurator interface { - AddNetworkRequest(dpu *provisioningv1.DPU) error + AddNetworkRequest(dpu *provisioningv1.DPU, vfCount *int) error } type InstallationService struct { @@ -329,7 +329,7 @@ func (s *InstallationService) ConfigureHostVFs(req *restful.Request, resp *restf return } - if err := s.networkManager.AddNetworkRequest(dpu); err != nil { + if err := s.networkManager.AddNetworkRequest(dpu, &request.VFCount); err != nil { klog.Errorf("failed to add network request for DPU %s/%s: %v", request.DPUNamespace, request.DPUName, err) _ = resp.WriteError(http.StatusInternalServerError, err) return diff --git a/internal/provisioning/hostagent/service/types/types.go b/internal/provisioning/hostagent/service/types/types.go index 9cf0e47de..4660a948d 100644 --- a/internal/provisioning/hostagent/service/types/types.go +++ b/internal/provisioning/hostagent/service/types/types.go @@ -30,4 +30,5 @@ type UpdateStatusRequest struct { type ConfigureHostVFsRequest struct { DPUName string `json:"dpuName"` DPUNamespace string `json:"dpuNamespace"` + VFCount int `json:"vfCount"` } From 1c9c7886173b73beee9af292fe1aac5e11c4d269 Mon Sep 17 00:00:00 2001 From: Eli Elgaev Date: Thu, 21 May 2026 02:38:42 +0300 Subject: [PATCH 2/4] fix(hostagent): update VF count on existing network request AddNetworkRequest returned early when a request already existed, ignoring the provided vfCount. Now updates NumOfVFs and persists the change so the next processing cycle applies it. --- .../hostagent/networkmanager/network_manager.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/internal/provisioning/hostagent/networkmanager/network_manager.go b/internal/provisioning/hostagent/networkmanager/network_manager.go index 9c573b1d8..79ad848c7 100644 --- a/internal/provisioning/hostagent/networkmanager/network_manager.go +++ b/internal/provisioning/hostagent/networkmanager/network_manager.go @@ -257,7 +257,15 @@ func (nm *NetworkManager) AddNetworkRequest(dpu *provisioningv1.DPU, vfCount *in return fmt.Errorf("DPU is nil") } - if _, ok := nm.reqs[string(dpu.UID)]; ok { + if existing, ok := nm.reqs[string(dpu.UID)]; ok { + if vfCount != nil && *vfCount != 0 && existing.NumOfVFs != *vfCount { + existing.NumOfVFs = *vfCount + if err := writeNetworkRequestFile(&existing); err != nil { + return fmt.Errorf("failed to update network request file: %w", err) + } + nm.reqs[existing.UID] = existing + klog.Infof("Updated VF count to %d for DPU %s/%s", *vfCount, existing.DPUNamespace, existing.DpuName) + } return nil } From 657f27c6c8c93f294e6dabdf6fce260ba47bc8e9 Mon Sep 17 00:00:00 2001 From: Eli Elgaev Date: Mon, 1 Jun 2026 09:46:22 +0300 Subject: [PATCH 3/4] Added testing for vfCount in /configure-host-vfs --- .../networkmanager/network_manager_test.go | 116 +++++++++++++++++- .../service/installation_service_test.go | 19 ++- 2 files changed, 125 insertions(+), 10 deletions(-) diff --git a/internal/provisioning/hostagent/networkmanager/network_manager_test.go b/internal/provisioning/hostagent/networkmanager/network_manager_test.go index bf13a747f..5ebfb3b54 100644 --- a/internal/provisioning/hostagent/networkmanager/network_manager_test.go +++ b/internal/provisioning/hostagent/networkmanager/network_manager_test.go @@ -102,7 +102,7 @@ var _ = Describe("NetworkManager", func() { }, } - err := nm.AddNetworkRequest(dpu) + err := nm.AddNetworkRequest(dpu, nil) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("network manager is not initialized")) }) @@ -111,7 +111,7 @@ var _ = Describe("NetworkManager", func() { nm := NewNetworkManager(nil) nm.initialized = true // Bypass initialization check - err := nm.AddNetworkRequest(nil) + err := nm.AddNetworkRequest(nil, nil) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("DPU is nil")) }) @@ -134,10 +134,118 @@ var _ = Describe("NetworkManager", func() { } // Should return nil since request already exists - err := nm.AddNetworkRequest(dpu) + err := nm.AddNetworkRequest(dpu, nil) Expect(err).NotTo(HaveOccurred()) }) + Context("vfCount override on existing request", func() { + var ( + tempDir string + origNetworkRequestDir string + ) + + BeforeEach(func() { + var err error + tempDir, err = os.MkdirTemp("", "nm-vfcount-test-*") + Expect(err).NotTo(HaveOccurred()) + origNetworkRequestDir = NetworkRequestDir + NetworkRequestDir = tempDir + }) + + AfterEach(func() { + NetworkRequestDir = origNetworkRequestDir + _ = os.RemoveAll(tempDir) + }) + + It("should update VF count on existing request when vfCount is provided", func() { + nm := NewNetworkManager(nil) + nm.initialized = true + + dpu := &provisioningv1.DPU{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-dpu", + Namespace: "default", + UID: "test-uid-vf-update", + }, + } + nm.reqs["test-uid-vf-update"] = NetworkRequest{ + UID: "test-uid-vf-update", + NumOfVFs: 4, + DpuName: "test-dpu", + } + + vfCount := 8 + err := nm.AddNetworkRequest(dpu, &vfCount) + Expect(err).NotTo(HaveOccurred()) + Expect(nm.reqs["test-uid-vf-update"].NumOfVFs).To(Equal(8)) + }) + + It("should not update VF count when vfCount is nil", func() { + nm := NewNetworkManager(nil) + nm.initialized = true + + dpu := &provisioningv1.DPU{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-dpu", + Namespace: "default", + UID: "test-uid-nil-vf", + }, + } + nm.reqs["test-uid-nil-vf"] = NetworkRequest{ + UID: "test-uid-nil-vf", + NumOfVFs: 4, + } + + err := nm.AddNetworkRequest(dpu, nil) + Expect(err).NotTo(HaveOccurred()) + Expect(nm.reqs["test-uid-nil-vf"].NumOfVFs).To(Equal(4)) + }) + + It("should not update VF count when vfCount is zero", func() { + nm := NewNetworkManager(nil) + nm.initialized = true + + dpu := &provisioningv1.DPU{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-dpu", + Namespace: "default", + UID: "test-uid-zero-vf", + }, + } + nm.reqs["test-uid-zero-vf"] = NetworkRequest{ + UID: "test-uid-zero-vf", + NumOfVFs: 4, + } + + vfCount := 0 + err := nm.AddNetworkRequest(dpu, &vfCount) + Expect(err).NotTo(HaveOccurred()) + Expect(nm.reqs["test-uid-zero-vf"].NumOfVFs).To(Equal(4)) + }) + + It("should not update VF count when it matches existing", func() { + nm := NewNetworkManager(nil) + nm.initialized = true + + dpu := &provisioningv1.DPU{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-dpu", + Namespace: "default", + UID: "test-uid-same-vf", + }, + } + nm.reqs["test-uid-same-vf"] = NetworkRequest{ + UID: "test-uid-same-vf", + NumOfVFs: 4, + } + + vfCount := 4 + err := nm.AddNetworkRequest(dpu, &vfCount) + Expect(err).NotTo(HaveOccurred()) + Expect(nm.reqs["test-uid-same-vf"].NumOfVFs).To(Equal(4)) + }) + }) + It("should return error when device not found by serial number", func() { nm := NewNetworkManager(nil) nm.initialized = true @@ -154,7 +262,7 @@ var _ = Describe("NetworkManager", func() { }, } - err := nm.AddNetworkRequest(dpu) + err := nm.AddNetworkRequest(dpu, nil) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("PCI address of device")) Expect(err.Error()).To(ContainSubstring("not found")) diff --git a/internal/provisioning/hostagent/service/installation_service_test.go b/internal/provisioning/hostagent/service/installation_service_test.go index 105b658d7..27b2b392b 100644 --- a/internal/provisioning/hostagent/service/installation_service_test.go +++ b/internal/provisioning/hostagent/service/installation_service_test.go @@ -35,12 +35,12 @@ import ( ) type mockNetworkConfigurator struct { - addNetworkRequestFunc func(dpu *provisioningv1.DPU) error + addNetworkRequestFunc func(dpu *provisioningv1.DPU, vfCount *int) error } -func (m *mockNetworkConfigurator) AddNetworkRequest(dpu *provisioningv1.DPU) error { +func (m *mockNetworkConfigurator) AddNetworkRequest(dpu *provisioningv1.DPU, vfCount *int) error { if m.addNetworkRequestFunc != nil { - return m.addNetworkRequestFunc(dpu) + return m.addNetworkRequestFunc(dpu, vfCount) } return nil } @@ -385,18 +385,21 @@ var _ = Describe("InstallationService", func() { installationService.networkManager = mockNM }) - It("should successfully configure host VF", func() { + It("should successfully configure host VF with VFCount", func() { dpu := createDPU("test-dpu", testNS.Name) var receivedDPU *provisioningv1.DPU - mockNM.addNetworkRequestFunc = func(dpu *provisioningv1.DPU) error { + var receivedVFCount *int + mockNM.addNetworkRequestFunc = func(dpu *provisioningv1.DPU, vfCount *int) error { receivedDPU = dpu + receivedVFCount = vfCount return nil } request := types.ConfigureHostVFsRequest{ DPUName: dpu.Name, DPUNamespace: dpu.Namespace, + VFCount: 16, } req, err := json.Marshal(request) Expect(err).To(Succeed()) @@ -412,6 +415,10 @@ var _ = Describe("InstallationService", func() { Expect(receivedDPU.Spec.SerialNumber).To(Equal(dpu.Spec.SerialNumber)) Expect(receivedDPU.Spec.DPUFlavor).To(Equal(dpu.Spec.DPUFlavor)) Expect(receivedDPU.Spec.BFB).To(Equal(dpu.Spec.BFB)) + + By("VFCount should be passed through to AddNetworkRequest") + Expect(receivedVFCount).NotTo(BeNil()) + Expect(*receivedVFCount).To(Equal(16)) }) It("should return 404 when DPU not found", func() { @@ -430,7 +437,7 @@ var _ = Describe("InstallationService", func() { It("should return 500 when AddNetworkRequest fails", func() { dpu := createDPU("test-dpu", testNS.Name) - mockNM.addNetworkRequestFunc = func(dpu *provisioningv1.DPU) error { + mockNM.addNetworkRequestFunc = func(dpu *provisioningv1.DPU, vfCount *int) error { return fmt.Errorf("network manager is not initialized") } From 9e76dad754c800c60e36ce9d5b61d9e286934c74 Mon Sep 17 00:00:00 2001 From: Eli Elgaev Date: Tue, 2 Jun 2026 11:58:26 +0300 Subject: [PATCH 4/4] Handling the case where vfCount is nil by falling back to use dpuflavor value --- .../hostagent/networkmanager/network_manager.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/internal/provisioning/hostagent/networkmanager/network_manager.go b/internal/provisioning/hostagent/networkmanager/network_manager.go index 79ad848c7..2415a3aa9 100644 --- a/internal/provisioning/hostagent/networkmanager/network_manager.go +++ b/internal/provisioning/hostagent/networkmanager/network_manager.go @@ -258,7 +258,14 @@ func (nm *NetworkManager) AddNetworkRequest(dpu *provisioningv1.DPU, vfCount *in } if existing, ok := nm.reqs[string(dpu.UID)]; ok { - if vfCount != nil && *vfCount != 0 && existing.NumOfVFs != *vfCount { + if vfCount == nil { + n, err := nm.getNumOfVFs(dpu) + if err != nil { + return fmt.Errorf("failed to get number of VFs: %w", err) + } + vfCount = &n + } + if *vfCount != 0 && existing.NumOfVFs != *vfCount { existing.NumOfVFs = *vfCount if err := writeNetworkRequestFile(&existing); err != nil { return fmt.Errorf("failed to update network request file: %w", err)