Skip to content

Commit d29691b

Browse files
committed
Install BFB when requested
Signed-off-by: Alexander Maslennikov <[email protected]>
1 parent ff7bc84 commit d29691b

File tree

11 files changed

+478
-14
lines changed

11 files changed

+478
-14
lines changed

cmd/nic-configuration-daemon/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ func main() {
119119

120120
configurationManager := configuration.NewConfigurationManager(eventRecorder, dmsManager)
121121
maintenanceManager := maintenance.New(mgr.GetClient(), hostUtils, nodeName, namespace)
122-
firmwareManager := firmware.NewFirmwareManager(mgr.GetClient(), namespace)
122+
firmwareManager := firmware.NewFirmwareManager(mgr.GetClient(), dmsManager, namespace)
123123

124124
if err := initNicFwMap(namespace); err != nil {
125125
log.Log.Error(err, "unable to init NicFwMap")

internal/controller/nicdevice_controller.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import (
4747
"github.com/Mellanox/nic-configuration-operator/pkg/host"
4848
"github.com/Mellanox/nic-configuration-operator/pkg/maintenance"
4949
"github.com/Mellanox/nic-configuration-operator/pkg/types"
50+
"github.com/Mellanox/nic-configuration-operator/pkg/utils"
5051
)
5152

5253
const nicDeviceSyncEventName = "nic-device-sync-event-name"
@@ -631,6 +632,12 @@ func (r *NicDeviceReconciler) handleFirmwareUpdate(ctx context.Context, status *
631632
return err
632633
}
633634

635+
if utils.IsBlueFieldDevice(status.device.Status.Type) {
636+
log.Log.Info("BlueField BFB installed, reboot is required to apply changes", "device", status.device.Name)
637+
status.rebootRequired = true
638+
return nil
639+
}
640+
634641
log.Log.Info("New firmware image was successfully burned. Firmware reset is required to apply changes", "device", status.device.Name)
635642

636643
err = r.ConfigurationManager.ResetNicFirmware(ctx, status.device)

pkg/consts/consts.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ limitations under the License.
1616
package consts
1717

1818
const (
19-
MellanoxVendor = "15b3"
19+
MellanoxVendor = "15b3"
20+
BlueField3DeviceID = "a2dc"
21+
BlueField2DeviceID = "a2d6"
2022

2123
Ethernet = "Ethernet"
2224
Infiniband = "Infiniband"

pkg/dms/client.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ limitations under the License.
1616
package dms
1717

1818
import (
19+
"context"
1920
"encoding/json"
2021
"fmt"
2122
"strings"
@@ -27,6 +28,7 @@ import (
2728

2829
"github.com/Mellanox/nic-configuration-operator/api/v1alpha1"
2930
"github.com/Mellanox/nic-configuration-operator/pkg/consts"
31+
"github.com/Mellanox/nic-configuration-operator/pkg/utils"
3032
)
3133

3234
const (
@@ -47,6 +49,8 @@ type DMSClient interface {
4749
GetQoSSettings(interfaceName string) (string, string, error)
4850
// SetQoSSettings sets the QoS settings for the device (trust mode and PFC configuration). Settings are applied to all ports of the device.
4951
SetQoSSettings(trustMode, pfc string) error
52+
// InstallBFB installs the BFB file with the new firmware version on a BlueField device
53+
InstallBFB(ctx context.Context, version string, bfbPath string) error
5054
}
5155

5256
// dmsInstance implements the DMSClient interface
@@ -248,3 +252,45 @@ func (i *dmsInstance) SetQoSSettings(trust, pfc string) error {
248252
log.Log.V(2).Info("QoS settings applied to all ports", "device", i.device.SerialNumber, "portCount", portCount)
249253
return nil
250254
}
255+
256+
// InstallBFB installs the BFB file with the new firmware version on a BlueField device
257+
func (i *dmsInstance) InstallBFB(ctx context.Context, version string, bfbPath string) error {
258+
log.Log.V(2).Info("dmsInstance.InstallBFB()", "version", version, "bfbPath", bfbPath, "device", i.device.SerialNumber)
259+
260+
if !i.running.Load() {
261+
log.Log.V(2).Info("DMS instance not running", "device", i.device.SerialNumber)
262+
return fmt.Errorf("DMS instance is not running")
263+
}
264+
265+
if !utils.IsBlueFieldDevice(i.device.Type) {
266+
err := fmt.Errorf("cannot install BFB file on non-BlueField device")
267+
log.Log.Error(err, "failed to install BFB", "device", i.device.SerialNumber, "deviceType", i.device.Type)
268+
return err
269+
}
270+
271+
args := []string{dmsClientPath, "-a", i.bindAddress, "--insecure", "os", "install", "--version", version, "--pkg", bfbPath}
272+
log.Log.V(2).Info("dmsInstance.InstallBFB() install command", "args", strings.Join(args, " "))
273+
274+
command := i.execInterface.CommandContext(ctx, args[0], args[1:]...)
275+
output, err := utils.RunCommand(command)
276+
if err != nil {
277+
log.Log.Error(err, "failed to install BFB", "device", i.device.SerialNumber, "deviceType", i.device.Type)
278+
return err
279+
}
280+
281+
log.Log.V(2).Info("BFB installed successfully", "device", i.device.SerialNumber, "version", version, "output", string(output))
282+
283+
args = []string{dmsClientPath, "-a", i.bindAddress, "--insecure", "os", "activate", "--version", version}
284+
log.Log.V(2).Info("dmsInstance.InstallBFB() activate command", "args", strings.Join(args, " "))
285+
286+
command = i.execInterface.CommandContext(ctx, args[0], args[1:]...)
287+
output, err = utils.RunCommand(command)
288+
if err != nil {
289+
log.Log.Error(err, "failed to activate BFB", "device", i.device.SerialNumber, "deviceType", i.device.Type)
290+
return err
291+
}
292+
293+
log.Log.V(2).Info("BFB activated successfully", "device", i.device.SerialNumber, "version", version, "output", string(output))
294+
295+
return nil
296+
}

pkg/dms/client_test.go

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ limitations under the License.
1616
package dms
1717

1818
import (
19+
"context"
1920
"errors"
2021
"strings"
2122

@@ -239,4 +240,117 @@ var _ = Describe("DMSClient", func() {
239240
})
240241
})
241242
})
243+
244+
Describe("InstallBFB", func() {
245+
const (
246+
testBFBVersion = "24.35.1000"
247+
testBFBPath = "/path/to/firmware.bfb"
248+
)
249+
250+
Context("when the instance is running", func() {
251+
Context("on BlueField device", func() {
252+
BeforeEach(func() {
253+
device.Type = "a2d6" // BlueField2 device ID
254+
instance.device = device
255+
instance.running.Store(true)
256+
})
257+
258+
It("should install and activate BFB successfully", func() {
259+
fakeInstallCmd := createFakeCmd([]byte("BFB install successful"), nil)
260+
fakeActivateCmd := createFakeCmd([]byte("BFB activate successful"), nil)
261+
262+
callCount := 0
263+
cmdAction := func(cmd string, args ...string) exec.Cmd {
264+
callCount++
265+
Expect(cmd).To(Equal(dmsClientPath))
266+
267+
if callCount == 1 {
268+
// First call should be install command
269+
Expect(args).To(ContainElement("--insecure"))
270+
Expect(args).To(ContainElement("os"))
271+
Expect(args).To(ContainElement("install"))
272+
Expect(args).To(ContainElement("--version"))
273+
Expect(args).To(ContainElement(testBFBVersion))
274+
Expect(args).To(ContainElement("--pkg"))
275+
Expect(args).To(ContainElement(testBFBPath))
276+
return fakeInstallCmd
277+
} else {
278+
// Second call should be activate command
279+
Expect(args).To(ContainElement("--insecure"))
280+
Expect(args).To(ContainElement("os"))
281+
Expect(args).To(ContainElement("activate"))
282+
Expect(args).To(ContainElement("--version"))
283+
Expect(args).To(ContainElement(testBFBVersion))
284+
return fakeActivateCmd
285+
}
286+
}
287+
288+
fakeExec.CommandScript = []execTesting.FakeCommandAction{cmdAction, cmdAction}
289+
290+
err := instance.InstallBFB(context.Background(), testBFBVersion, testBFBPath)
291+
Expect(err).NotTo(HaveOccurred())
292+
})
293+
294+
It("should return error when install command fails", func() {
295+
fakeInstallCmd := createFakeCmd(nil, errors.New("install failed"))
296+
297+
cmdAction := func(cmd string, args ...string) exec.Cmd {
298+
return fakeInstallCmd
299+
}
300+
301+
fakeExec.CommandScript = []execTesting.FakeCommandAction{cmdAction}
302+
303+
err := instance.InstallBFB(context.Background(), testBFBVersion, testBFBPath)
304+
Expect(err).To(HaveOccurred())
305+
Expect(err.Error()).To(ContainSubstring("install failed"))
306+
})
307+
308+
It("should return error when activate command fails", func() {
309+
fakeInstallCmd := createFakeCmd([]byte("BFB install successful"), nil)
310+
fakeActivateCmd := createFakeCmd(nil, errors.New("activate failed"))
311+
312+
cmdAction := func(cmd string, args ...string) exec.Cmd {
313+
if len(args) >= 3 && args[2] == "install" {
314+
return fakeInstallCmd
315+
}
316+
return fakeActivateCmd
317+
}
318+
319+
fakeExec.CommandScript = []execTesting.FakeCommandAction{cmdAction, cmdAction}
320+
321+
err := instance.InstallBFB(context.Background(), testBFBVersion, testBFBPath)
322+
Expect(err).To(HaveOccurred())
323+
Expect(err.Error()).To(ContainSubstring("activate failed"))
324+
})
325+
})
326+
327+
Context("on non-BlueField device", func() {
328+
BeforeEach(func() {
329+
device.Type = "cx6"
330+
instance.device = device
331+
instance.running.Store(true)
332+
})
333+
334+
It("should return error for non-BlueField device", func() {
335+
err := instance.InstallBFB(context.Background(), testBFBVersion, testBFBPath)
336+
Expect(err).To(HaveOccurred())
337+
Expect(err.Error()).To(ContainSubstring("cannot install BFB file on non-BlueField device"))
338+
})
339+
})
340+
})
341+
342+
Context("when the instance is not running", func() {
343+
BeforeEach(func() {
344+
device.Type = "a2d6" // BlueField2 device ID
345+
instance.device = device
346+
instance.running.Store(false)
347+
})
348+
349+
It("should return error", func() {
350+
err := instance.InstallBFB(context.Background(), testBFBVersion, testBFBPath)
351+
Expect(err).To(HaveOccurred())
352+
Expect(err.Error()).To(Equal("DMS instance is not running"))
353+
})
354+
})
355+
})
242356
})

pkg/dms/mocks/DMSClient.go

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

pkg/firmware/manager.go

Lines changed: 71 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@ import (
3131

3232
"github.com/Mellanox/nic-configuration-operator/api/v1alpha1"
3333
"github.com/Mellanox/nic-configuration-operator/pkg/consts"
34+
"github.com/Mellanox/nic-configuration-operator/pkg/dms"
3435
"github.com/Mellanox/nic-configuration-operator/pkg/types"
36+
utilsPkg "github.com/Mellanox/nic-configuration-operator/pkg/utils"
3537
)
3638

3739
// FirmwareManager contains logic for managing NIC devices FW on the host
@@ -53,6 +55,8 @@ type FirmwareManager interface {
5355
type firmwareManager struct {
5456
client client.Client
5557

58+
dmsManager dms.DMSManager
59+
5660
utils FirmwareUtils
5761

5862
cacheRootDir string
@@ -88,9 +92,19 @@ func (f firmwareManager) ValidateRequestedFirmwareSource(ctx context.Context, de
8892
case consts.FirmwareSourceDownloadFailedStatus, consts.FirmwareSourceProcessingFailedStatus, consts.FirmwareSourceCacheVerificationFailedStatus:
8993
return "", fmt.Errorf("requested firmware source %s failed: %s, %s", fwSourceName, status, fwSourceObj.Status.Reason)
9094
case consts.FirmwareSourceSuccessStatus:
95+
deviceID := device.Status.Type
96+
if utilsPkg.IsBlueFieldDevice(deviceID) {
97+
version, found := fwSourceObj.Status.BFBVersions[deviceID]
98+
if !found {
99+
return "", fmt.Errorf("requested firmware source (%s) has no image for this BlueField device (%s)", fwSourceName, deviceID)
100+
}
101+
102+
return version, nil
103+
}
104+
91105
devicePSID := device.Status.PSID
92106

93-
for version, PSIDs := range fwSourceObj.Status.Versions {
107+
for version, PSIDs := range fwSourceObj.Status.BinaryVersions {
94108
if slices.Contains(PSIDs, devicePSID) {
95109
return version, nil
96110
}
@@ -112,8 +126,61 @@ func (f firmwareManager) BurnNicFirmware(ctx context.Context, device *v1alpha1.N
112126
}
113127

114128
fwSourceName := device.Spec.Firmware.NicFirmwareSourceRef
115-
116129
cacheDir := path.Join(f.cacheRootDir, fwSourceName)
130+
131+
if utilsPkg.IsBlueFieldDevice(device.Status.Type) {
132+
return f.burnBlueFieldFirmware(ctx, device, version, cacheDir)
133+
}
134+
135+
return f.burnDefaultFirmware(ctx, device, version, cacheDir)
136+
}
137+
138+
// burnBlueFieldFirmware handles firmware updates for BlueField devices using BFB files
139+
func (f firmwareManager) burnBlueFieldFirmware(ctx context.Context, device *v1alpha1.NicDevice, version string, cacheDir string) error {
140+
log.Log.Info("FirmwareManager.burnBlueFieldFirmware()", "device", device.Name, "version", version)
141+
142+
dmsClient, err := f.dmsManager.GetDMSClientBySerialNumber(device.Status.SerialNumber)
143+
if err != nil {
144+
log.Log.Error(err, "failed to get DMS client", "device", device.Name)
145+
return err
146+
}
147+
148+
bfbFolderPath := filepath.Join(cacheDir, consts.BFBFolder)
149+
log.Log.V(2).Info("Searching for BFB file in the cache", "bfbFolderPath", bfbFolderPath)
150+
151+
var bfbPath string
152+
err = filepath.WalkDir(bfbFolderPath, func(path string, d os.DirEntry, err error) error {
153+
if err == nil && strings.EqualFold(filepath.Ext(d.Name()), ".bfb") {
154+
log.Log.V(2).Info("Found BFB file", "path", path)
155+
bfbPath = path
156+
return filepath.SkipAll
157+
}
158+
return nil
159+
})
160+
if err != nil {
161+
log.Log.Error(err, "failed to search for BFB file", "bfbFolderPath", bfbFolderPath)
162+
return err
163+
}
164+
165+
if bfbPath == "" {
166+
err := fmt.Errorf("couldn't find BFB file for version %s", version)
167+
log.Log.Error(err, "failed to upgrade FW on BlueField device", "device", device.Name)
168+
return err
169+
}
170+
171+
err = dmsClient.InstallBFB(ctx, version, bfbPath)
172+
if err != nil {
173+
log.Log.Error(err, "failed to install BFB", "device", device.Name)
174+
return err
175+
}
176+
177+
return nil
178+
}
179+
180+
// burnDefaultFirmware handles firmware updates for regular NIC devices using binary files
181+
func (f firmwareManager) burnDefaultFirmware(ctx context.Context, device *v1alpha1.NicDevice, version string, cacheDir string) error {
182+
log.Log.Info("FirmwareManager.burnDefaultFirmware()", "device", device.Name, "version", version)
183+
117184
devicePSID := device.Status.PSID
118185

119186
log.Log.V(2).Info("Searching for FW binary in the cache", "cacheDir", cacheDir, "PSID", devicePSID)
@@ -187,6 +254,6 @@ func (f firmwareManager) BurnNicFirmware(ctx context.Context, device *v1alpha1.N
187254
return nil
188255
}
189256

190-
func NewFirmwareManager(client client.Client, namespace string) FirmwareManager {
191-
return &firmwareManager{client: client, cacheRootDir: consts.NicFirmwareStorage, namespace: namespace, tmpDir: consts.TempDir, utils: newFirmwareUtils()}
257+
func NewFirmwareManager(client client.Client, dmsManager dms.DMSManager, namespace string) FirmwareManager {
258+
return &firmwareManager{client: client, dmsManager: dmsManager, cacheRootDir: consts.NicFirmwareStorage, namespace: namespace, tmpDir: consts.TempDir, utils: newFirmwareUtils()}
192259
}

0 commit comments

Comments
 (0)