diff --git a/entrypoint/internal/driver/driver.go b/entrypoint/internal/driver/driver.go index e71f0f3..d8765fd 100644 --- a/entrypoint/internal/driver/driver.go +++ b/entrypoint/internal/driver/driver.go @@ -119,7 +119,12 @@ func (d *driverMgr) Build(ctx context.Context) error { // Load is the default implementation of the driver.Interface. func (d *driverMgr) Load(ctx context.Context) (bool, error) { - // TODO: Implement + if err := d.generateOfedModulesBlacklist(ctx); err != nil { + return false, err + } + if err := d.removeOfedModulesBlacklist(ctx); err != nil { + return false, err + } return true, nil } @@ -323,3 +328,64 @@ func (d *driverMgr) extractMajorVersion(version string) (int, error) { return major, nil } + +// generateOfedModulesBlacklist creates a blacklist file for OFED modules to prevent +// inbox or host OFED driver loading. This function writes module blacklist entries +// to the configured blacklist file. +func (d *driverMgr) generateOfedModulesBlacklist(ctx context.Context) error { + log := logr.FromContextOrDiscard(ctx) + log.V(1).Info("Generating OFED modules blacklist") + + // Create the blacklist file + file, err := d.os.Create(d.cfg.OfedBlacklistModulesFile) + if err != nil { + log.Error(err, "Failed to create blacklist file", "file", d.cfg.OfedBlacklistModulesFile) + return fmt.Errorf("failed to create blacklist file %s: %w", d.cfg.OfedBlacklistModulesFile, err) + } + defer file.Close() + + // Build the entire content first + var content strings.Builder + content.WriteString("# blacklist ofed-related modules on host to prevent inbox or host OFED driver loading\n\n") + + // Add blacklist entries for each module + for _, module := range d.cfg.OfedBlacklistModules { + module = strings.TrimSpace(module) + if module == "" { + continue + } + content.WriteString(fmt.Sprintf("blacklist %s\n", module)) + log.V(2).Info("Added module to blacklist", "module", module) + } + + // Write all content at once + if _, err := file.WriteString(content.String()); err != nil { + log.Error(err, "Failed to write blacklist content to file") + return fmt.Errorf("failed to write blacklist content to file: %w", err) + } + + log.Info("Successfully generated OFED modules blacklist", "file", d.cfg.OfedBlacklistModulesFile, "modules", d.cfg.OfedBlacklistModules) + return nil +} + +// removeOfedModulesBlacklist removes the OFED modules blacklist file from the host. +// This function is typically called during cleanup or when the blacklist is no longer needed. +func (d *driverMgr) removeOfedModulesBlacklist(ctx context.Context) error { + log := logr.FromContextOrDiscard(ctx) + log.V(1).Info("Removing OFED modules blacklist file") + + // Check if file exists before attempting to remove + if _, err := d.os.Stat(d.cfg.OfedBlacklistModulesFile); os.IsNotExist(err) { + log.V(1).Info("Blacklist file does not exist, nothing to remove", "file", d.cfg.OfedBlacklistModulesFile) + return nil + } + + // Remove the blacklist file + if err := d.os.RemoveAll(d.cfg.OfedBlacklistModulesFile); err != nil { + log.Error(err, "Failed to remove blacklist file", "file", d.cfg.OfedBlacklistModulesFile) + return fmt.Errorf("failed to remove blacklist file %s: %w", d.cfg.OfedBlacklistModulesFile, err) + } + + log.Info("Successfully removed OFED modules blacklist file", "file", d.cfg.OfedBlacklistModulesFile) + return nil +} diff --git a/entrypoint/internal/driver/driver_test.go b/entrypoint/internal/driver/driver_test.go index 2ef8b45..fea621a 100644 --- a/entrypoint/internal/driver/driver_test.go +++ b/entrypoint/internal/driver/driver_test.go @@ -19,12 +19,16 @@ package driver import ( "context" "errors" + "os" + "path/filepath" + "strings" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "github.com/Mellanox/doca-driver-build/entrypoint/internal/config" "github.com/Mellanox/doca-driver-build/entrypoint/internal/constants" + "github.com/Mellanox/doca-driver-build/entrypoint/internal/wrappers" cmdMockPkg "github.com/Mellanox/doca-driver-build/entrypoint/internal/utils/cmd/mocks" hostMockPkg "github.com/Mellanox/doca-driver-build/entrypoint/internal/utils/host/mocks" wrappersMockPkg "github.com/Mellanox/doca-driver-build/entrypoint/internal/wrappers/mocks" @@ -272,3 +276,475 @@ var _ = Describe("Driver", func() { }) }) }) + +var _ = Describe("Driver OFED Blacklist", func() { + Context("generateOfedModulesBlacklist", func() { + var ( + dm *driverMgr + cmdMock *cmdMockPkg.Interface + hostMock *hostMockPkg.Interface + ctx context.Context + tempDir string + ) + + BeforeEach(func() { + cmdMock = cmdMockPkg.NewInterface(GinkgoT()) + hostMock = hostMockPkg.NewInterface(GinkgoT()) + ctx = context.Background() + tempDir = GinkgoT().TempDir() + }) + + It("should create blacklist file with all modules", func() { + blacklistFile := filepath.Join(tempDir, "blacklist-ofed-modules.conf") + cfg := config.Config{ + OfedBlacklistModulesFile: blacklistFile, + OfedBlacklistModules: []string{ + "mlx5_core", + "mlx5_ib", + "ib_umad", + "ib_uverbs", + "ib_ipoib", + "rdma_cm", + "rdma_ucm", + "ib_core", + "ib_cm", + }, + } + + dm = &driverMgr{ + cfg: cfg, + cmd: cmdMock, + host: hostMock, + os: wrappers.NewOS(), + } + + err := dm.generateOfedModulesBlacklist(ctx) + Expect(err).ToNot(HaveOccurred()) + + // Verify file exists + _, err = os.Stat(blacklistFile) + Expect(err).ToNot(HaveOccurred()) + + // Read and verify content + content, err := os.ReadFile(blacklistFile) + Expect(err).ToNot(HaveOccurred()) + + contentStr := string(content) + Expect(contentStr).To(ContainSubstring("# blacklist ofed-related modules on host to prevent inbox or host OFED driver loading")) + Expect(contentStr).To(ContainSubstring("blacklist mlx5_core")) + Expect(contentStr).To(ContainSubstring("blacklist mlx5_ib")) + Expect(contentStr).To(ContainSubstring("blacklist ib_umad")) + Expect(contentStr).To(ContainSubstring("blacklist ib_uverbs")) + Expect(contentStr).To(ContainSubstring("blacklist ib_ipoib")) + Expect(contentStr).To(ContainSubstring("blacklist rdma_cm")) + Expect(contentStr).To(ContainSubstring("blacklist rdma_ucm")) + Expect(contentStr).To(ContainSubstring("blacklist ib_core")) + Expect(contentStr).To(ContainSubstring("blacklist ib_cm")) + }) + + It("should handle empty modules list", func() { + blacklistFile := filepath.Join(tempDir, "empty-blacklist.conf") + cfg := config.Config{ + OfedBlacklistModulesFile: blacklistFile, + OfedBlacklistModules: []string{}, + } + + dm = &driverMgr{ + cfg: cfg, + cmd: cmdMock, + host: hostMock, + os: wrappers.NewOS(), + } + + err := dm.generateOfedModulesBlacklist(ctx) + Expect(err).ToNot(HaveOccurred()) + + // Verify file exists + _, err = os.Stat(blacklistFile) + Expect(err).ToNot(HaveOccurred()) + + // Read and verify content - should only have header + content, err := os.ReadFile(blacklistFile) + Expect(err).ToNot(HaveOccurred()) + + contentStr := string(content) + Expect(contentStr).To(ContainSubstring("# blacklist ofed-related modules on host to prevent inbox or host OFED driver loading")) + + // Count blacklist lines - should be 0 (only header comment) + lines := strings.Split(contentStr, "\n") + blacklistLines := 0 + for _, line := range lines { + if strings.HasPrefix(strings.TrimSpace(line), "blacklist") { + blacklistLines++ + } + } + Expect(blacklistLines).To(Equal(0)) + }) + + It("should skip empty or whitespace-only module names", func() { + blacklistFile := filepath.Join(tempDir, "filtered-blacklist.conf") + cfg := config.Config{ + OfedBlacklistModulesFile: blacklistFile, + OfedBlacklistModules: []string{ + "mlx5_core", + "", // empty string + " ", // whitespace only + "mlx5_ib", + "\t\n", // whitespace with newlines + "ib_umad", + }, + } + + dm = &driverMgr{ + cfg: cfg, + cmd: cmdMock, + host: hostMock, + os: wrappers.NewOS(), + } + + err := dm.generateOfedModulesBlacklist(ctx) + Expect(err).ToNot(HaveOccurred()) + + // Verify file exists + _, err = os.Stat(blacklistFile) + Expect(err).ToNot(HaveOccurred()) + + // Read and verify content + content, err := os.ReadFile(blacklistFile) + Expect(err).ToNot(HaveOccurred()) + + contentStr := string(content) + Expect(contentStr).To(ContainSubstring("# blacklist ofed-related modules on host to prevent inbox or host OFED driver loading")) + Expect(contentStr).To(ContainSubstring("blacklist mlx5_core")) + Expect(contentStr).To(ContainSubstring("blacklist mlx5_ib")) + Expect(contentStr).To(ContainSubstring("blacklist ib_umad")) + + // Count blacklist lines - should be 3 (empty and whitespace entries should be skipped) + lines := strings.Split(contentStr, "\n") + blacklistLines := 0 + for _, line := range lines { + if strings.HasPrefix(strings.TrimSpace(line), "blacklist") { + blacklistLines++ + } + } + Expect(blacklistLines).To(Equal(3)) + }) + + It("should handle single module", func() { + blacklistFile := filepath.Join(tempDir, "single-module.conf") + cfg := config.Config{ + OfedBlacklistModulesFile: blacklistFile, + OfedBlacklistModules: []string{"mlx5_core"}, + } + + dm = &driverMgr{ + cfg: cfg, + cmd: cmdMock, + host: hostMock, + os: wrappers.NewOS(), + } + + err := dm.generateOfedModulesBlacklist(ctx) + Expect(err).ToNot(HaveOccurred()) + + // Verify file exists + _, err = os.Stat(blacklistFile) + Expect(err).ToNot(HaveOccurred()) + + // Read and verify content + content, err := os.ReadFile(blacklistFile) + Expect(err).ToNot(HaveOccurred()) + + contentStr := string(content) + Expect(contentStr).To(ContainSubstring("# blacklist ofed-related modules on host to prevent inbox or host OFED driver loading")) + Expect(contentStr).To(ContainSubstring("blacklist mlx5_core")) + + // Count blacklist lines - should be 1 + lines := strings.Split(contentStr, "\n") + blacklistLines := 0 + for _, line := range lines { + if strings.HasPrefix(strings.TrimSpace(line), "blacklist") { + blacklistLines++ + } + } + Expect(blacklistLines).To(Equal(1)) + }) + }) + + Context("removeOfedModulesBlacklist", func() { + var ( + dm *driverMgr + cmdMock *cmdMockPkg.Interface + hostMock *hostMockPkg.Interface + ctx context.Context + tempDir string + ) + + BeforeEach(func() { + cmdMock = cmdMockPkg.NewInterface(GinkgoT()) + hostMock = hostMockPkg.NewInterface(GinkgoT()) + ctx = context.Background() + tempDir = GinkgoT().TempDir() + }) + + It("should remove existing blacklist file", func() { + blacklistFile := filepath.Join(tempDir, "blacklist.conf") + cfg := config.Config{ + OfedBlacklistModulesFile: blacklistFile, + OfedBlacklistModules: []string{"mlx5_core"}, + } + + dm = &driverMgr{ + cfg: cfg, + cmd: cmdMock, + host: hostMock, + os: wrappers.NewOS(), + } + + // First create the file + err := dm.generateOfedModulesBlacklist(ctx) + Expect(err).ToNot(HaveOccurred()) + + // Verify file exists + _, err = os.Stat(blacklistFile) + Expect(err).ToNot(HaveOccurred()) + + // Now remove it + err = dm.removeOfedModulesBlacklist(ctx) + Expect(err).ToNot(HaveOccurred()) + + // Verify file is removed + _, err = os.Stat(blacklistFile) + Expect(err).To(HaveOccurred()) + Expect(os.IsNotExist(err)).To(BeTrue()) + }) + + It("should handle file not existing gracefully", func() { + blacklistFile := filepath.Join(tempDir, "nonexistent.conf") + cfg := config.Config{ + OfedBlacklistModulesFile: blacklistFile, + OfedBlacklistModules: []string{"mlx5_core"}, + } + + dm = &driverMgr{ + cfg: cfg, + cmd: cmdMock, + host: hostMock, + os: wrappers.NewOS(), + } + + // Try to remove non-existent file + err := dm.removeOfedModulesBlacklist(ctx) + Expect(err).ToNot(HaveOccurred()) + }) + + It("should handle multiple remove operations", func() { + blacklistFile := filepath.Join(tempDir, "multi-remove.conf") + cfg := config.Config{ + OfedBlacklistModulesFile: blacklistFile, + OfedBlacklistModules: []string{"mlx5_core"}, + } + + dm = &driverMgr{ + cfg: cfg, + cmd: cmdMock, + host: hostMock, + os: wrappers.NewOS(), + } + + // Create file + err := dm.generateOfedModulesBlacklist(ctx) + Expect(err).ToNot(HaveOccurred()) + + // Remove it multiple times - should not error + err = dm.removeOfedModulesBlacklist(ctx) + Expect(err).ToNot(HaveOccurred()) + + err = dm.removeOfedModulesBlacklist(ctx) + Expect(err).ToNot(HaveOccurred()) + + err = dm.removeOfedModulesBlacklist(ctx) + Expect(err).ToNot(HaveOccurred()) + }) + }) + + Context("Integration tests", func() { + var ( + dm *driverMgr + cmdMock *cmdMockPkg.Interface + hostMock *hostMockPkg.Interface + ctx context.Context + tempDir string + ) + + BeforeEach(func() { + cmdMock = cmdMockPkg.NewInterface(GinkgoT()) + hostMock = hostMockPkg.NewInterface(GinkgoT()) + ctx = context.Background() + tempDir = GinkgoT().TempDir() + }) + + It("should create and then remove blacklist file", func() { + blacklistFile := filepath.Join(tempDir, "integration-test.conf") + cfg := config.Config{ + OfedBlacklistModulesFile: blacklistFile, + OfedBlacklistModules: []string{"mlx5_core", "mlx5_ib", "ib_umad"}, + } + + dm = &driverMgr{ + cfg: cfg, + cmd: cmdMock, + host: hostMock, + os: wrappers.NewOS(), + } + + // Test creation + err := dm.generateOfedModulesBlacklist(ctx) + Expect(err).ToNot(HaveOccurred()) + + // Verify file exists and has correct content + _, err = os.Stat(blacklistFile) + Expect(err).ToNot(HaveOccurred()) + + content, err := os.ReadFile(blacklistFile) + Expect(err).ToNot(HaveOccurred()) + + contentStr := string(content) + Expect(contentStr).To(ContainSubstring("# blacklist ofed-related modules on host to prevent inbox or host OFED driver loading")) + Expect(contentStr).To(ContainSubstring("blacklist mlx5_core")) + Expect(contentStr).To(ContainSubstring("blacklist mlx5_ib")) + Expect(contentStr).To(ContainSubstring("blacklist ib_umad")) + + // Test removal + err = dm.removeOfedModulesBlacklist(ctx) + Expect(err).ToNot(HaveOccurred()) + + // Verify file is removed + _, err = os.Stat(blacklistFile) + Expect(err).To(HaveOccurred()) + Expect(os.IsNotExist(err)).To(BeTrue()) + }) + + It("should handle complex module list with mixed valid and invalid entries", func() { + blacklistFile := filepath.Join(tempDir, "complex-test.conf") + cfg := config.Config{ + OfedBlacklistModulesFile: blacklistFile, + OfedBlacklistModules: []string{ + "mlx5_core", + "", // empty string + "mlx5_ib", + " ", // whitespace only + "ib_umad", + "\t\n", // whitespace with newlines + "ib_uverbs", + "", // another empty string + "rdma_cm", + }, + } + + dm = &driverMgr{ + cfg: cfg, + cmd: cmdMock, + host: hostMock, + os: wrappers.NewOS(), + } + + // Test creation + err := dm.generateOfedModulesBlacklist(ctx) + Expect(err).ToNot(HaveOccurred()) + + // Verify file exists + _, err = os.Stat(blacklistFile) + Expect(err).ToNot(HaveOccurred()) + + // Read and verify content + content, err := os.ReadFile(blacklistFile) + Expect(err).ToNot(HaveOccurred()) + + contentStr := string(content) + Expect(contentStr).To(ContainSubstring("# blacklist ofed-related modules on host to prevent inbox or host OFED driver loading")) + Expect(contentStr).To(ContainSubstring("blacklist mlx5_core")) + Expect(contentStr).To(ContainSubstring("blacklist mlx5_ib")) + Expect(contentStr).To(ContainSubstring("blacklist ib_umad")) + Expect(contentStr).To(ContainSubstring("blacklist ib_uverbs")) + Expect(contentStr).To(ContainSubstring("blacklist rdma_cm")) + + // Count blacklist lines - should be 5 (empty and whitespace entries should be skipped) + lines := strings.Split(contentStr, "\n") + blacklistLines := 0 + for _, line := range lines { + if strings.HasPrefix(strings.TrimSpace(line), "blacklist") { + blacklistLines++ + } + } + Expect(blacklistLines).To(Equal(5)) + + // Test removal + err = dm.removeOfedModulesBlacklist(ctx) + Expect(err).ToNot(HaveOccurred()) + + // Verify file is removed + _, err = os.Stat(blacklistFile) + Expect(err).To(HaveOccurred()) + Expect(os.IsNotExist(err)).To(BeTrue()) + }) + + It("should handle default configuration values", func() { + blacklistFile := filepath.Join(tempDir, "default-config.conf") + cfg := config.Config{ + OfedBlacklistModulesFile: blacklistFile, + OfedBlacklistModules: []string{ + "mlx5_core", + "mlx5_ib", + "ib_umad", + "ib_uverbs", + "ib_ipoib", + "rdma_cm", + "rdma_ucm", + "ib_core", + "ib_cm", + }, + } + + dm = &driverMgr{ + cfg: cfg, + cmd: cmdMock, + host: hostMock, + os: wrappers.NewOS(), + } + + // Test creation with default modules + err := dm.generateOfedModulesBlacklist(ctx) + Expect(err).ToNot(HaveOccurred()) + + // Verify file exists + _, err = os.Stat(blacklistFile) + Expect(err).ToNot(HaveOccurred()) + + // Read and verify content matches expected default modules + content, err := os.ReadFile(blacklistFile) + Expect(err).ToNot(HaveOccurred()) + + contentStr := string(content) + Expect(contentStr).To(ContainSubstring("# blacklist ofed-related modules on host to prevent inbox or host OFED driver loading")) + + // Verify all expected modules are present + expectedModules := []string{ + "mlx5_core", "mlx5_ib", "ib_umad", "ib_uverbs", "ib_ipoib", + "rdma_cm", "rdma_ucm", "ib_core", "ib_cm", + } + for _, module := range expectedModules { + Expect(contentStr).To(ContainSubstring("blacklist " + module)) + } + + // Test removal + err = dm.removeOfedModulesBlacklist(ctx) + Expect(err).ToNot(HaveOccurred()) + + // Verify file is removed + _, err = os.Stat(blacklistFile) + Expect(err).To(HaveOccurred()) + Expect(os.IsNotExist(err)).To(BeTrue()) + }) + }) +})