blob: 95095f64f8da568fa11102222ded8be390f1a5b5 [file] [edit]
/*****************************************************************************\
* hres.h - Hierarchical resource (HRES) plugin interface.
*****************************************************************************
* Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
*
* This file is part of Slurm, a resource management program.
* For details, see <https://slurm.schedmd.com/>.
* Please also read the included file: DISCLAIMER.
*
* Slurm is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* In addition, as a special exception, the copyright holders give permission
* to link the code of portions of this program with the OpenSSL library under
* certain conditions as described in each individual source file, and
* distribute linked combinations including the two. You must obey the GNU
* General Public License in all respects for all of the code used other than
* OpenSSL. If you modify file(s) with this exception, you may extend this
* exception to your version of the file(s), but you are not obligated to do
* so. If you do not wish to do so, delete this exception statement from your
* version. If you delete this exception statement from all source files in
* the program, then also delete it here.
*
* Slurm is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along
* with Slurm; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
\*****************************************************************************/
#ifndef _INTERFACES_HRES_H
#define _INTERFACES_HRES_H
#include "slurm/slurm.h"
#include "src/common/list.h"
#include "src/slurmctld/licenses.h"
/*
* One context per hierarchical resource that configures a plugin. Multiple
* contexts may be backed by the same plugin type.
*/
typedef struct {
list_t *config; /* list of hres_config_t, read-only for plugin */
uint16_t hres_id; /* HRES this context serves */
int idx; /* index into ops[]/g_context[] */
time_t last_maintenance; /* time of last maintenance run, 0 if never */
uint32_t maintenance_cycle; /* seconds between maintenance runs */
uint32_t maintenance_timeout; /* max seconds for a maintenance run */
char *name; /* hierarchical resource name */
void *plugin_ctx; /* per-context state, alloc/free'd by the plugin */
} hres_ctx_t;
/* Job identification handed to the per job plugin callbacks. */
typedef struct {
char *account;
time_t end_time; /* expected, as of step start in tasks; actual in the
* controller and non-RunInJob node job epilogs */
uint32_t group_id;
char *nodes; /* nodes allocated to the job */
char *partition; /* partition the job runs in */
slurm_step_id_t step_id; /* job identification, carries the sluid */
uint32_t user_id;
char *user_name; /* NULL if not known without a user lookup */
} hres_job_t;
/* One layer a job holds, as sent to its nodes. */
typedef struct {
uint32_t count; /* amount charged against this layer */
char *layer_name;
char *nodes; /* job's nodes under this layer, frozen at prep build */
} hres_prep_layer_t;
/*
* Everything a node needs to run a job's callbacks for one hierarchical
* resource. resources.yaml is only read by the controller, so this travels in
* the job credential.
*/
typedef struct {
bool async_running; /* async pre-run started, its result not stored,
* only meaningful to the controller */
list_t *config; /* list of hres_config_t, the plugin configuration */
uint16_t hres_id; /* DON'T PACK, only meaningful to the controller */
list_t *layers; /* list of hres_prep_layer_t the job holds */
char *name; /* hierarchical resource name */
void *payload; /* opaque blob from the controller pre-run */
uint32_t payload_size;
list_t *plugin_args; /* list of hres_config_t the job requested */
uint32_t plugin_id; /* plugin serving the HRES, routes node callbacks */
bool prerun_done; /* DON'T PACK, the pre-run succeeded */
} hres_prep_t;
/* Free a hres_prep_layer_t, suitable as a list destructor. */
extern void hres_prep_layer_free(void *x);
/* Free a hres_prep_t, suitable as a list destructor. */
extern void hres_prep_free(void *x);
/*
* Deep copy a list of hres_prep_t.
*
* IN src - list to copy, may be NULL
* RET list of hres_prep_t or NULL, caller must FREE_NULL_LIST()
*/
extern list_t *hres_prep_copy_list(list_t *src);
/* Pack a list of hres_prep_t. */
extern int hres_prep_pack_list(list_t *in, buf_t *buffer,
uint16_t protocol_version);
/*
* Unpack a list of hres_prep_t packed by hres_prep_pack_list().
*
* OUT out - list of hres_prep_t, caller must FREE_NULL_LIST()
* RET SLURM_SUCCESS or SLURM_ERROR
*/
extern int hres_prep_unpack_list(list_t **out, buf_t *buffer,
uint16_t protocol_version);
/* The slurmctld services the HRES interface uses on the controller. */
typedef struct {
/*
* Describe the job's HRES charges for its nodes. Only hres_id, name
* and layers are set, the HRES interface adds the plugin part.
* RET list of hres_prep_t or NULL, caller must FREE_NULL_LIST()
*/
list_t *(*job_prep_list)(job_record_t *job_ptr);
/*
* Apply the disable or down verdict of each hres_layer_t to its layer.
* IN locked - true if the caller holds the conf write lock and
* license_mutex, otherwise they are taken
* IN requeue - requeue the jobs on a down layer, only when not locked
*/
void (*layers_apply)(list_t *layers, bool locked, bool requeue);
/*
* Snapshot the current per-layer state of one hierarchical resource.
* IN locked - true if the caller holds license_mutex
* RET list of hres_layer_t, caller must FREE_NULL_LIST()
*/
list_t *(*layers_snapshot)(uint16_t hres_id, bool locked);
} hres_callbacks_t;
/*
* Initialize the HRES plugin interface and load the plugins. May be called
* again to load plugin types referenced by a new configuration.
*
* IN hres_recs - cluster license list (list_t of licenses_t); only the plugin
* types it references are loaded. NULL loads every installed
* HRES plugin.
* IN callbacks - slurmctld services, NULL outside slurmctld
* RET SLURM_SUCCESS or a Slurm errno
*/
extern int hres_g_init(list_t *hres_recs, hres_callbacks_t *callbacks);
/*
* Create one context per hierarchical resource that defines a plugin and call
* each plugin's create_context callback. A resource without a plugin is
* ignored. The plugin allocates and owns the context's plugin_ctx.
*
* Any existing context is destroyed first, so this may be called again to pick
* up a new configuration.
*
* IN hres_recs - cluster license list (list_t of licenses_t); records whose
* hres_rec.plugin_type is set define an HRES plugin context
* RET SLURM_SUCCESS or a Slurm errno
*/
extern int hres_g_create_context(list_t *hres_recs);
/*
* Destroy all contexts by calling each plugin's destroy_context callback. The
* plugin frees the context's plugin_ctx.
*
* RET SLURM_SUCCESS or a Slurm errno
*/
extern int hres_g_destroy_context(void);
/*
* Terminate the HRES plugin interface and unload the plugins.
*
* RET SLURM_SUCCESS or a Slurm errno
*/
extern int hres_g_fini(void);
/*
* Start the maintenance thread that periodically invokes each context's
* plugin maintenance callback. A no-op when no context is configured.
*
* RET SLURM_SUCCESS or a Slurm errno
*/
extern int hres_g_maintenance_start(void);
/*
* Stop and join the maintenance thread. Safe to call when not running.
*
* RET SLURM_SUCCESS or a Slurm errno
*/
extern int hres_g_maintenance_stop(void);
/*
* Stop the maintenance thread and wait until the background work of every
* plugin has finished and been applied to slurmctld state. No controller
* callback may start after this. Must be called without slurmctld locks, the
* plugins may need them to finish.
*
* RET SLURM_SUCCESS or a Slurm errno
*/
extern int hres_g_controller_stop(void);
/*
* Check the plugin arguments a request gives for one HRES. Without a plugin
* they are accepted and ignored.
*
* IN hres_id - the requested HRES
* IN plugin_args - list of hres_config_t, NULL when none are given
* RET SLURM_SUCCESS or an error to reject the request
*/
extern int hres_g_validate_args(uint16_t hres_id, list_t *plugin_args);
/*
* Describe the job's HRES on job_ptr->hres_prep and run the controller pre-run
* callback of every plugin it draws HRES from, in the order the resources are
* defined in resources.yaml. Each plugin may fill in the payload its own nodes
* receive. Stops at the first failure. Runs after the job is scheduled and
* before its nodes are allocated.
*
* IN job_ptr - job to run the callbacks for
* OUT err_msg - plugin supplied failure reason, caller must xfree()
* RET SLURM_SUCCESS, ESLURM_HRES_REMOTE_FAIL to leave the job pending and
* retry later, or any other error to hold the job
*/
extern int hres_g_controller_job_prerun(job_record_t *job_ptr, char **err_msg);
/*
* Run the controller epilog callback of every plugin the job draws HRES from,
* in the reverse order of the pre-runs, passing each the prep built by
* hres_g_controller_job_prerun(). Every callback runs, even after one of them
* fails. Runs once the job's node epilogs have all completed and before its
* HRES are returned.
*
* IN job_ptr - job to run the callbacks for
* RET SLURM_SUCCESS or SLURM_ERROR if any callback failed
*/
extern int hres_g_controller_job_epilog(job_record_t *job_ptr);
/*
* Undo the pre-runs of a job that will not start after all, in the reverse
* order, and free job_ptr->hres_prep. Only resources whose pre-run succeeded
* are revoked. Runs on every failure after hres_g_controller_job_prerun() in
* select_nodes().
*
* IN job_ptr - job to revoke the pre-runs of
*/
extern void hres_g_controller_job_revoke(job_record_t *job_ptr);
/*
* Run the node prolog callback of every plugin the job draws HRES from, in the
* order the resources are defined in resources.yaml. Every callback runs, even
* after one of them fails, and the most significant failure is returned.
*
* May run concurrently for different jobs.
*
* IN prep_list - list of hres_prep_t from the job credential
* IN job - job the prolog runs for
* RET SLURM_SUCCESS, ESLURM_HRES_REMOTE_FAIL to requeue the job or any other
* error to requeue it and drain the node
*/
extern int hres_g_node_job_prolog(list_t *prep_list, hres_job_t *job);
/*
* Run the node epilog callback of every plugin the job draws HRES from, in the
* reverse order of the prologs. Every callback runs, even after one of them
* fails, and even when the matching prolog failed. The most significant
* failure is returned.
*
* May run concurrently for different jobs.
*
* IN prep_list - list of hres_prep_t from the job credential
* IN job - job the epilog runs for
* RET SLURM_SUCCESS, ESLURM_HRES_REMOTE_FAIL to leave the node up or any other
* error to drain it
*/
extern int hres_g_node_job_epilog(list_t *prep_list, hres_job_t *job);
/*
* Run the task prolog callback of every plugin the job draws HRES from, in the
* order the resources are defined in resources.yaml. Every callback runs, even
* after one of them fails, and the most significant failure is returned.
*
* Runs in the task's forked child, still privileged, before it execs.
*
* IN prep_list - list of hres_prep_t from the job credential
* IN job - job the task belongs to
* IN node_tid - node local task id
* IN global_tid - task id within the step
* IN env - step environment
* RET SLURM_SUCCESS or a failure returned by one of the callbacks, which fails
* the step
*/
extern int hres_g_node_task_prolog(list_t *prep_list, hres_job_t *job,
uint32_t node_tid, uint32_t global_tid,
char **env);
/*
* Run the unprivileged task prolog callback of every plugin the job draws HRES
* from, in the order the resources are defined in resources.yaml. Every
* callback runs, even after one of them fails, and the most significant
* failure is returned.
*
* Runs in the task's forked child as the user, once its stdio is in place, so
* a callback may write to the task's stdout and stderr.
*
* IN prep_list - list of hres_prep_t from the job credential
* IN job - job the task belongs to
* IN node_tid - node local task id
* IN global_tid - task id within the step
* IN env - task environment
* RET SLURM_SUCCESS or a failure returned by one of the callbacks, which fails
* the task
*/
extern int hres_g_node_user_task_prolog(list_t *prep_list, hres_job_t *job,
uint32_t node_tid, uint32_t global_tid,
char **env);
/*
* Run the task epilog callback of every plugin the job draws HRES from, in the
* reverse order of the prologs. Every callback runs, even after one of them
* fails, and even when the matching prolog failed. The most significant
* failure is returned.
*
* Runs in slurmstepd once the task has exited.
*
* IN prep_list - list of hres_prep_t from the job credential
* IN job - job the task belongs to
* IN node_tid - node local task id
* IN global_tid - task id within the step
* IN env - task environment, as for TaskEpilog
* RET SLURM_SUCCESS or a failure returned by one of the callbacks
*/
extern int hres_g_node_task_epilog(list_t *prep_list, hres_job_t *job,
uint32_t node_tid, uint32_t global_tid,
char **env);
#endif /* !_INTERFACES_HRES_H */