blob: 65e904d56a91b43d1792a2254be5c9db7adc7c7d [file] [edit]
<!--#include virtual="header.txt"-->
<h1>Hierarchical Resource (HRES) Scheduling</h1>
<h2 id="overview">Overview<a class="slurm_link" href="#overview"></a></h2>
<p>Hierarchical Resources (HRES) were added in Slurm 25.05 in a <b>Beta</b>
state. While in beta, the configuration file format and other aspects of this
feature may undergo substantial changes in future versions and require major
changes to avoid errors. Sites that utilize this feature while in a beta state
should pay particularly close attention to changes noted in RELEASE_NOTES and
the CHANGELOGs when <a href="upgrades.html">upgrading</a>.</p>
<p>Hierarchical Resources allow for <a href="licenses.html">license</a>-like
resources to be defined as part of independent hierarchical topologies and
associated with specific nodes. Jobs may request any integer count of that
resource. Multiple resources may be defined in the configuration (in a
<a href="resources.yaml.html">resources.yaml</a> file), and will use
independently defined hierarchies.</p>
<p>Although these hierarchical topologies have some similarities to
<a href="topology.html">network topologies</a>, the definitions for each are
completely separate.</p>
<h2 id="modes">Planning Modes<a class="slurm_link" href="#modes"></a></h2>
<p>Three modes of resource planning are provided. In either case, a layer may be
defined with a count of zero or infinite (<code>count: -1</code>) resources,
with the impact of this depending on the mode used. Every layer is identified by
a unique <code>layer_name</code>; <code>nodes</code> and <code>count</code> are
optional, and <code>count</code> defaults to zero.</p>
<h3 id="mode1">Mode 1<a class="slurm_link" href="#mode1"></a></h3>
<p>In <b>Mode 1</b>, sufficient resources are only required on one layer that
overlaps with job allocation. In many cases only a single level is needed.
However, multiple levels may be defined if some additional flexibility in where
resources are used is preferred, as described in the example below.</p>
<p>A layer with a <b>zero</b> count of a resource will have no impact on
scheduling. Since that layer will never satisfy the request, it will never alter
the calculated list of nodes eligible to run a given job. It would be
semantically equivalent to removing that layer definition.</p>
<p>A layer with an <b>infinite</b> count of a resource will the always allow a
new job allocation to succeed. Unless prevented due to other requirements, jobs
will be able to execute immediately.</p>
<p>For example, consider the <b>natural</b> resource defined in the example
<a href="#resources_yaml">resources.yaml</a> file below. A job that requests
this resource and is allocated <code>node[01-04]</code> will pull from the pool
of 100 available to <code>node[01-16]</code>. With a count of 50 specified at
the higher level (<code>node[01-32]</code>), they can be allocated in addition
to the resources specified in each group of 16. That is, 100 each would be
available to each group of 16, and 50 more would be available on any of them,
for a total of 250 of this resource.</p>
<h3 id="mode2">Mode 2<a class="slurm_link" href="#mode2"></a></h3>
<p>In <b>Mode 2</b>, sufficient resources must be available on all layers on all
levels that overlap with job allocation. Note that scheduling stalls are
possible if higher levels do not make enough resources available.</p>
<p>A layer with a <b>zero</b> count of a resource will have a significant impact
on scheduling. All nodes underneath that layer would never be able to satisfy
the allocation constraints. This could be used to mark that resource unavailable
for a specific portion of the cluster, e.g., for hardware maintenance, without
altering the individual lower-layer resource counts.</p>
<p>A layer with an <b>infinite</b> count of a resource will have no impact on
scheduling. Since that layer will always satisfy the request, it will never
constrain execution and would be semantically equivalent to removing that layer
definition.</p>
<p>For example, consider the <b>flat</b> resource defined in the example
<a href="#resources_yaml">resources.yaml</a> file below. A job that requests
this resource and is allocated <code>node[01-04]</code> will pull from all
levels that include those nodes. So it would pull from the 12 available on
<code>node[01-08]</code>, from the 16 available on <code>node[01-16]</code>, and
from the 24 available on <code>node[01-32]</code>. Since the highest level only
contains 24, that is the maximum that can be allocated, even though a larger
total is available on lower levels. This example allows a fixed set of resources
to have some flexibility in where they are used.</p>
<h3 id="mode3">Mode 3<a class="slurm_link" href="#mode3"></a></h3>
<p>In <b>Mode 3</b>, resources are allocated on a per-node basis,
with the total consumption being calculated from the most granular layer and
summed up into the successively higher layers.
This mode is specifically designed for consumable resources like power,
where the total usage at a higher level (e.g., cluster-wide)
is the sum of the usage of its components (e.g., chassis level,
then rack level summing multiple chassis, then datacenter row ).</p>
<p>The scheduler ensures that the total summed resources at every layer
in the hierarchy do not exceed that layer's defined count.</p>
<p>A job or reservation can request only one <b>Mode 3</b> resource, a request
for more is rejected.</p>
<p><b>Important Note:</b> Layers defined under <b>Mode 3</b> must be able to be
represented as a <b>rooted uniform depth tree</b>. Every layer except the root
names its parent with <code>parent_name</code>, and <code>nodes</code> may only
be listed on leaf layers. Each parent inherits the union of its children's
nodes.</p>
<p>For example, consider the <b>power</b> resource defined in the example
<a href="#resources_yaml">resources.yaml</a> file below. A job that requests
1000 of the <b>power</b> resource and is allocated <code>node[01-16]</code>
will pull from all levels that include those nodes.
So it would pull 8000 from <code>node[01-08]</code>,
8000 from <code>node[09-16]</code>, 16000 from <code>node[01-16]</code>, and
16000 from <code>node[01-32]</code>.</p>
<h4 id="mode3_preemption">Preemption<a class="slurm_link" href="#mode3_preemption"></a></h4>
<p>A pending job can preempt lower-priority running jobs to reclaim a
<b>Mode 3</b> resource. Because per-node usage is summed into every layer above
it, a job holding the resource under a shared layer is a preemption candidate
even when it does not share any nodes with the preemptor.
Preemption is governed by the usual <b>PreemptType</b> and <b>PreemptMode</b>
configuration; only the <b>CANCEL</b> and <b>REQUEUE</b> preemption modes are
supported for <b>Mode 3</b> resources. Preemption to reclaim a hierarchical
resource is not supported for <b>Mode 1</b> or <b>Mode 2</b> resources.</p>
<h2 id="plugins">Plugins<a class="slurm_link" href="#plugins"></a></h2>
<p>An HRES plugin lets an external service take part in the life of the jobs
that use a resource, for example to reserve the resource when a job starts,
pass data to the job's nodes, release the resource when the job ends, and take
a layer out of service when the service reports a problem. A plugin is enabled
per resource with the <code>plugin</code> section of
<a href="resources.yaml.html">resources.yaml</a>. Currently only
<b>hres/lua</b> is available.</p>
<pre>
- resource: qpu
mode: MODE_1
plugin:
type: hres/lua
config:
- name: path
value: /etc/slurm/hres_qpu.lua
maintenance_cycle: 60
maintenance_timeout: 10
layers:
- layer_name: qpu1
nodes:
- "node[01-16]"
count: 1
</pre>
<h3 id="plugin_callbacks">Callbacks<a class="slurm_link" href="#plugin_callbacks"></a></h3>
<p>When a job uses several resources with a plugin, the callbacks run in the
order the resources are defined in <b>resources.yaml</b>, and the epilogs run
in the reverse order. The pre-runs stop at the first failure, every other
callback of a kind runs even after one of them fails. A plugin may return
<code>ESLURM_HRES_REMOTE_FAIL</code> to report that the external service is
unavailable rather than broken.</p>
<p>The validate arguments, pre-run, revoke, and controller epilog callbacks
run while slurmctld holds its job lock, which stops scheduling and most
requests until they return. They must be quick. Slow work belongs in the
maintenance callback, or in the asynchronous pre-run of
<a href="#hres_lua">hres/lua</a>.</p>
<table border=1 cellspacing=4 cellpadding=4>
<tr><th>Callback</th><th>Runs</th><th>On failure</th></tr>
<tr><td>create context</td>
<td>slurmctld, for each resource with a plugin when the configuration is
read. This is before the usage of running jobs is restored, so every layer is
shown with nothing used</td>
<td><code>ESLURM_HRES_MAINTENANCE_DISABLE</code> or
<code>ESLURM_HRES_MAINTENANCE_DOWN</code> disable every layer of the resource,
any other error is fatal</td></tr>
<tr><td>destroy context</td>
<td>slurmctld, before the configuration is read again</td>
<td>ignored</td></tr>
<tr><td>maintenance</td>
<td>slurmctld, every <code>maintenance_cycle</code> seconds, with the current
state of each layer. The plugin may set or clear <code>disable_layer</code> of
each layer, the same flag as <code>DisableLayer</code> of
<code>scontrol update</code>, or mark a layer down to also requeue the running
jobs that use it. A job that cannot be requeued is cancelled</td>
<td>logged</td></tr>
<tr><td>validate arguments</td>
<td>slurmctld, for each resource of a job's request, with the job's plugin
arguments, if any, when the job is submitted or its licenses are updated, and
for a pending job when slurmctld starts or reads its configuration again, or
when the job is requeued</td>
<td>any error rejects the request, or holds a pending job</td></tr>
<tr><td>pre-run</td>
<td>slurmctld, once the job is scheduled and before its nodes are allocated.
The plugin may attach a payload that is sent to the job's nodes</td>
<td><code>ESLURM_HRES_REMOTE_FAIL</code> leaves the job pending, any other
error holds it with the plugin's message as the reason, or rejects it at
submission</td></tr>
<tr><td>revoke</td>
<td>slurmctld, when the job does not start after its pre-run succeeded, for
example because the pre-run of another resource failed. The plugin must undo
what its pre-run did</td>
<td>logged</td></tr>
<tr><td>controller epilog</td>
<td>slurmctld, once the epilogs of all the job's nodes are done and before its
resources are returned</td>
<td>logged</td></tr>
<tr><td>job prolog</td>
<td>each node of the job where the <b>Prolog</b> runs, once, before it. That is
every node with <b>PrologFlags=Alloc</b>, otherwise only the nodes that start
a step</td>
<td>the job is requeued, or cancelled if it cannot be, for example when it was
started by <b>srun</b> or <b>salloc</b>. The node is drained unless the error
is <code>ESLURM_HRES_REMOTE_FAIL</code></td></tr>
<tr><td>job epilog</td>
<td>each node of the job, once, after the <b>Epilog</b> script if one is
configured, even when it failed</td>
<td>the node is drained unless the error is
<code>ESLURM_HRES_REMOTE_FAIL</code></td></tr>
<tr><td>task prolog</td>
<td>slurmstepd, in each task as root before it starts</td>
<td>the task fails</td></tr>
<tr><td>user task prolog</td>
<td>slurmstepd, in each task as the user, once its standard input and output
are in place, just before it starts</td>
<td>the task fails</td></tr>
<tr><td>task epilog</td>
<td>slurmstepd, after each task exits</td>
<td>logged</td></tr>
</table>
<p>What the nodes receive about each resource is taken when the job starts:
the resource name, the plugin configuration, the job's plugin arguments, the
payload from the pre-run, and the layers the job holds, each with the amount
charged and the job's nodes under it. It does not change while the job runs,
even if the layers do. It travels in the credential of each job step, which
<b>srun</b> receives, so the job's user can read the plugin configuration, the
payload and the arguments. They must not hold secrets.</p>
<h3 id="plugin_args">Plugin Arguments<a class="slurm_link" href="#plugin_args"></a></h3>
<p>A job may pass arguments to the plugin of a resource at the end of its
request, after the count if one is given. Slurm does not interpret the
arguments. They are only handed to the plugin, which may reject them:</p>
<pre>sbatch --license='qpu:1[mode=fast,shots=1000]' my_script.sh</pre>
<p>Each argument needs both a name and a value, without whitespace, and a name
may be given only once. A resource takes one set of arguments. Arguments are
not allowed for a license that is not a hierarchical resource, or in the
licenses of a reservation. They are shown in the <b>Licenses</b> and
<b>LicensesAlloc</b> of the job.</p>
<h3 id="hres_lua">hres/lua<a class="slurm_link" href="#hres_lua"></a></h3>
<p><b>hres/lua</b> runs a Lua script, given by the <code>path</code> entry of
the plugin <code>config</code>. The script must be available at the same path
on the controller and on the nodes, and be readable by every user running jobs
with the resource, because <code>hres_node_user_task_prolog</code> loads it as
the user. <b>WARNING</b>: Most of its functions run as root or as
<b>SlurmUser</b>, so the script and every directory on its path must be
writable only by root. It must define every function below except
<code>hres_validate_args</code> and
<code>hres_controller_job_async_prerun</code>, which are optional. Each function
returns a Slurm return code, such as <code>slurm.SUCCESS</code>,
<code>slurm.ERROR</code> or <code>slurm.ESLURM_HRES_REMOTE_FAIL</code>. A
missing or non-numeric return value means success. If the script cannot be
loaded, for example because a function is missing, the error is logged and the
callback fails. slurmctld does not start if the script cannot be loaded, and
later keeps using the last script it loaded, except for
<code>hres_controller_job_async_prerun</code>, which fails.</p>
<pre>
hres_create_context(ctx, layers)
hres_destroy_context(ctx)
hres_maintenance(ctx, layers)
hres_validate_args(ctx, args) -- optional
hres_controller_job_prerun(ctx, job, prep) -- returns rc[, payload]
hres_controller_job_async_prerun(ctx, job, prep) -- optional, returns rc[, payload]
hres_controller_job_revoke(ctx, job, prep)
hres_controller_job_epilog(ctx, job, prep)
hres_node_job_prolog(prep, job)
hres_node_job_epilog(prep, job)
hres_node_task_prolog(prep, job, node_tid, global_tid, env)
hres_node_task_epilog(prep, job, node_tid, global_tid, env)
hres_node_user_task_prolog(prep, job, node_tid, global_tid, env)
</pre>
<p>The arguments are tables:</p>
<ul>
<li><code>ctx</code>: <code>name</code> of the resource and <code>config</code>,
the plugin configuration as a <code>name = value</code> table</li>
<li><code>layers</code>: an array of the resource's layers, each with
<code>name</code>, <code>nodes</code>, <code>total</code>, <code>used</code> and
<code>disable_layer</code>. <code>hres_maintenance</code> may set
<code>disable_layer</code> on a layer to stop allocating from it, and clear it
to enable the layer again. It may also set <code>down</code>, which is not
passed in, to disable the layer and requeue its running jobs. This is done on
every run that sets it, and the layer stays disabled until
<code>disable_layer</code> is cleared</li>
<li><code>args</code>: the job's plugin arguments as a
<code>name = value</code> table, empty when none are given. Without
<code>hres_validate_args</code> every argument is accepted</li>
<li><code>job</code>: <code>job_id</code>, <code>sluid</code>,
<code>user_id</code>, <code>user_name</code>, <code>group_id</code>,
<code>account</code>, <code>partition</code>, <code>nodes</code> and
<code>end_time</code>. <code>account</code> is nil when the job has none, and
<code>user_name</code> may be nil, so use <code>user_id</code> to identify the
user.
<code>end_time</code> is the Unix time the job is expected to end, which is one
year after its start when the job has no time limit.
<code>hres_controller_job_revoke</code> gets the same expected end as the
pre-run, although the job does not start.
<code>hres_controller_job_epilog</code> and <code>hres_node_job_epilog</code>
get the time the job ended, except that with <b>PrologFlags=RunInJob</b> the
node job epilog gets the expected end as of the start of the job. The task
functions get the value from the start of the step, not a later change of the
time limit</li>
<li><code>env</code>: the environment of the task, looked up by name, as in
<code>env.HOME</code>. It cannot be iterated, and setting a field does not
change the environment. <code>hres_node_task_prolog</code> gets the step's
environment, before task variables such as <b>SLURM_PROCID</b> are set.
<code>hres_node_user_task_prolog</code> gets the task's environment as it is
before <b>TaskProlog</b> runs, and <code>hres_node_task_epilog</code> gets the
same environment as <b>TaskEpilog</b>. Use <code>node_tid</code> and
<code>global_tid</code> to identify the task. The user sets these values.
<b>WARNING</b>: <code>hres_node_task_prolog</code> and
<code>hres_node_task_epilog</code> run as root, so never use a value from
<code>env</code> unchecked in a command or in a file path</li>
<li><code>prep</code>: what the nodes receive about the resource:
<code>name</code>, <code>config</code>, <code>plugin_args</code> (a
<code>name = value</code> table), <code>payload</code>,
<code>async_running</code>, true in the revoke and controller epilog while
the asynchronous pre-run has not stored its result, and <code>layers</code>,
an array of the layers the job holds, each with <code>name</code>,
<code>count</code> and <code>nodes</code></li>
</ul>
<p>A string returned as the second value of
<code>hres_controller_job_prerun</code> becomes the payload sent to the job's
nodes, as <code>prep.payload</code>. When
<code>hres_controller_job_async_prerun</code> is defined, it runs after the
pre-run in a separate thread, while the job stays in the <b>CONFIGURING</b>
state, and its payload, if any, replaces the one from the pre-run. If it fails,
the job is requeued, or cancelled if it cannot be requeued. If the job's
nodes are not allocated after all, its result is dropped and
<code>hres_controller_job_revoke</code> is called. The job is not scheduled
again until the asynchronous pre-run ends. As the revoke, and the controller
epilog of a job cancelled while it is <b>CONFIGURING</b>, can run while the
asynchronous pre-run of the same job is still running, the service must treat
them as final for that run of the job, identified by <code>job.job_id</code>
and <code>job.sluid</code>, and refuse or undo a later reservation from it.
If slurmctld stops without a clean shutdown, or a backup takes over, while the
asynchronous pre-run is running, its result is lost and the job is requeued,
or cancelled if it cannot be requeued.</p>
<p>The controller functions of a resource share one Lua state, except
<code>hres_maintenance</code>, which has a state of its own, and
<code>hres_controller_job_async_prerun</code>, which gets a new one for each
call. Globals set by the other functions are not visible to them. A state is
reloaded when the script changes, which drops its globals without calling
<code>hres_create_context</code> again. Each node function runs in a new Lua
state. <code>hres_node_task_prolog</code> and
<code>hres_node_user_task_prolog</code> run in the task's process, so the
latter may write to the task's output, flushing it before returning.
<code>hres_node_task_epilog</code> runs in slurmstepd after the task
exits.</p>
<p>A node function that runs longer than <b>PrologTimeout</b>, or
<b>EpilogTimeout</b> for the epilogs, fails with an error and is handled
like any other failure of that function (see the table above). Both default
to <b>PrologEpilogTimeout</b>, which is not set by default, so there is no
limit. The limit only stops Lua code: a function blocked in a command or a
read, as in <code>os.execute()</code> or <code>io.popen()</code>, is not
interrupted, so bound such calls in the script, for example with
<code>timeout(1)</code>.</p>
<pre>
function hres_controller_job_prerun(ctx, job, prep)
slurm.log_info("reserving %s for JobId=%d", prep.name, job.job_id)
return slurm.SUCCESS, "token=" .. job.job_id
end
function hres_node_job_prolog(prep, job)
slurm.log_info("JobId=%d payload %s", job.job_id, prep.payload or "")
return slurm.SUCCESS
end
</pre>
<h2 id="limitations">Limitations<a class="slurm_link" href="#limitations"></a></h2>
<ul>
<li>This implementation is in a <b>beta</b> state (see
<a href="#overview">overview</a>)</li>
<li>Resource names defined through the hierarchical resources configuration must
not conflict with any cluster licenses</li>
<li>The layer structure (layers, variables, and modes) is static. Changing it
requires editing <b>resources.yaml</b> and restarting or reconfiguring
(see <a href="#updates">Runtime Updates</a>)</li>
<li><b>PreemptParameters=reclaim_licenses</b> in <b>slurm.conf</b> applies only
to regular licenses, not to HRES. <b>Mode 3</b> resources are instead
reclaimed by their own preemption path (see
<a href="#mode3_preemption">Preemption</a>)</li>
<li>Preemption to reclaim a <b>Mode 3</b> resource is only performed by the main
scheduler, not during backfill scheduling</li>
<li>The licenses of a running job that holds or requests an HRES cannot be
changed with <b>scontrol update job</b>. Requeue the job and change them
while it is pending</li>
<li>A hierarchical resource cannot be requested with the <code>|</code> form
of a license request</li>
</ul>
<h2 id="examples">Examples<a class="slurm_link" href="#examples"></a></h2>
<p>After defining a resource hierarchy in <b>resources.yaml</b> (see below for
example), you can interact with these resources in several ways, using the
syntax already used for licenses:</p>
<ul>
<li>View HRES<pre>scontrol show license</pre></li>
<li>Reserve HRES
<pre>scontrol create reservation account=root licenses=natural(low1):30,flat(low2):4,flat(low4):2</pre>
Note that the layer must be named for each reserved HRES, using its
<code>layer_name</code> from <b>resources.yaml</b></li>
<li>Request HRES on a job
<pre>sbatch --license=flat:2,natural:1 my_script.sh</pre></li>
</ul>
<h3 id="resources_yaml">resources.yaml<a class="slurm_link" href="#resources_yaml"></a></h3>
<pre>
---
- resource: flat
mode: MODE_2
layers:
- layer_name: top # highest level
nodes:
- "node[01-32]"
count: 24
- layer_name: mid1 # middle levels
nodes:
- "node[01-16]"
count: 16
- layer_name: mid2
nodes:
- "node[17-32]"
count: 16
- layer_name: low1 # lowest levels
nodes:
- "node[01-08]"
count: 12
- layer_name: low2
nodes:
- "node[09-16]"
count: 12
- layer_name: low3
nodes:
- "node[17-24]"
count: 12
- layer_name: low4
nodes:
- "node[25-32]"
count: 12
- resource: natural
mode: MODE_1
layers:
- layer_name: top # highest level
nodes:
- "node[01-32]"
count: 50
- layer_name: low1 # lowest levels
nodes:
- "node[01-16]"
count: 100
- layer_name: low2
nodes:
- "node[17-32]"
count: 100
- resource: power
mode: MODE_3
topology: blok1
variables:
- name: full_node
value: 1000
- name: full_gpu_node
value: 2000
layers:
- layer_name: rack1 # leaf layers list nodes
parent_name: row1
nodes:
- "node[01-08]"
count: 40000
base:
- name: storage
value: 5000
- layer_name: rack3
parent_name: row2
nodes:
- "node[17-24]"
count: 40000
- layer_name: rack2
parent_name: row1
nodes:
- "node[09-16]"
count: 40000
- layer_name: rack4
parent_name: row2
nodes:
- "node[25-32]"
count: 40000
- layer_name: row1 # inherits node[01-16]
parent_name: datacenter
count: 60000
base:
- name: network1
value: 3000
- layer_name: row2 # inherits node[17-32]
parent_name: datacenter
count: 80000
base:
- name: network2
value: 2000
- layer_name: datacenter # root, inherits node[01-32]
count: 130000
base:
- name: acUnit1
value: 10000
- name: acUnit2
value: 8000
</pre>
<h2 id="updates">Runtime Updates<a class="slurm_link" href="#updates"></a></h2>
<p>Administrators may change the nodes, count, base, and disabled state of an
existing layer with <code>scontrol update</code>. A plain <code>Nodes</code>
list replaces the layer's nodes, while a <code>+</code> or <code>-</code>
prefix adds or removes nodes. <code>Count</code> is absolute and must not be
lower than the layer's <b>BaseUsage</b>. <code>Base</code> replaces the whole
base list, and an empty <code>Base=</code> clears it. In <b>Mode 3</b>,
<code>Nodes</code> may only be changed on leaf layers. Node and base changes
are propagated to the ancestors. Running jobs are never affected, so lowering
a count below the current usage makes new jobs wait until usage drops.</p>
<p>A running job keeps the layers it was charged on, even when those layers
later gain or lose nodes. The job releases exactly what it took, so changing
the nodes of a layer under a running job does not distort the usage counts.
The layers a job holds are shown by the <b>LicensesAlloc</b> field of
<code>scontrol show job</code>, for example
<code>power(rack1*1000,row1*1000,datacenter*1000):1000</code>, each layer
followed by the amount charged on it. In <b>Mode 3</b> that amount grows with
the number of the job's nodes under the layer, as in <code>rack1*2000</code>
for two nodes.</p>
<pre>
scontrol update HRESName=power LayerName=rack2 Nodes=+node[33-36] Count=45000
scontrol update HRESName=power LayerName=rack1 Base=
</pre>
<p>The same operation is available through the REST API as
<code>POST /slurm/v0.0.46/hres/</code> with a JSON body containing
<code>hres_name</code>, <code>layer_name</code>, and any of <code>nodes</code>,
<code>count</code>, <code>base</code>, <code>disable_hres</code>, and
<code>disable_layer</code>.</p>
<p>Changes to the nodes of a layer are saved in the slurmctld node state file
(in <b>StateSaveLocation</b>), and are recovered as follows when slurmctld
starts or is reconfigured:</p>
<ul>
<li>Dynamic nodes and cloud nodes keep their saved layers after a restart or
<code>scontrol reconfigure</code>.</li>
<li>Other nodes keep their saved layers only when slurmctld is started with
<code>-R</code> or when a backup slurmctld takes control. A default restart or
<code>scontrol reconfigure</code> returns them to the layers defined in
<b>resources.yaml</b>. So when the primary slurmctld resumes control without
<code>-R</code>, it returns these nodes to their configured layers, even though
the backup kept their saved layers.</li>
<li>When a node is powered down, or fails to resume within
<b>ResumeTimeout</b>, it returns to its configured layers: those in
<b>resources.yaml</b>, or for a dynamic node, those it was created with. A node
that is powered down or powering down when slurmctld starts also uses its
configured layers.</li>
<li>When a node's saved layers are recovered, they replace its membership in
every layer defined in <b>resources.yaml</b>. Edits to <b>resources.yaml</b> do
not change that node's layers until it returns to its configured layers. If a
saved layer no longer exists or is no longer a leaf layer, all of the node's
saved changes are discarded and an error is logged.</li>
</ul>
<p>Changes to the count, base, and disabled state are not saved.
<b>resources.yaml</b> is read again on the next reconfigure or restart, and
those changes are lost.</p>
<h3 id="disable">Disabling a Resource<a class="slurm_link" href="#disable"></a></h3>
<p>A resource or a single layer may be switched off without removing it from the
configuration. <code>DisableHRES</code> applies to every layer of the resource,
whether or not a layer is named, while <code>DisableLayer</code> applies only to
the named layer, so jobs may still be allocated resources from the other layers.
Both accept only <code>true</code> and <code>false</code>. Jobs may still
request a disabled resource, but they stay pending until it is enabled again.
A job that no enabled layer can serve pends with the reason
<code>HRESDisabled</code>.
Running jobs keep the resources that they already hold. The same fields may be
set in <b>resources.yaml</b> as <code>disable_hres</code> and
<code>disable_layer</code>.</p>
<pre>
scontrol update HRESName=power DisableHRES=true
scontrol update HRESName=power LayerName=rack1 DisableLayer=true
</pre>
<h3 id="show">Viewing a Resource<a class="slurm_link" href="#show"></a></h3>
<p><code>scontrol show license</code> prints one entry per layer.</p>
<pre>
LicenseName=power
Total=35000 Used=0 Free=35000 Reserved=0 Remote=no
LayerName=rack1 ParentName=row1 Nodes=node[01-08] Mode=3 DisableHRES=false DisableLayer=false ConfTotal=40000 BaseUsage=5000
Base=storage:5000
</pre>
<ul>
<li><b>LayerName</b>, <b>ParentName</b>: the layer and, when it has one, its
parent layer</li>
<li><b>Mode</b>: the planning mode of the resource</li>
<li><b>DisableHRES</b>, <b>DisableLayer</b>: whether the resource or the layer
is disabled for scheduling</li>
<li><b>ConfTotal</b>: the count configured for the layer</li>
<li><b>BaseUsage</b>: the sum of the base values of the layer, plus, in
<b>Mode 3</b>, the base usage propagated up from its children</li>
<li><b>Base</b>: the base name and value pairs, or <code>(null)</code></li>
<li><b>Total</b>: what is left for jobs, the configured count minus the base
usage</li>
</ul>
<h2 id="dynamic_nodes">Dynamic Nodes<a class="slurm_link" href="#dynamic_nodes"></a></h2>
<p>Dynamic nodes are not listed in <b>resources.yaml</b>. They name their
layers when they are created, through the <b>HRES=</b> node parameter, which is
a JSON list of resource name and layer name pairs. In <b>Mode 3</b> only one
leaf layer per resource may be given.</p>
<pre>
scontrol create NodeName=node33 State=cloud HRES='[{"hres_name":"power","layer_name":"rack2"}]'
slurmd -Z --conf 'HRES=[{"hres_name":"power","layer_name":"rack2"}]'
</pre>
<p>The node is added to the listed layers and their parents when it is created,
and removed from all layers when it is deleted. An unknown or unsuitable layer
creates the node without HRES membership and logs an error. The parameter is
ignored for nodes defined in <b>slurm.conf</b>. Layers given at creation are
the node's configured layers. Runtime changes to them are kept across restarts
until the node is powered down, as described in
<a href="#updates">Runtime Updates</a>.</p>
<!--#include virtual="footer.txt"-->