From 55157d9b2afd0625d7b5794aabb5629fd5da728c Mon Sep 17 00:00:00 2001 From: Priyanka Saggu Date: Thu, 23 Oct 2025 11:45:28 +0530 Subject: [PATCH 1/4] add support for detecting SLES nodes and automatically selecting appropriate AMD GPU driver versions * add new `slesCMNameMapper` to parse SLES version strings like 'SUSE Linux Enterprise Server 15 SP7' to 'sles-15.7' * add `SLESDefaultDriverVersionsMapper` to select driver versions * register both 'sles' and 'suse' identifiers in mappers Co-authored-by: alex-isv --- internal/kmmmodule/kmmmodule.go | 15 +++++++++++++++ internal/utils.go | 17 +++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/internal/kmmmodule/kmmmodule.go b/internal/kmmmodule/kmmmodule.go index ea4f600a..636639f6 100644 --- a/internal/kmmmodule/kmmmodule.go +++ b/internal/kmmmodule/kmmmodule.go @@ -624,6 +624,8 @@ var cmNameMappers = map[string]func(fullImageStr string) string{ "rhel": rhelCMNameMapper, "red hat": rhelCMNameMapper, "redhat": rhelCMNameMapper, + "sles": slesCMNameMapper, + "suse": slesCMNameMapper, } func rhelCMNameMapper(osImageStr string) string { @@ -657,6 +659,19 @@ func ubuntuCMNameMapper(osImageStr string) string { return fmt.Sprintf("%s-%s", os, trimmedVersion) } +func slesCMNameMapper(osImageStr string) string { + // Example: "SUSE Linux Enterprise Server 15 SP7" -> "sles-15.7" + // Example: "suse linux enterprise server 15-sp7" -> "sles-15.7" + // Convert to lowercase for consistent matching + osImageLower := strings.ToLower(osImageStr) + re := regexp.MustCompile(`(\d+)\s*-?\s*sp(\d+)`) + matches := re.FindStringSubmatch(osImageLower) + if len(matches) >= 3 { + return fmt.Sprintf("sles-%s.%s", matches[1], matches[2]) + } + return "sles-" + osImageLower +} + func GetK8SNodes(ctx context.Context, cli client.Client, labelSelector labels.Selector) (*v1.NodeList, error) { options := &client.ListOptions{ LabelSelector: labelSelector, diff --git a/internal/utils.go b/internal/utils.go index 0ff2e4ed..0ed7eb76 100644 --- a/internal/utils.go +++ b/internal/utils.go @@ -198,6 +198,8 @@ var defaultDriverversionsMappers = map[string]func(fullImageStr string) (string, "red hat": func(f string) (string, error) { return defaultOcDriversVersion, nil }, + "sles": SLESDefaultDriverVersionsMapper, + "suse": SLESDefaultDriverVersionsMapper, } func UbuntuDefaultDriverVersionsMapper(fullImageStr string) (string, error) { @@ -232,6 +234,21 @@ func UbuntuDefaultDriverVersionsMapper(fullImageStr string) (string, error) { return "", fmt.Errorf("unsupported Ubuntu version: %s. Supported versions include 20.04, 22.04 and 24.04", fullImageStr) } +var slesSPRegexp = regexp.MustCompile(`15\s*-?\s*sp(\d+)`) + +func SLESDefaultDriverVersionsMapper(fullImageStr string) (string, error) { + if strings.Contains(fullImageStr, "15") { + match := slesSPRegexp.FindStringSubmatch(strings.ToLower(fullImageStr)) + if len(match) > 1 { + spVersion, err := strconv.Atoi(match[1]) + if err == nil && spVersion >= 7 { + return "7.0.3", nil // Latest stable version for SP7+ + } + } + } + return "", fmt.Errorf("unsupported SLES version: %s. Supported versions include SLES 15 SP7 and above", fullImageStr) +} + func HasNodeLabelKey(node v1.Node, labelKey string) bool { for k := range node.Labels { if k == labelKey { From 332a34037ea7d1a4f7ff9ef83b9342b9fe18ed84 Mon Sep 17 00:00:00 2001 From: Priyanka Saggu Date: Thu, 23 Oct 2025 12:02:30 +0530 Subject: [PATCH 2/4] add SLES Dockerfile template (DockerfileTemplate.sles) using prebuilt SUSE AMD GPU driver image --- .../dockerfiles/DockerfileTemplate.sles | 34 +++++++++++++++++++ internal/kmmmodule/kmmmodule.go | 31 +++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 internal/kmmmodule/dockerfiles/DockerfileTemplate.sles diff --git a/internal/kmmmodule/dockerfiles/DockerfileTemplate.sles b/internal/kmmmodule/dockerfiles/DockerfileTemplate.sles new file mode 100644 index 00000000..9fc6abd3 --- /dev/null +++ b/internal/kmmmodule/dockerfiles/DockerfileTemplate.sles @@ -0,0 +1,34 @@ +# SPDX-License-Identifier: MIT +# +# Uses a SUSE built AMD GPU driver image as source, avoiding the need +# for a SUSEConnect registration key. modules.dep uses relative paths so it is +# valid for any kernel version in the same codestream (stable kABI). +# +# Build args: +# KERNEL_FULL_VERSION - target kernel version, e.g. "6.4.0-150700.53.25-default" +# SUSE_PREBUILT_DRIVER_IMG - SUSE-published driver image (built against GA kernel) + +ARG SUSE_PREBUILT_DRIVER_IMG + +FROM ${SUSE_PREBUILT_DRIVER_IMG} AS driver-source + +FROM $$BASEIMG_REGISTRY/bci/bci-micro:$$VERSION + +ARG KERNEL_FULL_VERSION + +# Relocate modules, kernel tree and depmod metadata from the GA kernel path to +# the target kernel version path; no depmod re-run needed (relative paths). +COPY --from=driver-source /opt/lib/modules/ /opt/lib/modules-prebuilt/ +RUN set -euo pipefail; \ + GA_KERNEL=$(ls /opt/lib/modules-prebuilt/ | head -1); \ + mkdir -p /opt/lib/modules/${KERNEL_FULL_VERSION}/updates/dkms; \ + cp /opt/lib/modules-prebuilt/${GA_KERNEL}/updates/dkms/amd* \ + /opt/lib/modules/${KERNEL_FULL_VERSION}/updates/dkms/; \ + cp /opt/lib/modules-prebuilt/${GA_KERNEL}/modules.* \ + /opt/lib/modules/${KERNEL_FULL_VERSION}/; \ + cp -r /opt/lib/modules-prebuilt/${GA_KERNEL}/kernel \ + /opt/lib/modules/${KERNEL_FULL_VERSION}/kernel; \ + rm -rf /opt/lib/modules-prebuilt + +RUN mkdir -p /firmwareDir/updates/amdgpu +COPY --from=driver-source /firmwareDir/updates/amdgpu /firmwareDir/updates/amdgpu diff --git a/internal/kmmmodule/kmmmodule.go b/internal/kmmmodule/kmmmodule.go index 636639f6..2930bb57 100644 --- a/internal/kmmmodule/kmmmodule.go +++ b/internal/kmmmodule/kmmmodule.go @@ -85,6 +85,8 @@ var ( dockerfileTemplateCoreOSFromSrcImage string //go:embed dockerfiles/DockerfileTemplate.rpm.coreos dockerfileTemplateCoreOSFromRPM string + //go:embed dockerfiles/DockerfileTemplate.sles + dockerfileTemplateSLES string //go:embed devdockerfiles/devdockerfile.txt dockerfileDevTemplateUbuntu string //go:embed dockerfiles/DockerfileTemplate.ubuntu.gim @@ -93,6 +95,14 @@ var ( dockerfileTemplateGIMCoreOS string ) +// slesCSDPrebuiltDriverImages maps SLES codestream version -> driver version -> prebuilt image. +// Tag format: sles--- +var slesCSDPrebuiltDriverImages = map[string]map[string]string{ + "15.7": { + "7.0.3": "registry.suse.com/third-party/amd/amdgpu-driver:sles-15.7-7.0.3", + }, +} + //go:generate mockgen -source=kmmmodule.go -package=kmmmodule -destination=mock_kmmmodule.go KMMModuleAPI type KMMModuleAPI interface { SetNodeVersionLabelAsDesired(ctx context.Context, devConfig *amdv1alpha1.DeviceConfig, nodes *v1.NodeList) error @@ -290,6 +300,8 @@ func resolveDockerfile(cmName string, devConfig *amdv1alpha1.DeviceConfig) (stri dockerfileTemplate = dockerfileTemplateCoreOSFromSrcImage } } + case "sles": + dockerfileTemplate = dockerfileTemplateSLES // FIX ME // add the RHEL back when it is fully supported /*case "rhel": @@ -564,6 +576,25 @@ func getKM(devConfig *amdv1alpha1.DeviceConfig, node v1.Node, inTreeModuleToRemo ) } + // Inject SUSE_PREBUILT_DRIVER_IMG build arg for SLES nodes. + if strings.HasPrefix(osName, "sles-") { + csVersion := strings.TrimPrefix(osName, "sles-") // e.g. "15.7" + driverVersions, ok := slesCSDPrebuiltDriverImages[csVersion] + if !ok { + return kmmv1beta1.KernelMapping{}, "", fmt.Errorf("no prebuilt driver image registered for SLES codestream %q", csVersion) + } + prebuiltImg, ok := driverVersions[driversVersion] + if !ok { + return kmmv1beta1.KernelMapping{}, "", fmt.Errorf("no prebuilt driver image registered for SLES codestream %q with driver version %q", csVersion, driversVersion) + } + kmmBuild.BuildArgs = append(kmmBuild.BuildArgs, + kmmv1beta1.BuildArg{ + Name: "SUSE_PREBUILT_DRIVER_IMG", + Value: prebuiltImg, + }, + ) + } + // trim suffix "+" to handle the dirty build kernel version // e.g., "5.15.0-76-generic+" // on KMM side it is trimming the suffix "+" to read kernel mapping From 2adfee0d888457523d90390041c687f5d0f716ab Mon Sep 17 00:00:00 2001 From: Priyanka Saggu Date: Thu, 23 Oct 2025 14:33:52 +0530 Subject: [PATCH 3/4] tests: update internal/utils_test.go for added support for SLES 15 SP7+ --- internal/utils_test.go | 47 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/internal/utils_test.go b/internal/utils_test.go index b9c36085..6d1584a7 100644 --- a/internal/utils_test.go +++ b/internal/utils_test.go @@ -364,3 +364,50 @@ func TestUbuntuDefaultDriverVersionsMapper(t *testing.T) { }) } } + +func TestSLESDefaultDriverVersionsMapper(t *testing.T) { + tests := []struct { + name string + osImage string + expected string + wantErr bool + }{ + { + name: "SLES 15 SP7", + osImage: "SUSE Linux Enterprise Server 15 SP7", + expected: "7.0.3", + wantErr: false, + }, + { + name: "SLES 15 SP7 with dash format", + osImage: "sles 15-sp7", + expected: "7.0.3", + wantErr: false, + }, + { + name: "SLES 15 SP6 unsupported", + osImage: "SUSE Linux Enterprise Server 15 SP6", + wantErr: true, + }, + { + name: "SLES 15 base unsupported", + osImage: "SUSE Linux Enterprise Server 15", + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result, err := SLESDefaultDriverVersionsMapper(tt.osImage) + + if (err != nil) != tt.wantErr { + t.Errorf("SLESDefaultDriverVersionsMapper() error = %v, wantErr %v", err, tt.wantErr) + return + } + + if result != tt.expected { + t.Errorf("SLESDefaultDriverVersionsMapper() = %q, want %q", result, tt.expected) + } + }) + } +} From 5a0c824af7c1d232ac0ac3db7d877c55b1704345 Mon Sep 17 00:00:00 2001 From: Priyanka Saggu Date: Mon, 27 Oct 2025 12:21:43 +0530 Subject: [PATCH 4/4] use "registry.suse.com" as the default base image registry if OS == "sles" * although, use-specified `BaseImageRegistry` still takes precedence * also extend tests in `internal/kmmodule/kmmodule_test.go` to test above changes in `resolveDockerfile` func --- internal/kmmmodule/kmmmodule.go | 4 ++ internal/kmmmodule/kmmmodule_test.go | 57 ++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/internal/kmmmodule/kmmmodule.go b/internal/kmmmodule/kmmmodule.go index 2930bb57..838d9b15 100644 --- a/internal/kmmmodule/kmmmodule.go +++ b/internal/kmmmodule/kmmmodule.go @@ -320,7 +320,11 @@ func resolveDockerfile(cmName string, devConfig *amdv1alpha1.DeviceConfig) (stri // render base image registry baseImageRegistry := defaultBaseImageRegistry if devConfig.Spec.Driver.ImageBuild.BaseImageRegistry != "" { + // user-specified registry takes precedence baseImageRegistry = devConfig.Spec.Driver.ImageBuild.BaseImageRegistry + } else if osDistro == "sles" { + // if OS == "sles", use default image registry as "registry.suse.com" + baseImageRegistry = "registry.suse.com" } dockerfileTemplate = strings.Replace(dockerfileTemplate, "$$BASEIMG_REGISTRY", baseImageRegistry, -1) // render driver version diff --git a/internal/kmmmodule/kmmmodule_test.go b/internal/kmmmodule/kmmmodule_test.go index 67f17f76..8c5bd1b1 100644 --- a/internal/kmmmodule/kmmmodule_test.go +++ b/internal/kmmmodule/kmmmodule_test.go @@ -732,3 +732,60 @@ var _ = Describe("getKernelMappings", func() { } }) }) + +var _ = Describe("resolveDockerfile", func() { + It("should use correct default registry when not specified by user", func() { + testCases := []struct { + cmName string + expectedImageUrl string + }{ + {"ubuntu-22.04", "docker.io/ubuntu:22.04"}, + {"sles-15.6", "registry.suse.com/bci/bci-micro:15.6"}, + } + for _, tc := range testCases { + input := &amdv1alpha1.DeviceConfig{ + Spec: amdv1alpha1.DeviceConfigSpec{ + Driver: amdv1alpha1.DriverSpec{}, + }, + } + dockerfile, err := resolveDockerfile(tc.cmName, input) + Expect(err).To(BeNil()) + Expect(dockerfile).To(ContainSubstring(tc.expectedImageUrl)) + } + }) + It("should respect user-specified BaseImageRegistry for all OS types", func() { + testCases := []struct { + cmName string + expectedImageUrl string + }{ + {"ubuntu-22.04", "example-image-registry.com/ubuntu:22.04"}, + {"sles-15.6", "example-image-registry.com/bci/bci-micro:15.6"}, + } + for _, tc := range testCases { + input := &amdv1alpha1.DeviceConfig{ + Spec: amdv1alpha1.DeviceConfigSpec{ + Driver: amdv1alpha1.DriverSpec{ + ImageBuild: amdv1alpha1.ImageBuildSpec{ + BaseImageRegistry: "example-image-registry.com", + }, + }, + }, + } + dockerfile, err := resolveDockerfile(tc.cmName, input) + Expect(err).To(BeNil()) + Expect(dockerfile).To(ContainSubstring(tc.expectedImageUrl)) + Expect(dockerfile).NotTo(ContainSubstring("docker.io")) + Expect(dockerfile).NotTo(ContainSubstring("registry.suse.com")) + } + }) + It("should return error for unsupported OS", func() { + input := &amdv1alpha1.DeviceConfig{ + Spec: amdv1alpha1.DeviceConfigSpec{ + Driver: amdv1alpha1.DriverSpec{}, + }, + } + _, err := resolveDockerfile("unsupported-os", input) + Expect(err).NotTo(BeNil()) + Expect(err.Error()).To(ContainSubstring("not supported OS")) + }) +})