blob: fbdf4e6e9477d00315d6100a48eca18f320f2152 [file]
package installer
import (
"archive/tar"
"bytes"
"context"
"fmt"
"os"
"path"
"path/filepath"
"sort"
"testing"
"cos.googlesource.com/cos/tools.git/src/cmd/cos_gpu_installer/deviceinfo"
"cos.googlesource.com/cos/tools.git/src/pkg/cos"
"cos.googlesource.com/cos/tools.git/src/pkg/fakes"
"cos.googlesource.com/cos/tools.git/src/pkg/gpuconfig/pb"
"github.com/golang/protobuf/proto"
"github.com/google/go-cmp/cmp"
)
func TestDownloadGPUDriverVersionsProto(t *testing.T) {
fakeGCS := fakes.GCSForTest(t)
fakeBucket := "cos-tools"
fakePrefix := "10000.00.00/lakitu"
fakeGCSClient := fakeGCS.Client
ctx := context.Background()
var gpuDriverProtoBin = "gpu_driver_versions.bin"
var mockData = &pb.GPUDriverVersionInfoList{
GpuDriverVersionInfo: []*pb.GPUDriverVersionInfo{
{
GpuDevice: &pb.GPUDevice{
GpuType: "NVIDIA_TESLA_V100",
},
SupportedDriverVersions: []*pb.DriverVersion{
{
Label: "DEFAULT",
Version: "535.154.05",
},
{
Label: "LATEST",
Version: "535.154.05",
},
{
Label: "R535",
Version: "535.154.05",
},
{
Version: "535.129.03",
},
{
Version: "535.104.12",
},
{
Version: "535.104.05",
},
{
Label: "R470",
Version: "470.223.02",
},
{
Version: "470.199.02",
},
},
},
},
}
binaryMockData, err := proto.Marshal(mockData)
if err != nil {
t.Fatalf("Failed to marshal mockdata to binary array: %v", err)
}
fakeGCS.Objects[path.Join("/", fakeBucket, fakePrefix, gpuDriverProtoBin)] = binaryMockData
tempDir, err := os.MkdirTemp("", "mockGpuInstallDir")
if err != nil {
t.Fatalf("Failed to create tempdir: %v", err)
}
defer os.RemoveAll(tempDir)
expectedFilePath := filepath.Join(tempDir, gpuDriverProtoBin)
var FakeGCSDownloader = cos.NewGCSDownloader(fakeGCSClient, nil, fakeBucket, fakePrefix, "", "", false)
actualData, err := DownloadGPUDriverVersionsProto(ctx, FakeGCSDownloader, tempDir)
if err != nil {
t.Fatalf("DownloadGPUDriverVersionsProto returned an error: %v", err)
}
_, err = os.Stat(expectedFilePath)
if os.IsNotExist(err) {
t.Errorf("Expected file %s does not exist.", expectedFilePath)
} else if err != nil {
t.Fatalf("Error occurs when checking the existence of the file: %s", expectedFilePath)
}
if !bytes.Equal(binaryMockData, actualData) {
t.Errorf("Data mismatch. Expected %v, got %v", binaryMockData, actualData)
}
}
func TestDownloadGPUDriverVersionsProto_FromSubdir(t *testing.T) {
ctx := context.Background()
var gpuDriverProtoBin = "gpu_driver_versions.bin"
binaryMockData := []byte("mock-driver-proto-data")
tempDir, err := os.MkdirTemp("", "mockGpuInstallDirWithSubdir")
if err != nil {
t.Fatalf("Failed to create tempdir: %v", err)
}
defer os.RemoveAll(tempDir)
subDir := filepath.Join(tempDir, "580.95.05-open")
if err := os.MkdirAll(subDir, 0755); err != nil {
t.Fatalf("Failed to create subDir: %v", err)
}
subFilePath := filepath.Join(subDir, gpuDriverProtoBin)
if err := os.WriteFile(subFilePath, binaryMockData, 0644); err != nil {
t.Fatalf("Failed to write mock proto file in subDir: %v", err)
}
// Downloader whose GCS bucket does not have the file. Should not be downloaded
// because local proto exists in subdir.
fakeGCS := fakes.GCSForTest(t)
fakeDownloader := cos.NewGCSDownloader(fakeGCS.Client, nil, "fake-bucket", "fake-prefix", "", "", false)
actualData, err := DownloadGPUDriverVersionsProto(ctx, fakeDownloader, tempDir)
if err != nil {
t.Fatalf("DownloadGPUDriverVersionsProto returned an error: %v", err)
}
if !bytes.Equal(binaryMockData, actualData) {
t.Errorf("Data mismatch. Expected %s, got %s", string(binaryMockData), string(actualData))
}
destFilePath := filepath.Join(tempDir, gpuDriverProtoBin)
if _, err := os.Stat(destFilePath); !os.IsNotExist(err) {
t.Errorf("Expected %s to not exist in parent directory", destFilePath)
}
}
func TestGetLoadedNVIDIAKernelModuleVersion(t *testing.T) {
tempDir, err := os.MkdirTemp("", "mockNVIDIAKernelModuleVersionDir")
if err != nil {
t.Fatalf("Failed to create tempdir: %v", err)
}
defer os.RemoveAll(tempDir)
for index, tc := range []struct {
testName string
fileName string
contentStr string
expectOutput string
}{
{"550DriverVersionFile", "550versionFile", "NVRM version: NVIDIA UNIX Open Kernel Module for x86_64 550.90.07 \n Release Build " +
"(builder@1f48d05e873d) Fri Jul 12 16:29:04 UTC 2024 GCC version: Selected multilib: .;@m64", "550.90.07"},
{"535DriverVersionFile", "535versionFile", "NVRM version: NVIDIA UNIX x86_64 Kernel Module 535.129.03 Thu Oct 19 18:56:32 UTC 2023" +
"GCC version: Selected multilib: .;@m64", "535.129.03"},
{"EmptyFile", "emptyVersionFile", "Test empty", ""},
{"FileNotExist", "nonExistFile", "", ""},
} {
t.Run(fmt.Sprintf("Test %v: %s", index, tc.testName), func(t *testing.T) {
testVersionFilePath := filepath.Join(tempDir, tc.fileName)
if tc.fileName != "nonExistFile" {
file, err := os.Create(testVersionFilePath)
if err != nil {
t.Fatalf("Error creating %s with error: %v", testVersionFilePath, err)
}
defer file.Close()
_, err = file.WriteString(tc.contentStr)
if err != nil {
t.Fatalf("Error writing to %s with error: %v", testVersionFilePath, err)
}
}
actualResult := GetLoadedNVIDIAKernelModuleVersion(testVersionFilePath)
if actualResult != tc.expectOutput {
t.Errorf("Unexpected return value, want %v, got %v", tc.expectOutput, actualResult)
}
})
}
}
func TestInstallImexDriver(t *testing.T) {
driverVersion := "570.124.06"
tarballPath := createFakeImexTarball(t, driverVersion)
// Test destination dir
containerDir := t.TempDir()
hostDir := t.TempDir()
imexConfigDir := "imex-config"
binDir := filepath.Join(containerDir, "bin")
if err := os.MkdirAll(binDir, 0755); err != nil {
t.Fatalf("failed to create bin directory: %v", err)
}
err := InstallImexDriver(tarballPath, driverVersion, hostDir, containerDir)
if err != nil {
t.Fatalf("expected success but got error: %v", err)
}
expectedFiles := map[string]string{
fmt.Sprintf("%s/bin/nvidia-imex", containerDir): "test1",
fmt.Sprintf("%s/bin/nvidia-imex-ctl", containerDir): "test2",
fmt.Sprintf("%s/imex-config/config.cfg", containerDir): fmt.Sprintf("IMEX_NODE_CONFIG_FILE=%s", filepath.Join(hostDir, imexConfigDir, "nodes_config.cfg")),
}
for dstpath, expectedContent := range expectedFiles {
data, err := os.ReadFile(dstpath)
if err != nil {
t.Errorf("expected file %s not found: %v", dstpath, err)
continue
}
if string(data) != expectedContent {
t.Errorf("file content mismatch for %s: got %q, want %q", dstpath, string(data), expectedContent)
}
}
}
func createFakeImexTarball(t *testing.T, version string) string {
tarDir := t.TempDir()
tarPath := filepath.Join(tarDir, "fake-imex.tar")
file, err := os.Create(tarPath)
if err != nil {
t.Fatalf("failed to create tar file: %v", err)
}
defer file.Close()
tw := tar.NewWriter(file)
defer tw.Close()
archiveRoot := fmt.Sprintf("nvidia-imex-linux-sbsa-%s-archive", version)
files := []struct {
path, content string
}{
{fmt.Sprintf("%s/usr/bin/nvidia-imex", archiveRoot), "test1"},
{fmt.Sprintf("%s/usr/bin/nvidia-imex-ctl", archiveRoot), "test2"},
{fmt.Sprintf("%s/etc/nvidia-imex/config.cfg", archiveRoot), "IMEX_NODE_CONFIG_FILE=/etc/nvidia-imex/nodes_config.cfg"},
}
for _, f := range files {
header := &tar.Header{
Name: f.path,
Mode: 0755,
Size: int64(len(f.content)),
}
if err := tw.WriteHeader(header); err != nil {
t.Fatalf("failed to write tar header: %v", err)
}
if _, err := tw.Write([]byte(f.content)); err != nil {
t.Fatalf("failed to write file content to tar: %v", err)
}
}
return tarPath
}
func TestMergeModuleParams(t *testing.T) {
// These are the defaults from InstallGDRCopy
defaults := map[string]string{
"dbg_enabled": "0",
"info_enabled": "0",
"use_persistent_mapping": "1",
}
testCases := []struct {
name string
userParams []string
want []string
}{
{
name: "No user params, all defaults applied",
userParams: []string{},
want: []string{"dbg_enabled=0", "info_enabled=0", "use_persistent_mapping=1"},
},
{
name: "User overrides one default",
userParams: []string{"dbg_enabled=1"},
want: []string{"dbg_enabled=1", "info_enabled=0", "use_persistent_mapping=1"},
},
{
name: "User adds a custom param",
userParams: []string{"foo=bar"},
want: []string{"foo=bar", "dbg_enabled=0", "info_enabled=0", "use_persistent_mapping=1"},
},
{
name: "User overrides one and adds one",
userParams: []string{"use_persistent_mapping=0", "custom=true"},
want: []string{"use_persistent_mapping=0", "custom=true", "dbg_enabled=0", "info_enabled=0"},
},
{
name: "User overrides all defaults",
userParams: []string{"dbg_enabled=1", "info_enabled=1", "use_persistent_mapping=0"},
want: []string{"dbg_enabled=1", "info_enabled=1", "use_persistent_mapping=0"},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
got := mergeModuleParams(tc.userParams, defaults)
// Sort slices before comparing since parameter order does not matter.
sort.Strings(got)
sort.Strings(tc.want)
if diff := cmp.Diff(tc.want, got); diff != "" {
t.Errorf("mergeModuleParams() mismatch (-want +got):\n%s", diff)
}
})
}
}
func TestPrepareGSPFirmware(t *testing.T) {
origFirmwareDir := gpuFirmwareDirContainer
t.Cleanup(func() { gpuFirmwareDirContainer = origFirmwareDir })
driverVersion := "580.95.05"
allFirmwareFiles := []string{"gsp_tu10x.bin", "gsp_ga10x.bin", "ucodes_tu10x.bin", "ucodes_ga10x.bin"}
testCases := []struct {
name string
gpuType deviceinfo.GPUType
wantFiles []string
}{
{
name: "VR200 installs both GSP and ucodes firmware",
gpuType: deviceinfo.VR200,
wantFiles: []string{"gsp_tu10x.bin", "gsp_ga10x.bin", "ucodes_tu10x.bin", "ucodes_ga10x.bin"},
},
{
name: "Non-VR200 only installs GSP firmware",
gpuType: deviceinfo.GB200,
wantFiles: []string{"gsp_tu10x.bin", "gsp_ga10x.bin"},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
extractDir := t.TempDir()
srcFirmwareDir := filepath.Join(extractDir, "firmware")
if err := os.MkdirAll(srcFirmwareDir, 0755); err != nil {
t.Fatalf("failed to create source firmware dir: %v", err)
}
for _, f := range allFirmwareFiles {
if err := os.WriteFile(filepath.Join(srcFirmwareDir, f), []byte(f), 0644); err != nil {
t.Fatalf("failed to write %s: %v", f, err)
}
}
gpuFirmwareDirContainer = t.TempDir()
if err := prepareGSPFirmware(extractDir, driverVersion, false, tc.gpuType); err != nil {
t.Fatalf("prepareGSPFirmware failed: %v", err)
}
entries, err := os.ReadDir(filepath.Join(gpuFirmwareDirContainer, driverVersion))
if err != nil {
t.Fatalf("failed to read destination firmware dir: %v", err)
}
var gotFiles []string
for _, entry := range entries {
gotFiles = append(gotFiles, entry.Name())
}
sort.Strings(gotFiles)
sort.Strings(tc.wantFiles)
if diff := cmp.Diff(tc.wantFiles, gotFiles); diff != "" {
t.Errorf("prepareGSPFirmware installed files mismatch (-want +got):\n%s", diff)
}
})
}
}
func TestExtractPrebuiltModules(t *testing.T) {
driverVersion := "580.95.05"
tarDir := t.TempDir()
tarPath := filepath.Join(tarDir, "nvidia-drivers.tar")
file, err := os.Create(tarPath)
if err != nil {
t.Fatalf("failed to create tar file: %v", err)
}
tw := tar.NewWriter(file)
tarFiles := []string{
"drivers/nvidia.ko",
fmt.Sprintf("firmware/nvidia/%s/gsp_tu10x.bin", driverVersion),
fmt.Sprintf("firmware/nvidia/%s/gsp_ga10x.bin", driverVersion),
fmt.Sprintf("firmware/nvidia/%s/ucodes_tu10x.bin", driverVersion),
fmt.Sprintf("firmware/nvidia/%s/ucodes_ga10x.bin", driverVersion),
}
for _, f := range tarFiles {
header := &tar.Header{
Name: f,
Mode: 0644,
Size: int64(len(f)),
}
if err := tw.WriteHeader(header); err != nil {
t.Fatalf("failed to write tar header: %v", err)
}
if _, err := tw.Write([]byte(f)); err != nil {
t.Fatalf("failed to write tar content: %v", err)
}
}
if err := tw.Close(); err != nil {
t.Fatalf("failed to close tar writer: %v", err)
}
if err := file.Close(); err != nil {
t.Fatalf("failed to close tar file: %v", err)
}
testCases := []struct {
name string
gpuType deviceinfo.GPUType
wantFiles []string
}{
{
name: "VR200 extracts both GSP and ucodes firmware",
gpuType: deviceinfo.VR200,
wantFiles: []string{"gsp_tu10x.bin", "gsp_ga10x.bin", "ucodes_tu10x.bin", "ucodes_ga10x.bin"},
},
{
name: "Non-VR200 excludes ucodes firmware",
gpuType: deviceinfo.GB200,
wantFiles: []string{"gsp_tu10x.bin", "gsp_ga10x.bin"},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
dstDir := t.TempDir()
if err := extractPrebuiltModules(tarPath, dstDir, tc.gpuType); err != nil {
t.Fatalf("extractPrebuiltModules failed: %v", err)
}
entries, err := os.ReadDir(filepath.Join(dstDir, "firmware/nvidia", driverVersion))
if err != nil {
t.Fatalf("failed to read extracted firmware dir: %v", err)
}
var gotFiles []string
for _, entry := range entries {
gotFiles = append(gotFiles, entry.Name())
}
sort.Strings(gotFiles)
sort.Strings(tc.wantFiles)
if diff := cmp.Diff(tc.wantFiles, gotFiles); diff != "" {
t.Errorf("extractPrebuiltModules extracted firmware mismatch (-want +got):\n%s", diff)
}
})
}
}