flux.testing.fake_resources module
Fake resource generation and installation for testing.
Provides a FakeResources ABC describing a synthetic resource set with
a flat shape (nodes × cores × gpus), plus an InjectFakeResources
implementation that encodes R via flux R encode and writes it to the
resource.R KVS key. Intended for use by the fake-resources modprobe
rc1 task (see flux-config-fake-resources(5)), which runs this code before the
resource module loads so the synthetic R is in place at broker startup.
- class flux.testing.fake_resources.FakeResources(nodes, cores_per_node=1, gpus_per_node=0, host_prefix='fake', amender=None)
Bases:
ABCAbstract base for synthetic resource sets with a flat shape.
A resource set described as a count of nodes, cores per node, and (optionally) GPUs per node. On-node topology (sockets, NUMA domains) is not modeled here; subclasses or callers needing topology should pass real hwloc XML, which
flux R encode --localwill consume, or overrideamend_R()to inject scheduler-specific metadata.Subclasses must implement
install(), which makes the resource set visible to the broker. Subclasses may also overrideamend_R()to mutate the encoded R before it is installed.- amend_R(R, hwloc_xml=None)
Hook to mutate R after generation, before KVS install.
Subclasses can override directly for complex amendment logic. Callers (typically the fake-resources modprobe rc1 task) can also pass
amender=to the constructor to inject a callable taking(R, hwloc_xml=...)and returning amended R; the defaultamend_R()defers to it. Subclass overrides take precedence; a subclass that wants to incorporateself.amendershould callsuper().amend_R(R, hwloc_xml=...).Used to inject scheduler-specific metadata into R — for example, Fluxion JGF keys or property tags. When called against an XML-derived R,
hwloc_xmlis the loaded XML string; otherwise it is None.
- abstract install()
Make these resources visible to the broker.
Concrete subclasses may extend this signature with additional arguments.
- saturation_count(slot_cores=1, slot_gpus=0)
Number of slot-shape jobs needed to saturate this resource set.
See
saturation_count()for the algorithm. This method is a thin wrapper that pullsnodes,cores_per_node, andgpus_per_nodefromself; it exists so callers holding a FakeResources object don't have to unpack its attributes. The function form is whatflux schedbenchcalls for the real-resource path, where there's no FakeResources object — the shape comes from aflux.resource.resource_listquery.
- property total_cores
Total core count across all nodes.
- property total_gpus
Total GPU count across all nodes.
- class flux.testing.fake_resources.InjectFakeResources(nodes, cores_per_node=1, gpus_per_node=0, host_prefix='fake', hwloc_xml_path=None, verbose=False, log=None, amender=None)
Bases:
FakeResourcesFakeResourcesthat writes synthetic R to the KVS.install()encodes R from the configured shape (or from an hwloc XML file, ifhwloc_xml_pathis set) and writes it toresource.R. The caller is responsible for arranging the write to happen before the resource module reads its initial R, and for setting any resource-module options needed to accept synthetic R (noverify,monitor-force-up). Thefake-resourcesmodprobe rc1 task handles both.If
hwloc_xml_pathis set, R is encoded by passing the XML file toflux R encode --xml=FILErather than from the numericcores_per_node/gpus_per_nodefields. The XML's loaded contents are passed toamend_R()so subclasses can use them.The
logargument is a callable taking a single string (default_stderr_log()). PassLOGGER.infoto integrate with a CLI's logging configuration, orlambda _: Noneto suppress output entirely.Writing an amender
An amender is a Python callable that mutates the encoded R before it is written to the KVS. Use it to inject scheduler-specific metadata that the bare
flux R encodeoutput doesn't carry, most notably the contents of the scheduling key, but may also include things like node properties or custom attributes for property-based schedulers.An amender has the signature:
- def amend(R, hwloc_xml=None): # mutate R in place, or build a new dict
R["scheduling"] = {...} return R
Where:
Ris the parsed R dict (RFC 20) produced byflux R encode, ready to mutate. The amender owns it for the duration of the call; either mutate in place and return the same dict, or build a new one and return that.hwloc_xmlis the contents of the hwloc XML file whenhwloc_xml_pathwas set on the constructor, or None otherwise. Useful for amenders that derive their additions from the topology (Fluxion's JGF, for example, walks the XML to build a graph).The return value is what gets written to
resource.Rin KVS.
The amender is passed to the constructor as the
amenderargument and may be either:A callable, ready to invoke. Use this when the amender is defined in the same source file or pulled from a Python package the caller already imported.
A string in one of two forms, resolved at construction time via
load_amender():"module.path:function_name"— importlib loadsmodule.pathand looks upfunction_name. Use this when the amender ships in a package on PYTHONPATH.Anything without a colon is treated as a filesystem path; the file is loaded as a Python module and its
amend(literally that name) attribute is used. Use this for ad-hoc test amenders or for passing an amender from a modprobe config without packaging it.
The string forms are what the
[fake-resources]amend-rTOML key in flux-config-fake-resources(5) accepts; the rc1 task forwards the TOML value straight to this constructor.Subclassing
FakeResourcesand overridingamend_R()is an alternative to passing an amender — choose subclassing when the amendment logic is non-trivial (multiple methods, internal state) and the callable form when the logic fits in one function. Subclass overrides take precedence over the constructoramender; a subclass that wants to incorporate the constructor amender should callsuper().amend_R(R, hwloc_xml=...).- install(handle=None)
Encode R from the configured shape and install it into the
resource.RKVS key.- Parameters
handle -- an open
flux.Fluxhandle used for the KVSNone (put + commit. If) --
via (a fresh handle is opened) --
:param
flux.Flux().:
- flux.testing.fake_resources.build_R_encode_args(fr)
Build argv for
flux R encodefrom aFakeResources.Exposed separately from
InjectFakeResourcesso unit tests can verify command construction without requiring a broker.Idset-shaped arguments (
-r,-c,-g) are formatted viaflux.idset.IDsetrather thanf"0-{n-1}". A degenerate single-element range like"0-0"is rejected whenflux R encodeparses it as an idset, which silently produced R without the affected resource type (thegpus_per_node=1case originally surfaced this).IDsetemits"0"for a one-element set, which parses cleanly.
- flux.testing.fake_resources.load_amender(spec)
Resolve a TOML
amend-rspec to a callable.Two forms are supported:
module.path:function_name— importsmodule.pathand returns itsfunction_nameattribute. Useful when the amender ships with a Python package on PYTHONPATH (e.g. a Fluxion JGF helper).Anything without a
:is treated as a filesystem path; the file is loaded as a Python module and itsamendcallable is returned. Useful for ad-hoc test amenders that don't warrant a package.
The returned callable should have signature
amend(R, hwloc_xml=None) -> R. It is invoked fromFakeResources.amend_R()when set via theamenderconstructor argument.Raises
RuntimeErrorwith a descriptive message on any failure (file not found, import error, missing attribute), so configuration mistakes surface clearly from the modprobe rc1 task rather than as a generic ImportError.
- flux.testing.fake_resources.saturation_count(nodes, cores_per_node, gpus_per_node, slot_cores=1, slot_gpus=0)
Return the number of slot-shape jobs that saturate a flat resource set.
A
slotis described by its core and GPU counts; this returns the number of such slots that fit acrossnodesmachines ofcores_per_nodecores andgpus_per_nodeGPUs each, taking the more-constraining of cores and GPUs into account. Pure arithmetic — no broker access, no FakeResources object required — so it's reusable from code paths that gather the shape fromflux.resource.resource_list()instead.Either
slot_coresorslot_gpusmust be positive.Non-uniformity note: this assumes uniform cores/GPUs per node. Real broker resources may not be uniform; callers that derive the shape from a broker query typically average across nodes, which can over- or under-estimate true capacity by one slot per heterogeneous node. The error is bounded and not a correctness issue for benchmarks that measure aggregate rates.