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
36 changes: 29 additions & 7 deletions internal/provisioning/hostagent/networkmanager/network_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand All @@ -256,7 +257,22 @@ func (nm *NetworkManager) AddNetworkRequest(dpu *provisioningv1.DPU) error {
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 {
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)
}
nm.reqs[existing.UID] = existing
klog.Infof("Updated VF count to %d for DPU %s/%s", *vfCount, existing.DPUNamespace, existing.DpuName)
}
return nil
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I believe nm.getNumOfVFs(dpu) should be called here in the case of vfCount == nil. Otherwise, during Host Network Configuration phase, the host-agent will ignore the value in the DPUFlavor

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

You are right, I pushed a fix now.

}

Expand All @@ -272,9 +288,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

Expand Down
116 changes: 112 additions & 4 deletions internal/provisioning/hostagent/networkmanager/network_manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"))
})
Expand All @@ -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"))
})
Expand All @@ -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
Expand All @@ -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"))
Expand Down
6 changes: 3 additions & 3 deletions internal/provisioning/hostagent/phase/network/hostnetwork.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,18 @@ 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,
}
}

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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down Expand Up @@ -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())
Expand All @@ -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() {
Expand All @@ -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")
}

Expand Down
1 change: 1 addition & 0 deletions internal/provisioning/hostagent/service/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,5 @@ type UpdateStatusRequest struct {
type ConfigureHostVFsRequest struct {
DPUName string `json:"dpuName"`
DPUNamespace string `json:"dpuNamespace"`
VFCount int `json:"vfCount"`
}