NAME
Rex::GPU::NVIDIA::Setup - Base class of the per-distro NVIDIA driver setups (experimental)
VERSION
version 0.002
SYNOPSIS
package My::GPU::Setup;
use Moo;
extends 'Rex::GPU::NVIDIA::Setup::Ubuntu';
# one more step before the packages go in
sub prepare_source {
my ( $self, $plan ) = @_;
$self->run_cmd('add-apt-repository -y ppa:my/mirror', auto_die => 0);
$self->SUPER::prepare_source($plan);
}
DESCRIPTION
Experimental. The class layout, the step names, the source keys and the $plan keys may change in the next release without a deprecation cycle. "install_driver" in Rex::GPU::NVIDIA and "gpu_setup" in Rex::GPU use a class of your own when told to -- see "WRITING YOUR OWN SETUP".
One driver install is one object: the GPUs and the host facts it was built with, and a fixed "install" sequence of overridable steps. Which driver it installs is not a per-distro special case but data: the GPUs' "requirement" against the ordered "sources", the first that fits wins ("select_source"). The per-distro classes are Rex::GPU::NVIDIA::Setup::Debian and Rex::GPU::NVIDIA::Setup::Ubuntu on the apt packaging layer Rex::GPU::NVIDIA::Setup::Apt, and Rex::GPU::NVIDIA::Setup::RHEL and Rex::GPU::NVIDIA::Setup::SUSE on the rpm packaging layer Rex::GPU::NVIDIA::Setup::Rpm.
Every host interaction goes through "run_cmd", "pkg_cmd" and "file_cmd".
gpus
Arrayref of the GPUs this driver install is for. One driver has to drive them all, so "requirement" is the intersection of their requirements. Empty (the default) keeps the GPU-agnostic package selection. Elements that are not hashrefs are ignored.
Each GPU is a hashref; the elements "detect" in Rex::GPU::Detect returns for nvidia fit, but only the keys below are read, so a caller that finds the GPU another way (e.g. sysfs, without lspci) passes just these:
{ device_id => '2b85', name => 'NVIDIA GeForce RTX 5090' }
device_id-- the PCI device ID as four hex digits, without0xand without a trailing newline (2b85; sysfsdevicereads0x2b85). It is what the driver is chosen by (Rex::GPU::NVIDIA::Requirement): Kepler is refused, Blackwell gets the open kernel module, Maxwell/Pascal/Volta the 580 branch. Any other defined value croaks innew(and in "adopt"), before anything touches the host -- it would otherwise silently count as an unknown GPU and lose those guards. Leaving it out (orundef, as detection does for anlspciline without[10de:XXXX]) is accepted and means exactly that: an unknown GPU, no constraint.name-- for log lines and messages only. Optional.vgpu-- true for an NVIDIA vGPU guest device ("NVIDIA vGPU guests" in Rex::GPU::Detect);vgpu_typeandsubsystem_idgo into the message. "plan" dies for one, before anything on the host is changed; "already_installed" runs first, so a guest whose vGPU driver already works is not refused. Missing (a caller that finds its GPUs itself) means not a vGPU. Optional.
compute, pci_class and vendor are not read: whether a GPU gets a driver at all is the caller's decision ("gpu_setup" in Rex::GPU passes only compute ones).
gpu
A single GPU hashref, the older form of "gpus": gpu => $g is the same as gpus => [ $g ], gpu => undef the same as no GPU. Passing both croaks.
nvswitches
Arrayref of the host's NVSwitch chips, as "detect" in Rex::GPU::Detect returns them under nvswitch; only whether there is one counts, no key is read. Empty (the default): no Fabric Manager, nothing changes. Non-empty: the driver source must name a Fabric Manager package (fabric_manager, see "sources") -- a source without one is rejected with that reason by "select_source" -- and "install_fabric_manager" runs after the driver packages are verified. On a host whose driver is already installed, "install_driver" in Rex::GPU::NVIDIA runs "retrofit_fabric_manager" instead. A caller that finds its GPUs without lspci and passes none gets no Fabric Manager.
fabric_manager_needed
True when "nvswitches" lists at least one NVSwitch, or "nvlink_fabric_needed" (HGX B200/B300, whose NVSwitches are not on the host PCI bus). Everything said above for a host with "nvswitches" then holds for it too.
fabric_label
What the Fabric Manager messages name as the reason: NVSwitch on a host with "nvswitches", HGX B200/B300 NVLink fabric on one that only has "nvlink_fabric_needed".
nvlink_platform_ids
my %platform = $self->nvlink_platform_ids; # device_id => platform
The GPUs that mark an NVLink platform, as a list of lowercase PCI device IDs and the platform each one marks (karr #49; IDs from the supported-GPU table of NVIDIA's open-gpu-kernel-modules README, driver 615):
hgx-nvlink5-- HGX B200 (2901,2909) and B300 (3182). Their NVSwitches are not PCI devices on the host, so there are no "nvswitches"; CUDA needs NVIDIA Fabric Manager, the NVLink Subnet Manager (nvlsm), the InfiniBand user-space stack and kernel 5.17 or newer. It makes "nvlink_fabric_needed" true, so the driver comes with Fabric Manager and "install_nvlink_fabric" (karr #56).nvl72-- GB200 (2941) and GB300 (31c2,31c3) NVL72 compute trays: multi-node NVLink needsnvidia-imex; Fabric Manager runs on the NVLink switch trays, not here. Nothing is installed for them, the driver choice does not depend on it; "install_driver" in Rex::GPU::NVIDIA logs a note.
Override it to add or drop an ID. Read only by "nvlink_platforms".
nvlink_platforms
my @platforms = $self->nvlink_platforms; # ('hgx-nvlink5')
The platforms of "nvlink_platform_ids" that "gpus" mark, each once, sorted; empty on every other host. Reads nothing from the host.
nvlink_fabric_needed
True when "nvlink_platforms" contains hgx-nvlink5.
requirement
The Rex::GPU::NVIDIA::Requirement the driver has to meet: the "intersect" in Rex::GPU::NVIDIA::Requirement of every GPU in "gpus", looked up through "requirement_class"; either with no bounds for no GPU. Built on first use -- by "plan" -- and dies there, before anything on the host is changed, when the GPUs need different kernel modules or no common branch (a V100 next to a B200), naming the GPUs on each side. May be passed to new instead.
extra_requirement
My::GPU::Setup->new(extra_requirement => { kernel_module => 'open', min_branch => 580 });
An additional constraint of your own, intersected with what the GPUs need -- the requirement option of "install_driver" in Rex::GPU::NVIDIA and "gpu_setup" in Rex::GPU ends up here. It can only tighten: a V100 stays proprietary and at most 580 whatever you ask for, and a constraint the GPUs cannot meet (open on a V100) makes "plan" die before anything on the host is changed, naming both sides. With no GPU it is the whole requirement.
A hashref with the keys kernel_module, min_branch, max_branch and optionally name (for messages; default the requirement option), or a Rex::GPU::NVIDIA::Requirement object. A hashref becomes an object of "requirement_class" in new, so an unknown key or a bad value croaks there. undef (the default) adds nothing.
requirement_class
The requirement class, Rex::GPU::NVIDIA::Requirement. Override it to use a subclass with rows of your own in its generations table.
adopt
$setup->adopt(gpus => \@gpus, extra_requirement => { min_branch => 580 });
What "install_driver" in Rex::GPU::NVIDIA does to a setup object passed as setup: it hands over the GPUs it was called with and its requirement option. gpus is taken only if the object has none of its own (built without gpu/gpus, or with an empty list) -- an object built for specific GPUs keeps them. nvswitches likewise, only if the object has none. extra_requirement (hashref or object, see "extra_requirement") is set if given. Returns the object.
Croaks, before anything on the host is changed, if the object has already run "install" -- it caches the host facts ("os", "kernel", ...) of that host, so one object serves one host -- or if it would have to change an object whose "requirement" is already fixed (passed to new, or built by an earlier "plan") -- the detected GPUs would otherwise not be checked -- or if both the object and the option carry an extra_requirement.
os
The OS name as "operating_system" in Rex::Commands::Gather reports it (Debian, Ubuntu, ...). Read from the host on first use unless passed to new.
release
The raw release string, "operating_system_release" in Rex::Commands::Gather (12.11, 13.1, 10.0, trixie/sid). Never operating_system_version, which strips the dots (10.1 becomes 101). Read on first use unless passed to new.
arch
The host architecture as the packaging layer names it. The base class reads uname -m (x86_64, aarch64); Rex::GPU::NVIDIA::Setup::Apt reads dpkg --print-architecture (amd64, arm64). Read on first use unless passed to new.
kernel
The running kernel, uname -r. Read on first use unless passed to new.
run_cmd
my $out = $self->run_cmd('uname -r');
$self->run_cmd('modprobe nvidia', auto_die => 0);
The only way this class and its subclasses run a command on the host: the arguments go to "run" in Rex::Commands::Run unchanged, in the caller's context, and $? is left as run set it. Override it to record or fake the host in a test.
pkg_cmd
The only way to "pkg" in Rex::Commands::Pkg. Reserved for inert helpers (curl, gnupg, epel-release): Rex::Pkg dies on the non-zero exit that DKMS builds, grub and initramfs regeneration return on success, so a driver or toolkit package never goes through it.
file_cmd
The only way to "file" in Rex::Commands::File.
install
my $installed = $setup->install;
Runs the fixed sequence, each step a method a subclass can override:
already_installed -> return 0, nothing else runs
plan -> host-read-only; dies before any change
prepare_host($plan)
prepare_source($plan)
resolve_plan($plan) -> fixes the packages; dies before any install
install_packages($plan)
verify_packages($plan)
install_fabric_manager($plan) -> only if fabric_manager_needed
install_nvlink_fabric($plan) -> only if nvlink_fabric_needed
post_install($plan)
Returns 1 after an install, 0 if a working driver was already there. Loading the module (modprobe nvidia) or rebooting, and "verify_nvidia_driver" in Rex::GPU::NVIDIA, are done by "install_driver" in Rex::GPU::NVIDIA after this returns, not by the setup: the reboot is a per-call option that needs Rex's live connection.
already_installed
True if a working NVIDIA driver with its CUDA user-space library is there: nvidia-smi -L lists a GPU N: device and libcuda.so.1 is in the dynamic linker cache ("libcuda_command"). Then nothing is installed, nouveau is not blacklisted and the host is not rebooted, so a re-run, or a host provisioned from NVIDIA's own repository, does not get a second, conflicting driver.
The library probe runs only when nvidia-smi lists a GPU. If it does and libcuda.so.1 is missing -- the kernel module and nvidia-smi are there, but no CUDA program could run (e.g. Debian's nvidia-driver installed without recommends, which does not pull libcuda1) -- a warning is logged and the driver is installed, over whatever is there: on a host whose driver came from elsewhere (NVIDIA's CUDA repository, a .run installer) that is the distro driver next to it, and its install may fail with a package conflict.
libcuda_command
The read-only probe "already_installed" runs for the CUDA library, /sbin/ldconfig -p 2>/dev/null | grep -q '^[[:space:]]*libcuda\.so\.1 '; exit 0 means present. The linker cache is where every distro's driver packages register libcuda.so.1 (Debian through the nvidia alternative in the multiarch directory, Ubuntu libnvidia-compute-NNN, RHEL nvidia-driver-cuda-libs, openSUSE nvidia-compute-G06/G07), whatever the library directory, and it is what the loader of a CUDA program consults. /sbin/ldconfig by path: /sbin is not on every login's PATH, and it exists on every supported release (a symlink to /usr/sbin where /usr is merged). Matches the soname only, not libcudart or libcudadebugger. Override it for a host that keeps the library elsewhere.
plan
my $plan = $self->plan;
Decides what to install and returns it as a hashref: source (the chosen driver source, see "sources"), packages (arrayref, in install order: "kernel_packages", then the source's) and verify (the source's packages that must be installed afterwards); a subclass adds keys for its own later steps. A source whose packages are known only once its repository is refreshed (see "resolve_source") has none here yet; "resolve_plan" adds them. Must only read the host: every "this cannot work here" that is known without a refreshed package index dies from here, before anything is changed:
an NVIDIA vGPU guest device among the GPUs (see "gpus");
a GPU no installable driver branch supports (Kepler or older, even one among several GPUs);
GPUs that cannot share one driver ("requirement");
no source that fits the requirement ("select_source");
on an HGX B200/B300 ("nvlink_fabric_needed"): no known source for the NVLink fabric packages ("nvlink_fabric_unavailable").
A class without "sources" (the base class) gets an empty plan.
kernel_packages
The packages the driver build needs before any source's: kernel headers. None in the base class.
sources
my @candidates = $self->sources;
The driver sources this setup can install from, in order of preference. Each is a hashref:
name-- for log lines and messages.kernel_module--openorproprietary.branch-- the exact driver branch it installs; orbranch_at_leastwhen it installs the newest branch its repository carries, which is known only to be at least that one (see "satisfied_by" in Rex::GPU::NVIDIA::Requirement for how each counts); or neither when the branch is unknown.packages,verify-- as in "plan". May be filled only by "resolve_source", after the repository is refreshed.unavailable-- a reason: this source does not exist on this host (no repository for the release or architecture). Skipped with that reason.fabric_manager-- the NVIDIA Fabric Manager package for this source's driver,%sstanding for the exact branch (nvidia-fabricmanager-%s); andfabric_manager_match, the package whose installed version is the driver's ("installed_driver_version"). A source withoutfabric_manageris rejected on a host with "nvswitches".
Plus whatever keys the class's later steps read. Host-read-only, like "plan". Empty in the base class. Override it in a subclass to add, reorder or drop candidates; $self->SUPER::sources gives the built-in ones.
select_source
my $source = $self->select_source(@candidates);
The first candidate "requirement" accepts ("satisfied_by" in Rex::GPU::NVIDIA::Requirement) on what it declares -- kernel_module, branch or branch_at_least, unavailable. Called by "plan", so it must only read the host, and it does not look at a package index: on a fresh host that index is stale or empty until "prepare_source" refreshes it. Dies when none fits, naming the GPUs, what they need and every rejected candidate with its reason; no driver package has been installed and no package source added then.
resolve_plan
$self->resolve_plan($plan);
The step between "prepare_source" and "install_packages": passes the chosen source through "resolve_source", now that its repository is refreshed, and checks the result against "requirement" again. If "resolve_source" returned a new source, it goes into $plan: source, packages (the plan's other packages, then the resolved source's) and verify. Dies, before any driver package is installed, when the resolved source is unavailable or no longer fits, or -- on a host with "nvswitches" -- its Fabric Manager package has no exact name or no installation candidate ("fabric_manager_unavailable"); the source "plan" chose is not swapped for another candidate then. A plan without a source is left alone.
resolve_source
my $resolved = $self->resolve_source($source);
Turns the chosen source into a concrete one, called by "resolve_plan" after "prepare_source" has refreshed the package index. The base class returns it unchanged (the same reference: nothing to do); Rex::GPU::NVIDIA::Setup::Ubuntu asks apt-cache search for the newest package and records its branch. Returns a new hashref with packages, verify and, if known, the exact branch -- or with unavailable set to the reason when the repository has nothing to install. May read the host, must not change it. Override it to pick the package some other way (a site index, ubuntu-drivers list); "resolve_plan" checks whatever it returns against the requirement.
fabric_manager_package
my $pkg = $self->fabric_manager_package($source);
The source's fabric_manager with %s replaced by its exact branch; undef without fabric_manager, or with %s and no exact branch.
fabric_manager_unavailable
my $why = $self->fabric_manager_unavailable($pkg);
Run by "resolve_plan" after the package index is refreshed and before any driver package is installed: a reason when the repository has no installation candidate for $pkg, undef when it has one or this layer cannot tell. undef here and on the rpm layer; the apt layer asks apt-cache policy.
install_fabric_manager
$self->install_fabric_manager($plan);
Runs after "verify_packages" when "fabric_manager_needed": reads the installed driver's version ("installed_driver_version"), installs the source's "fabric_manager_package" at exactly that upstream version ("install_versioned_package") and checks it ("verify_versioned_package"), then systemctl enable of "fabric_manager_service". It is not started here: before the reboot that unloads nouveau the NVIDIA module may not be bound, and Fabric Manager aborts when the loaded driver does not match; "install_driver" in Rex::GPU::NVIDIA starts it after the modprobe, or checks it after the reboot, which starts the enabled unit. Dies -- the driver is installed then, no Fabric Manager of another version is -- when the driver version cannot be read, the repository has no Fabric Manager of that version, it does not end up installed at that version, or the unit cannot be enabled.
fabric_manager_service
nvidia-fabricmanager.service.
installed_driver_version
my $version = $self->installed_driver_version($source); # "580.95.05"
The upstream version of the installed driver, read from the package the source's fabric_manager_match names (%s = branch). Dies in the base class; the packaging layers read dpkg-query / rpm -q.
install_versioned_package
$self->install_versioned_package($pkg, $version);
Installs $pkg at upstream version $version through "run_cmd", never "pkg" in Rex::Commands::Pkg. Dies in the base class.
verify_versioned_package
$self->verify_versioned_package($pkg, $version);
Dies unless $pkg is installed at upstream version $version. Dies in the base class.
retrofit_fabric_manager
my $installed = $setup->retrofit_fabric_manager;
For a host whose driver was already installed ("already_installed") and that has NVSwitches ("fabric_manager_needed") -- e.g. an HGX host provisioned before Rex::GPU installed Fabric Manager. Returns 1 after installing Fabric Manager, 0 otherwise; it never changes the driver or the host's package sources. In this order:
"loaded_driver_version": the version of the driver that runs.
"installed_fabric_managers": a Fabric Manager package is already on the host -- nothing is installed or changed. If its version is not the loaded driver's, it warns (the version change is the maintainer's).
the loaded driver version is unreadable: warn, nothing installed.
the Fabric Manager package names of "sources" -- the same names a fresh install uses ("fabric_manager_package"),
%sfilled with the loaded driver's branch. None (openSUSE, Debiannon-freeonly): warn."refresh_package_index", then per name "fabric_manager_version_unavailable": whether the host's current package sources offer it at exactly the loaded driver's version. No repository is added. The first one that does is installed with "install_versioned_package", checked with "verify_versioned_package", and "fabric_manager_service" is enabled; a failure there dies (the driver is untouched). None does: warn with each reason and the version needed.
"install_driver" in Rex::GPU::NVIDIA runs it and starts the unit after an install.
installed_fabric_managers
my @fm = $self->installed_fabric_managers; # ([ 'nvidia-fabricmanager-580', '580.95.05' ])
The Fabric Manager packages on the host, whatever their version or branch, each [ name, upstream version ]. Empty in the base class.
loaded_driver_version
my $version = $self->loaded_driver_version; # "580.95.05"
The version of the driver that runs, from nvidia-smi --query-gpu=driver_version: what Fabric Manager must match. undef unless every GPU reports the same driver version.
refresh_package_index
Refreshes the package index before "fabric_manager_version_unavailable" is asked. Nothing in the base class and on the rpm layer (dnf refreshes expired metadata itself, as for a fresh install); apt-get update on the apt layer.
fabric_manager_version_unavailable
my $why = $self->fabric_manager_version_unavailable($pkg, $version);
undef when the host's configured package sources offer $pkg at upstream version $version and installing it removes nothing; a reason otherwise. Host-read-only. The base class cannot tell and returns a reason.
nvlink_fabric_packages
The packages an HGX B200/B300 needs next to the driver and Fabric Manager, installed unversioned (the newest the sources offer, as NVIDIA's own gpu-driver-container does: nvlsm is versioned independently of the driver): the NVLink Subnet Manager nvlsm (from NVIDIA's CUDA repository; it has no service of its own -- nvidia-fabricmanager.service starts it before Fabric Manager), infiniband-diags (ibstat, which the Fabric Manager start script requires) and libibumad. Empty in the base class; the apt layer, Rex::GPU::NVIDIA::Setup::Ubuntu and Rex::GPU::NVIDIA::Setup::RHEL fill it.
nvlink_fabric_unavailable
my $why = $self->nvlink_fabric_unavailable($source);
Host-read-only, from "plan" on an HGX B200/B300: a reason when this setup knows no source for "nvlink_fabric_packages" next to the chosen driver $source, undef otherwise. "plan" dies with it before anything is changed. The base class returns a reason when there are no packages.
install_nvlink_fabric
$self->install_nvlink_fabric($plan);
The step after "install_fabric_manager" on an HGX B200/B300 ("nvlink_fabric_needed"): "warn_nvlink_kernel", then "prepare_nvlink_fabric_source", "install_packages" and "verify_packages" of "nvlink_fabric_packages" -- the same bypass of Rex::Pkg as the driver, a package not installed afterwards dies (the driver and Fabric Manager stay installed) -- and "load_ib_umad".
prepare_nvlink_fabric_source
$self->prepare_nvlink_fabric_source($plan);
Makes "nvlink_fabric_packages" installable. Nothing here: on Debian and the RHEL family they come from the CUDA repository the driver came from. Rex::GPU::NVIDIA::Setup::Ubuntu adds NVIDIA's CUDA repository, pinned to nvlsm alone.
retrofit_nvlink_fabric
my $installed = $setup->retrofit_nvlink_fabric;
For an HGX B200/B300 whose driver was already installed, after "retrofit_fabric_manager": "warn_nvlink_kernel"; then the "nvlink_fabric_packages" that are not installed are installed from the host's current package sources ("refresh_package_index" first) -- no repository is added, the driver is not touched -- and "load_ib_umad". Returns 1 if it installed or loaded something, 0 when everything was there already (then nothing is changed). A package still missing afterwards (e.g. nvlsm on an Ubuntu host without NVIDIA's CUDA repository) only warns, naming it.
load_ib_umad
Writes /etc/modules-load.d/ib_umad.conf (so the module is loaded on every boot) and runs modprobe ib_umad: the Fabric Manager start script aborts unless ib_umad is loaded. A failed modprobe warns (on Ubuntu the module is in linux-modules-extra-$kernel), it does not die.
warn_nvlink_kernel
Warns when the running kernel ("kernel") is older than 5.17, which NVIDIA's Fabric Manager guide requires for HGX B200/B300, unless "nvlink_kernel_backported". Never stops anything: the kernel is not changed by Rex::GPU.
nvlink_kernel_backported
True where the distribution supports HGX B200/B300 on an older kernel with the needed patches backported, so "warn_nvlink_kernel" stays quiet: the RHEL family (NVIDIA lists RHEL 9.6/9.8 with kernel 5.14 for B200/B300). False here.
check_nvlink_fabric
my $ok = $setup->check_nvlink_fabric($fabric_manager_active);
Run by "install_driver" in Rex::GPU::NVIDIA on an HGX B200/B300 after Fabric Manager is started (or the host rebooted): reads nvidia-smi -q and expects every GPU's Fabric section at State: Completed, Status: Success. While it is not and $fabric_manager_active, it reads again, "fabric_state_poll" times at most. Returns 1 when the fabric is up; otherwise it logs one loud warning with what it read and where to look, and returns 0. It never dies, and it does not change the host.
fabric_state_poll
my ( $reads, $seconds ) = $self->fabric_state_poll; # (12, 10)
How often "check_nvlink_fabric" reads nvidia-smi -q while the fabric registers, and the seconds between two reads.
prepare_host
Readies the host's own package manager.
prepare_source
Registers and refreshes the repository the driver comes from.
install_packages
Installs $plan->{packages}.
verify_packages
Dies unless every package in $plan->{verify} ended up installed. That check, not the package manager's exit code, is the evidence of an install.
Each of these four takes the $plan from "plan" (completed by "resolve_plan" before "install_packages") and does nothing in the base class; the packaging layer (Rex::GPU::NVIDIA::Setup::Apt, Rex::GPU::NVIDIA::Setup::Rpm) and the distro classes fill them.
post_install
Blacklists nouveau in /etc/modprobe.d/blacklist-nouveau.conf and runs "initramfs_command", so the blacklist takes effect on the next boot.
initramfs_command
The command that regenerates the initramfs: dracut --force 2>/dev/null here (and so on the rpm layer), update-initramfs -u 2>/dev/null in Rex::GPU::NVIDIA::Setup::Apt. Run with auto_die => 0.
WRITING YOUR OWN SETUP
A setup of your own is a Moo class that extends one of the built-in ones and overrides what it needs to; nothing in Rex::GPU has to be patched. Put it in your Rex project's lib/ directory -- Rex puts the lib/ next to the Rexfile, and the one in the current directory, first on @INC -- or write the package straight into the Rexfile:
my-project/
Rexfile
lib/My/GPU/Setup.pm
Adding a driver source
Override "sources" and put your candidate first; SUPER::sources keeps the built-in ones behind it. The GPUs' "requirement" still decides: a source they cannot use is skipped with its reason, and the next one is tried.
package My::GPU::Setup;
use Moo;
use namespace::autoclean;
extends 'Rex::GPU::NVIDIA::Setup::Ubuntu';
sub sources {
my ( $self ) = @_;
return (
{
name => 'pinned-580-open',
kernel_module => 'open',
branch => 580,
packages => [ 'nvidia-driver-580-server-open' ],
verify => [ 'nvidia-driver-580-server-open' ],
check_candidate => 'nvidia-driver-580-server-open'
},
$self->SUPER::sources
);
}
1;
An Ada or a B200 gets the pinned open driver; a V100 (proprietary only) rejects it and gets the built-in nvidia-driver-580-server. check_candidate is a key Rex::GPU::NVIDIA::Setup::Ubuntu reads in its resolve_source, after apt-get update; which extra keys a source may carry depends on the class you extend.
Changing a step
Override the step and call SUPER:: for the built-in part. Reach the host only through "run_cmd" and "file_cmd" ("pkg_cmd" only for inert helpers such as curl): the driver packages are installed and verified by the packaging layer's "install_packages" and "verify_packages", never through Rex::Pkg, which dies on the non-zero exit a successful DKMS build can return. "plan" and everything it calls must only read the host.
has apt_line => ( is => 'ro', predicate => 1 );
# a local mirror, in before the inherited step runs `apt-get update`
sub prepare_source {
my ( $self, $plan ) = @_;
$self->file_cmd('/etc/apt/sources.list.d/internal-nvidia.list',
content => $self->apt_line."\n") if $self->has_apt_line;
$self->SUPER::prepare_source($plan);
}
Choosing the package another way
Override "resolve_source": it runs after apt-get update, may read the host but not change it, and whatever it returns is checked against the requirement again. eg/ubuntu-drivers/ in the distribution asks ubuntu-drivers list --gpgpu (read-only) for the Ubuntu package instead of apt-cache search; the package it names is installed and verified by the inherited steps, not by ubuntu-drivers install:
package My::GPU::UbuntuDrivers;
use Moo;
extends 'Rex::GPU::NVIDIA::Setup::Ubuntu';
sub resolve_source {
my ( $self, $source ) = @_;
return $self->SUPER::resolve_source($source) unless defined $source->{search};
my $list = $self->run_cmd('ubuntu-drivers list --gpgpu 2>/dev/null', auto_die => 0);
# ... pick the newest nvidia-driver-NNN-server(-open) line of the
# source's kernel module flavour, then:
my %resolved = ( %$source, packages => [ $pkg ], verify => [ $pkg ], branch => $branch );
delete $resolved{branch_at_least};
return \%resolved; # or { %$source, unavailable => 'why' }
}
Choosing it
First hit wins ("setup_for" in Rex::GPU::NVIDIA):
# 1. per call -- a class name, or an object with settings of its own
gpu_setup(setup => 'My::GPU::Setup');
gpu_setup(setup => My::GPU::Setup->new(apt_line => 'deb [...] http://... noble main'));
# 2. for the whole Rexfile -- also reaches Rex::Rancher's gpu => 1
set gpu_nvidia_setup => 'My::GPU::Setup';
# 3. neither: Rex::GPU::NVIDIA->setup_class_for_os
The same setup option works on "install_driver" in Rex::GPU::NVIDIA. The class is built with the detected GPUs (gpus); an object gets them through "adopt" if it has none. A class extends one distro's setup, so it is for hosts of that distro: to cover several, choose per host in the Rexfile, or override "setup_class_for_os" in Rex::GPU::NVIDIA in a subclass of Rex::GPU::NVIDIA.
To narrow the driver choice without a class, pass requirement (see "extra_requirement"). To teach the GPU table a device, override "requirement_class" with a Rex::GPU::NVIDIA::Requirement subclass that adds rows to its generations.
A runnable example: eg/custom-setup/ in the distribution.
SEE ALSO
Rex::GPU::NVIDIA, Rex::GPU::NVIDIA::Requirement
SUPPORT
Issues
Please report bugs and feature requests on GitHub at https://github.com/Getty/rex-gpu/issues.
CONTRIBUTING
Contributions are welcome! Please fork the repository and submit a pull request.
AUTHOR
Torsten Raudssus <getty@cpan.org>
COPYRIGHT AND LICENSE
This software is copyright (c) 2026 by Torsten Raudssus <torsten@raudssus.de> https://raudssus.de/.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.