Merge branch 'nate-i50189-http-context' into 'master'
See merge request SchedMD/dev/slurm!1949
diff --git a/CHANGELOG/slurm-25.05.md b/CHANGELOG/slurm-25.05.md
index 623fe7c..2ecb91e 100644
--- a/CHANGELOG/slurm-25.05.md
+++ b/CHANGELOG/slurm-25.05.md
@@ -1,3 +1,30 @@
+## Changes in 25.05.3
+
+* slurmctld.service - Set LimitMEMLOCK=infinity by default to avoid slurmctld crashes due to default for locked memory being too low.
+* slurmdbd.service - Set LimitMEMLOCK=infinity by default to avoid slurmdbd crashes due to default for locked memory being too low.
+* slurmrestd.service - Set LimitMEMLOCK=infinity by default to avoid slurmrestd crashes due to default for locked memory being too low.
+* Fix a segfault in the slurmctld caused by invalid core affinity for GPUs on a node.
+* Fix a node not being set to the invalid state when GPU core affinity is invalid.
+* A cluster will start the MaxJobCount of jobs and not one less.
+* Allow QOS usage to be purged and optionally archived as part of a Usage purge and optional archive.
+* Fix slurmctld crash caused by accessing job_desc.assoc_qos in job_submit.lua for an association that doesn't exist.
+* Fix slurmctld segfault when SIGUSR2 is received early and jobcomp plugin is enabled.
+* Fix use-cases incorrectly rejecting job requests when MaxCPUsPer[Socket|Node] applied and CPUSpecList/CoreSpecCount configured.
+* tls/s2n - Fix heterogeneous jobs failing to run in a TLS enabled environment.
+* sbatch - Fix a regression where SLURM_NETWORK would not be exported for non-Cray systems when using --network.
+* REGEX_REPLACE() was not supported before MySQL 8.0.4 and MariaDB 10, and the regex syntax used previously was not supported for both MySQL and MariaDB (not all POSIX syntax is supported in both)
+* fatal() if the SQL server does not support REGEXP_REPLACE(). This was introduced in MySQL 8.0.4 or MariaDB 10.0.5.
+* Pass environment variables to container when using Apptainer/Singularity OCI runtimes.
+* slurmscriptd,slurmstepd - Fix use-after-free issue with the "ident" string when logging to syslog.
+* Fix bug where the backfill scheduler changed the specified --time of a job and incorrectly reset it to --time-min.
+* Prevent healthy nodes being marked as unresponsive due to forwarding message timeouts increasing as the tree is traversed. The issue occurred if Slurm was running with a mix of 24.05- and 24.11+ slurmds. This only fixes 25.05+ slurmds.
+* Fix crash while using the wckeys rest endpoint.
+* Fix cases of job updates incorrectly rejected when specifying modifications on fields unrelated to tasks computation (i.e. changing JobName).
+* slurmrestd - Prevent triggering a fatal abort when parasing a non-empty group id string by replacing it with an error. This affects all endpoints with request bodies containing openapi_meta_client group field. It also affects the following endpoints: 'GET /slurmdb/v0.0.4[1-3]/jobs' 'POST /slurm/v0.0.4[1-3]/job/submit' 'POST /slurm/v0.0.4[1-3]/job/{job_id}' 'POST /slurm/v0.0.4[1-3]/job/allocate'
+* slurmrestd - Fix memory leak that happened when submitting a request body containing the meta.plugin.accounting_storage field.
+* slurmrestd - Fix memory leak that happened when submitting a request body containing the "warnings", "errors", or "meta" field. This affects the following endpoints: 'POST /slurmdb/v0.0.4*/qos'
+* slurmctld - Fix how gres with cores or a type defined are selected to prevent jobs not using reservations from being allocated reserved gres and vice versa.
+
## Changes in 25.05.2
* sbatch - Fix case where --get-user-env and some --export flags could make a job fail and get requeued+held if Slurm's installation path was too long.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 3192380..5305932 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,12 +1,12 @@
-NOTES FOR GITHUB DEVELOPERS
----------------------------
+NOTES FOR DEVELOPERS
+--------------------
The official issue tracker for Slurm is at
- https://bugs.schedmd.com/
+ https://support.schedmd.com/
We welcome code contributions and patches, but **we do not accept Pull Requests
through GitHub at this time.** Please submit patches as attachments to new
-bugs under the "C - Contributions" severity level.
+tickets under the "C - Contributions" severity level.
TARGET RELEASES
---------------
@@ -54,7 +54,8 @@
PATCH SUBMISSION
----------------
-An entry in `NEWS` should describe the change or new functionality.
+A "Changelog: " trailer in the commit message should describe the change or
+new functionality.
Please break patches up into logically separate chunks, while ensuring that
each patch can still be compiled. (Anticipate that a developer using `git
@@ -69,15 +70,7 @@
spelling mistake), please send the command/regex used to generate the changes
along with the patch, or note it in the commit message.
-While not required, we encourage use of `git format-patch` to generate the
-patch. This ensures the relevant author line and commit message stay attached.
-Plain `diff`'d output is also okay. In either case, please attach them to the
-bug for us to review. Spelling corrections or documentation improvements can be
-suggested without attaching the patch as long as you describe their location.
-
-LEGAL
------
-
-We ask that a contributor licensing agreement be signed for all substantial
-contributions. Please see https://slurm.schedmd.com/contributor.html for
-details.
+We strongly encourage the use of `git format-patch` to generate the patch. This
+ensures the relevant author line and commit message stay attached. Spelling
+corrections or documentation improvements can be suggested without attaching
+the patch as long as you describe their location.
diff --git a/contribs/slurm_completion_help/slurm_completion.sh b/contribs/slurm_completion_help/slurm_completion.sh
index 073760e..d354101 100644
--- a/contribs/slurm_completion_help/slurm_completion.sh
+++ b/contribs/slurm_completion_help/slurm_completion.sh
@@ -4309,6 +4309,7 @@
function __scontrol_update_reservationname() {
local parameters=(
"accounts="
+ "allowedpartitions="
"burstbuffer="
"corecnt="
"duration="
@@ -4321,6 +4322,7 @@
"nodecnt="
"nodes="
"partition="
+ "qos="
"reservation" # meta
"reservationname=" # meta
"skip"
@@ -4364,6 +4366,8 @@
license?(s)) __slurm_compreply_list "$(__slurm_licenses)" ;;
node?(s)) __slurm_compreply_list "$(__slurm_nodes)" "ALL" "true" ;;
partition?(s)) __slurm_compreply "$(__slurm_partitions)" ;;
+ allowedpartition?(s)) __slurm_compreply "$(__slurm_partitions)" ;;
+ qos?(s)) __slurm_compreply "$(__slurm_qos)" ;;
reservationname?(s)) __slurm_compreply "$(__slurm_reservations)" ;;
user?(s)) __slurm_compreply_list "$(__slurm_users)" ;;
*)
diff --git a/doc/html/accounting.shtml b/doc/html/accounting.shtml
index 5191525..ae3eb45 100644
--- a/doc/html/accounting.shtml
+++ b/doc/html/accounting.shtml
@@ -128,7 +128,7 @@
The cached data is written by <i>slurmctld</i> to local storage upon shutdown
and recovered at startup.
If SlurmDBD is not available when <i>slurmctld</i> starts, a cache of
-valid bank accounts, user limits, etc. based upon their state when the
+valid accounts, user limits, etc. based upon their state when the
daemons were last communicating will be used.
Note that SlurmDBD must be responding when <i>slurmctld</i> is first started
since no cache of this critical data will be available.
@@ -878,7 +878,7 @@
account name)</li>
<li><b>Name=</b> Name of account. Note the name must be unique and can not
-represent different bank accounts at different points in the account
+represent different accounts at different points in the account
hierarchy</li>
<li><b>Organization=</b>Organization of the account. (Default is
diff --git a/doc/html/contributor.shtml b/doc/html/contributor.shtml
index 6552bc4..92c66b9 100644
--- a/doc/html/contributor.shtml
+++ b/doc/html/contributor.shtml
@@ -1,28 +1,3 @@
<!--#include virtual="header.txt"-->
-
-<h1>Contributor Agreement</h1>
-
-<p>In order to help ensure the legal integrity of Slurm and provide protection
-for all contributors and users a contributor agreement is required for all
-substantial contributions.
-Each individual making contributions should complete the<br>
-<a href="Slurm_Individual.pdf">Slurm Workload Manager Individual Contributor License Agreement</a>.<br>
-In addition the individual's organization should complete the<br>
-<a href="Slurm_Entity.pdf">Slurm Workload Manager Entity Contributor License Agreement</a>.</p>
-
-<p>Signed forms or questions should be directed to
-<a href="mailto:cla@schedmd.com">cla@schedmd.com</a> or posted to<br><br>
-SchedMD LLC<br>
-905 North 100 East<br>
-Lehi, UT 84043<br>
-USA</p>
-
-<p>Contacting SchedMD before starting any substantial development effort is
-recommended to coordinate development and avoid duplication of effort.
-SchedMD can provide design review, code review, integration and long-term
-support at reasonable cost. SchedMD can also perform custom development work
-to satisfy your requirements.</p>
-
-<p style="text-align:center;">Last modified 30 December 2022</p>
-
+<!--#include markdown="../../CONTRIBUTING.md"-->
<!--#include virtual="footer.txt"-->
diff --git a/doc/html/documentation.shtml b/doc/html/documentation.shtml
index 4b9979b..75f8a11 100644
--- a/doc/html/documentation.shtml
+++ b/doc/html/documentation.shtml
@@ -114,7 +114,7 @@
<h2 id="developers">Slurm Developers
<a class="slurm_link" href="#developers"></a></h2>
<ul>
-<li><a href="contributor.html">Contributor Agreement</a></li>
+<li><a href="contributor.html">Contributor Guide</a></li>
<li><a href="programmer_guide.html">Programmer Guide</a></li>
<li><a href="api.html">Application Programmer Interface (API) Guide</a></li>
<li><a href="add.html">Adding Files or Plugins to Slurm</a></li>
diff --git a/doc/html/overview.shtml b/doc/html/overview.shtml
index 51d0b62..5827244 100644
--- a/doc/html/overview.shtml
+++ b/doc/html/overview.shtml
@@ -18,7 +18,7 @@
<a href="gang_scheduling.html">gang scheduling</a> (time sharing for
parallel jobs), backfill scheduling,
<a href="topology.html">topology optimized resource selection</a>,
-<a href="resource_limits.html">resource limits</a> by user or bank account,
+<a href="resource_limits.html">resource limits</a> by user or account,
and sophisticated <a href="priority_multifactor.html"> multifactor job
prioritization</a> algorithms.
@@ -49,7 +49,7 @@
There is an administrative tool <b>scontrol</b> available to monitor
and/or modify configuration and state information on the cluster.
The administrative tool used to manage the database is <b>sacctmgr</b>.
-It can be used to identify the clusters, valid users, valid bank accounts, etc.
+It can be used to identify the clusters, valid users, valid accounts, etc.
APIs are available for all functions.</p>
<div class="figure">
diff --git a/doc/html/reservations.shtml b/doc/html/reservations.shtml
index 2067e07..61fa67f 100644
--- a/doc/html/reservations.shtml
+++ b/doc/html/reservations.shtml
@@ -3,7 +3,8 @@
<h1>Advanced Resource Reservation Guide</h1>
<p>Slurm has the ability to reserve resources for jobs
-being executed by select users and/or select bank accounts.
+being executed by select users and/or QOS and/or partitions and/or select
+accounts.
A resource reservation identifies the resources in that reservation
and a time period during which the reservation is available.
The resources which can be reserved include cores, nodes, licenses and/or
@@ -173,8 +174,9 @@
Nodes=tux8,tux9 CoreCnt=2,4 User=brenda
</pre>
-<p>Reservations can not only be created for the use of specific accounts and
-users, but specific accounts and/or users can be prevented from using them.
+<p>Reservations can not only be created for the use of specific accounts and/or
+QOS and/or partitions and/or users, but specific accounts and/or QOS and/or
+partitions and/or users can be prevented from using them.
In the following example, a reservation is created for account "foo", but user
"alan" is prevented from using the reservation even when using the account
"foo".</p>
@@ -505,8 +507,8 @@
</h2>
<p>Jobs executed within a reservation are accounted for using the appropriate
-user and bank account. If resources within a reservation are not used, those
-resources will be accounted for as being used by all users or bank accounts
+user and account. If resources within a reservation are not used, those
+resources will be accounted for as being used by all users or accounts
associated with the reservation on an equal basis (e.g. if two users are
eligible to use a reservation and neither does, each user will be reported
to have used half of the reserved resources).</p>
@@ -529,6 +531,6 @@
This will prevent the initiation of some jobs which would complete execution
before a reservation given fewer jobs to time-slice with.</p>
-<p style="text-align: center;">Last modified 02 August 2024</p>
+<p style="text-align: center;">Last modified 13 June 2025</p>
<!--#include virtual="footer.txt"-->
diff --git a/doc/html/resource_limits.shtml b/doc/html/resource_limits.shtml
index 11010ee..906dd78 100644
--- a/doc/html/resource_limits.shtml
+++ b/doc/html/resource_limits.shtml
@@ -68,7 +68,7 @@
<p>Both accounting and scheduling policies are configured based upon
an <i>association</i>. An <i>association</i> is a 4-tuple consisting
-of the cluster name, bank account, user and (optionally) the Slurm
+of the cluster name, account, user and (optionally) the Slurm
partition.
In order to enforce scheduling policy, set the value of
<b>AccountingStorageEnforce</b>.
@@ -117,7 +117,7 @@
<h2 id="tools">Tools<a class="slurm_link" href="#tools"></a></h2>
<p>The tool used to manage accounting policy is <i>sacctmgr</i>.
-It can be used to create and delete cluster, user, bank account,
+It can be used to create and delete cluster, user, account,
and partition records plus their combined <i>association</i> record.
See <i>man sacctmgr</i> for details on this tools and examples of
its use.</p>
@@ -547,7 +547,7 @@
unique partitions, users will be limited to 20 CPUs for the QOS
rather than being allowed 20 CPUs for each partition.</p>
-<p>Fair-share scheduling is based upon the hierarchical bank account
+<p>Fair-share scheduling is based upon the hierarchical account
data maintained in the Slurm database. More information can be found
in the <a href="priority_multifactor.html">priority/multifactor</a>
plugin description.</p>
diff --git a/doc/html/slurm.shtml b/doc/html/slurm.shtml
index 3cd15a7..5e99d9e 100644
--- a/doc/html/slurm.shtml
+++ b/doc/html/slurm.shtml
@@ -21,7 +21,7 @@
world-class computer centers and rely upon a
<a href="http://www.mysql.com/">MySQL</a> database for archiving
<a href="accounting.html">accounting</a> records, managing
-<a href="resource_limits.html">resource limits</a> by user or bank account,
+<a href="resource_limits.html">resource limits</a> by user or account,
or supporting sophisticated
<a href="priority_multifactor.html">job prioritization</a> algorithms.</p>
diff --git a/doc/man/man1/sacctmgr.1 b/doc/man/man1/sacctmgr.1
index 81657eb..162b466 100644
--- a/doc/man/man1/sacctmgr.1
+++ b/doc/man/man1/sacctmgr.1
@@ -19,7 +19,7 @@
\fIcluster\fR is the name of a Slurm managed cluster as specified by
the \fIClusterName\fR parameter in the \fIslurm.conf\fR configuration file.
\fIpartition\fR is the name of a Slurm partition on that cluster.
-\fIaccount\fR is the bank account for a job.
+\fIaccount\fR is the account for a job.
The intended mode of operation is to initiate the \fBsacctmgr\fR command,
add, delete, modify, and/or list \fIassociation\fR records then
commit the changes and exit.
@@ -236,7 +236,7 @@
.TP
\fBaccount\fR
-A bank account, typically specified at job submit time using the
+An account, typically specified at job submit time using the
\fB\-\-account=\fR option.
These may be arranged in a hierarchical fashion, for example
accounts 'chemistry' and 'physics' may be children of
@@ -421,22 +421,16 @@
\fBGrpTRES\fR=TRES=<\fImax_TRES\fR>[,TRES=<\fImax_TRES\fR>,...]
Maximum number of TRES able to be allocated by running jobs in aggregate for
this association and its children.
-To clear an existing value, set a new value of \-1 for each TRES type/name.
-\fBTRES\fR can be one of the Slurm defaults (i.e. \fIcpu\fR, \fImem\fR,
-\fInode\fR, etc...), or any defined generic resource. You can see the list
-of available resources by running \fBsacctmgr show tres\fR.
+Refer to the \fBTRES information\fR section below for further details.
.IP
.TP
\fBGrpTRESMins\fR=TRES=<\fIminutes\fR>[,TRES=<\fIminutes\fR>,...]
Maximum number of TRES minutes that can possibly be used by past, present, and
future jobs in this association and its children.
-To clear an existing value, set a new value of \-1 for each TRES type/name.
-\fBTRES\fR can be one of the Slurm defaults (i.e. \fIcpu\fR, \fImem\fR,
-\fInode\fR, etc...), or any defined generic resource. You can see the list
-of available resources by running \fBsacctmgr show tres\fR.
+Refer to the \fBTRES information\fR section below for further details.
\fBNOTE\fR: This limit is not enforced if set on the root
association of a cluster. So even though it may appear in sacctmgr
@@ -456,11 +450,8 @@
association and its children. This takes into consideration time limit of
running jobs and consumes it. If the limit is reached no new jobs are started
until other jobs finish to allow time to free up.
-To clear an existing value, set a new value of \-1 for each TRES type/name.
-\fBTRES\fR can be one of the Slurm defaults (i.e. \fIcpu\fR, \fImem\fR,
-\fInode\fR, etc...), or any defined generic resource. You can see the list
-of available resources by running \fBsacctmgr show tres\fR.
+Refer to the \fBTRES information\fR section below for further details.
.IP
.TP
@@ -516,7 +507,9 @@
Maximum number of TRES each job can use in this association.
This is overridden if set directly on a user.
Default is the cluster's limit.
-To clear an existing value, set a new value of \-1 for each TRES type/name.
+
+Refer to the \fBTRES information\fR section below for further details.
+.IP
.IP \fBMaxTRESMins\fR=TRES=<\fIminutes\fR>[,TRES=<\fIminutes\fR>,...]
.PD 0
@@ -527,16 +520,8 @@
Maximum number of TRES minutes each job can use in this association.
This is overridden if set directly on a user.
Default is the cluster's limit.
-To clear an existing value, set a new value of \-1 for each TRES type/name.
-\fBTRES\fR can be one of the Slurm defaults (i.e. \fIcpu\fR, \fImem\fR,
-\fInode\fR, etc...), or any defined generic resource. You can see the list
-of available resources by running \fBsacctmgr show tres\fR.
-.IP
-
-\fBTRES\fR can be one of the Slurm defaults (i.e. \fIcpu\fR, \fImem\fR,
-\fInode\fR, etc...), or any defined generic resource. You can see the list
-of available resources by running \fBsacctmgr show tres\fR.
+Refer to the \fBTRES information\fR section below for further details.
.IP
.IP \fBMaxTRESPN\fR=TRES=<\fImax_TRES\fR>[,TRES=<\fImax_TRES\fR>,...]
@@ -546,11 +531,8 @@
Maximum number of TRES each node in a job allocation can use in this association.
This is overridden if set directly on a user.
Default is the cluster's limit.
-To clear an existing value, set a new value of \-1 for each TRES type/name.
-\fBTRES\fR can be one of the Slurm defaults (i.e. \fIcpu\fR, \fImem\fR,
-etc...), or any defined generic resource. You can see the list
-of available resources by running \fBsacctmgr show tres\fR.
+Refer to the \fBTRES information\fR section below for further details.
.IP
.IP \fBMaxWall\fR=<\fImax_wall\fR>
@@ -648,8 +630,8 @@
.TP
\fBName\fR=<\fIname\fR>
-The name of a bank account.
-Note the name must be unique and can not be represent different bank
+The name of an account.
+Note the name must be unique and can not be represent different
accounts at different points in the account hierarchy.
.IP
@@ -708,7 +690,7 @@
.TP
\fBAccount\fR
-The name of a bank account.
+The name of an account.
.IP
.TP
@@ -833,7 +815,7 @@
.TP
\fBAccount\fR
-The name of a bank account in the association.
+The name of an account in the association.
.IP
.TP
@@ -1875,22 +1857,16 @@
\fBGrpTRES\fR
Maximum number of TRES able to be allocated by running jobs in aggregate for
this QOS.
-To clear an existing value, set a new value of \-1 for each TRES type/name.
-\fBTRES\fR can be one of the Slurm defaults (i.e. \fIcpu\fR, \fImem\fR,
-\fInode\fR, etc...), or any defined generic resource. You can see the list
-of available resources by running \fBsacctmgr show tres\fR.
+Refer to the \fBTRES information\fR section below for further details.
.IP
.TP
\fBGrpTRESMins\fR
Maximum number of TRES minutes that can possibly be used by past, present, and
future jobs with this QOS.
-To clear an existing value, set a new value of \-1 for each TRES type/name.
-\fBTRES\fR can be one of the Slurm defaults (i.e. \fIcpu\fR, \fImem\fR,
-\fInode\fR, etc...), or any defined generic resource. You can see the list
-of available resources by running \fBsacctmgr show tres\fR.
+Refer to the \fBTRES information\fR section below for further details.
\fBNOTE\fR: This limit only applies when using the Priority Multifactor
plugin. The time is decayed using the value of PriorityDecayHalfLife
@@ -1906,11 +1882,8 @@
QOS. This takes into consideration time limit of running jobs and consumes it.
If the limit is reached no new jobs are started until other jobs finish to allow
time to free up.
-To clear an existing value, set a new value of \-1 for each TRES type/name.
-\fBTRES\fR can be one of the Slurm defaults (i.e. \fIcpu\fR, \fImem\fR,
-\fInode\fR, etc...), or any defined generic resource. You can see the list
-of available resources by running \fBsacctmgr show tres\fR.
+Refer to the \fBTRES information\fR section below for further details.
.IP
.TP
@@ -2001,10 +1974,7 @@
.PD
Maximum number of TRES each job can use.
-\fBTRES\fR can be one of the Slurm defaults (i.e. \fIcpu\fR, \fImem\fR,
-\fInode\fR, etc...), or any defined generic resource. You can see the list
-of available resources by running \fBsacctmgr show tres\fR.
-To clear an existing value, set a new value of \-1 for each TRES type/name.
+Refer to the \fBTRES information\fR section below for further details.
.IP
.IP \fBMaxTRESMins\fR
@@ -2015,10 +1985,7 @@
.PD
Maximum number of TRES minutes each job can use.
-\fBTRES\fR can be one of the Slurm defaults (i.e. \fIcpu\fR, \fImem\fR,
-\fInode\fR, etc...), or any defined generic resource. You can see the list
-of available resources by running \fBsacctmgr show tres\fR.
-To clear an existing value, set a new value of \-1 for each TRES type/name.
+Refer to the \fBTRES information\fR section below for further details.
.IP
.IP \fBMaxTRESPA\fR
@@ -2027,10 +1994,7 @@
.PD
Maximum number of TRES each account can use.
-\fBTRES\fR can be one of the Slurm defaults (i.e. \fIcpu\fR, \fImem\fR,
-\fInode\fR, etc...), or any defined generic resource. You can see the list
-of available resources by running \fBsacctmgr show tres\fR.
-To clear an existing value, set a new value of \-1 for each TRES type/name.
+Refer to the \fBTRES information\fR section below for further details.
.IP
.IP \fBMaxTRESPN\fR
@@ -2039,10 +2003,7 @@
.PD
Maximum number of TRES each node in a job allocation can use.
-\fBTRES\fR can be one of the Slurm defaults (i.e. \fIcpu\fR, \fImem\fR,
-\fInode\fR, etc...), or any defined generic resource. You can see the list
-of available resources by running \fBsacctmgr show tres\fR.
-To clear an existing value, set a new value of \-1 for each TRES type/name.
+Refer to the \fBTRES information\fR section below for further details.
.IP
.IP \fBMaxTRESPU\fR
@@ -2051,10 +2012,7 @@
.PD
Maximum number of TRES each user can use.
-\fBTRES\fR can be one of the Slurm defaults (i.e. \fIcpu\fR, \fImem\fR,
-\fInode\fR, etc...), or any defined generic resource. You can see the list
-of available resources by running \fBsacctmgr show tres\fR.
-To clear an existing value, set a new value of \-1 for each TRES type/name.
+Refer to the \fBTRES information\fR section below for further details.
.IP
.IP \fBMaxTRESRunMinsPA\fR
@@ -2065,10 +2023,7 @@
consideration the time limit of running jobs. If the limit is reached, no new
jobs are started until other jobs finish to allow time to free up.
-\fBTRES\fR can be one of the Slurm defaults (i.e. \fIcpu\fR, \fImem\fR,
-\fInode\fR, etc...), or any defined generic resource. You can see the list
-of available resources by running \fBsacctmgr show tres\fR.
-To clear an existing value, set a new value of \-1 for each TRES type/name.
+Refer to the \fBTRES information\fR section below for further details.
.IP
.IP \fBMaxTRESRunMinsPU\fR
@@ -2079,10 +2034,7 @@
consideration the time limit of running jobs. If the limit is reached, no new
jobs are started until other jobs finish to allow time to free up.
-\fBTRES\fR can be one of the Slurm defaults (i.e. \fIcpu\fR, \fImem\fR,
-\fInode\fR, etc...), or any defined generic resource. You can see the list
-of available resources by running \fBsacctmgr show tres\fR.
-To clear an existing value, set a new value of \-1 for each TRES type/name.
+Refer to the \fBTRES information\fR section below for further details.
.IP
.IP \fBMaxWall\fR
@@ -2108,10 +2060,7 @@
Minimum number of TRES each job running under this QOS must request.
Otherwise the job will pend until modified.
-\fBTRES\fR can be one of the Slurm defaults (i.e. \fIcpu\fR, \fImem\fR,
-\fInode\fR, etc...), or any defined generic resource. You can see the list
-of available resources by running \fBsacctmgr show tres\fR.
-To clear an existing value, set a new value of \-1 for each TRES type/name.
+Refer to the \fBTRES information\fR section below for further details.
.IP
.TP
@@ -3016,7 +2965,7 @@
.TP
\fBDefaultAccount\fR=<\fIaccount\fR>
-Identify the default bank account name to be used for a job if none is
+Identify the default account name to be used for a job if none is
specified at submission time.
.IP
@@ -3191,21 +3140,34 @@
.IP
.SH "TRES information"
-Trackable RESources (TRES) are used in many QOS or Association limits.
-When setting the limits they are comma\-separated list. Each TRES has
-a different limit, i.e. GrpTRESMins=cpu=10,mem=20 would make 2
-different limits 1 for 10 cpu minutes and 1 for 20 MB memory minutes.
-This is the case for each limit that deals with TRES. To remove the
-limit \-1 is used i.e. GrpTRESMins=cpu=\-1 would remove only the cpu
-TRES limit.
+Trackable RESources (TRES) are used in many QOS or Association limits
+(GrpTRES* / MaxTRES*). When setting limits, one or more comma\-separated
+"TRES=value" assignments can be specified.
-\fBNOTE\fR: When dealing with Memory as a TRES all limits are in MB.
+Each TRES can be one of the Slurm defaults (e.g., \fIcpu\fR, \fImem\fR,
+\fInode\fR), or any defined generic resource. You can see the list of available
+resources by running \fBsacctmgr show tres\fR.
+
+Each TRES has a different limit, e.g., "GrpTRES=cpu=16,mem=32G" would
+establish 2 different limits: one for 16 CPUs and another for 32 GB of memory.
+
+Setting a new value for a TRES type does not affect any other TRES types that
+were previously specified. For example, on the same entity as above, a
+subsequent assignment of "GrpTRES=gres/gpu=2" will result in
+"GrpTRES=cpu=16,mem=32G,gres/gpu=2" applying to that entity.
+
+To remove a limit, assign \-1 to the TRES type whose limit should be removed,
+e.g., "GrpTRESMins=cpu=\-1" would remove only the cpu TRES limit, resulting in
+"GrpTRES=mem=32G,gres/gpu=2" on that entity.
+
+\fBNOTE\fR: When dealing with Memory as a TRES the default units are MB. A
+suffix of G, T, or P can be added to specify a limit in larger units.
\fBNOTE\fR: The Billing TRES is calculated from a partition's
TRESBillingWeights. It is temporarily calculated during scheduling for each
-partition to enforce billing TRES limits. The final Billing TRES is calculated
-after the job has been allocated resources. The final number can be seen in
-\fBscontrol show jobs\fP and \fBsacct\fP output.
+partition to enforce billing TRES limits. The final Billing TRES value is
+calculated after the job has been allocated resources and can be seen in
+\fBscontrol show jobs\fR and \fBsacct\fR output.
.SH "GLOBAL FORMAT OPTION"
When using the format option for listing various fields you can put a
diff --git a/doc/man/man1/scontrol.1 b/doc/man/man1/scontrol.1
index a3d463f..60b9ba3 100644
--- a/doc/man/man1/scontrol.1
+++ b/doc/man/man1/scontrol.1
@@ -2448,7 +2448,8 @@
List of accounts permitted to use the reserved nodes, for example
"Accounts=physcode1,physcode2".
A user in any of the specified accounts or subaccounts may use the reserved
-nodes. A new reservation must specify Users or Groups and/or Accounts.
+nodes. A new reservation must specify Users or Groups and/or QOS and/or
+AllowedPartitions and/or Accounts.
If both Users/Groups and Accounts are specified, a job must match both in order
to use the reservation.
Accounts can also be denied access to reservations by preceding all of the
@@ -2465,6 +2466,28 @@
.IP
.TP
+\fBAllowedPartitions\fR=<\fIpartition list\fR>
+List of Partitions permitted to use the reserved resources, for example
+"Partition=debug,low".
+A user using any of the specified Partition may use the reserved
+resources. A new reservation must specify Users or Groups and/or QOS and/or
+AllowedPartitions and/or Accounts.
+If Users/Groups and/or Accounts and/or Partitions and/or QOS are specified, a
+job must match all in order to use the reservation.
+Partition can also be denied access to reservations by preceding all of the
+Partition names with '\-'. Alternately precede the equal sign with '\-'.
+For example, "Partition=\-debug,\-low" or "Partition\-=debug,low"
+will permit any Partition except debug and low to use the reservation.
+You can add or remove individual Partition from an existing reservation by
+using the update command and adding a '+' or '\-' sign before the '=' sign.
+If Partition are denied access to a reservation (Partition name preceded
+by a '\-'), then all other Partition are implicitly allowed to use the
+reservation and it is not possible to also explicitly specify allowed Partition.
+Root and the SlurmUser are given access to all reservations, regardless of the
+Partition set here.
+.IP
+
+.TP
\fBBurstBuffer\fR=<\fIbuffer_spec\fR>[,<\fIbuffer_spec\fR>,...]
Specification of burst buffer resources which are to be reserved.
"buffer_spec" consists of four elements:
@@ -2599,6 +2622,9 @@
partition. Jobs explicitly requesting a different partition will still be
allowed to use this reservation as long as there are enough overlapping nodes
between both partitions to allocate the job.
+\fBNOTE\fR: If \fIAllowedPartitions\fR is used and \fIPartitionName\fR is not
+given, the first partition in \fIAllowedPartitions\fR is what is used as
+\fIPartitionName\fR.
.IP
.TP
@@ -2796,9 +2822,10 @@
\fBGroups\fR=<\fIgroup list\fR>
List of groups permitted to use the reserved nodes, for example
"Group=bio,chem".
-A new reservation must specify Users or Groups and/or Accounts.
-If both Users/Groups and Accounts are specified, a job must match both in order
-to use the reservation.
+A new reservation must specify Users or Groups and/or QOS and/or
+AllowedPartitions and/or Accounts.
+If Users/Groups and/or Accounts and/or Partitions and/or QOS are specified, a
+job must match all in order to use the reservation.
Unlike users groups do not allow denied access to reservations.
You can add or remove individual groups from an existing reservation by
using the update command and adding a '+' or '\-' sign before the '=' sign.
@@ -2816,12 +2843,35 @@
.IP
.TP
+\fBQOS\fR=<\fIqos list\fR>
+List of QOS permitted to use the reserved resources, for example
+"QOS=normal,standby".
+A user using any of the specified QOS may use the reserved
+resources. A new reservation must specify Users or Groups and/or QOS and/or
+AllowedPartitions and/or Accounts.
+If Users/Groups and/or Accounts and/or Partitions and/or QOS are specified, a
+job must match all in order to use the reservation.
+QOS can also be denied access to reservations by preceding all of the
+QOS names with '\-'. Alternately precede the equal sign with '\-'.
+For example, "QOS=\-normal,\-standby" or "QOS\-=normal,standby"
+will permit any QOS except normal and standby to use the reservation.
+You can add or remove individual QOS from an existing reservation by
+using the update command and adding a '+' or '\-' sign before the '=' sign.
+If QOS are denied access to a reservation (QOS name preceded by a '\-'),
+then all other QOS are implicitly allowed to use the reservation and it is
+not possible to also explicitly specify allowed QOS.
+Root and the SlurmUser are given access to all reservations, regardless of the
+QOS set here.
+.IP
+
+.TP
\fBUsers\fR=<\fIuser list\fR>
List of users permitted to use the reserved nodes, for example
"User=jones1,smith2".
-A new reservation must specify Users or Groups and/or Accounts.
-If both Users/Groups and Accounts are specified, a job must match both in order
-to use the reservation.
+A new reservation must specify Users or Groups and/or QOS and/or
+AllowedPartitions and/or Accounts.
+If Users/Groups and/or Accounts and/or Partitions and/or QOS are specified, a
+job must match all in order to use the reservation.
Users can also be denied access to reservations by preceding all of the
user names with '\-'. Alternately precede the equal sign with '\-'.
For example, "User=\-jones1,\-smith2" or "User\-=jones1,smith2"
diff --git a/doc/man/man1/slurm.1 b/doc/man/man1/slurm.1
index 4d5a26d..0f1e2cd 100644
--- a/doc/man/man1/slurm.1
+++ b/doc/man/man1/slurm.1
@@ -16,7 +16,7 @@
pending work.
Optional plugins can be used for accounting, advanced reservation,
gang scheduling (time sharing for parallel jobs), backfill scheduling,
-resource limits by user or bank account,
+resource limits by user or account,
and sophisticated multifactor job prioritization algorithms.
Slurm has a centralized manager, \fBslurmctld\fR, to monitor resources and
diff --git a/doc/man/man5/slurm.conf.5 b/doc/man/man5/slurm.conf.5
index 6bb69d2..0d69cb5 100644
--- a/doc/man/man5/slurm.conf.5
+++ b/doc/man/man5/slurm.conf.5
@@ -3058,7 +3058,7 @@
.TP
\fBPriorityDecayHalfLife\fR
This controls how long prior resource use is considered in determining
-how over\- or under\-serviced an association is (user, bank account and
+how over\- or under\-serviced an association is (user, account and
cluster) in determining job priority.
The record of usage will be decayed over time, with half of the original value
cleared at age \fBPriorityDecayHalfLife\fR.
diff --git a/slurm/slurm.h b/slurm/slurm.h
index 5b63a6a..3a132b1 100644
--- a/slurm/slurm.h
+++ b/slurm/slurm.h
@@ -349,9 +349,9 @@
WAIT_HELD, /* job is held by administrator */
WAIT_TIME, /* job waiting for specific begin time */
WAIT_LICENSES, /* job is waiting for licenses */
- WAIT_ASSOC_JOB_LIMIT, /* user/bank job limit reached */
- WAIT_ASSOC_RESOURCE_LIMIT,/* user/bank resource limit reached */
- WAIT_ASSOC_TIME_LIMIT, /* user/bank time limit reached */
+ WAIT_ASSOC_JOB_LIMIT, /* user/account job limit reached */
+ WAIT_ASSOC_RESOURCE_LIMIT,/* user/account resource limit reached */
+ WAIT_ASSOC_TIME_LIMIT, /* user/account time limit reached */
WAIT_RESERVATION, /* reservation not available */
WAIT_NODE_NOT_AVAIL, /* required node is DOWN or DRAINED */
WAIT_HELD_USER, /* job is held by user */
@@ -1940,6 +1940,8 @@
char *std_in; /* pathname of job's stdin file */
char *std_out; /* pathname of job's stdout file */
uint16_t segment_size; /* requested segment size */
+ char *submit_line; /* full submit line of the slurm command used to
+ * submit this job */
time_t submit_time; /* time of job submission */
time_t suspend_time; /* time job last suspended or resumed */
char *system_comment; /* slurmctld's arbitrary comment */
@@ -2728,6 +2730,7 @@
typedef struct reserve_info {
char *accounts; /* names of accounts permitted to use */
+ char *allowed_parts; /* names of partitions permitted to use */
char *burst_buffer; /* burst buffer resources to be included */
char *comment; /* arbitrary comment */
uint32_t core_cnt; /* count of cores required */
@@ -2753,6 +2756,7 @@
* minutes this reservation will sit idle
* until it is revoked.
*/
+ char *qos; /* names of QOS permitted to use */
time_t start_time; /* start time of reservation */
char *tres_str; /* list of TRES's used by reservation */
char *users; /* names of users permitted to use */
@@ -2766,6 +2770,7 @@
typedef struct resv_desc_msg {
char *accounts; /* names of accounts permitted to use */
+ char *allowed_parts; /* names of partitions permitted to use */
char *burst_buffer; /* burst buffer resources to be included */
char *comment; /* arbitrary comment */
uint32_t core_cnt; /* Count of cores required */
@@ -2788,6 +2793,7 @@
* minutes this reservation will sit idle
* until it is revoked.
*/
+ char *qos; /* names of QOS permitted to use */
time_t start_time; /* start time of reservation */
time_t time_force; /* Actual start of reservation */
char *tres_str; /* list of TRES's used by reservation */
diff --git a/src/api/reservation_info.c b/src/api/reservation_info.c
index a1e33d9..10da3ae 100644
--- a/src/api/reservation_info.c
+++ b/src/api/reservation_info.c
@@ -148,6 +148,12 @@
xstrcat(out, line_end);
/****** Line ******/
+ xstrfmtcat(out,
+ "AllowedPartitions=%s QOS=%s",
+ resv_ptr->allowed_parts, resv_ptr->qos);
+ xstrcat(out, line_end);
+
+ /****** Line ******/
if ((resv_ptr->start_time <= now) && (resv_ptr->end_time >= now))
state = "ACTIVE";
xstrfmtcat(out,
diff --git a/src/common/data.c b/src/common/data.c
index ef86168..c822eaa 100644
--- a/src/common/data.c
+++ b/src/common/data.c
@@ -1459,40 +1459,38 @@
return count;
}
-static int _convert_data_string(data_t *data)
+static void _convert_data_string(data_t *data)
{
_check_magic(data);
switch (data->type) {
case TYPE_STRING_INLINE:
case TYPE_STRING_PTR:
- return SLURM_SUCCESS;
+ break;
case TYPE_BOOL:
data_set_string(data, (data->data.bool_u ? "true" : "false"));
- return SLURM_SUCCESS;
+ break;
case TYPE_NULL:
data_set_string(data, "");
- return SLURM_SUCCESS;
+ break;
case TYPE_FLOAT:
{
char *str = xstrdup_printf("%lf", data->data.float_u);
data_set_string_own(data, str);
- return SLURM_SUCCESS;
+ break;
}
case TYPE_INT_64:
{
char *str = xstrdup_printf("%"PRId64, data->data.int_u);
data_set_string_own(data, str);
- return SLURM_SUCCESS;
+ break;
}
default:
- return ESLURM_DATA_CONV_FAILED;
+ break;
}
-
- return ESLURM_DATA_CONV_FAILED;
}
-static int _convert_data_force_bool(data_t *data)
+static void _convert_data_force_bool(data_t *data)
{
_check_magic(data);
@@ -1504,23 +1502,21 @@
case TYPE_STRING_PTR:
/* non-empty string but not recognized format */
data_set_bool(data, true);
- return SLURM_SUCCESS;
+ break;
case TYPE_BOOL:
- return SLURM_SUCCESS;
+ break;
case TYPE_NULL:
data_set_bool(data, false);
- return SLURM_SUCCESS;
+ break;
case TYPE_FLOAT:
data_set_bool(data, data->data.float_u != 0);
- return SLURM_SUCCESS;
+ break;
case TYPE_INT_64:
data_set_bool(data, data->data.int_u != 0);
- return SLURM_SUCCESS;
+ break;
default:
- return ESLURM_DATA_CONV_FAILED;
+ break;
}
-
- return ESLURM_DATA_CONV_FAILED;
}
static int _convert_data_null(data_t *data)
@@ -1890,37 +1886,29 @@
switch (match) {
case DATA_TYPE_STRING:
- return _convert_data_string(data) ? DATA_TYPE_NONE :
- DATA_TYPE_STRING;
+ _convert_data_string(data);
+ break;
case DATA_TYPE_BOOL:
- return _convert_data_force_bool(data) ? DATA_TYPE_NONE :
- DATA_TYPE_BOOL;
+ _convert_data_force_bool(data);
+ break;
case DATA_TYPE_INT_64:
- return _convert_data_int(data, true) ? DATA_TYPE_NONE :
- DATA_TYPE_INT_64;
+ (void) _convert_data_int(data, true);
+ break;
case DATA_TYPE_FLOAT:
- return _convert_data_float(data) ? DATA_TYPE_NONE :
- DATA_TYPE_FLOAT;
+ (void) _convert_data_float(data);
+ break;
case DATA_TYPE_NULL:
- return _convert_data_null(data) ? DATA_TYPE_NONE :
- DATA_TYPE_NULL;
+ (void) _convert_data_null(data);
+ break;
case DATA_TYPE_NONE:
- if (!_convert_data_null(data))
- return DATA_TYPE_NULL;
+ /* If a conversion succeeds skip calling the others */
+ if (!_convert_data_null(data) ||
+ !_convert_data_int(data, false) ||
+ !_convert_data_float(data) ||
+ !_convert_data_int(data, true) || !_convert_data_bool(data))
+ ; /* blank on purpose */
- if (!_convert_data_int(data, false))
- return DATA_TYPE_INT_64;
-
- if (!_convert_data_float(data))
- return DATA_TYPE_FLOAT;
-
- if (!_convert_data_int(data, true))
- return DATA_TYPE_INT_64;
-
- if (!_convert_data_bool(data))
- return DATA_TYPE_BOOL;
-
- return DATA_TYPE_NONE;
+ break;
case DATA_TYPE_DICT:
if (data->type == TYPE_DICT)
return DATA_TYPE_DICT;
@@ -1929,7 +1917,7 @@
return DATA_TYPE_DICT;
/* data_parser should be used for this conversion instead. */
- return DATA_TYPE_NONE;
+ break;
case DATA_TYPE_LIST:
if (data->type == TYPE_LIST)
return DATA_TYPE_LIST;
@@ -1938,12 +1926,14 @@
return DATA_TYPE_LIST;
/* data_parser should be used for this conversion instead. */
- return DATA_TYPE_NONE;
+ break;
case DATA_TYPE_MAX:
fatal_abort("%s: unexpected data type", __func__);
+ default:
+ fatal_abort("%s: invalid conversion requested", __func__);
}
- fatal_abort("%s: invalid conversion requested", __func__);
+ return data_get_type(data);
}
static data_for_each_cmd_t _convert_list_entry(data_t *data, void *arg)
diff --git a/src/common/data.h b/src/common/data.h
index 2377686..eb4cfb6 100644
--- a/src/common/data.h
+++ b/src/common/data.h
@@ -282,7 +282,7 @@
* IN data structure to try to type and convert
* IN match try to detect this type only
* or DATA_TYPE_NONE to give best guess as to data type
- * RET new data type or DATA_TYPE_NONE on no change
+ * RET (new or unchanged) data type of data
*/
extern data_type_t data_convert_type(data_t *data, const data_type_t match);
diff --git a/src/common/openapi.c b/src/common/openapi.c
index 0aa2915..9a66bc3 100644
--- a/src/common/openapi.c
+++ b/src/common/openapi.c
@@ -257,16 +257,17 @@
if (!obj)
return;
- xfree(x->command);
- xfree(x->plugin.type);
- xfree(x->plugin.name);
- xfree(x->plugin.data_parser);
xfree(x->client.source);
+ xfree(x->command);
+ xfree(x->plugin.accounting_storage);
+ xfree(x->plugin.data_parser);
+ xfree(x->plugin.name);
+ xfree(x->plugin.type);
+ xfree(x->slurm.cluster);
+ xfree(x->slurm.release);
xfree(x->slurm.version.major);
xfree(x->slurm.version.micro);
xfree(x->slurm.version.minor);
- xfree(x->slurm.release);
- xfree(x->slurm.cluster);
xfree(x);
}
diff --git a/src/common/slurm_protocol_defs.c b/src/common/slurm_protocol_defs.c
index 931bbc4..a75faba 100644
--- a/src/common/slurm_protocol_defs.c
+++ b/src/common/slurm_protocol_defs.c
@@ -1859,6 +1859,7 @@
xfree(job->std_err);
xfree(job->std_in);
xfree(job->std_out);
+ xfree(job->submit_line);
xfree(job->system_comment);
xfree(job->tres_alloc_str);
xfree(job->tres_bind);
@@ -1988,6 +1989,10 @@
xfree(msg->burst_buffer);
if (res_free_flags & RESV_FREE_STR_COMMENT)
xfree(msg->comment);
+ if (res_free_flags & RESV_FREE_STR_ALLOWED_PARTS)
+ xfree(msg->allowed_parts);
+ if (res_free_flags & RESV_FREE_STR_QOS)
+ xfree(msg->qos);
if (res_free_flags & RESV_FREE_STR_TRES_LIC)
xfree(msg->licenses);
if (res_free_flags & RESV_FREE_STR_GROUP)
@@ -4397,6 +4402,7 @@
int i;
if (resv) {
xfree(resv->accounts);
+ xfree(resv->allowed_parts);
xfree(resv->burst_buffer);
xfree(resv->comment);
if (resv->core_spec) {
@@ -4413,6 +4419,7 @@
xfree(resv->node_inx);
xfree(resv->node_list);
xfree(resv->partition);
+ xfree(resv->qos);
xfree(resv->tres_str);
xfree(resv->users);
}
@@ -6487,8 +6494,11 @@
if (((resv_msg->users == NULL) || (resv_msg->users[0] == '\0')) &&
((resv_msg->groups == NULL) || (resv_msg->groups[0] == '\0')) &&
+ (!resv_msg->qos || (resv_msg->qos[0] == '\0')) &&
+ (!resv_msg->allowed_parts ||
+ (resv_msg->allowed_parts[0] == '\0')) &&
((resv_msg->accounts == NULL) || (resv_msg->accounts[0] == '\0'))) {
- *err_msg = "Either Users/Groups and/or Accounts must be specified. No reservation created.";
+ *err_msg = "Either Users/Groups, AllowedPartitions, QOS and/or Accounts must be specified. No reservation created.";
return SLURM_ERROR;
} else if (resv_msg->users && resv_msg->groups) {
*err_msg = "Users and Groups are mutually exclusive. You can have one or the other, but not both. No reservation created.";
diff --git a/src/common/slurm_protocol_defs.h b/src/common/slurm_protocol_defs.h
index 1556bb6..fa2999e 100644
--- a/src/common/slurm_protocol_defs.h
+++ b/src/common/slurm_protocol_defs.h
@@ -218,9 +218,9 @@
#define RESV_FREE_STR_USER SLURM_BIT(0)
#define RESV_FREE_STR_ACCT SLURM_BIT(1)
#define RESV_FREE_STR_TRES_BB SLURM_BIT(2)
-/* SLURM_BIT(3) unused */
+#define RESV_FREE_STR_QOS SLURM_BIT(3)
#define RESV_FREE_STR_TRES_LIC SLURM_BIT(4)
-/* SLURM_BIT(5) unused */
+#define RESV_FREE_STR_ALLOWED_PARTS SLURM_BIT(5)
#define RESV_FREE_STR_GROUP SLURM_BIT(6)
#define RESV_FREE_STR_COMMENT SLURM_BIT(7)
#define RESV_FREE_STR_NODES SLURM_BIT(8)
diff --git a/src/common/slurm_protocol_pack.c b/src/common/slurm_protocol_pack.c
index 10310f4..1034731 100644
--- a/src/common/slurm_protocol_pack.c
+++ b/src/common/slurm_protocol_pack.c
@@ -1994,7 +1994,29 @@
{
xassert(msg);
- if (protocol_version >= SLURM_MIN_PROTOCOL_VERSION) {
+ if (protocol_version >= SLURM_25_11_PROTOCOL_VERSION) {
+ packstr(msg->name, buffer);
+ pack_time(msg->start_time, buffer);
+ pack_time(msg->end_time, buffer);
+ pack32(msg->duration, buffer);
+ pack64(msg->flags, buffer);
+ pack32(msg->node_cnt, buffer);
+ pack32(msg->core_cnt, buffer);
+ packstr(msg->node_list, buffer);
+ packstr(msg->features, buffer);
+ packstr(msg->licenses, buffer);
+ pack32(msg->max_start_delay, buffer);
+ packstr(msg->partition, buffer);
+ pack32(msg->purge_comp_time, buffer);
+ packstr(msg->allowed_parts, buffer);
+ packstr(msg->qos, buffer);
+ packstr(msg->users, buffer);
+ packstr(msg->accounts, buffer);
+ packstr(msg->burst_buffer, buffer);
+ packstr(msg->groups, buffer);
+ packstr(msg->comment, buffer);
+ packstr(msg->tres_str, buffer);
+ } else if (protocol_version >= SLURM_MIN_PROTOCOL_VERSION) {
packstr(msg->name, buffer);
pack_time(msg->start_time, buffer);
pack_time(msg->end_time, buffer);
@@ -2031,7 +2053,32 @@
tmp_ptr = xmalloc(sizeof(resv_desc_msg_t));
*msg = tmp_ptr;
- if (protocol_version >= SLURM_MIN_PROTOCOL_VERSION) {
+ if (protocol_version >= SLURM_25_11_PROTOCOL_VERSION) {
+ safe_unpackstr(&tmp_ptr->name, buffer);
+ safe_unpack_time(&tmp_ptr->start_time, buffer);
+ safe_unpack_time(&tmp_ptr->end_time, buffer);
+ safe_unpack32(&tmp_ptr->duration, buffer);
+ safe_unpack64(&tmp_ptr->flags, buffer);
+ safe_unpack32(&tmp_ptr->node_cnt, buffer);
+ safe_unpack32(&tmp_ptr->core_cnt, buffer);
+ safe_unpackstr(&tmp_ptr->node_list, buffer);
+ safe_unpackstr(&tmp_ptr->features, buffer);
+ safe_unpackstr(&tmp_ptr->licenses, buffer);
+
+ safe_unpack32(&tmp_ptr->max_start_delay, buffer);
+
+ safe_unpackstr(&tmp_ptr->partition, buffer);
+ safe_unpack32(&tmp_ptr->purge_comp_time, buffer);
+
+ safe_unpackstr(&tmp_ptr->allowed_parts, buffer);
+ safe_unpackstr(&tmp_ptr->qos, buffer);
+ safe_unpackstr(&tmp_ptr->users, buffer);
+ safe_unpackstr(&tmp_ptr->accounts, buffer);
+ safe_unpackstr(&tmp_ptr->burst_buffer, buffer);
+ safe_unpackstr(&tmp_ptr->groups, buffer);
+ safe_unpackstr(&tmp_ptr->comment, buffer);
+ safe_unpackstr(&tmp_ptr->tres_str, buffer);
+ } else if (protocol_version >= SLURM_MIN_PROTOCOL_VERSION) {
safe_unpackstr(&tmp_ptr->name, buffer);
safe_unpack_time(&tmp_ptr->start_time, buffer);
safe_unpack_time(&tmp_ptr->end_time, buffer);
@@ -2930,7 +2977,41 @@
_unpack_reserve_info_members(reserve_info_t * resv, buf_t *buffer,
uint16_t protocol_version)
{
- if (protocol_version >= SLURM_MIN_PROTOCOL_VERSION) {
+ if (protocol_version >= SLURM_25_11_PROTOCOL_VERSION) {
+ safe_unpackstr(&resv->accounts, buffer);
+ safe_unpackstr(&resv->burst_buffer,buffer);
+ safe_unpackstr(&resv->comment, buffer);
+ safe_unpack32(&resv->core_cnt, buffer);
+ safe_unpack_time(&resv->end_time, buffer);
+ safe_unpackstr(&resv->features, buffer);
+ safe_unpack64(&resv->flags, buffer);
+ safe_unpackstr(&resv->licenses, buffer);
+ safe_unpack32(&resv->max_start_delay, buffer);
+ safe_unpackstr(&resv->name, buffer);
+ safe_unpack32(&resv->node_cnt, buffer);
+ safe_unpackstr(&resv->node_list, buffer);
+ safe_unpackstr(&resv->partition, buffer);
+ safe_unpack32(&resv->purge_comp_time, buffer);
+ safe_unpack_time(&resv->start_time, buffer);
+
+ safe_unpackstr(&resv->tres_str, buffer);
+ safe_unpackstr(&resv->users, buffer);
+ safe_unpackstr(&resv->groups, buffer);
+ safe_unpackstr(&resv->qos, buffer);
+ safe_unpackstr(&resv->allowed_parts, buffer);
+
+ unpack_bit_str_hex_as_inx(&resv->node_inx, buffer);
+
+ safe_unpack32(&resv->core_spec_cnt, buffer);
+ if (resv->core_spec_cnt > 0) {
+ safe_xcalloc(resv->core_spec, resv->core_spec_cnt,
+ sizeof(resv_core_spec_t));
+ }
+ for (int i = 0; i < resv->core_spec_cnt; i++) {
+ safe_unpackstr(&resv->core_spec[i].node_name, buffer);
+ safe_unpackstr(&resv->core_spec[i].core_id, buffer);
+ }
+ } else if (protocol_version >= SLURM_MIN_PROTOCOL_VERSION) {
uint32_t uint32_tmp;
safe_unpackstr(&resv->accounts, buffer);
safe_unpackstr(&resv->burst_buffer,buffer);
@@ -3236,7 +3317,214 @@
uint32_t uint32_tmp;
bool need_unpack = false;
- if (protocol_version >= SLURM_25_05_PROTOCOL_VERSION) {
+ if (protocol_version >= SLURM_25_11_PROTOCOL_VERSION) {
+ /* job_record_pack_common */
+ safe_unpackstr(&job->account, buffer);
+ safe_unpackstr(&job->admin_comment, buffer);
+ safe_unpackstr(&job->alloc_node, buffer);
+ safe_unpack32(&job->alloc_sid, buffer);
+ safe_unpack32(&job->array_job_id, buffer);
+ safe_unpack32(&job->array_task_id, buffer);
+ safe_unpack32(&job->assoc_id, buffer);
+
+ safe_unpackstr(&job->batch_features, buffer);
+ safe_unpack16(&job->batch_flag, buffer);
+ safe_unpackstr(&job->batch_host, buffer);
+ safe_unpack64(&job->bitflags, buffer);
+ safe_unpackstr(&job->burst_buffer, buffer);
+ safe_unpackstr(&job->burst_buffer_state, buffer);
+ safe_unpackdouble(&job->billable_tres, buffer);
+
+ safe_unpackstr(&job->comment, buffer);
+ safe_unpackstr(&job->container, buffer);
+ safe_unpackstr(&job->container_id, buffer);
+ safe_unpackstr(&job->cpus_per_tres, buffer);
+
+ safe_unpack_time(&job->deadline, buffer);
+ safe_unpack32(&job->delay_boot, buffer);
+ safe_unpack32(&job->derived_ec, buffer);
+
+ safe_unpack32(&job->exit_code, buffer);
+ safe_unpackstr(&job->extra, buffer);
+
+ safe_unpackstr(&job->failed_node, buffer);
+ /* job_record_pack_fed_details */
+ safe_unpackbool(&need_unpack, buffer);
+ if (need_unpack) {
+ safe_unpackstr(&job->fed_origin_str, buffer);
+ safe_unpack64(&job->fed_siblings_active, buffer);
+ safe_unpackstr(&job->fed_siblings_active_str, buffer);
+ safe_unpack64(&job->fed_siblings_viable, buffer);
+ safe_unpackstr(&job->fed_siblings_viable_str, buffer);
+ }
+ /*******************************/
+
+ safe_unpackstr(&job->gres_total, buffer);
+ safe_unpack32(&job->group_id, buffer);
+
+ safe_unpack32(&job->het_job_id, buffer);
+ safe_unpackstr(&job->het_job_id_set, buffer);
+ safe_unpack32(&job->het_job_offset, buffer);
+
+ safe_unpack32(&job->job_id, buffer);
+ safe_unpack32(&job->job_state, buffer);
+
+ safe_unpack_time(&job->last_sched_eval, buffer);
+ safe_unpackstr(&job->licenses, buffer);
+ safe_unpackstr(&job->licenses_allocated, buffer);
+
+ safe_unpack16(&job->mail_type, buffer);
+ safe_unpackstr(&job->mail_user, buffer);
+ safe_unpackstr(&job->mcs_label, buffer);
+ safe_unpackstr(&job->mem_per_tres, buffer);
+
+ safe_unpackstr(&job->name, buffer);
+ safe_unpackstr(&job->network, buffer);
+
+ safe_unpack_time(&job->preempt_time, buffer);
+ safe_unpack_time(&job->pre_sus_time, buffer);
+ safe_unpack32(&job->priority, buffer);
+ safe_unpack32(&job->profile, buffer);
+
+ safe_unpack8(&job->reboot, buffer);
+ safe_unpack32(&job->req_switch, buffer);
+ safe_unpack_time(&job->resize_time, buffer);
+ safe_unpack16(&job->restart_cnt, buffer);
+ safe_unpackstr(&job->resv_name, buffer);
+ safe_unpackstr(&job->resv_ports, buffer);
+
+ safe_unpackstr(&job->selinux_context, buffer);
+ safe_unpack32(&job->site_factor, buffer);
+ safe_unpack16(&job->start_protocol_ver, buffer);
+ safe_unpackstr(&job->state_desc, buffer);
+ safe_unpack32(&job->state_reason, buffer);
+ safe_unpack_time(&job->suspend_time, buffer);
+ safe_unpackstr(&job->system_comment, buffer);
+
+ safe_unpack32(&job->time_min, buffer);
+ safe_unpackstr(&job->tres_bind, buffer);
+ safe_unpackstr(&job->tres_alloc_str, buffer);
+ safe_unpackstr(&job->tres_req_str, buffer);
+ safe_unpackstr(&job->tres_freq, buffer);
+ safe_unpackstr(&job->tres_per_job, buffer);
+ safe_unpackstr(&job->tres_per_node, buffer);
+ safe_unpackstr(&job->tres_per_socket, buffer);
+ safe_unpackstr(&job->tres_per_task, buffer);
+
+ safe_unpack32(&job->user_id, buffer);
+ safe_unpackstr(&job->user_name, buffer);
+
+ safe_unpack32(&job->wait4switch, buffer);
+ safe_unpackstr(&job->wckey, buffer);
+ /**************************************/
+
+
+ /* The array_task_str value is stored in slurmctld and passed
+ * here in hex format for best scalability. Its format needs
+ * to be converted to human readable form by the client. */
+ safe_unpackstr(&job->array_task_str, buffer);
+ safe_unpack32(&job->array_max_tasks, buffer);
+ xlate_array_task_str(&job->array_task_str, job->array_max_tasks,
+ &job->array_bitmap);
+
+ safe_unpack32(&job->time_limit, buffer);
+
+ safe_unpack_time(&job->start_time, buffer);
+ safe_unpack_time(&job->end_time, buffer);
+ safe_unpack32_array(&job->priority_array, &uint32_tmp, buffer);
+ safe_unpackstr(&job->priority_array_names, buffer);
+ safe_unpackstr(&job->cluster, buffer);
+ safe_unpackstr(&job->nodes, buffer);
+ safe_unpackstr(&job->sched_nodes, buffer);
+ safe_unpackstr(&job->partition, buffer);
+ safe_unpackstr(&job->qos, buffer);
+ safe_unpack_time(&job->preemptable_time, buffer);
+
+ if (unpack_job_resources(&job->job_resrcs, buffer,
+ protocol_version))
+ goto unpack_error;
+ safe_unpackstr_array(&job->gres_detail_str,
+ &job->gres_detail_cnt, buffer);
+
+ unpack_bit_str_hex_as_inx(&job->node_inx, buffer);
+
+ /*** unpack default job details ***/
+ safe_unpackbool(&need_unpack, buffer);
+ if (!need_unpack) {
+ safe_unpack32(&job->num_cpus, buffer);
+ safe_unpack32(&job->num_nodes, buffer);
+ safe_unpack32(&job->nice, buffer);
+ } else {
+ /* job_record_pack_details_common */
+ safe_unpack_time(&job->accrue_time, buffer);
+ safe_unpack_time(&job->eligible_time, buffer);
+ safe_unpackstr(&job->cluster_features, buffer);
+ safe_unpack32(&job->cpu_freq_gov, buffer);
+ safe_unpack32(&job->cpu_freq_max, buffer);
+ safe_unpack32(&job->cpu_freq_min, buffer);
+ safe_unpackstr(&job->dependency, buffer);
+ unpack_bit_str_hex_as_fmt_str(&job->job_size_str,
+ buffer);
+ safe_unpack32(&job->nice, buffer);
+ safe_unpack16(&job->ntasks_per_node, buffer);
+ safe_unpack16(&job->ntasks_per_tres, buffer);
+ safe_unpack16(&job->requeue, buffer);
+ safe_unpack16(&job->segment_size, buffer);
+ safe_unpack_time(&job->submit_time, buffer);
+ safe_unpackstr(&job->work_dir, buffer);
+ /**********************************/
+
+ safe_unpackstr(&job->features, buffer);
+ safe_unpackstr(&job->prefer, buffer);
+ safe_unpackstr(&job->command, buffer);
+ safe_unpackstr(&job->submit_line, buffer);
+
+ safe_unpack32(&job->num_cpus, buffer);
+ safe_unpack32(&job->max_cpus, buffer);
+ safe_unpack32(&job->num_nodes, buffer);
+ safe_unpack32(&job->max_nodes, buffer);
+ safe_unpack32(&job->num_tasks, buffer);
+
+ safe_unpack16(&job->shared, buffer);
+
+ safe_unpackstr(&job->cronspec, buffer);
+ }
+
+ /*** unpack pending job details ***/
+ safe_unpack16(&job->contiguous, buffer);
+ safe_unpack16(&job->core_spec, buffer);
+ safe_unpack16(&job->cpus_per_task, buffer);
+ safe_unpack16(&job->pn_min_cpus, buffer);
+
+ safe_unpack64(&job->pn_min_memory, buffer);
+ safe_unpack32(&job->pn_min_tmp_disk, buffer);
+ safe_unpack16(&job->oom_kill_step, buffer);
+ safe_unpackstr(&job->req_nodes, buffer);
+
+ unpack_bit_str_hex_as_inx(&job->req_node_inx, buffer);
+
+ safe_unpackstr(&job->exc_nodes, buffer);
+
+ unpack_bit_str_hex_as_inx(&job->exc_node_inx, buffer);
+
+ safe_unpackstr(&job->std_err, buffer);
+ safe_unpackstr(&job->std_in, buffer);
+ safe_unpackstr(&job->std_out, buffer);
+
+ if (unpack_multi_core_data(&mc_ptr, buffer, protocol_version))
+ goto unpack_error;
+ if (mc_ptr) {
+ job->boards_per_node = mc_ptr->boards_per_node;
+ job->sockets_per_board = mc_ptr->sockets_per_board;
+ job->sockets_per_node = mc_ptr->sockets_per_node;
+ job->cores_per_socket = mc_ptr->cores_per_socket;
+ job->threads_per_core = mc_ptr->threads_per_core;
+ job->ntasks_per_board = mc_ptr->ntasks_per_board;
+ job->ntasks_per_socket = mc_ptr->ntasks_per_socket;
+ job->ntasks_per_core = mc_ptr->ntasks_per_core;
+ xfree(mc_ptr);
+ }
+ } else if (protocol_version >= SLURM_25_05_PROTOCOL_VERSION) {
/* job_record_pack_common */
safe_unpackstr(&job->account, buffer);
safe_unpackstr(&job->admin_comment, buffer);
diff --git a/src/plugins/data_parser/v0.0.44/parsers.c b/src/plugins/data_parser/v0.0.44/parsers.c
index cd0fd9a..cd0c9ac 100644
--- a/src/plugins/data_parser/v0.0.44/parsers.c
+++ b/src/plugins/data_parser/v0.0.44/parsers.c
@@ -8480,6 +8480,7 @@
add_cparse(JOB_INFO_STDOUT_EXP, "stdout_expanded", "Job stdout with expanded fields"),
add_cparse(JOB_INFO_STDERR_EXP, "stderr_expanded", "Job stderr with expanded fields"),
add_parse(TIMESTAMP_NO_VAL, submit_time, "submit_time", "Time when the job was submitted (UNIX timestamp)"),
+ add_parse(STRING, submit_line, "submit_line", "Job submit line (e.g. 'sbatch -N3 job.sh job_arg'"),
add_parse(TIMESTAMP_NO_VAL, suspend_time, "suspend_time", "Time the job was last suspended or resumed (UNIX timestamp)"),
add_parse(STRING, system_comment, "system_comment", "Arbitrary comment from slurmctld"),
add_parse(UINT32_NO_VAL, time_limit, "time_limit", "Maximum run time in minutes"),
diff --git a/src/plugins/select/cons_tres/gres_sock_list.c b/src/plugins/select/cons_tres/gres_sock_list.c
index 9502a8e..ebdc42f 100644
--- a/src/plugins/select/cons_tres/gres_sock_list.c
+++ b/src/plugins/select/cons_tres/gres_sock_list.c
@@ -181,21 +181,25 @@
return;
}
-static void _handle_gres_exc_bit_and_not(resv_exc_t *resv_exc_ptr,
- bitstr_t *bits_by_sock, int node_inx)
+static void _handle_gres_exc_bit_restrict(resv_exc_t *resv_exc_ptr,
+ bitstr_t *bits_by_sock, int node_inx)
{
gres_job_state_t *gres_js;
if (!resv_exc_ptr)
return;
- gres_js = resv_exc_ptr->gres_js_exc;
+ gres_js = resv_exc_ptr->gres_js_exc ? resv_exc_ptr->gres_js_exc :
+ resv_exc_ptr->gres_js_inc;
if (!gres_js || !gres_js->gres_bit_alloc ||
!gres_js->gres_bit_alloc[node_inx])
return;
- bit_and_not(bits_by_sock, gres_js->gres_bit_alloc[node_inx]);
+ if (resv_exc_ptr->gres_js_exc) /* use only not reserved bits */
+ bit_and_not(bits_by_sock, gres_js->gres_bit_alloc[node_inx]);
+ else /* resv_exc_ptr->gres_js_inc - use only reserved bits */
+ bit_and(bits_by_sock, gres_js->gres_bit_alloc[node_inx]);
return;
}
@@ -374,6 +378,11 @@
bit_or(sock_gres->bits_any_sock,
gres_ns->topo_gres_bitmap[i]);
}
+
+ _handle_gres_exc_bit_restrict(resv_exc_ptr,
+ sock_gres->bits_any_sock,
+ create_args->node_inx);
+
match = true;
continue;
}
@@ -411,7 +420,7 @@
gres_ns->topo_gres_bitmap[i]);
}
- _handle_gres_exc_bit_and_not(
+ _handle_gres_exc_bit_restrict(
resv_exc_ptr,
sock_gres->bits_by_sock[s],
create_args->node_inx);
diff --git a/src/scontrol/create_res.c b/src/scontrol/create_res.c
index d7db29c..f6502d0 100644
--- a/src/scontrol/create_res.c
+++ b/src/scontrol/create_res.c
@@ -109,6 +109,23 @@
} else {
resv_msg_ptr->accounts = val;
}
+ } else if (!xstrncasecmp(tag, "AllowedPartitions",
+ MAX(taglen, 2))) {
+ if (resv_msg_ptr->allowed_parts) {
+ exit_code = 1;
+ error("Parameter %s specified more than once",
+ argv[i]);
+ return SLURM_ERROR;
+ }
+ if (plus_minus) {
+ resv_msg_ptr->allowed_parts =
+ scontrol_process_plus_minus(plus_minus,
+ val, false);
+ *res_free_flags |= RESV_FREE_STR_ALLOWED_PARTS;
+ plus_minus = '\0';
+ } else {
+ resv_msg_ptr->allowed_parts = val;
+ }
} else if (xstrncasecmp(tag, "Comment", MAX(taglen, 3)) == 0) {
if (resv_msg_ptr->comment) {
exit_code = 1;
@@ -167,6 +184,22 @@
resv_msg_ptr->users = val;
}
+ } else if (!xstrncasecmp(tag, "QOS", MAX(taglen, 1))) {
+ if (resv_msg_ptr->qos) {
+ exit_code = 1;
+ error("Parameter %s specified more than once",
+ argv[i]);
+ return SLURM_ERROR;
+ }
+ if (plus_minus) {
+ resv_msg_ptr->qos =
+ scontrol_process_plus_minus(plus_minus,
+ val, false);
+ *res_free_flags |= RESV_FREE_STR_QOS;
+ plus_minus = '\0';
+ } else {
+ resv_msg_ptr->qos = val;
+ }
} else if (!xstrncasecmp(tag, "ReservationName",
MAX(taglen, 1))) {
resv_msg_ptr->name = val;
diff --git a/src/scontrol/info_job.c b/src/scontrol/info_job.c
index 6142152..f6c41fe 100644
--- a/src/scontrol/info_job.c
+++ b/src/scontrol/info_job.c
@@ -727,6 +727,10 @@
xstrcat(out, line_end);
/****** Line 22 ******/
+ xstrfmtcat(out, "SubmitLine=%s", job_ptr->submit_line);
+ xstrcat(out, line_end);
+
+ /****** Line 23 ******/
xstrfmtcat(out, "WorkDir=%s", job_ptr->work_dir);
xstrcat(out, line_end);
diff --git a/src/slurmctld/acct_policy.c b/src/slurmctld/acct_policy.c
index 4ecba1d..9f81c5f 100644
--- a/src/slurmctld/acct_policy.c
+++ b/src/slurmctld/acct_policy.c
@@ -1153,11 +1153,11 @@
}
/*
- * When we are removing submit we need to set the pointer back if it was
- * changed.
+ * Now that we are done with accrue set things back to the way
+ * it was qos wise. Accrue limits are always based on the
+ * highest priority QOS.
*/
- if ((type == ACCT_POLICY_REM_SUBMIT) &&
- (orig_qos_ptr != job_ptr->qos_ptr)) {
+ if (orig_qos_ptr && (orig_qos_ptr != job_ptr->qos_ptr)) {
job_ptr->qos_ptr = orig_qos_ptr;
job_ptr->qos_id = orig_qos_ptr->id;
}
diff --git a/src/slurmctld/job_mgr.c b/src/slurmctld/job_mgr.c
index 57a4359..e4dcffb 100644
--- a/src/slurmctld/job_mgr.c
+++ b/src/slurmctld/job_mgr.c
@@ -10359,13 +10359,6 @@
assoc_mgr_lock_t locks = { .qos = READ_LOCK };
xassert(!has_qos_lock || verify_assoc_lock(QOS_LOCK, READ_LOCK));
- /*
- * NOTE: There are nested pack blocks in
- * job_record_pack_details_common() and
- * job_record_pack_details_common(). Bump this protocol block when
- * bumping the blocks in these functions to help keep symmetry between
- * pack and unpacks.
- */
if (protocol_version >= SLURM_25_05_PROTOCOL_VERSION) {
job_record_pack_common(dump_job_ptr, false, buffer,
protocol_version);
@@ -10953,7 +10946,141 @@
} else
_find_node_config(&max_cpu_cnt, &max_core_cnt);
- if (protocol_version >= SLURM_24_11_PROTOCOL_VERSION) {
+ if (protocol_version >= SLURM_25_11_PROTOCOL_VERSION) {
+ if (!detail_ptr) {
+ packbool(false, buffer);
+
+ if (job_ptr->total_cpus)
+ pack32(job_ptr->total_cpus, buffer);
+ else
+ pack32(job_ptr->cpu_cnt, buffer);
+
+ pack32(job_ptr->node_cnt, buffer);
+ pack32(NICE_OFFSET, buffer); /* Best guess */
+ return;
+ }
+ packbool(true, buffer);
+ job_record_pack_details_common(detail_ptr, buffer,
+ protocol_version);
+
+ if (!IS_JOB_PENDING(job_ptr)) {
+ packstr(detail_ptr->features_use, buffer);
+ packnull(buffer);
+ } else {
+ packstr(detail_ptr->features, buffer);
+ packstr(detail_ptr->prefer, buffer);
+ }
+
+ if (detail_ptr->argv)
+ packstr(detail_ptr->argv[0], buffer);
+ else
+ packnull(buffer);
+ packstr(detail_ptr->submit_line, buffer);
+
+ if (IS_JOB_COMPLETING(job_ptr) && job_ptr->cpu_cnt) {
+ pack32(job_ptr->cpu_cnt, buffer);
+ pack32((uint32_t) 0, buffer);
+ } else if (job_ptr->total_cpus &&
+ !IS_JOB_PENDING(job_ptr)) {
+ /* If job is PENDING ignore total_cpus,
+ * which may have been set by previous run
+ * followed by job requeue. */
+ pack32(job_ptr->total_cpus, buffer);
+ pack32((uint32_t) 0, buffer);
+ } else {
+ pack32(detail_ptr->min_cpus, buffer);
+ if (detail_ptr->max_cpus != NO_VAL)
+ pack32(detail_ptr->max_cpus, buffer);
+ else
+ pack32((uint32_t) 0, buffer);
+ }
+
+ if (IS_JOB_COMPLETING(job_ptr) && job_ptr->node_cnt) {
+ pack32(job_ptr->node_cnt, buffer);
+ pack32((uint32_t) 0, buffer);
+ } else if (job_ptr->total_nodes) {
+ pack32(job_ptr->total_nodes, buffer);
+ pack32((uint32_t) 0, buffer);
+ } else if (job_ptr->node_cnt_wag) {
+ /* This should catch everything else, but
+ * just in case this is 0 (startup or
+ * whatever) we will keep the rest of
+ * this if statement around.
+ */
+ pack32(job_ptr->node_cnt_wag, buffer);
+ pack32((uint32_t) detail_ptr->max_nodes,
+ buffer);
+ } else if (detail_ptr->ntasks_per_node) {
+ /* min_nodes based upon task count and ntasks
+ * per node */
+ uint32_t min_nodes;
+ min_nodes = detail_ptr->num_tasks /
+ detail_ptr->ntasks_per_node;
+ min_nodes = MAX(min_nodes,
+ detail_ptr->min_nodes);
+ pack32(min_nodes, buffer);
+ pack32(detail_ptr->max_nodes, buffer);
+ } else if (detail_ptr->cpus_per_task > 1) {
+ /* min_nodes based upon task count and cpus
+ * per task */
+ uint32_t ntasks_per_node, min_nodes;
+ ntasks_per_node = max_cpu_cnt /
+ detail_ptr->cpus_per_task;
+ ntasks_per_node = MAX(ntasks_per_node, 1);
+ min_nodes = detail_ptr->num_tasks /
+ ntasks_per_node;
+ min_nodes = MAX(min_nodes,
+ detail_ptr->min_nodes);
+ pack32(min_nodes, buffer);
+ pack32(detail_ptr->max_nodes, buffer);
+ } else if (detail_ptr->mc_ptr &&
+ detail_ptr->mc_ptr->ntasks_per_core &&
+ (detail_ptr->mc_ptr->ntasks_per_core
+ != INFINITE16)) {
+ /* min_nodes based upon task count and ntasks
+ * per core */
+ uint32_t min_cores, min_nodes;
+ min_cores = ROUNDUP(detail_ptr->num_tasks,
+ detail_ptr->mc_ptr->
+ ntasks_per_core);
+ min_nodes = ROUNDUP(min_cores, max_core_cnt);
+ min_nodes = MAX(min_nodes,
+ detail_ptr->min_nodes);
+ pack32(min_nodes, buffer);
+ pack32(detail_ptr->max_nodes, buffer);
+ } else {
+ /* min_nodes based upon task count only */
+ uint32_t min_nodes;
+ uint32_t max_nodes;
+
+ min_nodes = ROUNDUP(detail_ptr->num_tasks,
+ max_cpu_cnt);
+ min_nodes = MAX(min_nodes,
+ detail_ptr->min_nodes);
+ max_nodes = MAX(min_nodes,
+ detail_ptr->max_nodes);
+ pack32(min_nodes, buffer);
+ pack32(max_nodes, buffer);
+ }
+ if (detail_ptr->num_tasks)
+ pack32(detail_ptr->num_tasks, buffer);
+ else if (IS_JOB_PENDING(job_ptr))
+ pack32(detail_ptr->min_nodes, buffer);
+ else if (job_ptr->tres_alloc_cnt)
+ pack32((uint32_t)
+ job_ptr->tres_alloc_cnt[TRES_ARRAY_NODE],
+ buffer);
+ else
+ pack32(NO_VAL, buffer);
+
+ pack16(shared, buffer);
+
+ if (detail_ptr->crontab_entry)
+ packstr(detail_ptr->crontab_entry->cronspec,
+ buffer);
+ else
+ packnull(buffer);
+ } else if (protocol_version >= SLURM_24_11_PROTOCOL_VERSION) {
if (!detail_ptr) {
packbool(false, buffer);
@@ -15319,17 +15446,16 @@
}
reply:
- if (msg->tls_conn) {
- if (resp_array) {
- job_array_resp_msg_t *resp_array_msg =
- _resp_array_xlate(resp_array, job_id);
- (void) send_msg_response(msg, RESPONSE_JOB_ARRAY_ERRORS,
- resp_array_msg);
- slurm_free_job_array_resp(resp_array_msg);
- } else {
- slurm_send_rc_err_msg(msg, rc, err_msg);
- }
+ if (resp_array) {
+ job_array_resp_msg_t *resp_array_msg =
+ _resp_array_xlate(resp_array, job_id);
+ (void) send_msg_response(msg, RESPONSE_JOB_ARRAY_ERRORS,
+ resp_array_msg);
+ slurm_free_job_array_resp(resp_array_msg);
+ } else {
+ slurm_send_rc_err_msg(msg, rc, err_msg);
}
+
xfree(err_msg);
_resp_array_free(resp_array);
diff --git a/src/slurmctld/job_scheduler.c b/src/slurmctld/job_scheduler.c
index 342e63e..bb96b90 100644
--- a/src/slurmctld/job_scheduler.c
+++ b/src/slurmctld/job_scheduler.c
@@ -4318,6 +4318,9 @@
FREE_NULL_BITMAP(avail_bitmap);
reservation_delete_resv_exc_parts(&resv_exc);
job_start_data->rc = rc2;
+ if ((rc2 == ESLURM_INVALID_QOS) ||
+ (rc2 == ESLURM_INVALID_PARTITION_NAME))
+ return 0;
return -1;
}
diff --git a/src/slurmctld/reservation.c b/src/slurmctld/reservation.c
index 7db20be..55721ce 100644
--- a/src/slurmctld/reservation.c
+++ b/src/slurmctld/reservation.c
@@ -1,4 +1,4 @@
-/*****************************************************************************\
+ /*****************************************************************************\
* reservation.c - resource reservation management
*****************************************************************************
* Copyright (C) 2009-2010 Lawrence Livermore National Security.
@@ -139,16 +139,17 @@
} constraint_slot_t;
typedef struct {
+ list_t *allowed_parts_list;
bitstr_t *core_bitmap;
list_t *gres_list_exc;
bitstr_t *node_bitmap;
} resv_select_t;
typedef struct {
- char *assoc_list_pos;
char *prefix;
- slurmctld_resv_t *resv_ptr;
-} foreach_set_assoc_t;
+ char **str;
+ char *str_pos;
+} foreach_set_allow_str_t;
static int _advance_resv_time(slurmctld_resv_t *resv_ptr);
static void _advance_time(time_t *res_time, int day_cnt, int hour_cnt);
@@ -567,6 +568,14 @@
resv_copy_ptr->node_list = xstrdup(resv_orig_ptr->node_list);
resv_copy_ptr->partition = xstrdup(resv_orig_ptr->partition);
resv_copy_ptr->part_ptr = resv_orig_ptr->part_ptr;
+ resv_copy_ptr->allowed_parts = xstrdup(resv_orig_ptr->allowed_parts);
+ if (resv_orig_ptr->allowed_parts_list)
+ resv_copy_ptr->allowed_parts_list =
+ list_shallow_copy(resv_orig_ptr->allowed_parts_list);
+ resv_copy_ptr->qos = xstrdup(resv_orig_ptr->qos);
+ if (resv_orig_ptr->qos_list)
+ resv_copy_ptr->qos_list =
+ list_shallow_copy(resv_orig_ptr->qos_list);
resv_copy_ptr->resv_id = resv_orig_ptr->resv_id;
resv_copy_ptr->start_time = resv_orig_ptr->start_time;
resv_copy_ptr->start_time_first = resv_orig_ptr->start_time_first;
@@ -670,6 +679,19 @@
src_resv->partition = NULL;
dest_resv->part_ptr = src_resv->part_ptr;
+
+ FREE_NULL_LIST(dest_resv->allowed_parts_list);
+ dest_resv->allowed_parts_list = src_resv->allowed_parts_list;
+ src_resv->allowed_parts_list = NULL;
+
+ xfree(dest_resv->qos);
+ dest_resv->qos = src_resv->qos;
+ src_resv->qos = NULL;
+
+ FREE_NULL_LIST(dest_resv->qos_list);
+ dest_resv->qos_list = src_resv->qos_list;
+ src_resv->qos_list = NULL;
+
dest_resv->resv_id = src_resv->resv_id;
dest_resv->start_time = src_resv->start_time;
dest_resv->start_time_first = src_resv->start_time_first;
@@ -734,6 +756,10 @@
FREE_NULL_BITMAP(resv_ptr->node_bitmap);
xfree(resv_ptr->node_list);
xfree(resv_ptr->partition);
+ xfree(resv_ptr->allowed_parts);
+ FREE_NULL_LIST(resv_ptr->allowed_parts_list);
+ xfree(resv_ptr->qos);
+ FREE_NULL_LIST(resv_ptr->qos_list);
xfree(resv_ptr->tres_str);
xfree(resv_ptr->tres_fmt_str);
xfree(resv_ptr->users);
@@ -985,13 +1011,13 @@
else
duration = resv_ptr->duration;
- info("%s: Name=%s StartTime=%s EndTime=%s Duration=%d Flags=%s NodeCnt=%u CoreCnt=%u NodeList=%s Features=%s PartitionName=%s Users=%s Groups=%s Accounts=%s Licenses=%s BurstBuffer=%s TRES=%s Comment=%s",
+ info("%s: Name=%s StartTime=%s EndTime=%s Duration=%d Flags=%s NodeCnt=%u CoreCnt=%u NodeList=%s Features=%s PartitionName=%s Users=%s Groups=%s Accounts=%s Licenses=%s AllowedPartitions=%s QOS=%s BurstBuffer=%s TRES=%s Comment=%s",
mode, resv_ptr->name, start_str, end_str, duration,
flag_str, resv_ptr->node_cnt, resv_ptr->core_cnt,
resv_ptr->node_list,
resv_ptr->features, resv_ptr->partition,
resv_ptr->users, resv_ptr->groups, resv_ptr->accounts,
- resv_ptr->licenses,
+ resv_ptr->licenses, resv_ptr->allowed_parts, resv_ptr->qos,
resv_ptr->burst_buffer, resv_ptr->tres_str,
resv_ptr->comment);
@@ -1100,17 +1126,21 @@
return rc;
}
-static int _foreach_set_assoc_list(void *x, void *arg)
+static void _addto_alloc_str(foreach_set_allow_str_t *set_allow_str,
+ uint32_t id)
+{
+ xstrfmtcatat(*set_allow_str->str, &set_allow_str->str_pos, "%s%s%u,",
+ *set_allow_str->str ? "" : ",",
+ set_allow_str->prefix,
+ id);
+}
+
+static int _foreach_set_assoc_allow_str(void *x, void *arg)
{
slurmdb_assoc_rec_t *assoc_ptr = x;
- foreach_set_assoc_t *set_assoc = arg;
+ foreach_set_allow_str_t *set_allow_str = arg;
- xstrfmtcatat(set_assoc->resv_ptr->assoc_list,
- &set_assoc->assoc_list_pos,
- "%s%s%u,",
- set_assoc->resv_ptr->assoc_list ? "" : ",",
- set_assoc->prefix,
- assoc_ptr->id);
+ _addto_alloc_str(set_allow_str, assoc_ptr->id);
return 0;
}
@@ -1122,8 +1152,8 @@
list_t *assoc_list = NULL;
slurmdb_assoc_rec_t assoc;
assoc_mgr_lock_t locks = { .assoc = READ_LOCK, .user = READ_LOCK };
- foreach_set_assoc_t set_assoc = {
- .resv_ptr = resv_ptr,
+ foreach_set_allow_str_t set_allow_str = {
+ .str = &resv_ptr->assoc_list,
};
/* no need to do this if we can't ;) */
@@ -1241,7 +1271,8 @@
goto end_it;
}
}
- } else if (accounting_enforce & ACCOUNTING_ENFORCE_ASSOCS) {
+ } else if (!resv_ptr->qos && !resv_ptr->allowed_parts &&
+ (accounting_enforce & ACCOUNTING_ENFORCE_ASSOCS)) {
error("We need at least 1 user or 1 account to "
"create a reservtion.");
rc = SLURM_ERROR;
@@ -1249,20 +1280,22 @@
xfree(resv_ptr->assoc_list); /* clear for modify */
if (list_count(assoc_list_allow)) {
- set_assoc.prefix = "";
- set_assoc.assoc_list_pos = NULL;
+ set_allow_str.prefix = "";
+ set_allow_str.str_pos = NULL;
(void) list_for_each(assoc_list_allow,
- _foreach_set_assoc_list,
- &set_assoc);
+ _foreach_set_assoc_allow_str,
+ &set_allow_str);
}
if (list_count(assoc_list_deny)) {
- set_assoc.prefix = "-";
- set_assoc.assoc_list_pos = NULL;
+ set_allow_str.prefix = "-";
+ set_allow_str.str_pos = NULL;
(void) list_for_each(assoc_list_deny,
- _foreach_set_assoc_list,
- &set_assoc);
+ _foreach_set_assoc_allow_str,
+ &set_allow_str);
}
- debug("assoc_list:%s", resv_ptr->assoc_list);
+
+ if (resv_ptr->assoc_list)
+ debug("assoc_list:%s", resv_ptr->assoc_list);
end_it:
FREE_NULL_LIST(assoc_list_allow);
@@ -1272,6 +1305,515 @@
return rc;
}
+static void _addto_name_str(foreach_set_allow_str_t *set_allow_str,
+ char *name)
+{
+ xstrfmtcatat(*set_allow_str->str, &set_allow_str->str_pos, "%s%s%s",
+ *set_allow_str->str ? "," : "",
+ set_allow_str->prefix,
+ name);
+}
+
+static int _foreach_set_allowed_parts_name_str(void *x, void *arg)
+{
+ part_record_t *part_ptr = x;
+ foreach_set_allow_str_t *set_allow_str = arg;
+
+ _addto_name_str(set_allow_str, part_ptr->name);
+ return 0;
+}
+
+extern int _sort_allowed_parts_list_asc(void *v1, void *v2)
+{
+ part_record_t *part_a = *(part_record_t **) v1;
+ part_record_t *part_b = *(part_record_t **) v2;
+
+ return slurm_sort_char_list_asc(&part_a->name, &part_b->name);
+}
+
+/*
+ * Since the returned allowed_parts_list is full of pointers from the
+ * allowed_parts_list slurmctld_lock_t READ_LOCK on PART must be set before
+ * calling this function and while handling it after a return.
+ */
+static int _append_to_allowed_parts_list(list_t *allowed_parts_list,
+ char *part_name)
+{
+ int rc = ESLURM_INVALID_PARTITION_NAME;
+ part_record_t *part_ptr = NULL;
+
+ xassert(allowed_parts_list);
+ xassert(part_name);
+ xassert(verify_lock(PART_LOCK, READ_LOCK));
+
+ if (!(part_ptr = list_find_first(part_list, &list_find_part,
+ part_name))) {
+ error("No Partition by name %s", part_name);
+ return rc;
+ }
+
+ if (!list_find_first(allowed_parts_list, slurm_find_ptr_in_list,
+ part_ptr)) {
+ list_append(allowed_parts_list, part_ptr);
+ rc = SLURM_SUCCESS;
+ }
+
+ return rc;
+}
+
+/*
+ * Since the returned allowed_parts_list is full of pointers from the
+ * allowed_parts_list slurmctld_lock_t READ_LOCK on PART must be set before
+ * calling this function and while handling it after a return.
+ */
+static int _remove_from_allowed_parts_list(list_t *allowed_parts_list,
+ char *part_name)
+{
+ int rc = ESLURM_INVALID_PARTITION_NAME;
+ part_record_t *part_ptr = NULL;
+
+ xassert(allowed_parts_list);
+ xassert(part_name);
+ xassert(verify_lock(PART_LOCK, READ_LOCK));
+
+ if (!(part_ptr = list_find_first(part_list, &list_find_part,
+ part_name))) {
+ error("No Partition by name %s", part_name);
+ return rc;
+ }
+
+ if (part_ptr) {
+ (void) list_delete_first(allowed_parts_list,
+ slurm_find_ptr_in_list,
+ part_ptr);
+ rc = SLURM_SUCCESS;
+ }
+
+ return rc;
+}
+
+/*
+ * Validate a comma delimited list of account names and build an array of them
+ *
+ * IN part_name - a list of partition names
+ * OUT allowed_parts_list - list of the partitions names,
+ * CALLER MUST XFREE this plus each individual record
+ * OUT part_not - true if allowed_parts_list is that of partitions to be blocked
+ * from reservation access.
+ * RETURN 0 on success
+ */
+static int _build_allowed_parts_list(char *part_names,
+ list_t **allowed_parts_list,
+ bool *part_not,
+ bool break_on_failure)
+{
+ char *last = NULL, *tmp, *tok;
+ int rc = SLURM_SUCCESS;
+
+ xassert(allowed_parts_list);
+
+ *part_not = false;
+
+ if (!part_names)
+ return ESLURM_INVALID_PARTITION_NAME;
+
+ xassert(verify_lock(PART_LOCK, READ_LOCK));
+
+ tmp = xstrdup(part_names);
+ tok = strtok_r(tmp, ",", &last);
+ while (tok) {
+ if (tok[0] == '-') {
+ if (!*allowed_parts_list) {
+ *part_not = true;
+ } else if (*part_not != true) {
+ info("Reservation request has some not/partitions");
+ rc = ESLURM_INVALID_PARTITION_NAME;
+ break;
+ }
+ tok++;
+ } else if (*part_not != false) {
+ info("Reservation request has some not/partitions");
+ rc = ESLURM_INVALID_PARTITION_NAME;
+ break;
+ }
+
+ if (!*allowed_parts_list)
+ *allowed_parts_list = list_create(NULL);
+ if (((rc = _append_to_allowed_parts_list(
+ *allowed_parts_list, tok)) != SLURM_SUCCESS) &&
+ break_on_failure)
+ break;
+
+ tok = strtok_r(NULL, ",", &last);
+ }
+
+ if (rc != SLURM_SUCCESS)
+ FREE_NULL_LIST(*allowed_parts_list);
+
+ if (*allowed_parts_list) {
+ list_sort(*allowed_parts_list, _sort_allowed_parts_list_asc);
+ }
+
+ xfree(tmp);
+
+ return rc;
+}
+
+/*
+ * Update a part list for an existing reservation based upon an
+ * update comma delimited specification of part to add (+name),
+ * remove (-name), or set value of
+ * IN/OUT resv_ptr - pointer to reservation structure being updated
+ * IN part_names - a list of part names, to set, add, or remove
+ * RETURN 0 on success
+ */
+static int _update_allowed_parts_list(slurmctld_resv_t *resv_ptr,
+ char *part_names)
+{
+ int rc = SLURM_SUCCESS;
+ char *last = NULL, *tmp, *tok;
+ bool minus_part = false, cleared = false;
+ list_t *allowed_parts_list = NULL;
+
+ if (!part_names)
+ return ESLURM_INVALID_PARTITION_NAME;
+
+ xassert(verify_lock(PART_LOCK, READ_LOCK));
+
+ if (!resv_ptr->allowed_parts_list)
+ resv_ptr->allowed_parts_list = list_create(NULL);
+ allowed_parts_list = list_shallow_copy(resv_ptr->allowed_parts_list);
+
+
+ tmp = xstrdup(part_names);
+ tok = strtok_r(tmp, ",", &last);
+ while (tok) {
+ if (tok[0] == '-') {
+ minus_part = 1;
+ tok++;
+ } else if (tok[0] == '+') {
+ minus_part = 0;
+ tok++;
+ } else if (tok[0] == '\0') {
+ continue;
+ } else {
+ /* The request is a completely new list */
+ if (!cleared) {
+ list_flush(allowed_parts_list);
+ resv_ptr->ctld_flags &=
+ (~RESV_CTLD_ALLOWED_PARTS_NOT);
+ cleared = true;
+ }
+ }
+
+ if (resv_ptr->ctld_flags & RESV_CTLD_ALLOWED_PARTS_NOT) {
+ if (minus_part)
+ rc = _append_to_allowed_parts_list(
+ allowed_parts_list, tok);
+ else
+ rc = _remove_from_allowed_parts_list(
+ allowed_parts_list, tok);
+ } else {
+ if (minus_part)
+ rc = _remove_from_allowed_parts_list(
+ allowed_parts_list, tok);
+ else
+ rc = _append_to_allowed_parts_list(
+ allowed_parts_list, tok);
+ }
+
+ if (rc != SLURM_SUCCESS)
+ break;
+
+ tok = strtok_r(NULL, ",", &last);
+ }
+
+ if ((rc == SLURM_SUCCESS) && list_count(allowed_parts_list)) {
+ foreach_set_allow_str_t set_allow_str = {
+ .prefix = (resv_ptr->ctld_flags &
+ RESV_CTLD_ALLOWED_PARTS_NOT) ?
+ "-" : "",
+ .str = &resv_ptr->allowed_parts,
+ };
+
+ list_sort(allowed_parts_list, _sort_allowed_parts_list_asc);
+ xfree(resv_ptr->allowed_parts);
+ (void) list_for_each(allowed_parts_list,
+ _foreach_set_allowed_parts_name_str,
+ &set_allow_str);
+ FREE_NULL_LIST(resv_ptr->allowed_parts_list);
+ resv_ptr->allowed_parts_list = allowed_parts_list;
+ allowed_parts_list = NULL;
+ } else if (rc == SLURM_SUCCESS)
+ rc = ESLURM_INVALID_PARTITION_NAME;
+
+ FREE_NULL_LIST(allowed_parts_list);
+ xfree(tmp);
+
+ return rc;
+}
+
+static int _foreach_set_qos_name_str(void *x, void *arg)
+{
+ slurmdb_qos_rec_t *qos_ptr = x;
+ foreach_set_allow_str_t *set_allow_str = arg;
+
+ _addto_name_str(set_allow_str, qos_ptr->name);
+ return 0;
+}
+
+extern int _sort_qos_list_asc(void *v1, void *v2)
+{
+ slurmdb_qos_rec_t *qos_a = *(slurmdb_qos_rec_t **) v1;
+ slurmdb_qos_rec_t *qos_b = *(slurmdb_qos_rec_t **) v2;
+
+ return slurm_sort_char_list_asc(&qos_a->name, &qos_b->name);
+}
+
+/*
+ * Since the returned qos_list is full of pointers from the
+ * assoc_mgr_qos_list assoc_mgr_lock_t READ_LOCK on
+ * qos must be set before calling this function and while
+ * handling it after a return.
+ */
+static int _append_to_qos_list(list_t *qos_list, char *qos_name)
+{
+ int rc = ESLURM_INVALID_QOS;
+ slurmdb_qos_rec_t *qos_ptr = NULL;
+ slurmdb_qos_rec_t qos = {
+ .name = qos_name,
+ };
+
+ xassert(qos_list);
+ xassert(qos.name);
+ xassert(verify_assoc_lock(QOS_LOCK, READ_LOCK));
+
+ if (assoc_mgr_fill_in_qos(acct_db_conn, &qos, accounting_enforce,
+ &qos_ptr, true)) {
+ if (accounting_enforce & ACCOUNTING_ENFORCE_QOS) {
+ error("No QOS by name %s", qos.name);
+ } else {
+ verbose("No QOS by name %s", qos.name);
+ rc = SLURM_SUCCESS;
+ }
+ }
+
+ if (qos_ptr) {
+ if (!list_find_first(qos_list, slurm_find_ptr_in_list, qos_ptr))
+ list_append(qos_list, qos_ptr);
+ rc = SLURM_SUCCESS;
+ }
+
+ return rc;
+}
+
+/*
+ * Since the returned qos_list is full of pointers from the
+ * assoc_mgr_qos_list assoc_mgr_lock_t READ_LOCK on
+ * qos must be set before calling this function and while
+ * handling it after a return.
+ */
+static int _remove_from_qos_list(list_t *qos_list, char *qos_name)
+{
+ int rc = ESLURM_INVALID_QOS;
+ slurmdb_qos_rec_t *qos_ptr = NULL;
+ slurmdb_qos_rec_t qos = {
+ .name = qos_name,
+ };
+
+ xassert(qos_list);
+ xassert(qos.name);
+ xassert(verify_assoc_lock(QOS_LOCK, READ_LOCK));
+
+ if (assoc_mgr_fill_in_qos(acct_db_conn, &qos, accounting_enforce,
+ &qos_ptr, true)) {
+ if (accounting_enforce & ACCOUNTING_ENFORCE_QOS) {
+ error("No QOS by name %s", qos.name);
+ } else {
+ verbose("No QOS by name %s", qos.name);
+ rc = SLURM_SUCCESS;
+ }
+ }
+
+ if (qos_ptr) {
+ (void) list_delete_first(qos_list, slurm_find_ptr_in_list,
+ qos_ptr);
+ rc = SLURM_SUCCESS;
+ }
+
+ return rc;
+}
+
+/*
+ * Validate a comma delimited list of account names and build an array of
+ * them
+ * IN account - a list of account names
+ * OUT account_cnt - number of accounts in the list
+ * OUT account_list - list of the account names,
+ * CALLER MUST XFREE this plus each individual record
+ * OUT account_not - true of account_list is that of accounts to be blocked
+ * from reservation access
+ * RETURN 0 on success
+ */
+static int _build_qos_list(char *qos, list_t **qos_list, bool *qos_not,
+ bool break_on_failure)
+{
+ char *last = NULL, *tmp, *tok;
+ int rc = SLURM_SUCCESS;
+ assoc_mgr_lock_t locks = {
+ .qos = READ_LOCK,
+ };
+
+ xassert(qos_list);
+
+ *qos_not = false;
+
+ if (!qos)
+ return ESLURM_INVALID_QOS;
+
+ assoc_mgr_lock(&locks);
+
+ tmp = xstrdup(qos);
+ tok = strtok_r(tmp, ",", &last);
+ while (tok) {
+ if (tok[0] == '-') {
+ if (!*qos_list) {
+ *qos_not = true;
+ } else if (*qos_not != true) {
+ info("Reservation request has some not/qos");
+ rc = ESLURM_INVALID_QOS;
+ break;
+ }
+ tok++;
+ } else if (*qos_not != false) {
+ info("Reservation request has some not/qos");
+ rc = ESLURM_INVALID_QOS;
+ break;
+ }
+
+ if (!*qos_list)
+ *qos_list = list_create(NULL);
+ if (((rc = _append_to_qos_list(*qos_list, tok)) !=
+ SLURM_SUCCESS) && break_on_failure)
+ break;
+
+ tok = strtok_r(NULL, ",", &last);
+ }
+
+ if (rc != SLURM_SUCCESS)
+ FREE_NULL_LIST(*qos_list);
+
+ if (*qos_list) {
+ list_sort(*qos_list, _sort_qos_list_asc);
+ }
+
+ assoc_mgr_unlock(&locks);
+
+ xfree(tmp);
+
+ return rc;
+}
+
+/*
+ * Update a qos list for an existing reservation based upon an
+ * update comma delimited specification of qos to add (+name),
+ * remove (-name), or set value of
+ * IN/OUT resv_ptr - pointer to reservation structure being updated
+ * IN qos - a list of qos names, to set, add, or remove
+ * RETURN 0 on success
+ */
+static int _update_qos_list(slurmctld_resv_t *resv_ptr, char *qos)
+{
+ int rc = SLURM_SUCCESS;
+ char *last = NULL, *tmp, *tok;
+ bool minus_qos = false, cleared = false;
+ list_t *qos_list = NULL;
+ assoc_mgr_lock_t locks = {
+ .qos = READ_LOCK,
+ };
+
+ if (!qos)
+ return ESLURM_INVALID_QOS;
+
+ if (!resv_ptr->qos_list)
+ resv_ptr->qos_list = list_create(NULL);
+ qos_list = list_shallow_copy(resv_ptr->qos_list);
+
+ assoc_mgr_lock(&locks);
+
+ tmp = xstrdup(qos);
+ tok = strtok_r(tmp, ",", &last);
+ while (tok) {
+ if (tok[0] == '-') {
+ minus_qos = 1;
+ tok++;
+ } else if (tok[0] == '+') {
+ minus_qos = 0;
+ tok++;
+ } else if (tok[0] == '\0') {
+ continue;
+ } else {
+ /* The request is a completely new list */
+ if (!cleared) {
+ list_flush(qos_list);
+ resv_ptr->ctld_flags &= (~RESV_CTLD_QOS_NOT);
+ cleared = true;
+ }
+ }
+
+ if (resv_ptr->ctld_flags & RESV_CTLD_QOS_NOT) {
+ if (minus_qos)
+ rc = _append_to_qos_list(qos_list, tok);
+ else
+ rc = _remove_from_qos_list(qos_list, tok);
+ } else {
+ if (minus_qos)
+ rc = _remove_from_qos_list(qos_list, tok);
+ else
+ rc = _append_to_qos_list(qos_list, tok);
+ }
+
+ if (rc != SLURM_SUCCESS)
+ break;
+
+ tok = strtok_r(NULL, ",", &last);
+ }
+
+ if ((rc == SLURM_SUCCESS) && list_count(qos_list)) {
+ foreach_set_allow_str_t set_allow_str = {
+ .prefix = (resv_ptr->ctld_flags & RESV_CTLD_QOS_NOT) ?
+ "-" : "",
+ .str = &resv_ptr->qos,
+ };
+
+ list_sort(qos_list, _sort_qos_list_asc);
+ xfree(resv_ptr->qos);
+ (void) list_for_each(qos_list, _foreach_set_qos_name_str,
+ &set_allow_str);
+ FREE_NULL_LIST(resv_ptr->qos_list);
+ resv_ptr->qos_list = qos_list;
+ qos_list = NULL;
+ } else if (rc == SLURM_SUCCESS)
+ rc = ESLURM_INVALID_QOS;
+
+ assoc_mgr_unlock(&locks);
+
+ FREE_NULL_LIST(qos_list);
+ xfree(tmp);
+
+ return rc;
+}
+
+static int _set_access(slurmctld_resv_t *resv_ptr)
+{
+ int rc = SLURM_SUCCESS;
+
+ if ((rc = _set_assoc_list(resv_ptr)) != SLURM_SUCCESS)
+ return rc;
+
+ return rc;
+}
+
/* Post reservation create */
static int _post_resv_create(slurmctld_resv_t *resv_ptr)
{
@@ -2097,7 +2639,80 @@
end_relative = resv_ptr->end_time;
}
- if (protocol_version >= SLURM_MIN_PROTOCOL_VERSION) {
+ if (protocol_version >= SLURM_25_11_PROTOCOL_VERSION) {
+ packstr(resv_ptr->accounts, buffer);
+ packstr(resv_ptr->burst_buffer, buffer);
+ packstr(resv_ptr->comment, buffer);
+ pack32(resv_ptr->core_cnt, buffer);
+ pack_time(end_relative, buffer);
+ packstr(resv_ptr->features, buffer);
+ pack64(resv_ptr->flags, buffer);
+ packstr(resv_ptr->licenses, buffer);
+ pack32(resv_ptr->max_start_delay, buffer);
+ packstr(resv_ptr->name, buffer);
+ pack32(resv_ptr->node_cnt, buffer);
+ packstr(resv_ptr->node_list, buffer);
+ packstr(resv_ptr->partition, buffer);
+ pack32(resv_ptr->purge_comp_time, buffer);
+ pack_time(start_relative, buffer);
+ packstr(resv_ptr->tres_fmt_str, buffer);
+ packstr(resv_ptr->users, buffer);
+ packstr(resv_ptr->groups, buffer);
+ packstr(resv_ptr->qos, buffer);
+ packstr(resv_ptr->allowed_parts, buffer);
+
+ if (internal) {
+ packstr(resv_ptr->assoc_list, buffer);
+ pack32(resv_ptr->boot_time, buffer);
+ /*
+ * NOTE: Restoring core_bitmap directly only works if
+ * the system's node and core counts don't change.
+ * core_resrcs is used so configuration changes can be
+ * supported
+ */
+ pack_job_resources(resv_ptr->core_resrcs, buffer,
+ protocol_version);
+ pack32(resv_ptr->duration, buffer);
+ pack32(resv_ptr->resv_id, buffer);
+ pack_time(resv_ptr->start_time_prev, buffer);
+ pack_time(resv_ptr->start_time, buffer);
+ pack_time(resv_ptr->idle_start_time, buffer);
+ packstr(resv_ptr->tres_str, buffer);
+ pack32(resv_ptr->ctld_flags, buffer);
+ (void) gres_job_state_pack(resv_ptr->gres_list_alloc,
+ buffer, 0,
+ false,
+ protocol_version);
+ } else {
+ pack_bit_str_hex(resv_ptr->node_bitmap, buffer);
+ if (!resv_ptr->core_bitmap ||
+ !resv_ptr->core_resrcs ||
+ !resv_ptr->core_resrcs->node_bitmap ||
+ !resv_ptr->core_resrcs->core_bitmap ||
+ (bit_ffs(resv_ptr->core_bitmap) == -1)) {
+ pack32((uint32_t) 0, buffer);
+ } else {
+ core_resrcs = resv_ptr->core_resrcs;
+ i_cnt = bit_set_count(core_resrcs->node_bitmap);
+ pack32(i_cnt, buffer);
+ for (int i = 0;
+ (node_ptr =
+ next_node_bitmap(core_resrcs->node_bitmap,
+ &i));
+ i++) {
+ offset_start = cr_get_coremap_offset(i);
+ offset_end = cr_get_coremap_offset(i+1);
+ packstr(node_ptr->name, buffer);
+ core_str = bit_fmt_range(
+ resv_ptr->core_bitmap,
+ offset_start,
+ (offset_end - offset_start));
+ packstr(core_str, buffer);
+ xfree(core_str);
+ }
+ }
+ }
+ } else if (protocol_version >= SLURM_MIN_PROTOCOL_VERSION) {
packstr(resv_ptr->accounts, buffer);
packstr(resv_ptr->burst_buffer, buffer);
packstr(resv_ptr->comment, buffer);
@@ -2179,7 +2794,50 @@
resv_ptr = xmalloc(sizeof(slurmctld_resv_t));
resv_ptr->magic = RESV_MAGIC;
- if (protocol_version >= SLURM_MIN_PROTOCOL_VERSION) {
+ if (protocol_version >= SLURM_25_11_PROTOCOL_VERSION) {
+ safe_unpackstr(&resv_ptr->accounts, buffer);
+ safe_unpackstr(&resv_ptr->burst_buffer, buffer);
+ safe_unpackstr(&resv_ptr->comment, buffer);
+ safe_unpack32(&resv_ptr->core_cnt, buffer);
+ safe_unpack_time(&resv_ptr->end_time, buffer);
+ safe_unpackstr(&resv_ptr->features, buffer);
+ safe_unpack64(&resv_ptr->flags, buffer);
+ safe_unpackstr(&resv_ptr->licenses, buffer);
+ safe_unpack32(&resv_ptr->max_start_delay, buffer);
+ safe_unpackstr(&resv_ptr->name, buffer);
+
+ safe_unpack32(&resv_ptr->node_cnt, buffer);
+ safe_unpackstr(&resv_ptr->node_list, buffer);
+ safe_unpackstr(&resv_ptr->partition, buffer);
+ safe_unpack32(&resv_ptr->purge_comp_time, buffer);
+ safe_unpack_time(&resv_ptr->start_time_first, buffer);
+ safe_unpackstr(&resv_ptr->tres_fmt_str, buffer);
+ safe_unpackstr(&resv_ptr->users, buffer);
+ safe_unpackstr(&resv_ptr->groups, buffer);
+ safe_unpackstr(&resv_ptr->qos, buffer);
+ safe_unpackstr(&resv_ptr->allowed_parts, buffer);
+
+ /* Fields saved for internal use only (save state) */
+ safe_unpackstr(&resv_ptr->assoc_list, buffer);
+ safe_unpack32(&resv_ptr->boot_time, buffer);
+ if (unpack_job_resources(&resv_ptr->core_resrcs, buffer,
+ protocol_version) != SLURM_SUCCESS)
+ goto unpack_error;
+ safe_unpack32(&resv_ptr->duration, buffer);
+ safe_unpack32(&resv_ptr->resv_id, buffer);
+ safe_unpack_time(&resv_ptr->start_time_prev, buffer);
+ safe_unpack_time(&resv_ptr->start_time, buffer);
+ safe_unpack_time(&resv_ptr->idle_start_time, buffer);
+ safe_unpackstr(&resv_ptr->tres_str, buffer);
+ safe_unpack32(&resv_ptr->ctld_flags, buffer);
+ if (gres_job_state_unpack(&resv_ptr->gres_list_alloc, buffer,
+ 0, protocol_version) !=
+ SLURM_SUCCESS)
+ goto unpack_error;
+ gres_job_state_log(resv_ptr->gres_list_alloc, 0);
+ if (!resv_ptr->purge_comp_time)
+ resv_ptr->purge_comp_time = 300;
+ } else if (protocol_version >= SLURM_MIN_PROTOCOL_VERSION) {
uint32_t uint32_tmp;
safe_unpackstr(&resv_ptr->accounts, buffer);
safe_unpackstr(&resv_ptr->burst_buffer, buffer);
@@ -2565,8 +3223,7 @@
{
uint64_t cpu_cnt = 0;
node_record_t *node_ptr;
- char start_time[256], end_time[256], tmp_msd[40];
- char *name1, *name2, *name3, *val1, *val2, *val3;
+ char *name1;
assoc_mgr_lock_t locks = { .tres = READ_LOCK };
if ((resv_ptr->ctld_flags & RESV_CTLD_FULL_NODE) &&
@@ -2629,40 +3286,62 @@
CONVERT_NUM_UNIT_EXACT, 0, NULL);
assoc_mgr_unlock(&locks);
- slurm_make_time_str(&resv_ptr->start_time, start_time,
- sizeof(start_time));
- slurm_make_time_str(&resv_ptr->end_time, end_time, sizeof(end_time));
- if (resv_ptr->accounts) {
- name1 = " accounts=";
- val1 = resv_ptr->accounts;
- } else
- name1 = val1 = "";
- if (resv_ptr->users) {
- name2 = " users=";
- val2 = resv_ptr->users;
- } else
- name2 = val2 = "";
+ if (get_sched_log_level() >= LOG_LEVEL_INFO) {
+ char *tmp_str = NULL, *tmp_str_pos = NULL;
+ char start_time[256], end_time[256];
- if (resv_ptr->groups) {
- name3 = " groups=";
- val3 = resv_ptr->groups;
- } else
- name3 = val3 = "";
+ slurm_make_time_str(&resv_ptr->start_time, start_time,
+ sizeof(start_time));
+ slurm_make_time_str(&resv_ptr->end_time, end_time, sizeof(end_time));
+ xstrfmtcatat(tmp_str, &tmp_str_pos, "%sstart=%s end=%s",
+ tmp_str ? " " : "", start_time, end_time);
- if (resv_ptr->max_start_delay)
- secs2time_str(resv_ptr->max_start_delay,
- tmp_msd, sizeof(tmp_msd));
+ if (resv_ptr->accounts)
+ xstrfmtcatat(tmp_str, &tmp_str_pos, " accounts=%s",
+ resv_ptr->accounts);
+ if (resv_ptr->allowed_parts)
+ xstrfmtcatat(tmp_str, &tmp_str_pos, " allowed_parts=%s",
+ resv_ptr->allowed_parts);
+ if (resv_ptr->core_cnt)
+ xstrfmtcatat(tmp_str, &tmp_str_pos, " cores=%u",
+ resv_ptr->core_cnt);
+ if (resv_ptr->groups)
+ xstrfmtcatat(tmp_str, &tmp_str_pos, " groups=%s",
+ resv_ptr->groups);
+ if (resv_ptr->licenses)
+ xstrfmtcatat(tmp_str, &tmp_str_pos, " licenses=%s",
+ resv_ptr->licenses);
- sched_info("%s reservation=%s%s%s%s%s%s%s nodes=%s cores=%u "
- "licenses=%s tres=%s start=%s end=%s MaxStartDelay=%s "
- "Comment=%s",
- old_resv_ptr ? "Updated" : "Created",
- resv_ptr->name, name1, val1, name2, val2, name3, val3,
- resv_ptr->node_list, resv_ptr->core_cnt, resv_ptr->licenses,
- resv_ptr->tres_fmt_str,
- start_time, end_time,
- resv_ptr->max_start_delay ? tmp_msd : "",
- resv_ptr->comment ? resv_ptr->comment : "");
+ if (resv_ptr->max_start_delay) {
+ char tmp_msd[40];
+ secs2time_str(resv_ptr->max_start_delay,
+ tmp_msd, sizeof(tmp_msd));
+ xstrfmtcatat(tmp_str, &tmp_str_pos, " MaxStartDelay=%s",
+ tmp_msd);
+ }
+
+ if (resv_ptr->node_list)
+ xstrfmtcatat(tmp_str, &tmp_str_pos, " nodes=%s",
+ resv_ptr->node_list);
+ if (resv_ptr->qos)
+ xstrfmtcatat(tmp_str, &tmp_str_pos, " qos=%s",
+ resv_ptr->qos);
+ if (resv_ptr->tres_fmt_str)
+ xstrfmtcatat(tmp_str, &tmp_str_pos, " tres=%s",
+ resv_ptr->tres_fmt_str);
+ if (resv_ptr->users)
+ xstrfmtcatat(tmp_str, &tmp_str_pos, " users=%s",
+ resv_ptr->users);
+ if (resv_ptr->comment)
+ xstrfmtcatat(tmp_str, &tmp_str_pos, " comment='%s'",
+ resv_ptr->comment);
+
+ sched_info("%s reservation=%s %s",
+ old_resv_ptr ? "Updated" : "Created", resv_ptr->name,
+ tmp_str);
+
+ xfree(tmp_str);
+ }
if (old_resv_ptr)
_post_resv_update(resv_ptr, old_resv_ptr);
else
@@ -2846,8 +3525,11 @@
uid_t *user_list = NULL;
list_t *license_list = NULL;
uint32_t total_node_cnt = 0;
- bool account_not = false, user_not = false;
+ bool account_not = false, user_not = false, qos_not = false,
+ allowed_parts_not = false;
resv_select_t resv_select = { 0 };
+ list_t *qos_list = NULL;
+ list_t *allowed_parts_list = NULL;
_create_resv_lists(false);
@@ -3001,9 +3683,11 @@
goto bad_parse;
} else if (!resv_desc_ptr->accounts &&
- !resv_desc_ptr->users &&
- !resv_desc_ptr->groups) {
- info("Reservation request lacks users, accounts or groups");
+ !resv_desc_ptr->users &&
+ !resv_desc_ptr->qos &&
+ !resv_desc_ptr->allowed_parts &&
+ !resv_desc_ptr->groups) {
+ info("Reservation request lacks users, accounts, QOS, AllowedPartition or groups");
rc = ESLURM_RESERVATION_EMPTY;
goto bad_parse;
}
@@ -3030,7 +3714,43 @@
rc = ESLURM_GROUP_ID_MISSING;
goto bad_parse;
}
- info("processed groups %s", resv_desc_ptr->groups);
+ }
+
+ if (resv_desc_ptr->qos) {
+ foreach_set_allow_str_t set_allow_str = {
+ .str = &resv_desc_ptr->qos,
+ };
+
+ rc = _build_qos_list(resv_desc_ptr->qos, &qos_list,
+ &qos_not, true);
+ if (rc != SLURM_SUCCESS)
+ goto bad_parse;
+
+ set_allow_str.prefix = qos_not ? "-" : "";
+ xfree(resv_desc_ptr->qos);
+ (void) list_for_each(qos_list, _foreach_set_qos_name_str,
+ &set_allow_str);
+ }
+
+ if (resv_desc_ptr->allowed_parts) {
+ foreach_set_allow_str_t set_allow_str = {
+ .str = &resv_desc_ptr->allowed_parts,
+ };
+
+ rc = _build_allowed_parts_list(
+ resv_desc_ptr->allowed_parts,
+ &allowed_parts_list,
+ &allowed_parts_not,
+ true);
+ if (rc != SLURM_SUCCESS)
+ goto bad_parse;
+
+ set_allow_str.prefix = allowed_parts_not ? "-" : "";
+ xfree(resv_desc_ptr->allowed_parts);
+ (void) list_for_each(allowed_parts_list,
+ _foreach_set_allowed_parts_name_str,
+ &set_allow_str);
+ resv_select.allowed_parts_list = allowed_parts_list;
}
if (resv_desc_ptr->licenses) {
@@ -3241,6 +3961,10 @@
resv_ptr->ctld_flags |= RESV_CTLD_USER_NOT;
if (account_not)
resv_ptr->ctld_flags |= RESV_CTLD_ACCT_NOT;
+ if (qos_not)
+ resv_ptr->ctld_flags |= RESV_CTLD_QOS_NOT;
+ if (allowed_parts_not)
+ resv_ptr->ctld_flags |= RESV_CTLD_ALLOWED_PARTS_NOT;
resv_ptr->duration = resv_desc_ptr->duration;
if (resv_desc_ptr->purge_comp_time != NO_VAL)
@@ -3288,6 +4012,14 @@
resv_ptr->user_cnt = user_cnt;
resv_ptr->user_list = user_list;
user_list = NULL;
+ resv_ptr->qos = resv_desc_ptr->qos;
+ resv_desc_ptr->qos = NULL;
+ resv_ptr->qos_list = qos_list;
+ qos_list = NULL;
+ resv_ptr->allowed_parts = resv_desc_ptr->allowed_parts;
+ resv_desc_ptr->allowed_parts = NULL;
+ resv_ptr->allowed_parts_list = allowed_parts_list;
+ allowed_parts_list = NULL;
if (!(resv_desc_ptr->flags & RESERVE_FLAG_GRES_REQ) &&
(resv_desc_ptr->core_cnt == NO_VAL)) {
@@ -3300,7 +4032,7 @@
resv_ptr->ctld_flags &= (~RESV_CTLD_FULL_NODE);
}
- if ((rc = _set_assoc_list(resv_ptr)) != SLURM_SUCCESS) {
+ if ((rc = _set_access(resv_ptr)) != SLURM_SUCCESS) {
_del_resv_rec(resv_ptr);
goto bad_parse;
}
@@ -3323,6 +4055,8 @@
xfree(account_list[i]);
xfree(account_list);
FREE_NULL_LIST(license_list);
+ FREE_NULL_LIST(qos_list);
+ FREE_NULL_LIST(allowed_parts_list);
_free_resv_select_members(&resv_select);
xfree(user_list);
return rc;
@@ -3368,10 +4102,14 @@
}
static int _validate_and_set_partition(part_record_t **part_ptr,
- char **partition)
+ char **partition,
+ list_t *allowed_parts_list)
{
if (*part_ptr == NULL) {
- *part_ptr = default_part_loc;
+ if (allowed_parts_list && list_count(allowed_parts_list))
+ *part_ptr = list_peek(allowed_parts_list);
+ else
+ *part_ptr = default_part_loc;
if (*part_ptr == NULL)
return ESLURM_DEFAULT_PARTITION_NOT_SET;
}
@@ -3736,10 +4474,29 @@
goto update_failure;
}
+ if (resv_desc_ptr->qos) {
+ rc = _update_qos_list(resv_ptr, resv_desc_ptr->qos);
+ if (rc) {
+ error_code = rc;
+ goto update_failure;
+ }
+ }
+
+ if (resv_desc_ptr->allowed_parts) {
+ rc = _update_allowed_parts_list(resv_ptr,
+ resv_desc_ptr->allowed_parts);
+ if (rc) {
+ error_code = rc;
+ goto update_failure;
+ }
+ }
+
if (!resv_ptr->users &&
!resv_ptr->accounts &&
+ !resv_ptr->qos &&
+ !resv_ptr->allowed_parts &&
!resv_ptr->groups) {
- info("Reservation %s request lacks users, accounts or groups",
+ info("Reservation %s request lacks users, accounts, AllowedPartitions, QOS or groups",
resv_desc_ptr->name);
error_code = ESLURM_RESERVATION_EMPTY;
goto update_failure;
@@ -3920,13 +4677,13 @@
}
/* This needs to be after checks for both account and user changes */
- if ((error_code = _set_assoc_list(resv_ptr)) != SLURM_SUCCESS)
+ if ((error_code = _set_access(resv_ptr)) != SLURM_SUCCESS)
goto update_failure;
/*
* Reject reservation update if we have pending or running jobs using
* the reservation, that lose access to the reservation by the update.
- * This has to happen after _set_assoc_list
+ * This has to happen after _set_access
*/
if ((job_ptr = list_find_first(job_list,
_validate_reservation_access_update,
@@ -4331,6 +5088,33 @@
}
}
+ if (resv_ptr->qos) {
+ bool qos_not; /* we don't care about this */
+ (void) _build_qos_list(resv_ptr->qos,
+ &resv_ptr->qos_list,
+ &qos_not, false);
+
+ if (!resv_ptr->qos_list || !list_count(resv_ptr->qos_list)) {
+ error("Reservation %s has invalid QOS (%s)",
+ resv_ptr->name, resv_ptr->qos);
+ return false;
+ }
+ }
+
+ if (resv_ptr->allowed_parts) {
+ bool allowed_parts_not; /* we don't care about this */
+ (void) _build_allowed_parts_list(resv_ptr->allowed_parts,
+ &resv_ptr->allowed_parts_list,
+ &allowed_parts_not, false);
+
+ if (!resv_ptr->allowed_parts_list ||
+ !list_count(resv_ptr->allowed_parts_list)) {
+ error("Reservation %s has invalid AllowedPartitions (%s)",
+ resv_ptr->name, resv_ptr->allowed_parts);
+ return false;
+ }
+ }
+
if ((resv_ptr->flags & RESERVE_FLAG_PART_NODES) &&
resv_ptr->part_ptr && resv_ptr->part_ptr->node_bitmap) {
memset(&old_resv_ptr, 0, sizeof(slurmctld_resv_t));
@@ -4500,7 +5284,7 @@
_clear_job_resv(resv_ptr);
list_delete_item(iter);
} else {
- _set_assoc_list(resv_ptr);
+ _set_access(resv_ptr);
top_suffix = MAX(top_suffix, resv_ptr->resv_id);
_validate_node_choice(resv_ptr);
}
@@ -4522,7 +5306,9 @@
int i, add_nodes, new_nodes, preserve_nodes, busy_nodes_needed;
bool log_it = true;
bool replaced = false;
- resv_select_t resv_select = { 0 };
+ resv_select_t resv_select = {
+ .allowed_parts_list = resv_ptr->allowed_parts_list,
+ };
/* Identify nodes which can be preserved in this reservation */
preserve_bitmap = bit_copy(resv_ptr->node_bitmap);
@@ -4667,7 +5453,9 @@
{
int i;
resv_desc_msg_t resv_desc;
- resv_select_t resv_select = { 0 };
+ resv_select_t resv_select = {
+ .allowed_parts_list = resv_ptr->allowed_parts_list,
+ };
if ((resv_ptr->node_bitmap == NULL) ||
(!(resv_ptr->ctld_flags & RESV_CTLD_FULL_NODE) &&
@@ -5013,7 +5801,9 @@
bitstr_t *tmp2_bitmap = NULL;
int delta_node_cnt, i, rc;
resv_desc_msg_t resv_desc;
- resv_select_t resv_select = { 0 };
+ resv_select_t resv_select = {
+ .allowed_parts_list = resv_ptr->allowed_parts_list,
+ };
delta_node_cnt = resv_ptr->node_cnt - node_cnt;
if (delta_node_cnt == 0) /* Already correct node count */
@@ -5057,7 +5847,8 @@
/* Ensure if partition exists in reservation otherwise use default */
if ((rc = _validate_and_set_partition(&resv_ptr->part_ptr,
- &resv_ptr->partition))) {
+ &resv_ptr->partition,
+ resv_ptr->allowed_parts_list))) {
return rc;
}
@@ -5313,7 +6104,9 @@
job_record_t *job_ptr;
if ((rc = _validate_and_set_partition(part_ptr,
- &resv_desc_ptr->partition))) {
+ &resv_desc_ptr->partition,
+ resv_select_ret->
+ allowed_parts_list))) {
return rc;
}
@@ -6254,6 +7047,9 @@
char tmp_char[30];
slurmdb_assoc_rec_t *assoc;
if (!resv_ptr->assoc_list) {
+ if (resv_ptr->qos_list || resv_ptr->allowed_parts_list)
+ return SLURM_SUCCESS;
+
error("Reservation %s has no association list. "
"Checking user/account lists",
resv_ptr->name);
@@ -6354,6 +7150,98 @@
}
/*
+ * Check if user is requesting a QOS that isn't
+ * allowed in the reservation.
+ * RET SLURM_SUCCESS if true, some error code otherwise
+ */
+static int _valid_job_access_resv_at_sched(job_record_t *job_ptr,
+ slurmctld_resv_t *resv_ptr)
+{
+ int rc = SLURM_SUCCESS;
+
+ if (validate_slurm_user(job_ptr->user_id))
+ return SLURM_SUCCESS;
+
+ /* Check QOS */
+ if (resv_ptr->qos_list) {
+ slurmdb_qos_rec_t *qos_ptr = NULL;
+ if (!job_ptr->qos_ptr) {
+ slurmdb_qos_rec_t qos_rec = {
+ .id = job_ptr->qos_id,
+ };
+ /*
+ * This should never be called, but just to be
+ * safe we will try to fill it in.
+ */
+ if (assoc_mgr_fill_in_qos(
+ acct_db_conn, &qos_rec,
+ accounting_enforce,
+ &job_ptr->qos_ptr, false) ||
+ !job_ptr->qos_ptr) {
+ return ESLURM_INVALID_QOS;
+ }
+ }
+
+ /*
+ * Since we do not allow mixed state check the list's pointers.
+ */
+ qos_ptr = list_find_first(resv_ptr->qos_list,
+ slurm_find_ptr_in_list,
+ job_ptr->qos_ptr);
+
+ if (resv_ptr->ctld_flags & RESV_CTLD_QOS_NOT) {
+ if (qos_ptr) { /* explicitly denied */
+ rc = ESLURM_INVALID_QOS;
+ }
+ } else if (!qos_ptr) { /* not allowed */
+ rc = ESLURM_INVALID_QOS;
+ }
+
+ if (rc != SLURM_SUCCESS) {
+ debug2("%pJ attempted to use reservation '%s' with QOS '%s' not allowed in reservation (%s)",
+ job_ptr,
+ resv_ptr->name,
+ job_ptr->qos_ptr->name,
+ resv_ptr->qos);
+ return rc;
+ }
+ }
+
+ /* Check AllowedPartitions */
+ if (resv_ptr->allowed_parts_list) {
+ part_record_t *part_ptr = NULL;
+
+ xassert(job_ptr->part_ptr);
+
+ /*
+ * Since we do not allow mixed state check the list's pointers.
+ */
+ part_ptr = list_find_first(resv_ptr->allowed_parts_list,
+ slurm_find_ptr_in_list,
+ job_ptr->part_ptr);
+
+ if (resv_ptr->ctld_flags & RESV_CTLD_ALLOWED_PARTS_NOT) {
+ if (part_ptr) { /* explicitly denied */
+ rc = ESLURM_INVALID_PARTITION_NAME;
+ }
+ } else if (!part_ptr) { /* not allowed */
+ rc = ESLURM_INVALID_PARTITION_NAME;
+ }
+
+ if (rc != SLURM_SUCCESS) {
+ debug2("%pJ attempted to use reservation '%s' with partition '%s' not allowed in reservation (%s)",
+ job_ptr,
+ resv_ptr->name,
+ job_ptr->part_ptr->name,
+ resv_ptr->allowed_parts);
+ return rc;
+ }
+ }
+
+ return rc;
+}
+
+/*
* Determine if a job can start now based only upon reservations
*
* IN job_ptr - job to test
@@ -6768,6 +7656,11 @@
rc2 = _valid_job_access_resv(job_ptr, resv_ptr, true);
if (rc2 != SLURM_SUCCESS)
return rc2;
+
+ rc2 = _valid_job_access_resv_at_sched(job_ptr, resv_ptr);
+ if (rc2 != SLURM_SUCCESS)
+ return rc2;
+
/*
* Just in case the reservation was altered since last looking
* we want to make sure things are good in the database.
@@ -7093,7 +7986,7 @@
tmp_uids = NULL;
/* Now update the associations to match */
- (void)_set_assoc_list(resv_ptr);
+ (void) _set_access(resv_ptr);
/* Now see if something really did change */
if (!slurm_with_slurmdbd() ||
@@ -7643,7 +8536,7 @@
iter = list_iterator_create(resv_list);
while ((resv_ptr = list_next(iter)))
- _set_assoc_list(resv_ptr);
+ _set_access(resv_ptr);
list_iterator_destroy(iter);
unlock_slurmctld(node_write_lock);
diff --git a/src/slurmctld/slurmctld.h b/src/slurmctld/slurmctld.h
index c61ea9d..429591a 100644
--- a/src/slurmctld/slurmctld.h
+++ b/src/slurmctld/slurmctld.h
@@ -335,12 +335,14 @@
* RESERVATION parameters and data structures
\*****************************************************************************/
-#define RESV_CTLD_ACCT_NOT 0x00000001
-#define RESV_CTLD_USER_NOT 0x00000002
-#define RESV_CTLD_FULL_NODE 0x00000004
-#define RESV_CTLD_NODE_FLAGS_SET 0x00000008
-#define RESV_CTLD_EPILOG 0x00000010
-#define RESV_CTLD_PROLOG 0x00000020
+#define RESV_CTLD_ACCT_NOT SLURM_BIT(0)
+#define RESV_CTLD_USER_NOT SLURM_BIT(1)
+#define RESV_CTLD_FULL_NODE SLURM_BIT(2)
+#define RESV_CTLD_NODE_FLAGS_SET SLURM_BIT(3)
+#define RESV_CTLD_EPILOG SLURM_BIT(4)
+#define RESV_CTLD_PROLOG SLURM_BIT(5)
+#define RESV_CTLD_QOS_NOT SLURM_BIT(6)
+#define RESV_CTLD_ALLOWED_PARTS_NOT SLURM_BIT(7)
typedef struct slurmctld_resv {
uint16_t magic; /* magic cookie, RESV_MAGIC */
@@ -348,6 +350,9 @@
char *accounts; /* names of accounts permitted to use */
int account_cnt; /* count of accounts permitted to use */
char **account_list; /* list of accounts permitted to use */
+ char *allowed_parts; /* names of partitions permitted to use */
+ list_t *allowed_parts_list; /* pointers to partitions permitted or not
+ * to use */
char *assoc_list; /* list of associations */
uint32_t boot_time; /* time it would take to reboot a node */
char *burst_buffer; /* burst buffer resources */
@@ -385,6 +390,8 @@
* minutes this reservation will sit idle
* until it is revoked.
*/
+ char *qos; /* names of qos permitted to use */
+ list_t *qos_list; /* pointers to qos permitted or not to use */
uint32_t resv_id; /* unique reservation ID, internal use */
time_t start_time; /* start time of reservation */
time_t start_time_first;/* when the reservation first started */
diff --git a/src/slurmrestd/plugins/openapi/slurmdbd/LICENSE-openapi.json b/src/slurmrestd/plugins/openapi/slurmdbd/LICENSE-openapi.json
index f470ffb..ff19979 100644
--- a/src/slurmrestd/plugins/openapi/slurmdbd/LICENSE-openapi.json
+++ b/src/slurmrestd/plugins/openapi/slurmdbd/LICENSE-openapi.json
@@ -9,11 +9,3 @@
Or, under the terms of The Apache License, Version 2.0, and abbreviated as
"Apache 2.0" within the OpenAPI specification itself. A copy of this license is
available at https://www.apache.org/licenses/LICENSE-2.0.html.
-
-As an additional requirement for changes to this specification: Contributors
-wishing to submit changes to these files must sign SchedMD's Contributor
-License Agreement ("CLA"). (https://slurm.schedmd.com/contributor.html).
-By signing the CLA, contributors are specifically licensing any changes,
-modifications, revisions, or edits to the OpenAPI specification for Slurm’s
-RESTful interface back to SchedMD to the maximum extent permitted by the
-relevant law and the CLA.
diff --git a/src/slurmrestd/plugins/openapi/slurmdbd/qos.c b/src/slurmrestd/plugins/openapi/slurmdbd/qos.c
index dbafd41..b1ad265 100644
--- a/src/slurmrestd/plugins/openapi/slurmdbd/qos.c
+++ b/src/slurmrestd/plugins/openapi/slurmdbd/qos.c
@@ -176,6 +176,7 @@
qos_list, ctxt);
} else if (ctxt->method == HTTP_REQUEST_POST) {
openapi_resp_single_t post = { 0 };
+ openapi_resp_single_t *post_ptr = &post;
if (!DATA_PARSE(ctxt->parser, OPENAPI_SLURMDBD_QOS_RESP, post,
ctxt->query, ctxt->parent_path) &&
@@ -183,6 +184,7 @@
qos_list = post.response;
update_qos(ctxt, true, qos_list);
}
+ FREE_OPENAPI_RESP_COMMON_CONTENTS(post_ptr);
} else {
resp_error(ctxt, ESLURM_REST_INVALID_QUERY, __func__,
"Unsupported HTTP method requested: %s",
diff --git a/testsuite/README b/testsuite/README
index 113a79a..c11087e 100644
--- a/testsuite/README
+++ b/testsuite/README
@@ -955,6 +955,8 @@
test_123_2 Test RESV_DEL_HOLD
test_123_3 Test overlapping reservations.
test_123_4 Test reservation access
+test_123_5 Test reservation access based on QOS
+test_123_6 Test reservation access based on Partition
test_124_# Testing of AccountingStorageEnforce.
=================================================
diff --git a/testsuite/python/lib/atf.py b/testsuite/python/lib/atf.py
index 13915e9..95ba0a2 100644
--- a/testsuite/python/lib/atf.py
+++ b/testsuite/python/lib/atf.py
@@ -1286,22 +1286,23 @@
return tuple(int(part) if part.isdigit() else 0 for part in version_str.split("."))
-def require_version(version, component="sbin/slurmctld", slurm_prefix=""):
+def require_version(version, component="sbin/slurmctld", slurm_prefix="", reason=None):
"""Checks if the component is at least the required version, or skips.
Args:
version (tuple): The tuple representing the version.
component (string): The bin/ or sbin/ component of Slurm to check.
slurm_prefix (string): The path where the component is. By default the defined in testsuite.conf.
+ reason (string): The reason why the version of the component is required.
Returns:
A tuple representing the version. E.g. (25.05.0).
"""
component_version = get_version(component, slurm_prefix)
if component_version < version:
- pytest.skip(
- f"The version of {component} is {component_version}, required is {version}"
- )
+ if not reason:
+ reason = f"The version of {component} is {component_version}, required is {version}"
+ pytest.skip(reason)
def request_slurmrestd(request):
diff --git a/testsuite/python/tests/test_123_5.py b/testsuite/python/tests/test_123_5.py
new file mode 100644
index 0000000..9b20de8
--- /dev/null
+++ b/testsuite/python/tests/test_123_5.py
@@ -0,0 +1,108 @@
+############################################################################
+# Copyright (C) SchedMD LLC.
+############################################################################
+import os
+import pytest
+import atf
+
+test_name = os.path.splitext(os.path.basename(__file__))[0]
+qos1 = f"{test_name}_qos"
+acct1 = f"{test_name}_acct"
+res_name = f"{test_name}_resv"
+testuser = atf.properties["test-user"]
+
+
+@pytest.fixture(scope="module", autouse=True)
+def setup():
+ global local_cluster_name
+
+ atf.require_version(
+ (25, 11),
+ "bin/scontrol",
+ reason="Creating reservations with qos= added in scontrol 25.11",
+ )
+ atf.require_accounting(True)
+ atf.require_slurm_running()
+ atf.require_config_parameter_includes("AccountingStorageEnforce", "qos")
+ local_cluster_name = atf.get_config_parameter("ClusterName")
+
+ # Create test QOS and User that can use it
+ atf.run_command(
+ f"sacctmgr -i add qos {qos1}",
+ user=atf.properties["slurm-user"],
+ )
+ atf.run_command(
+ f"sacctmgr -i add account {acct1} cluster={local_cluster_name}",
+ user=atf.properties["slurm-user"],
+ )
+ atf.run_command(
+ f"sacctmgr -i add user {testuser} cluster={local_cluster_name} account={acct1} qos=normal,{qos1}",
+ user=atf.properties["slurm-user"],
+ )
+
+ atf.run_command(
+ f"sacctmgr -i add user {testuser} cluster={local_cluster_name} wckey={acct1}",
+ user=atf.properties["slurm-user"],
+ )
+ # Create the reservation for QOS test
+ result = atf.run_command(
+ f"scontrol create reservationname={res_name} qos={qos1} start=now duration=1 nodecnt=1",
+ user=atf.properties["slurm-user"],
+ )
+ assert result["exit_code"] == 0, "Couldn't create the reservation!"
+
+ yield
+
+ atf.run_command(
+ f"scontrol delete reservation {res_name}",
+ user=atf.properties["slurm-user"],
+ quiet=True,
+ )
+ atf.run_command(
+ f"sacctmgr -i remove user {testuser} wckey={acct1}",
+ user=atf.properties["slurm-user"],
+ quiet=True,
+ )
+ atf.run_command(
+ f"sacctmgr -i remove user {testuser} account={acct1}",
+ user=atf.properties["slurm-user"],
+ quiet=True,
+ )
+ atf.run_command(
+ f"sacctmgr -i remove account {acct1}",
+ user=atf.properties["slurm-user"],
+ quiet=True,
+ )
+ atf.run_command(
+ f"sacctmgr -i remove qos {qos1}",
+ user=atf.properties["slurm-user"],
+ quiet=True,
+ )
+
+
+def test_reservation_qos():
+ """Test that a reservation created for QOS {qos1} can't be used by atf"""
+
+ # Try to run a job as in the wrong QOS
+ result = atf.run_command(
+ f"srun -N1 --reservation={res_name} --account={acct1} --qos=normal true",
+ user=atf.properties["test-user"],
+ )
+ assert (
+ result["exit_code"] != 0
+ ), "The job should have been denied! {result[exit_code]}"
+ assert (
+ "Problem using reservation" in result["stderr"]
+ ), "The job should have been denied!"
+
+ # Try to run a job as in the correct QOS
+ result = atf.run_command(
+ f"srun -N1 --reservation={res_name} --account={acct1} --qos=normal,{qos1} true",
+ user=atf.properties["test-user"],
+ )
+ assert (
+ result["exit_code"] == 0
+ ), "ExitCode wasn't 0. The job should not have been denied!"
+ assert (
+ "Problem using reservation" not in result["stderr"]
+ ), "The job should not have been denied for user!"
diff --git a/testsuite/python/tests/test_123_6.py b/testsuite/python/tests/test_123_6.py
new file mode 100644
index 0000000..1f940c8
--- /dev/null
+++ b/testsuite/python/tests/test_123_6.py
@@ -0,0 +1,102 @@
+############################################################################
+# Copyright (C) SchedMD LLC.
+############################################################################
+import os
+import pytest
+import atf
+
+test_name = os.path.splitext(os.path.basename(__file__))[0]
+partitions = [f"{test_name}_part1", f"{test_name}_part2"]
+res_name = f"{test_name}_resv"
+testuser = atf.properties["test-user"]
+acct1 = f"{test_name}_acct"
+
+
+@pytest.fixture(scope="module", autouse=True)
+def setup():
+ global local_cluster_name
+
+ atf.require_version(
+ (25, 11),
+ "bin/scontrol",
+ reason="Creating reservations with allowedpartition= added in scontrol 25.11",
+ )
+ atf.require_accounting(True)
+ atf.require_slurm_running()
+ local_cluster_name = atf.get_config_parameter("ClusterName")
+
+ # Create test Partitions
+ for part in partitions:
+ atf.run_command(
+ f"scontrol create partitionname={part} Nodes=ALL",
+ user=atf.properties["slurm-user"],
+ fatal=True,
+ )
+
+ atf.run_command(
+ f"sacctmgr -i add account {acct1} cluster={local_cluster_name}",
+ user=atf.properties["slurm-user"],
+ )
+ atf.run_command(
+ f"sacctmgr -i add user {testuser} cluster={local_cluster_name} account={acct1}",
+ user=atf.properties["slurm-user"],
+ )
+
+ # Create the reservation for partition 1
+ result = atf.run_command(
+ f"scontrol create reservationname={res_name} allowedpartition={partitions[0]} start=now duration=1 nodecnt=1",
+ user=atf.properties["slurm-user"],
+ )
+ assert result["exit_code"] == 0, "Couldn't create the reservation!"
+
+ yield
+
+ atf.run_command(
+ f"scontrol delete reservation {res_name}",
+ user=atf.properties["slurm-user"],
+ quiet=True,
+ )
+ atf.run_command(
+ f"sacctmgr -i remove user {testuser} wckey={acct1}",
+ user=atf.properties["slurm-user"],
+ quiet=True,
+ )
+ atf.run_command(
+ f"sacctmgr -i remove account {acct1}",
+ user=atf.properties["slurm-user"],
+ quiet=True,
+ )
+ for part in partitions:
+ atf.run_command(
+ f"scontrol delete partitionname={part}",
+ user=atf.properties["slurm-user"],
+ quiet=True,
+ )
+
+
+def test_reservation_partition():
+ """Test that a reservation created for Partition {partitions[0]} can't be used by atf"""
+
+ # Try to run a job as in the wrong partition
+ result = atf.run_command(
+ f"srun -N1 --reservation={res_name} --partition={partitions[1]} true",
+ user=atf.properties["test-user"],
+ )
+ assert (
+ result["exit_code"] != 0
+ ), "The job should have been denied! {result[exit_code]}"
+ assert (
+ "Problem using reservation" in result["stderr"]
+ ), "The job should have been denied!"
+
+ # Try to run a job that can use either partition
+ result = atf.run_command(
+ f"srun -N1 --reservation={res_name} --partition={partitions[1]},{partitions[0]} true",
+ user=atf.properties["test-user"],
+ )
+ assert (
+ result["exit_code"] == 0
+ ), "ExitCode wasn't 0. The job should not have been denied!"
+ assert (
+ "Problem using reservation" not in result["stderr"]
+ ), "The job should not have been denied for user!"