blob: ec9a78337c6a14523bbc02fee84b2b45de4adbd3 [file] [log] [blame]
/*****************************************************************************\
* reservation.c - resource reservation management
*****************************************************************************
* Copyright (C) 2009-2010 Lawrence Livermore National Security.
* Copyright (C) SchedMD LLC.
* Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
* Written by Morris Jette <jette1@llnl.gov> et. al.
* CODE-OCEC-09-009. 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.
\*****************************************************************************/
#include "config.h"
#include <fcntl.h>
#include <pthread.h>
#include <signal.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#include "slurm/slurm.h"
#include "slurm/slurm_errno.h"
#include "src/common/assoc_mgr.h"
#include "src/common/bitstring.h"
#include "src/common/core_array.h"
#include "src/common/fd.h"
#include "src/common/hostlist.h"
#include "src/common/job_features.h"
#include "src/common/list.h"
#include "src/common/log.h"
#include "src/common/macros.h"
#include "src/common/pack.h"
#include "src/common/parse_time.h"
#include "src/common/run_command.h"
#include "src/common/slurm_time.h"
#include "src/common/state_save.h"
#include "src/common/uid.h"
#include "src/common/xassert.h"
#include "src/common/xmalloc.h"
#include "src/common/xstring.h"
#include "src/interfaces/accounting_storage.h"
#include "src/interfaces/burst_buffer.h"
#include "src/interfaces/gres.h"
#include "src/interfaces/node_features.h"
#include "src/interfaces/select.h"
#include "src/interfaces/topology.h"
#include "src/slurmctld/groups.h"
#include "src/slurmctld/job_scheduler.h"
#include "src/slurmctld/licenses.h"
#include "src/slurmctld/locks.h"
#include "src/slurmctld/node_scheduler.h"
#include "src/slurmctld/reservation.h"
#include "src/slurmctld/slurmctld.h"
#include "src/slurmctld/slurmscriptd.h"
#include "src/slurmctld/state_save.h"
#include "src/stepmgr/gres_stepmgr.h"
#include "src/stepmgr/stepmgr.h"
#define RESV_MAGIC 0x3b82
/* Permit sufficient time for slurmctld failover or other long delay before
* considering a reservation time specification being invalid */
#define MAX_RESV_DELAY 600
#define MAX_RESV_COUNT 9999
/* No need to change we always pack SLURM_PROTOCOL_VERSION */
#define RESV_STATE_VERSION "PROTOCOL_VERSION"
/*
* Max number of ordered bitmaps a reservation can select against.
* Last bitmap is always a NULL pointer
*/
#define MAX_BITMAPS 6
/* Available Nodes without any reservations */
#define SELECT_NOT_RSVD 0
/* Available Nodes including overlapping/main reserved nodes */
#define SELECT_OVR_RSVD 1
/* all available nodes in partition */
#define SELECT_AVL_RSVD 2
/* all online nodes in partition */
#define SELECT_ONL_RSVD 3
/* All possible nodes in partition */
#define SELECT_ALL_RSVD 4
static const char *select_node_bitmap_tags[] = {
"SELECT_NOT_RSVD", "SELECT_OVR_RSVD", "SELECT_AVL_RSVD",
"SELECT_ONL_RSVD", "SELECT_ALL_RSVD", NULL
};
uint32_t validate_resv_cnt = 0;
time_t last_resv_update = (time_t) 0;
list_t *resv_list = NULL;
static list_t *magnetic_resv_list = NULL;
uint32_t top_suffix = 0;
typedef struct constraint_slot {
time_t start;
time_t end;
uint32_t value;
uint32_t duration;
uint64_t flags;
} constraint_slot_t;
typedef struct {
bitstr_t *core_bitmap;
list_t *gres_list_exc;
bitstr_t *node_bitmap;
} resv_select_t;
typedef struct {
char *prefix;
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);
static int _build_account_list(char *accounts, int *account_cnt,
char ***account_list, bool *account_not);
static int _build_uid_list(char *users, int *user_cnt, uid_t **user_list,
bool *user_not, bool strict);
static void _clear_job_resv(slurmctld_resv_t *resv_ptr);
static int _cmp_resv_id(void *x, void *y);
static slurmctld_resv_t *_copy_resv(slurmctld_resv_t *resv_orig_ptr);
static void _del_resv_rec(void *x);
static void _dump_resv_req(resv_desc_msg_t *resv_ptr, char *mode);
static int _find_resv_id(void *x, void *key);
static int _find_resv_ptr(void *x, void *key);
static int _find_resv_name(void *x, void *key);
static void _flush_node_down_cache(bitstr_t *down_bitmap, time_t now);
static int _generate_resv_id(void);
static void _generate_resv_name(resv_desc_msg_t *resv_ptr);
static int _get_core_resrcs(slurmctld_resv_t *resv_ptr);
static uint32_t _get_job_duration(job_record_t *job_ptr, bool reboot);
static bool _is_account_valid(char *account);
static bool _is_resv_used(slurmctld_resv_t *resv_ptr);
static bool _job_overlap(time_t start_time, uint64_t flags,
bitstr_t *node_bitmap, char *resv_name);
static int _job_resv_check(void *x, void *arg);
static list_t *_list_dup(list_t *license_list);
static void _pack_resv(slurmctld_resv_t *resv_ptr, buf_t *buffer,
bool internal, uint16_t protocol_version);
static void _pick_nodes(resv_desc_msg_t *resv_desc_ptr,
resv_select_t *resv_select,
resv_select_t *resv_select_ret);
static int _pick_nodes_ordered(resv_desc_msg_t *resv_desc_ptr,
resv_select_t *resv_select,
int resv_select_cnt,
resv_select_t *resv_select_ret,
const char **bitmap_tags);
static void _pick_nodes_by_feature_node_cnt(bitstr_t *avail_bitmap,
bitstr_t *preserve_bitmap,
resv_desc_msg_t *resv_desc_ptr,
resv_select_t *resv_select_ret,
int total_node_cnt,
list_t *feature_list);
static bitstr_t *_pick_node_cnt(resv_desc_msg_t *resv_desc_ptr,
resv_select_t *resv_select,
uint32_t node_cnt);
static int _post_resv_create(slurmctld_resv_t *resv_ptr);
static int _post_resv_delete(slurmctld_resv_t *resv_ptr);
static int _post_resv_update(slurmctld_resv_t *resv_ptr,
slurmctld_resv_t *old_resv_ptr);
static int _resize_resv(slurmctld_resv_t *resv_ptr, uint32_t node_cnt);
static void _restore_resv(slurmctld_resv_t *dest_resv,
slurmctld_resv_t *src_resv);
static bool _resv_overlap(resv_desc_msg_t *resv_desc_ptr,
bitstr_t *node_bitmap,
slurmctld_resv_t *this_resv_ptr);
static bool _resv_time_overlap(resv_desc_msg_t *resv_desc_ptr,
slurmctld_resv_t *resv_ptr);
static void _run_script(char *script, slurmctld_resv_t *resv_ptr,
bool is_resv_epilog);
static int _select_nodes(resv_desc_msg_t *resv_desc_ptr,
part_record_t **part_ptr,
resv_select_t *resv_select_ret,
bitstr_t *preserve_bitmap);
static int _set_assoc_list(slurmctld_resv_t *resv_ptr);
static void _set_tres_cnt(slurmctld_resv_t *resv_ptr,
slurmctld_resv_t *old_resv_ptr);
static void _set_nodes_flags(slurmctld_resv_t *resv_ptr, time_t now,
uint32_t flags, bool reset_all,
bitstr_t *node_down_bitmap);
static int _set_node_maint_mode(bool reset_all, bitstr_t *node_down_bitmap);
static int _update_account_list(slurmctld_resv_t *resv_ptr,
char *accounts);
static int _update_uid_list(slurmctld_resv_t *resv_ptr, char *users);
static int _update_group_uid_list(slurmctld_resv_t *resv_ptr, char *groups);
static int _update_job_resv_list_str(void *x, void *arg);
static int _update_resv_pend_cnt(void *x, void *arg);
static void _validate_all_reservations(void);
static int _valid_job_access_resv(job_record_t *job_ptr,
slurmctld_resv_t *resv_ptr,
bool show_security_violation_error);
static bool _validate_one_reservation(slurmctld_resv_t *resv_ptr);
static void _validate_node_choice(slurmctld_resv_t *resv_ptr);
static bool _validate_user_access(slurmctld_resv_t *resv_ptr,
list_t *user_assoc_list, uid_t uid);
static void _free_resv_select_members(resv_select_t *resv_select)
{
if (!resv_select)
return;
FREE_NULL_BITMAP(resv_select->core_bitmap);
FREE_NULL_LIST(resv_select->gres_list_exc);
FREE_NULL_BITMAP(resv_select->node_bitmap);
}
static int _switch_select_alloc_gres(void *x, void *arg)
{
gres_state_t *gres_state_job = x;
gres_job_state_t *gres_js = gres_state_job->gres_data;
/*
* Until a job is allocated ->node_cnt isn't set ->total_node_cnt is
* used.
*/
gres_js->node_cnt = gres_js->total_node_cnt;
gres_js->total_node_cnt = 0;
if (gres_js->gres_bit_alloc) {
/* This should never happen */
for (int i = 0; i < gres_js->node_cnt; i++) {
FREE_NULL_BITMAP(gres_js->gres_bit_alloc[i]);
}
xfree(gres_js->gres_bit_alloc);
}
gres_js->gres_bit_alloc = gres_js->gres_bit_select;
gres_js->gres_bit_select = NULL;
xfree(gres_js->gres_cnt_node_alloc);
gres_js->gres_cnt_node_alloc = gres_js->gres_cnt_node_select;
gres_js->gres_cnt_node_select = NULL;
return 0;
}
static int _foreach_create_str(void *x, void *arg)
{
slurmdb_tres_rec_t *tres_rec = x;
resv_desc_msg_t *resv_desc_ptr = arg;
xstrfmtcat(resv_desc_ptr->tres_str, ",%s/%s=%" PRIu64, tres_rec->type,
tres_rec->name, tres_rec->count);
return 0;
}
static int _append_typeless_gres(resv_desc_msg_t *resv_desc_ptr)
{
char *name = NULL, *type = NULL, *save_ptr = NULL;
int rc = true;
uint64_t cnt = 0;
char *tres_type = "gres";
list_t *typeless_list = NULL;
slurmdb_tres_rec_t *tres_rec;
/* check for a typed gres */
if (!strchr(resv_desc_ptr->tres_str, ':'))
return 0;
while (((rc = slurm_get_next_tres(&tres_type, resv_desc_ptr->tres_str,
&name, &type, &cnt, &save_ptr)) ==
SLURM_SUCCESS) &&
save_ptr) {
char *typeless = NULL, *typeless_pos = NULL;
xstrfmtcatat(typeless, &typeless_pos, "gres/%s=", name);
if (xstrstr(resv_desc_ptr->tres_str, typeless)) {
xfree(typeless);
xfree(name);
xfree(type);
continue;
}
if (!typeless_list)
typeless_list = list_create(slurmdb_destroy_tres_rec);
typeless_pos--;
*typeless_pos = '\0';
tres_rec = list_find_first(typeless_list,
slurmdb_find_tres_in_list_by_type,
typeless);
xfree(typeless);
if (!tres_rec) {
tres_rec = xmalloc(sizeof(*tres_rec));
tres_rec->name = xstrdup(name);
tres_rec->type = xstrdup(tres_type);
list_append(typeless_list, tres_rec);
}
tres_rec->count += cnt;
xfree(name);
xfree(type);
}
if (typeless_list) {
(void) list_for_each(typeless_list, _foreach_create_str,
resv_desc_ptr);
FREE_NULL_LIST(typeless_list);
}
return 0;
}
static int _parse_tres_str(resv_desc_msg_t *resv_desc_ptr)
{
char *tmp_str, *tres_sub_str;
if (!resv_desc_ptr->tres_str)
return SLURM_SUCCESS;
/*
* Here we need to verify all the TRES (including GRES) are real TRES.
*/
if (!assoc_mgr_valid_tres_cnt(resv_desc_ptr->tres_str, true))
return ESLURM_INVALID_TRES;
/*
* There are a few different ways to request a tres string, this
* will format it correctly for the rest of Slurm.
*/
tmp_str = slurm_get_tres_sub_string(
resv_desc_ptr->tres_str, NULL, NO_VAL, true, true);
if (!tmp_str)
return ESLURM_INVALID_TRES;
xfree(resv_desc_ptr->tres_str);
resv_desc_ptr->tres_str = tmp_str;
tmp_str = NULL;
tres_sub_str = slurm_get_tres_sub_string(
resv_desc_ptr->tres_str, "license", NO_VAL,
false, false);
if (tres_sub_str) {
if (resv_desc_ptr->licenses)
return ESLURM_INVALID_LICENSES;
resv_desc_ptr->licenses = tres_sub_str;
tres_sub_str = NULL;
}
tres_sub_str = slurm_get_tres_sub_string(
resv_desc_ptr->tres_str, "node", NO_VAL,
false, false);
if (tres_sub_str) {
if (resv_desc_ptr->node_cnt != NO_VAL)
return ESLURM_INVALID_NODE_COUNT;
resv_desc_ptr->node_cnt = atoi(tres_sub_str + 1);
xfree(tres_sub_str);
}
tres_sub_str = slurm_get_tres_sub_string(
resv_desc_ptr->tres_str, "cpu", NO_VAL,
false, false);
if (tres_sub_str) {
if (resv_desc_ptr->core_cnt != NO_VAL)
return ESLURM_INVALID_CPU_COUNT;
resv_desc_ptr->core_cnt = atoi(tres_sub_str + 1);
if ((resv_desc_ptr->flags & RESERVE_TRES_PER_NODE) &&
(resv_desc_ptr->node_cnt != NO_VAL))
resv_desc_ptr->core_cnt *= resv_desc_ptr->node_cnt;
xfree(tres_sub_str);
}
tres_sub_str = slurm_get_tres_sub_string(
resv_desc_ptr->tres_str, "bb", NO_VAL,
false, false);
if (tres_sub_str) {
if (resv_desc_ptr->burst_buffer)
return ESLURM_INVALID_BURST_BUFFER_REQUEST;
resv_desc_ptr->burst_buffer = tres_sub_str;
tres_sub_str = NULL;
}
if (_append_typeless_gres(resv_desc_ptr))
return ESLURM_INVALID_TRES;
return SLURM_SUCCESS;
}
static bitstr_t *_resv_select(resv_desc_msg_t *resv_desc_ptr,
resv_select_t *resv_select)
{
job_record_t *job_ptr;
resv_exc_t resv_exc = { 0 };
int rc;
xassert(avail_node_bitmap);
xassert(resv_desc_ptr);
xassert(resv_desc_ptr->job_ptr);
resv_exc.core_bitmap = resv_select->core_bitmap;
resv_exc.exc_cores = core_bitmap_to_array(resv_exc.core_bitmap);
resv_exc.gres_list_exc = resv_select->gres_list_exc;
job_ptr = resv_desc_ptr->job_ptr;
/*
* request the maximum nodes, require the minimum
*/
rc = select_g_job_test(
job_ptr, resv_select->node_bitmap, job_ptr->details->min_nodes,
job_ptr->details->max_nodes,
job_ptr->details->max_nodes,
SELECT_MODE_WILL_RUN, NULL, NULL,
&resv_exc,
NULL);
free_core_array(&resv_exc.exc_cores);
if (rc != SLURM_SUCCESS) {
return NULL;
}
if ((resv_desc_ptr->flags & RESERVE_FLAG_GRES_REQ) ||
(resv_desc_ptr->core_cnt != NO_VAL)) {
if (resv_select->core_bitmap)
bit_clear_all(resv_select->core_bitmap);
/*
* ncpus will usually only have a core count here, total_cpus is
* always correct.
*/
job_ptr->job_resrcs->ncpus = job_ptr->total_cpus;
add_job_to_cores(job_ptr->job_resrcs,
&resv_select->core_bitmap);
if (job_ptr->gres_list_req) {
(void) list_for_each(job_ptr->gres_list_req,
_switch_select_alloc_gres,
NULL);
}
} else
free_job_resources(&job_ptr->job_resrcs);
return bit_copy(resv_select->node_bitmap);
}
static void _set_boot_time(slurmctld_resv_t *resv_ptr)
{
resv_ptr->boot_time = 0;
if (!resv_ptr->node_bitmap)
return;
if (node_features_g_overlap(resv_ptr->node_bitmap))
resv_ptr->boot_time = node_features_g_boot_time();
}
/* Advance res_time by the specified day and hour counts,
* account for daylight savings time */
static void _advance_time(time_t *res_time, int day_cnt, int hour_cnt)
{
time_t save_time = *res_time;
struct tm time_tm;
localtime_r(res_time, &time_tm);
time_tm.tm_mday += day_cnt;
time_tm.tm_hour += hour_cnt;
*res_time = slurm_mktime(&time_tm);
if (*res_time == (time_t)(-1)) {
error("Could not compute reservation time %lu",
(long unsigned int) save_time);
*res_time = save_time + (24 * 60 * 60);
}
}
static list_t *_list_dup(list_t *license_list)
{
list_itr_t *iter;
licenses_t *license_src, *license_dest;
list_t *lic_list = NULL;
if (!license_list)
return lic_list;
lic_list = list_create(license_free_rec);
iter = list_iterator_create(license_list);
while ((license_src = list_next(iter))) {
license_dest = xmalloc(sizeof(licenses_t));
license_dest->name = xstrdup(license_src->name);
license_dest->used = license_src->used;
list_append(lic_list, license_dest);
}
list_iterator_destroy(iter);
return lic_list;
}
static slurmctld_resv_t *_copy_resv(slurmctld_resv_t *resv_orig_ptr)
{
slurmctld_resv_t *resv_copy_ptr;
int i;
xassert(resv_orig_ptr->magic == RESV_MAGIC);
resv_copy_ptr = xmalloc(sizeof(slurmctld_resv_t));
resv_copy_ptr->accounts = xstrdup(resv_orig_ptr->accounts);
resv_copy_ptr->boot_time = resv_orig_ptr->boot_time;
resv_copy_ptr->burst_buffer = xstrdup(resv_orig_ptr->burst_buffer);
resv_copy_ptr->account_cnt = resv_orig_ptr->account_cnt;
resv_copy_ptr->account_list = xcalloc(resv_orig_ptr->account_cnt,
sizeof(char *));
for (i = 0; i < resv_copy_ptr->account_cnt; i++) {
resv_copy_ptr->account_list[i] =
xstrdup(resv_orig_ptr->account_list[i]);
}
resv_copy_ptr->assoc_list = xstrdup(resv_orig_ptr->assoc_list);
if (resv_orig_ptr->core_bitmap) {
resv_copy_ptr->core_bitmap = bit_copy(resv_orig_ptr->
core_bitmap);
}
resv_copy_ptr->ctld_flags = resv_orig_ptr->ctld_flags;
resv_copy_ptr->core_cnt = resv_orig_ptr->core_cnt;
if (resv_orig_ptr->core_resrcs) {
resv_copy_ptr->core_resrcs = copy_job_resources(resv_orig_ptr->
core_resrcs);
}
resv_copy_ptr->duration = resv_orig_ptr->duration;
resv_copy_ptr->end_time = resv_orig_ptr->end_time;
resv_copy_ptr->features = xstrdup(resv_orig_ptr->features);
resv_copy_ptr->flags = resv_orig_ptr->flags;
resv_copy_ptr->groups = xstrdup(resv_orig_ptr->groups);
resv_copy_ptr->job_pend_cnt = resv_orig_ptr->job_pend_cnt;
resv_copy_ptr->job_run_cnt = resv_orig_ptr->job_run_cnt;
resv_copy_ptr->licenses = xstrdup(resv_orig_ptr->licenses);
resv_copy_ptr->license_list = _list_dup(resv_orig_ptr->
license_list);
resv_copy_ptr->magic = resv_orig_ptr->magic;
resv_copy_ptr->name = xstrdup(resv_orig_ptr->name);
if (resv_orig_ptr->node_bitmap) {
resv_copy_ptr->node_bitmap =
bit_copy(resv_orig_ptr->node_bitmap);
}
resv_copy_ptr->node_cnt = resv_orig_ptr->node_cnt;
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;
resv_copy_ptr->start_time_prev = resv_orig_ptr->start_time_prev;
resv_copy_ptr->tres_str = xstrdup(resv_orig_ptr->tres_str);
resv_copy_ptr->tres_fmt_str = xstrdup(resv_orig_ptr->tres_fmt_str);
resv_copy_ptr->users = xstrdup(resv_orig_ptr->users);
resv_copy_ptr->user_cnt = resv_orig_ptr->user_cnt;
resv_copy_ptr->user_list = xcalloc(resv_orig_ptr->user_cnt,
sizeof(uid_t));
for (i = 0; i < resv_copy_ptr->user_cnt; i++)
resv_copy_ptr->user_list[i] = resv_orig_ptr->user_list[i];
return resv_copy_ptr;
}
/* Move the contents of src_resv into dest_resv.
* NOTE: This is a destructive function with respect to the contents of
* src_resv. The data structure src_resv is suitable only for destruction
* after this function is called */
static void _restore_resv(slurmctld_resv_t *dest_resv,
slurmctld_resv_t *src_resv)
{
int i;
xfree(dest_resv->accounts);
dest_resv->accounts = src_resv->accounts;
src_resv->accounts = NULL;
for (i = 0; i < dest_resv->account_cnt; i++)
xfree(dest_resv->account_list[i]);
xfree(dest_resv->account_list);
dest_resv->account_cnt = src_resv->account_cnt;
src_resv->account_cnt = 0;
dest_resv->account_list = src_resv->account_list;
src_resv->account_list = NULL;
xfree(dest_resv->assoc_list);
dest_resv->assoc_list = src_resv->assoc_list;
src_resv->assoc_list = NULL;
dest_resv->boot_time = src_resv->boot_time;
xfree(dest_resv->burst_buffer);
dest_resv->burst_buffer = src_resv->burst_buffer;
src_resv->burst_buffer = NULL;
FREE_NULL_BITMAP(dest_resv->core_bitmap);
dest_resv->core_bitmap = src_resv->core_bitmap;
src_resv->core_bitmap = NULL;
dest_resv->core_cnt = src_resv->core_cnt;
free_job_resources(&dest_resv->core_resrcs);
dest_resv->core_resrcs = src_resv->core_resrcs;
src_resv->core_resrcs = NULL;
dest_resv->ctld_flags = src_resv->ctld_flags;
dest_resv->duration = src_resv->duration;
dest_resv->end_time = src_resv->end_time;
xfree(dest_resv->features);
dest_resv->features = src_resv->features;
src_resv->features = NULL;
dest_resv->flags = src_resv->flags;
dest_resv->job_pend_cnt = src_resv->job_pend_cnt;
dest_resv->job_run_cnt = src_resv->job_run_cnt;
xfree(dest_resv->groups);
dest_resv->groups = src_resv->groups;
src_resv->groups = NULL;
xfree(dest_resv->licenses);
dest_resv->licenses = src_resv->licenses;
src_resv->licenses = NULL;
FREE_NULL_LIST(dest_resv->license_list);
dest_resv->license_list = src_resv->license_list;
src_resv->license_list = NULL;
dest_resv->magic = src_resv->magic;
xfree(dest_resv->name);
dest_resv->name = src_resv->name;
src_resv->name = NULL;
FREE_NULL_BITMAP(dest_resv->node_bitmap);
dest_resv->node_bitmap = src_resv->node_bitmap;
src_resv->node_bitmap = NULL;
dest_resv->node_cnt = src_resv->node_cnt;
xfree(dest_resv->node_list);
dest_resv->node_list = src_resv->node_list;
src_resv->node_list = NULL;
xfree(dest_resv->partition);
dest_resv->partition = src_resv->partition;
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;
dest_resv->start_time_prev = src_resv->start_time_prev;
xfree(dest_resv->tres_str);
dest_resv->tres_str = src_resv->tres_str;
src_resv->tres_str = NULL;
xfree(dest_resv->tres_fmt_str);
dest_resv->tres_fmt_str = src_resv->tres_fmt_str;
src_resv->tres_fmt_str = NULL;
xfree(dest_resv->users);
dest_resv->users = src_resv->users;
src_resv->users = NULL;
dest_resv->user_cnt = src_resv->user_cnt;
xfree(dest_resv->user_list);
dest_resv->user_list = src_resv->user_list;
src_resv->user_list = NULL;
}
static void _del_resv_rec(void *x)
{
int i;
slurmctld_resv_t *resv_ptr = (slurmctld_resv_t *) x;
if (resv_ptr) {
/*
* If shutting down magnetic_resv_list is already freed, meaning
* we don't need to remove anything from it.
*/
if (magnetic_resv_list &&
(resv_ptr->flags & RESERVE_FLAG_MAGNETIC)) {
int cnt;
cnt = list_delete_all(magnetic_resv_list,
_find_resv_ptr,
resv_ptr);
if (cnt > 1) {
error("%s: magnetic_resv_list contained %d references to %s",
__func__, cnt, resv_ptr->name);
}
}
xassert(resv_ptr->magic == RESV_MAGIC);
resv_ptr->magic = 0;
xfree(resv_ptr->accounts);
for (i = 0; i < resv_ptr->account_cnt; i++)
xfree(resv_ptr->account_list[i]);
xfree(resv_ptr->account_list);
xfree(resv_ptr->assoc_list);
xfree(resv_ptr->burst_buffer);
FREE_NULL_BITMAP(resv_ptr->core_bitmap);
free_job_resources(&resv_ptr->core_resrcs);
xfree(resv_ptr->features);
FREE_NULL_LIST(resv_ptr->gres_list_alloc);
xfree(resv_ptr->groups);
FREE_NULL_LIST(resv_ptr->license_list);
xfree(resv_ptr->licenses);
xfree(resv_ptr->name);
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);
xfree(resv_ptr->user_list);
xfree(resv_ptr);
}
}
static void _create_resv_lists(bool flush)
{
if (flush && resv_list) {
list_flush(magnetic_resv_list);
list_flush(resv_list);
return;
}
if (!resv_list)
resv_list = list_create(_del_resv_rec);
if (!magnetic_resv_list)
magnetic_resv_list = list_create(NULL);
}
static void _add_resv_to_lists(slurmctld_resv_t *resv_ptr)
{
xassert(resv_list);
xassert(magnetic_resv_list);
list_append(resv_list, resv_ptr);
if (resv_ptr->flags & RESERVE_FLAG_MAGNETIC)
list_append(magnetic_resv_list, resv_ptr);
}
static int _queue_magnetic_resv(void *x, void *key)
{
slurmctld_resv_t *resv_ptr = (slurmctld_resv_t *) x;
job_queue_req_t *job_queue_req = (job_queue_req_t *) key;
xassert(resv_ptr->magic == RESV_MAGIC);
if (!(resv_ptr->flags & RESERVE_FLAG_MAGNETIC) ||
(_valid_job_access_resv(job_queue_req->job_ptr, resv_ptr, false) !=
SLURM_SUCCESS))
return 0;
job_queue_req->resv_ptr = resv_ptr;
job_queue_append_internal(job_queue_req);
return 0;
}
static int _cmp_resv_id(void *x, void *y)
{
slurmctld_resv_t *resv_ptr1 = *(slurmctld_resv_t **) x;
slurmctld_resv_t *resv_ptr2 = *(slurmctld_resv_t **) y;
if (resv_ptr1->resv_id < resv_ptr2->resv_id)
return -1;
if (resv_ptr1->resv_id > resv_ptr2->resv_id)
return 1;
return 0;
}
static int _find_job_with_resv_ptr(void *x, void *key)
{
job_record_t *job_ptr = (job_record_t *) x;
slurmctld_resv_t *resv_ptr = (slurmctld_resv_t *) key;
if (job_ptr->resv_ptr == resv_ptr)
return 1;
if (job_ptr->resv_list &&
list_find_first(job_ptr->resv_list, _find_resv_ptr, resv_ptr))
return 1;
return 0;
}
static int _find_running_job_with_resv_ptr(void *x, void *key)
{
job_record_t *job_ptr = (job_record_t *) x;
slurmctld_resv_t *resv_ptr = (slurmctld_resv_t *) key;
if ((!IS_JOB_FINISHED(job_ptr)) &&
_find_job_with_resv_ptr(job_ptr, resv_ptr))
return 1;
return 0;
}
static int _find_resv_id(void *x, void *key)
{
slurmctld_resv_t *resv_ptr = (slurmctld_resv_t *) x;
uint32_t *resv_id = (uint32_t *) key;
xassert(resv_ptr->magic == RESV_MAGIC);
if (resv_ptr->resv_id != *resv_id)
return 0;
else
return 1; /* match */
}
static int _find_resv_ptr(void *x, void *key)
{
slurmctld_resv_t *resv_ptr = (slurmctld_resv_t *) x;
slurmctld_resv_t *resv_ptr_key = (slurmctld_resv_t *) key;
xassert(resv_ptr->magic == RESV_MAGIC);
if (resv_ptr != resv_ptr_key)
return 0;
else
return 1; /* match */
}
static int _find_resv_name(void *x, void *key)
{
slurmctld_resv_t *resv_ptr = (slurmctld_resv_t *) x;
xassert(resv_ptr->magic == RESV_MAGIC);
if (xstrcmp(resv_ptr->name, (char *) key))
return 0;
else
return 1; /* match */
}
static void _flush_node_down_cache(bitstr_t *down_bitmap, time_t now)
{
node_record_t *node_ptr;
for (int i = 0; (node_ptr = next_node_bitmap(down_bitmap, &i)); i++)
clusteracct_storage_g_node_down(acct_db_conn, node_ptr, now,
NULL, slurm_conf.slurm_user_id);
}
static int _foreach_clear_job_resv(void *x, void *key)
{
job_record_t *job_ptr = (job_record_t *) x;
slurmctld_resv_t *resv_ptr = (slurmctld_resv_t *) key;
/*
* Do this before checking if we have the correct reservation or
* not. Since this could be set whether or not the job requested a
* reservation.
*/
if ((resv_ptr->flags & RESERVE_FLAG_MAINT) &&
(job_ptr->state_reason == WAIT_NODE_NOT_AVAIL) &&
!xstrcmp(job_ptr->state_desc,
"ReqNodeNotAvail, Reserved for maintenance")) {
/*
* In case of cluster maintenance many jobs may get this
* state set. If we wait for scheduler to update
* the reason it may take long time after the
* reservation completion. Instead of that clear it
* when MAINT reservation ends.
*/
job_ptr->state_reason = WAIT_NO_REASON;
xfree(job_ptr->state_desc);
}
if (!_find_job_with_resv_ptr(job_ptr, resv_ptr))
return 0;
if (!IS_JOB_FINISHED(job_ptr)) {
info("%pJ linked to defunct reservation %s, clearing that reservation",
job_ptr, resv_ptr->name);
}
job_ptr->resv_id = 0;
job_ptr->resv_ptr = NULL;
xfree(job_ptr->resv_name);
if (job_ptr->resv_list) {
int resv_cnt;
list_remove_first(job_ptr->resv_list, _find_resv_ptr, resv_ptr);
job_ptr->resv_ptr = list_peek(job_ptr->resv_list);
resv_cnt = list_count(job_ptr->resv_list);
if (resv_cnt <= 0) {
FREE_NULL_LIST(job_ptr->resv_list);
} else if (resv_cnt == 1) {
job_ptr->resv_id = job_ptr->resv_ptr->resv_id;
job_ptr->resv_name = xstrdup(job_ptr->resv_ptr->name);
FREE_NULL_LIST(job_ptr->resv_list);
} else {
list_for_each(job_ptr->resv_list,
_update_job_resv_list_str,
&job_ptr->resv_name);
}
}
if (!(resv_ptr->flags & RESERVE_FLAG_NO_HOLD_JOBS) &&
IS_JOB_PENDING(job_ptr) && !job_ptr->resv_ptr &&
(job_ptr->state_reason != WAIT_HELD)) {
xfree(job_ptr->state_desc);
job_ptr->state_reason = WAIT_RESV_DELETED;
job_state_set_flag(job_ptr, JOB_RESV_DEL_HOLD);
xstrfmtcat(job_ptr->state_desc,
"Reservation %s was deleted",
resv_ptr->name);
debug("%s: Holding %pJ, reservation %s was deleted",
__func__, job_ptr, resv_ptr->name);
job_ptr->priority = 0; /* Hold job */
}
return 0;
}
static int _update_job_resv_list_str(void *x, void *arg)
{
slurmctld_resv_t *resv_ptr = x;
char **resv_name = arg;
xstrfmtcat(*resv_name, "%s%s", *resv_name ? "," : "", resv_ptr->name);
return 0;
}
static int _update_resv_pend_cnt(void *x, void *arg)
{
slurmctld_resv_t *resv_ptr = x;
xassert(resv_ptr->magic == RESV_MAGIC);
resv_ptr->job_pend_cnt++;
return 0;
}
static void _dump_resv_req(resv_desc_msg_t *resv_ptr, char *mode)
{
char start_str[256] = "-1", end_str[256] = "-1", *flag_str = NULL;
int duration;
if (!(slurm_conf.debug_flags & DEBUG_FLAG_RESERVATION))
return;
if (resv_ptr->start_time != (time_t) NO_VAL) {
slurm_make_time_str(&resv_ptr->start_time,
start_str, sizeof(start_str));
}
if (resv_ptr->end_time != (time_t) NO_VAL) {
slurm_make_time_str(&resv_ptr->end_time,
end_str, sizeof(end_str));
}
if (resv_ptr->flags != NO_VAL64) {
reserve_info_t resv_info = {
.flags = resv_ptr->flags,
.purge_comp_time = resv_ptr->purge_comp_time
};
flag_str = reservation_flags_string(&resv_info);
}
if (resv_ptr->duration == NO_VAL)
duration = -1;
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 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->qos,
resv_ptr->burst_buffer, resv_ptr->tres_str,
resv_ptr->comment);
xfree(flag_str);
}
static int _generate_resv_id(void)
{
int i;
for (i = 0; i < MAX_RESV_COUNT; i++) {
if (top_suffix >= MAX_RESV_COUNT)
top_suffix = 1; /* wrap around */
else
top_suffix++;
if (!list_find_first(resv_list, _find_resv_id, &top_suffix))
return SLURM_SUCCESS;
}
error("%s: Too many reservations in the system, can't create any more.",
__func__);
return ESLURM_RESERVATION_INVALID;
}
static void _generate_resv_name(resv_desc_msg_t *resv_ptr)
{
char *key, *name, *sep;
int len;
/* Generate name prefix, based upon the first account
* name if provided otherwise first user name */
if (resv_ptr->accounts && resv_ptr->accounts[0])
key = resv_ptr->accounts;
else if (resv_ptr->users && resv_ptr->users[0])
key = resv_ptr->users;
else if (resv_ptr->groups && resv_ptr->groups[0])
key = resv_ptr->groups;
else
key = "resv";
if (key[0] == '-')
key++;
sep = strchr(key, ',');
if (sep)
len = sep - key;
else
len = strlen(key);
name = xstrdup_printf("%.*s_%d", len, key, top_suffix);
xfree(resv_ptr->name);
resv_ptr->name = name;
}
/* Validate an account name */
static bool _is_account_valid(char *account)
{
slurmdb_assoc_rec_t assoc_rec, *assoc_ptr;
if (!(accounting_enforce & ACCOUNTING_ENFORCE_ASSOCS))
return true; /* don't worry about account validity */
memset(&assoc_rec, 0, sizeof(slurmdb_assoc_rec_t));
assoc_rec.uid = NO_VAL;
assoc_rec.acct = account;
if (assoc_mgr_fill_in_assoc(acct_db_conn, &assoc_rec,
accounting_enforce, &assoc_ptr, false)) {
return false;
}
return true;
}
/* Since the returned assoc_list is full of pointers from the
* assoc_mgr_association_list assoc_mgr_lock_t READ_LOCK on
* associations must be set before calling this function and while
* handling it after a return.
*/
static int _append_acct_to_assoc_list(list_t *assoc_list,
slurmdb_assoc_rec_t *assoc)
{
int rc = ESLURM_INVALID_ACCOUNT;
slurmdb_assoc_rec_t *assoc_ptr = NULL;
xassert(assoc->uid == NO_VAL);
if (assoc_mgr_fill_in_assoc(
acct_db_conn, assoc,
accounting_enforce,
&assoc_ptr, true)) {
if (accounting_enforce & ACCOUNTING_ENFORCE_ASSOCS) {
error("No association for user %u and account %s",
assoc->uid, assoc->acct);
} else {
verbose("No association for user %u and account %s",
assoc->uid, assoc->acct);
rc = SLURM_SUCCESS;
}
}
if (assoc_ptr) {
list_append(assoc_list, assoc_ptr);
rc = SLURM_SUCCESS;
}
return rc;
}
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_allow_str_t *set_allow_str = arg;
_addto_alloc_str(set_allow_str, assoc_ptr->id);
return 0;
}
/* Set a association list based upon accounts and users */
static int _set_assoc_list(slurmctld_resv_t *resv_ptr)
{
int rc = SLURM_SUCCESS, i = 0, j = 0;
list_t *assoc_list_allow = NULL, *assoc_list_deny = NULL;
list_t *assoc_list = NULL;
slurmdb_assoc_rec_t assoc;
assoc_mgr_lock_t locks = { .assoc = READ_LOCK, .user = READ_LOCK };
foreach_set_allow_str_t set_allow_str = {
.str = &resv_ptr->assoc_list,
};
/* no need to do this if we can't ;) */
if (!slurm_with_slurmdbd())
return rc;
assoc_list_allow = list_create(NULL);
assoc_list_deny = list_create(NULL);
memset(&assoc, 0, sizeof(slurmdb_assoc_rec_t));
xfree(resv_ptr->assoc_list);
assoc_mgr_lock(&locks);
if (resv_ptr->account_cnt && resv_ptr->user_cnt) {
if (!(resv_ptr->ctld_flags &
(RESV_CTLD_USER_NOT | RESV_CTLD_ACCT_NOT))) {
/* Add every association that matches both account
* and user */
for (i=0; i < resv_ptr->user_cnt; i++) {
for (j=0; j < resv_ptr->account_cnt; j++) {
memset(&assoc, 0,
sizeof(slurmdb_assoc_rec_t));
assoc.acct = resv_ptr->account_list[j];
assoc.uid = resv_ptr->user_list[i];
rc = assoc_mgr_get_user_assocs(
acct_db_conn, &assoc,
accounting_enforce,
assoc_list_allow);
if (rc != SLURM_SUCCESS)
goto end_it;
}
}
} else {
if (resv_ptr->ctld_flags & RESV_CTLD_USER_NOT)
assoc_list = assoc_list_deny;
else
assoc_list = assoc_list_allow;
for (i=0; i < resv_ptr->user_cnt; i++) {
memset(&assoc, 0,
sizeof(slurmdb_assoc_rec_t));
assoc.uid = resv_ptr->user_list[i];
rc = assoc_mgr_get_user_assocs(
acct_db_conn, &assoc,
accounting_enforce,
assoc_list);
if (rc != SLURM_SUCCESS) {
/*
* When using groups we might have users
* that don't have associations, just
* skip them
*/
if (resv_ptr->groups) {
rc = SLURM_SUCCESS;
continue;
}
error("No associations for UID %u",
assoc.uid);
rc = ESLURM_INVALID_ACCOUNT;
goto end_it;
}
}
if (resv_ptr->ctld_flags & RESV_CTLD_ACCT_NOT)
assoc_list = assoc_list_deny;
else
assoc_list = assoc_list_allow;
for (j=0; j < resv_ptr->account_cnt; j++) {
memset(&assoc, 0,
sizeof(slurmdb_assoc_rec_t));
assoc.acct = resv_ptr->account_list[j];
assoc.uid = NO_VAL;
rc = _append_acct_to_assoc_list(assoc_list,
&assoc);
if (rc != SLURM_SUCCESS)
goto end_it;
}
}
} else if (resv_ptr->user_cnt) {
if (resv_ptr->ctld_flags & RESV_CTLD_USER_NOT)
assoc_list = assoc_list_deny;
else
assoc_list = assoc_list_allow;
for (i=0; i < resv_ptr->user_cnt; i++) {
memset(&assoc, 0, sizeof(slurmdb_assoc_rec_t));
assoc.uid = resv_ptr->user_list[i];
rc = assoc_mgr_get_user_assocs(
acct_db_conn, &assoc,
accounting_enforce, assoc_list);
if (rc != SLURM_SUCCESS) {
/*
* When using groups we might have users that
* don't have associations, just skip them
*/
if (resv_ptr->groups) {
rc = SLURM_SUCCESS;
continue;
}
error("No associations for UID %u",
assoc.uid);
rc = ESLURM_INVALID_ACCOUNT;
goto end_it;
}
}
} else if (resv_ptr->account_cnt) {
if (resv_ptr->ctld_flags & RESV_CTLD_ACCT_NOT)
assoc_list = assoc_list_deny;
else
assoc_list = assoc_list_allow;
for (i=0; i < resv_ptr->account_cnt; i++) {
memset(&assoc, 0, sizeof(slurmdb_assoc_rec_t));
assoc.acct = resv_ptr->account_list[i];
assoc.uid = NO_VAL;
if ((rc = _append_acct_to_assoc_list(assoc_list,
&assoc)) !=
SLURM_SUCCESS) {
goto end_it;
}
}
} else if (!resv_ptr->qos &&
(accounting_enforce & ACCOUNTING_ENFORCE_ASSOCS)) {
error("We need at least 1 user or 1 account to "
"create a reservtion.");
rc = SLURM_ERROR;
}
xfree(resv_ptr->assoc_list); /* clear for modify */
if (list_count(assoc_list_allow)) {
set_allow_str.prefix = "";
set_allow_str.str_pos = NULL;
(void) list_for_each(assoc_list_allow,
_foreach_set_assoc_allow_str,
&set_allow_str);
}
if (list_count(assoc_list_deny)) {
set_allow_str.prefix = "-";
set_allow_str.str_pos = NULL;
(void) list_for_each(assoc_list_deny,
_foreach_set_assoc_allow_str,
&set_allow_str);
}
if (resv_ptr->assoc_list)
debug("assoc_list:%s", resv_ptr->assoc_list);
end_it:
FREE_NULL_LIST(assoc_list_allow);
FREE_NULL_LIST(assoc_list_deny);
assoc_mgr_unlock(&locks);
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_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)
{
int rc = SLURM_SUCCESS;
slurmdb_reservation_rec_t resv;
_set_boot_time(resv_ptr);
if (resv_ptr->flags & RESERVE_FLAG_TIME_FLOAT)
return rc;
memset(&resv, 0, sizeof(slurmdb_reservation_rec_t));
resv.assocs = resv_ptr->assoc_list;
resv.cluster = slurm_conf.cluster_name;
resv.comment = resv_ptr->comment;
resv.tres_str = resv_ptr->tres_str;
resv.flags = resv_ptr->flags;
resv.id = resv_ptr->resv_id;
resv.name = resv_ptr->name;
resv.nodes = resv_ptr->node_list;
resv.node_inx = acct_storage_g_node_inx(acct_db_conn,
resv_ptr->node_list);
resv.time_end = resv_ptr->end_time;
resv.time_force = resv_ptr->time_force;
resv.time_start = resv_ptr->start_time;
resv.tres_str = resv_ptr->tres_str;
rc = acct_storage_g_add_reservation(acct_db_conn, &resv);
xfree(resv.node_inx);
return rc;
}
/* Note that a reservation has been deleted */
static int _post_resv_delete(slurmctld_resv_t *resv_ptr)
{
int rc = SLURM_SUCCESS;
slurmdb_reservation_rec_t resv;
time_t now = time(NULL);
if (resv_ptr->flags & RESERVE_FLAG_TIME_FLOAT)
return rc;
memset(&resv, 0, sizeof(slurmdb_reservation_rec_t));
resv.cluster = slurm_conf.cluster_name;
resv.id = resv_ptr->resv_id;
resv.name = resv_ptr->name;
resv.time_end = now;
resv.time_start = resv_ptr->start_time;
/* This is just a time stamp here to delete if the reservation
* hasn't started yet so we don't get trash records in the
* database if said database isn't up right now */
resv.time_start_prev = now;
resv.tres_str = resv_ptr->tres_str;
rc = acct_storage_g_remove_reservation(acct_db_conn, &resv);
return rc;
}
/* Note that a reservation has been updated */
static int _post_resv_update(slurmctld_resv_t *resv_ptr,
slurmctld_resv_t *old_resv_ptr)
{
int rc = SLURM_SUCCESS;
bool change = false;
slurmdb_reservation_rec_t resv;
time_t now = time(NULL);
xassert(old_resv_ptr);
_set_boot_time(resv_ptr);
if (resv_ptr->flags & RESERVE_FLAG_TIME_FLOAT)
return rc;
memset(&resv, 0, sizeof(slurmdb_reservation_rec_t));
resv.cluster = slurm_conf.cluster_name;
resv.id = resv_ptr->resv_id;
resv.time_end = resv_ptr->end_time;
resv.assocs = resv_ptr->assoc_list;
resv.tres_str = resv_ptr->tres_str;
resv.flags = resv_ptr->flags;
resv.nodes = resv_ptr->node_list;
resv.comment = resv_ptr->comment;
if (xstrcmp(old_resv_ptr->assoc_list, resv_ptr->assoc_list) ||
xstrcmp(old_resv_ptr->tres_str, resv_ptr->tres_str) ||
(old_resv_ptr->flags != resv_ptr->flags) ||
xstrcmp(old_resv_ptr->node_list, resv_ptr->node_list) ||
xstrcmp(old_resv_ptr->comment, resv_ptr->comment))
change = true;
/* Here if the reservation has started already we need
* to mark a new start time for it if certain
* variables are needed in accounting. Right now if
* the assocs, nodes, flags or cpu count changes we need a
* new start time of now. */
if ((resv_ptr->start_time < now) && change) {
resv_ptr->start_time_prev = resv_ptr->start_time;
resv_ptr->start_time = now;
}
/* now set the (maybe new) start_times */
resv.time_start = resv_ptr->start_time;
resv.time_start_prev = resv_ptr->start_time_prev;
resv.node_inx = acct_storage_g_node_inx(acct_db_conn,
resv_ptr->node_list);
rc = acct_storage_g_modify_reservation(acct_db_conn, &resv);
xfree(resv.node_inx);
return rc;
}
static void _remove_name_from_str(char *name, char *str)
{
int k = strlen(name);
char *tmp = str, *tok;
while ((tok = xstrstr(tmp, name))) {
if (((tok != str) &&
(tok[-1] != ',') && (tok[-1] != '-')) ||
((tok[k] != '\0') && (tok[k] != ','))) {
tmp = tok + 1;
continue;
}
if (tok[-1] == '-') {
tok--;
k++;
}
if (tok[-1] == ',') {
tok--;
k++;
} else if (tok[k] == ',')
k++;
for (int j=0; ; j++) {
tok[j] = tok[j + k];
if (tok[j] == '\0')
break;
}
}
}
static bool _check_uid(uid_t x, uid_t arg)
{
return (x == arg);
}
static bool _check_char(char *x, char *arg)
{
if (x[0] == '-')
x++;
return !xstrcmp(x, arg);
}
static int _handle_add_remove_names(
slurmctld_resv_t *resv_ptr, uint32_t not_flag,
int alter_cnt, char **alter_list, uid_t *uid_list, int *alter_types,
bool minus, bool plus)
{
int *object_cnt;
char **object_str;
int i, j, k;
bool found_it;
switch (not_flag) {
case RESV_CTLD_USER_NOT:
object_cnt = &resv_ptr->user_cnt;
object_str = &resv_ptr->users;
break;
case RESV_CTLD_ACCT_NOT:
object_cnt = &resv_ptr->account_cnt;
object_str = &resv_ptr->accounts;
break;
default:
return SLURM_ERROR;
}
/*
* If: The update sets a new list (it was previously empty).
* All accounts are negated, so this is a new exclusion list.
* NOTE: An empty list is always of type "inclusion".
*/
if (!*object_cnt && minus && !plus)
resv_ptr->ctld_flags |= not_flag;
if (resv_ptr->ctld_flags & not_flag) {
/*
* change minus to plus (add to NOT list) and
* change plus to minus (remove from NOT list)
*/
for (i = 0; i < alter_cnt; i++) {
if (alter_types[i] == 1)
alter_types[i] = 2;
else if (alter_types[i] == 2)
alter_types[i] = 1;
}
if (minus && !plus) {
minus = false;
plus = true;
} else if (!minus && plus) {
minus = true;
plus = false;
}
}
/*
* At this point, minus/plus mean removing/adding literally to the list.
* If "RESV_CTLD_*_NOT" was previously set, it means the list is of
* type "exclusion", otherwise it means "inclusion"
*/
if (minus) {
if (!*object_cnt)
return SLURM_ERROR;
for (i=0; i < alter_cnt; i++) {
if (alter_types[i] != 1) /* not minus */
continue;
found_it = false;
for (j=0; j < *object_cnt; j++) {
switch (not_flag) {
case RESV_CTLD_USER_NOT:
found_it = _check_uid(
resv_ptr->user_list[j],
uid_list[i]);
break;
case RESV_CTLD_ACCT_NOT:
found_it = _check_char(
resv_ptr->account_list[j],
alter_list[i]);
break;
default:
break;
}
if (found_it)
break;
}
if (!found_it)
return SLURM_ERROR;
_remove_name_from_str(alter_list[i], *object_str);
if (!*object_str[0])
xfree(*object_str);
(*object_cnt)--;
for (k=j; k<*object_cnt; k++) {
switch (not_flag) {
case RESV_CTLD_USER_NOT:
resv_ptr->user_list[k] =
resv_ptr->user_list[k+1];
break;
case RESV_CTLD_ACCT_NOT:
resv_ptr->account_list[k] =
resv_ptr->account_list[k+1];
break;
default:
break;
}
}
}
}
if (plus) {
for (i=0; i<alter_cnt; i++) {
if (alter_types[i] != 2) /* not plus */
continue;
found_it = false;
for (j=0; j<*object_cnt; j++) {
switch (not_flag) {
case RESV_CTLD_USER_NOT:
found_it = _check_uid(
resv_ptr->user_list[j],
uid_list[i]);
break;
case RESV_CTLD_ACCT_NOT:
found_it = _check_char(
resv_ptr->account_list[j],
alter_list[i]);
break;
default:
break;
}
if (found_it)
break;
}
if (found_it)
continue; /* duplicate entry */
if (*object_str && *object_str[0])
xstrcat(*object_str, ",");
if (resv_ptr->ctld_flags & not_flag)
xstrcat(*object_str, "-");
xstrcat(*object_str, alter_list[i]);
switch (not_flag) {
case RESV_CTLD_USER_NOT:
xrecalloc(resv_ptr->user_list,
(*object_cnt + 1), sizeof(uid_t));
resv_ptr->user_list[(*object_cnt)++] =
uid_list[i];
break;
case RESV_CTLD_ACCT_NOT:
xrecalloc(resv_ptr->account_list,
(*object_cnt + 1), sizeof(char *));
resv_ptr->account_list[(*object_cnt)++] =
xstrdup(alter_list[i]);
break;
default:
break;
}
}
}
return SLURM_SUCCESS;
}
/*
* 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_account_list(char *accounts, int *account_cnt,
char ***account_list, bool *account_not)
{
char *last = NULL, *tmp, *tok;
int ac_cnt = 0, i;
char **ac_list;
*account_cnt = 0;
*account_list = (char **) NULL;
*account_not = false;
if (!accounts)
return ESLURM_INVALID_ACCOUNT;
i = strlen(accounts);
ac_list = xcalloc((i + 2), sizeof(char *));
tmp = xstrdup(accounts);
tok = strtok_r(tmp, ",", &last);
while (tok) {
if (tok[0] == '-') {
if (ac_cnt == 0) {
*account_not = true;
} else if (*account_not != true) {
info("Reservation request has some "
"not/accounts");
goto inval;
}
tok++;
} else if (*account_not != false) {
info("Reservation request has some not/accounts");
goto inval;
}
if (!_is_account_valid(tok)) {
info("Reservation request has invalid account %s",
tok);
goto inval;
}
ac_list[ac_cnt++] = xstrdup(tok);
tok = strtok_r(NULL, ",", &last);
}
*account_cnt = ac_cnt;
*account_list = ac_list;
xfree(tmp);
return SLURM_SUCCESS;
inval: for (i=0; i<ac_cnt; i++)
xfree(ac_list[i]);
xfree(ac_list);
xfree(tmp);
return ESLURM_INVALID_ACCOUNT;
}
/*
* Update a account list for an existing reservation based upon an
* update comma delimited specification of accounts to add (+name),
* remove (-name), or set value of
* IN/OUT resv_ptr - pointer to reservation structure being updated
* IN accounts - a list of account names, to set, add, or remove
* RETURN 0 on success
*/
static int _update_account_list(slurmctld_resv_t *resv_ptr,
char *accounts)
{
char *last = NULL, *ac_cpy, *tok;
int ac_cnt = 0, i, rc = SLURM_ERROR;
int *ac_type, minus_account = 0, plus_account = 0;
char **ac_list;
if (!accounts)
return ESLURM_INVALID_ACCOUNT;
i = strlen(accounts);
ac_list = xcalloc((i + 2), sizeof(char *));
ac_type = xcalloc((i + 2), sizeof(int));
ac_cpy = xstrdup(accounts);
tok = strtok_r(ac_cpy, ",", &last);
while (tok) {
if (tok[0] == '-') {
ac_type[ac_cnt] = 1; /* minus */
minus_account = 1;
tok++;
} else if (tok[0] == '+') {
ac_type[ac_cnt] = 2; /* plus */
plus_account = 1;
tok++;
} else if (tok[0] == '\0') {
continue;
} else if (plus_account || minus_account) {
info("Reservation account expression invalid %s",
accounts);
goto inval;
} else
ac_type[ac_cnt] = 3; /* set */
if (!_is_account_valid(tok)) {
info("Reservation request has invalid account %s",
tok);
goto inval;
}
ac_list[ac_cnt++] = xstrdup(tok);
tok = strtok_r(NULL, ",", &last);
}
if ((plus_account == 0) && (minus_account == 0)) {
/* Just a reset of account list */
xfree(resv_ptr->accounts);
if (accounts[0] != '\0')
resv_ptr->accounts = xstrdup(accounts);
for (i = 0; i < resv_ptr->account_cnt; i++)
xfree(resv_ptr->account_list[i]);
xfree(resv_ptr->account_list);
resv_ptr->account_list = ac_list;
resv_ptr->account_cnt = ac_cnt;
resv_ptr->ctld_flags &= (~RESV_CTLD_ACCT_NOT);
xfree(ac_cpy);
xfree(ac_type);
return SLURM_SUCCESS;
}
rc = _handle_add_remove_names(resv_ptr, RESV_CTLD_ACCT_NOT,
ac_cnt, ac_list, NULL, ac_type,
minus_account, plus_account);
inval:
for (i=0; i<ac_cnt; i++)
xfree(ac_list[i]);
xfree(ac_list);
xfree(ac_type);
xfree(ac_cpy);
if (rc != SLURM_SUCCESS)
rc = ESLURM_INVALID_ACCOUNT;
return rc;
}
/*
* Validate a comma delimited list of user names and build an array of
* their UIDs
* IN users - a list of user names
* OUT user_cnt - number of UIDs in the list
* OUT user_list - list of the user's uid, CALLER MUST XFREE;
* OUT user_not - true of user_list is that of users to be blocked
* from reservation access
* IN strict - true if an invalid user invalidates the reservation
* RETURN 0 on success
*/
static int _build_uid_list(char *users, int *user_cnt, uid_t **user_list,
bool *user_not, bool strict)
{
char *last = NULL, *tmp = NULL, *tok;
int u_cnt = 0, i;
uid_t *u_list, u_tmp;
*user_cnt = 0;
*user_list = (uid_t *) NULL;
*user_not = false;
if (!users)
return ESLURM_USER_ID_MISSING;
i = strlen(users);
u_list = xcalloc((i + 2), sizeof(uid_t));
tmp = xstrdup(users);
tok = strtok_r(tmp, ",", &last);
while (tok) {
if (tok[0] == '-') {
if (u_cnt == 0) {
*user_not = true;
} else if (*user_not != true) {
info("Reservation request has some not/users");
goto inval;
}
tok++;
} else if (*user_not != false) {
info("Reservation request has some not/users");
goto inval;
}
if (uid_from_string(tok, &u_tmp) != SLURM_SUCCESS) {
info("Reservation request has invalid user %s", tok);
if (strict)
goto inval;
} else {
u_list[u_cnt++] = u_tmp;
}
tok = strtok_r(NULL, ",", &last);
}
if (u_cnt > 0) {
*user_cnt = u_cnt;
*user_list = u_list;
xfree(tmp);
return SLURM_SUCCESS;
} else {
info("Reservation request has no valid users");
return ESLURM_USER_ID_MISSING;
}
inval: xfree(tmp);
xfree(u_list);
return SLURM_ERROR;
}
/*
* Update a user/uid list for an existing reservation based upon an
* update comma delimited specification of users to add (+name),
* remove (-name), or set value of
* IN/OUT resv_ptr - pointer to reservation structure being updated
* IN users - a list of user names, to set, add, or remove
* RETURN 0 on success
*/
static int _update_uid_list(slurmctld_resv_t *resv_ptr, char *users)
{
char *last = NULL, *u_cpy = NULL, *tok;
int u_cnt = 0, i;
uid_t *u_list, u_tmp;
int *u_type, minus_user = 0, plus_user = 0, rc = ESLURM_USER_ID_MISSING;
char **u_name;
if (!users)
return ESLURM_USER_ID_MISSING;
/* Parse the incoming user expression */
i = strlen(users);
u_list = xcalloc((i + 2), sizeof(uid_t));
u_name = xcalloc((i + 2), sizeof(char *));
u_type = xcalloc((i + 2), sizeof(int));
u_cpy = xstrdup(users);
tok = strtok_r(u_cpy, ",", &last);
while (tok) {
if (tok[0] == '-') {
u_type[u_cnt] = 1; /* minus */
minus_user = 1;
tok++;
} else if (tok[0] == '+') {
u_type[u_cnt] = 2; /* plus */
plus_user = 1;
tok++;
} else if (tok[0] == '\0') {
continue;
} else if (plus_user || minus_user) {
info("Reservation user expression invalid %s", users);
goto inval;
} else
u_type[u_cnt] = 3; /* set */
if (uid_from_string(tok, &u_tmp) != SLURM_SUCCESS) {
info("Reservation request has invalid user %s", tok);
goto inval;
}
u_name[u_cnt] = tok;
u_list[u_cnt++] = u_tmp;
tok = strtok_r(NULL, ",", &last);
}
if ((plus_user == 0) && (minus_user == 0)) {
/* Just a reset of user list */
xfree(resv_ptr->users);
xfree(resv_ptr->user_list);
if (users[0] != '\0')
resv_ptr->users = xstrdup(users);
resv_ptr->user_cnt = u_cnt;
resv_ptr->user_list = u_list;
resv_ptr->ctld_flags &= (~RESV_CTLD_USER_NOT);
xfree(u_cpy);
xfree(u_name);
xfree(u_type);
return SLURM_SUCCESS;
}
rc = _handle_add_remove_names(resv_ptr, RESV_CTLD_USER_NOT,
u_cnt, u_name, u_list, u_type,
minus_user, plus_user);
inval:
xfree(u_cpy);
xfree(u_list);
xfree(u_name);
xfree(u_type);
if (rc != SLURM_SUCCESS)
rc = ESLURM_USER_ID_MISSING;
return rc;
}
/*
* Update a group/uid list for an existing reservation based upon an
* update comma delimited specification of groups to add (+name),
* remove (-name), or set value of
* IN/OUT resv_ptr - pointer to reservation structure being updated
* IN groups - a list of user names, to set, add, or remove
* RETURN 0 on success
*/
static int _update_group_uid_list(slurmctld_resv_t *resv_ptr, char *groups)
{
char *last = NULL, *g_cpy = NULL, *tok, *tok2, *resv_groups = NULL;
bool plus = false, minus = false;
if (!groups)
return ESLURM_GROUP_ID_MISSING;
/* Parse the incoming group expression */
g_cpy = xstrdup(groups);
tok = strtok_r(g_cpy, ",", &last);
if (tok)
resv_groups = xstrdup(resv_ptr->groups);
while (tok) {
if (tok[0] == '-') {
char *tmp = resv_groups;
int k;
tok++;
/* Now we need to remove from groups string */
k = strlen(tok);
while ((tok2 = xstrstr(tmp, tok))) {
if (((tok2 != resv_groups) &&
(tok2[-1] != ',') && (tok2[-1] != '-')) ||
((tok2[k] != '\0') && (tok2[k] != ','))) {
tmp = tok2 + 1;
continue;
}
if (tok2[-1] == '-') {
tok2--;
k++;
}
if (tok2[-1] == ',') {
tok2--;
k++;
} else if (tok2[k] == ',')
k++;
for (int j=0; ; j++) {
tok2[j] = tok2[j+k];
if (tok2[j] == '\0')
break;
}
}
minus = true;
} else if (tok[0] == '+') {
tok++;
if ((tok2 = xstrstr(resv_groups, tok)))
continue;
xstrfmtcat(resv_groups, "%s%s",
resv_groups ? "," : "",
tok);
plus = true;
} else if (tok[0] == '\0') {
continue;
} else if (plus || minus) {
info("Reservation group expression invalid %s", groups);
goto inval;
} else {
/*
* It is a straight list set the pointers right and
* break
*/
xfree(resv_groups);
resv_groups = xstrdup(groups);
break;
}
tok = strtok_r(NULL, ",", &last);
}
/* Just a reset of group list */
resv_ptr->ctld_flags &= (~RESV_CTLD_USER_NOT);
xfree(resv_ptr->groups);
xfree(resv_ptr->user_list);
resv_ptr->user_cnt = 0;
if (resv_groups && resv_groups[0] != '\0') {
resv_ptr->user_list =
get_groups_members(resv_groups, &resv_ptr->user_cnt);
if (resv_ptr->user_cnt) {
resv_ptr->groups = resv_groups;
resv_groups = NULL;
} else {
goto inval;
}
}
xfree(g_cpy);
xfree(resv_groups);
return SLURM_SUCCESS;
inval:
xfree(g_cpy);
xfree(resv_groups);
return ESLURM_GROUP_ID_MISSING;
}
/* Given a core_resrcs data structure (which has information only about the
* nodes in that reservation), build a global core_bitmap (which includes
* information about all nodes in the system).
* RET SLURM_SUCCESS or error code */
static int _get_core_resrcs(slurmctld_resv_t *resv_ptr)
{
int i, j, node_inx;
int c, core_offset_local, core_offset_global, core_end, core_set;
node_record_t *node_ptr;
if (!resv_ptr->core_resrcs || resv_ptr->core_bitmap ||
!resv_ptr->core_resrcs->core_bitmap ||
(bit_ffs(resv_ptr->core_resrcs->core_bitmap) == -1))
return SLURM_SUCCESS;
FREE_NULL_BITMAP(resv_ptr->core_resrcs->node_bitmap);
if (resv_ptr->core_resrcs->nodes &&
(node_name2bitmap(resv_ptr->core_resrcs->nodes, false,
&resv_ptr->core_resrcs->node_bitmap, NULL))) {
error("Invalid nodes (%s) for reservation %s",
resv_ptr->core_resrcs->nodes, resv_ptr->name);
return SLURM_ERROR;
} else if (resv_ptr->core_resrcs->nodes == NULL) {
resv_ptr->core_resrcs->node_bitmap =
bit_alloc(node_record_count);
}
i = bit_set_count(resv_ptr->core_resrcs->node_bitmap);
if (resv_ptr->core_resrcs->nhosts != i) {
error("Invalid change in resource allocation node count for "
"reservation %s, %u to %d",
resv_ptr->name, resv_ptr->core_resrcs->nhosts, i);
return SLURM_ERROR;
}
node_conf_create_cluster_core_bitmap(&resv_ptr->core_bitmap);
for (i = 0, node_inx = -1;
(node_ptr = next_node_bitmap(resv_ptr->core_resrcs->node_bitmap,
&i));
i++) {
node_inx++;
core_offset_global = cr_get_coremap_offset(i);
core_end = cr_get_coremap_offset(i + 1);
core_offset_local = get_job_resources_offset(
resv_ptr->core_resrcs, node_inx, 0, 0);
core_set = 0;
for (c = core_offset_global, j = core_offset_local;
c < core_end &&
core_set < resv_ptr->core_resrcs->cpus[node_inx];
c++, j++) {
if (!bit_test(resv_ptr->core_resrcs->core_bitmap, j))
continue;
bit_set(resv_ptr->core_bitmap, c);
core_set += node_ptr->threads;
}
if (core_set < resv_ptr->core_resrcs->cpus[node_inx]) {
error("Unable to restore reservation %s on node_inx %d of nodes %s. Probably node configuration changed",
resv_ptr->name, node_inx,
resv_ptr->core_resrcs->nodes);
return SLURM_ERROR;
}
}
return SLURM_SUCCESS;
}
/*
* _pack_resv - dump configuration information about a specific reservation
* in machine independent form (for network transmission or state save)
* IN resv_ptr - pointer to reservation for which information is requested
* IN/OUT buffer - buffer in which data is placed, pointers automatically
* updated
* IN internal - true if for internal save state, false for xmit to users
* NOTE: if you make any changes here be sure to make the corresponding
* to _unpack_reserve_info_members() in common/slurm_protocol_pack.c
* plus load_all_resv_state() below.
*/
static void _pack_resv(slurmctld_resv_t *resv_ptr, buf_t *buffer,
bool internal, uint16_t protocol_version)
{
time_t now = time(NULL), start_relative, end_relative;
int offset_start, offset_end;
uint32_t i_cnt;
node_record_t *node_ptr;
job_resources_t *core_resrcs;
char *core_str;
if (resv_ptr->flags & RESERVE_FLAG_TIME_FLOAT)
last_resv_update = now;
if (!internal && (resv_ptr->flags & RESERVE_FLAG_TIME_FLOAT)) {
start_relative = resv_ptr->start_time + now;
if (resv_ptr->duration == INFINITE)
end_relative = start_relative + YEAR_SECONDS;
else if (resv_ptr->duration && (resv_ptr->duration != NO_VAL))
end_relative = start_relative + resv_ptr->duration * 60;
else {
end_relative = resv_ptr->end_time;
if (start_relative > end_relative)
start_relative = end_relative;
}
} else {
start_relative = resv_ptr->start_time_first;
end_relative = resv_ptr->end_time;
}
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);
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);
pack32(NO_VAL, buffer); /* was resv_watts */
pack_time(start_relative, buffer);
packstr(resv_ptr->tres_fmt_str, buffer);
packstr(resv_ptr->users, buffer);
packstr(resv_ptr->groups, 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);
}
}
}
}
}
slurmctld_resv_t *_load_reservation_state(buf_t *buffer,
uint16_t protocol_version)
{
slurmctld_resv_t *resv_ptr;
resv_ptr = xmalloc(sizeof(slurmctld_resv_t));
resv_ptr->magic = RESV_MAGIC;
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);
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_unpack32(&uint32_tmp, buffer); /* was resv_watts */
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);
/* 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
goto unpack_error;
return resv_ptr;
unpack_error:
error("Incomplete reservation state save file");
_del_resv_rec(resv_ptr);
return NULL;
}
/*
* Test if a new/updated reservation request will overlap running jobs
* Ignore jobs already running in that specific reservation
* resv_name IN - Name of existing reservation or NULL
* RET true if overlap
*/
static bool _job_overlap(time_t start_time, uint64_t flags,
bitstr_t *node_bitmap, char *resv_name)
{
list_itr_t *job_iterator;
job_record_t *job_ptr;
bool overlap = false;
if (!node_bitmap || /* No nodes to test for */
(flags & RESERVE_FLAG_IGN_JOBS)) /* ignore job overlap */
return overlap;
if (flags & RESERVE_FLAG_TIME_FLOAT)
start_time += time(NULL);
job_iterator = list_iterator_create(job_list);
while ((job_ptr = list_next(job_iterator))) {
if (IS_JOB_RUNNING(job_ptr) &&
(job_ptr->end_time > start_time) &&
bit_overlap_any(job_ptr->node_bitmap, node_bitmap) &&
((resv_name == NULL) ||
(xstrcmp(resv_name, job_ptr->resv_name) != 0))) {
overlap = true;
break;
}
}
list_iterator_destroy(job_iterator);
return overlap;
}
/*
* Test if a new/updated reservation request overlaps an existing
* reservation
* RET true if overlap
*/
static bool _resv_overlap(resv_desc_msg_t *resv_desc_ptr,
bitstr_t *node_bitmap,
slurmctld_resv_t *this_resv_ptr)
{
list_itr_t *iter;
slurmctld_resv_t *resv_ptr;
bool rc = false;
if ((resv_desc_ptr->flags & RESERVE_FLAG_MAINT) ||
(resv_desc_ptr->flags & RESERVE_FLAG_OVERLAP) ||
(!node_bitmap))
return rc;
iter = list_iterator_create(resv_list);
while ((resv_ptr = list_next(iter))) {
if (resv_ptr == this_resv_ptr)
continue; /* skip self */
if (resv_ptr->node_bitmap == NULL)
continue; /* no specific nodes in reservation */
if ((resv_ptr->flags & RESERVE_FLAG_MAINT) ||
(resv_ptr->flags & RESERVE_FLAG_OVERLAP))
continue;
if (!bit_overlap_any(resv_ptr->node_bitmap, node_bitmap))
continue; /* no overlap */
if (!(resv_ptr->ctld_flags & RESV_CTLD_FULL_NODE))
continue;
if (_resv_time_overlap(resv_desc_ptr, resv_ptr)) {
rc = true;
break;
}
}
list_iterator_destroy(iter);
return rc;
}
static bool _slots_overlap(const constraint_slot_t *slot0,
const constraint_slot_t *slot1)
{
if ((slot0->start < slot1->end) &&
(slot1->start < slot0->end))
return true;
return false;
}
/*
* Get number of seconds to next reoccurring time slot.
*
* Used to check overlapping time slots. See _advance_time() for actual
* advancement of reservation.
*/
static time_t _get_advance_secs(const constraint_slot_t *slot)
{
time_t reoccurring_secs = -1;
struct tm tm;
if (slot->flags & RESERVE_FLAG_WEEKDAY) {
localtime_r(&(slot->start), &tm);
if (tm.tm_wday == 5) /* Friday */
reoccurring_secs = 60 * 60 * 24 * 3;
else if (tm.tm_wday == 6) /* Saturday */
reoccurring_secs = 60 * 60 * 24 * 2;
else
reoccurring_secs = 60 * 60 * 24;
} else if (slot->flags & RESERVE_FLAG_WEEKEND) {
localtime_r(&(slot->start), &tm);
if (tm.tm_wday == 6) /* Saturday */
reoccurring_secs = 60 * 60 * 24;
else
reoccurring_secs = 60 * 60 * 24 * (6 - tm.tm_wday);
} else if (slot->flags & RESERVE_FLAG_WEEKLY) {
reoccurring_secs = 60 * 60 * 24 * 7;
} else if (slot->flags & RESERVE_FLAG_DAILY) {
reoccurring_secs = 60 * 60 * 24;
} else if (slot->flags & RESERVE_FLAG_HOURLY) {
reoccurring_secs = 60 * 60;
} else {
error("%s: Unknown recurring reservation flags",
__func__);
return -1;
}
return reoccurring_secs;
}
static void _advance_slot(constraint_slot_t *slot)
{
time_t reoccurring_secs = 0;
if (!slot) {
error("%s: Reservation slot is NULL and it shouldn't happen",
__func__);
return;
}
if (!(slot->flags & RESERVE_REOCCURRING))
return;
if ((reoccurring_secs = _get_advance_secs(slot)) == -1)
return;
slot->start += reoccurring_secs;
slot->end += reoccurring_secs;
}
static void _advance_slot_until(constraint_slot_t *slot, time_t end)
{
constraint_slot_t slot_advanced;
time_t reoccurring_secs = 0;
int reoccurrings = 0;
if (!slot) {
error("%s: Reservation slot is NULL and it shouldn't happen",
__func__);
return;
}
if (!(slot->flags & RESERVE_REOCCURRING))
return;
if (slot->start > end) {
error("%s: Reservation slot starts after the requested end this shouldn't happen",
__func__);
return;
}
if (slot->flags & (RESERVE_FLAG_WEEKDAY | RESERVE_FLAG_WEEKEND)) {
slot_advanced = *slot;
while (slot_advanced.start < end) {
*slot = slot_advanced;
_advance_slot(&slot_advanced);
}
} else {
/* Avoid while loop for regular reoccurrings for performance */
if ((reoccurring_secs = _get_advance_secs(slot)) == -1)
return;
/*
* As reoccurrings is a truncated integer we ensure that
* slot->start will be <= end-1 (ie, < end).
*/
reoccurrings = (end - 1 - slot->start) / reoccurring_secs;
slot->start += reoccurrings * reoccurring_secs;
slot->end += reoccurrings * reoccurring_secs;
if (reoccurrings < 0)
error("%s: Number of reoccurrings for the reservation slot is negative and this shouldn't happen",
__func__);
}
}
static bool _resv_time_overlap(resv_desc_msg_t *resv_desc_ptr,
slurmctld_resv_t *resv_ptr)
{
time_t now = time(NULL);
constraint_slot_t slot_a = {
.start = resv_desc_ptr->start_time,
.end = resv_desc_ptr->end_time,
.duration = resv_desc_ptr->duration,
.flags = resv_desc_ptr->flags,
};
constraint_slot_t slot_b = {
.start = resv_ptr->start_time,
.end = resv_ptr->end_time,
.duration = resv_ptr->duration,
.flags = resv_ptr->flags,
};
constraint_slot_t *slot[2] = {&slot_a, &slot_b};
for (int i = 0; i < 2; i++) {
/* Set real start/end for floating reservations */
if (slot[i]->flags & RESERVE_FLAG_TIME_FLOAT) {
slot[i]->start += now;
if (slot[i]->duration == INFINITE)
slot[i]->end = slot[i]->start + YEAR_SECONDS;
else if (slot[i]->duration &&
(slot[i]->duration != NO_VAL))
slot[i]->end = slot[i]->start +
slot[i]->duration * 60;
}
/* Sanity check */
if (slot[i]->start > slot[i]->end) {
error("%s: Reservation slot has start > end and it shouldn't happen",
__func__);
return true;
}
}
/* Ensure that slot0 is the earlier resv and slot1 the latter one */
if (slot[1]->end < slot[0]->end) {
slot[0] = &slot_b;
slot[1] = &slot_a;
}
/* Check base overlapping */
if (_slots_overlap(slot[0], slot[1])) {
log_flag(RESERVATION, "%s: Reservation slots overlap",
__func__);
return true;
}
/*
* Handling reoccurring slots
* 1) Advance the earlier slot to the closest period to the end of the
* latter one, while keeping the order between them.
* Check if slot0 reservation's end overlaps start time of slot1.
* 2) Advance slot0 one more time, so it's now the latter.
* Check if slot0 reservation's start overlaps end time of slot1.
* 3) If slot1 is also recurring, repeat 1) switching slot0 and slot1.
*
* e.g.
* slot0 reoccurs WEEKLY on Mondays at 5:00 for 1h starting today.
* slot1 reoccurs HOURLY for 30m starting next Tuesday at 4:00.
*
* 1) slot0 is advanced to next Monday and won't overlap.
* 2) slot0 is advanced to next next Monday and won't overlap.
* 3) slot1 is advanced HOURLY until it reaches next next Monday and
* will overlap.
*/
if (slot[0]->flags & RESERVE_REOCCURRING) {
/*
* 1) Advance earlier slot to the last reoccurring period
* before the later slot ends.
*/
_advance_slot_until(slot[0], slot[1]->end);
if (slot[0]->end > slot[1]->end) {
error("%s: Reservation slot is already the last one, and it shouldn't happen",
__func__);
return true;
}
/* Check overlap in the original order */
if (_slots_overlap(slot[0], slot[1])) {
log_flag(RESERVATION, "%s: Reservation slots overlap due reoccurrings of the earlier reservation",
__func__);
return true;
}
/*
* 2) Advance earlier slot once to convert it into the later one
*/
_advance_slot(slot[0]);
if (slot[0]->end < slot[1]->end) {
error("%s: Reservation slot is still the first one, and it shouldn't happen",
__func__);
return true;
}
/*
* Check overlap after slot0 is now the latter reservation
* Check if slot0 reservation's start overlaps end time of slot1.
*/
if (_slots_overlap(slot[0], slot[1])) {
log_flag(RESERVATION, "%s: Reservation slots overlap due reocurrings of the earlier reservation, once it becomes the later one",
__func__);
return true;
}
if (slot[1]->flags & RESERVE_REOCCURRING) {
/* 3) Repeat 1) with slot1 being the earlier one */
_advance_slot_until(slot[1], slot[0]->end);
if (slot[1]->end > slot[0]->end) {
error("%s: Reservation slot is the later one again, and it shouldn't happen",
__func__);
return true;
}
/* Check overlap */
if (_slots_overlap(slot[0], slot[1])) {
log_flag(RESERVATION, "%s: Reservations overlap due recurrence of the later reservation",
__func__);
return true;
}
}
}
return false;
}
/* Set a reservation's TRES count. Requires that the reservation's
* node_bitmap be set.
* This needs to be done after all other setup is done.
*/
static void _set_tres_cnt(slurmctld_resv_t *resv_ptr,
slurmctld_resv_t *old_resv_ptr)
{
uint64_t cpu_cnt = 0;
node_record_t *node_ptr;
char *name1;
assoc_mgr_lock_t locks = { .tres = READ_LOCK };
if ((resv_ptr->ctld_flags & RESV_CTLD_FULL_NODE) &&
resv_ptr->node_bitmap) {
resv_ptr->core_cnt = 0;
for (int i = 0;
(node_ptr = next_node_bitmap(resv_ptr->node_bitmap, &i));
i++) {
resv_ptr->core_cnt += node_ptr->tot_cores;
cpu_cnt += node_ptr->cpus;
}
} else if (resv_ptr->core_bitmap) {
resv_ptr->core_cnt =
bit_set_count(resv_ptr->core_bitmap);
cpu_cnt = resv_ptr->core_resrcs->ncpus;
}
xfree(resv_ptr->tres_str);
if (resv_ptr->gres_list_alloc) { /* First, doesn't add comma */
assoc_mgr_lock_t locks = { .tres = READ_LOCK };
uint64_t *tres_alloc_cnt;
assoc_mgr_lock(&locks);
tres_alloc_cnt = xcalloc(slurmctld_tres_cnt, sizeof(uint64_t));
gres_stepmgr_set_job_tres_cnt(
resv_ptr->gres_list_alloc,
resv_ptr->node_cnt,
tres_alloc_cnt,
true);
resv_ptr->tres_str = assoc_mgr_make_tres_str_from_array(
tres_alloc_cnt, TRES_STR_FLAG_SIMPLE, true);
xfree(tres_alloc_cnt);
assoc_mgr_unlock(&locks);
}
if (cpu_cnt)
xstrfmtcat(resv_ptr->tres_str, "%s%u=%"PRIu64,
resv_ptr->tres_str ? "," : "",
TRES_CPU, cpu_cnt);
if ((name1 = licenses_2_tres_str(resv_ptr->license_list))) {
xstrfmtcat(resv_ptr->tres_str, "%s%s",
resv_ptr->tres_str ? "," : "",
name1);
xfree(name1);
}
if ((name1 = bb_g_xlate_bb_2_tres_str(resv_ptr->burst_buffer))) {
xstrfmtcat(resv_ptr->tres_str, "%s%s",
resv_ptr->tres_str ? "," : "",
name1);
xfree(name1);
}
xfree(resv_ptr->tres_fmt_str);
assoc_mgr_lock(&locks);
resv_ptr->tres_fmt_str = slurmdb_make_tres_string_from_simple(
resv_ptr->tres_str, assoc_mgr_tres_list, NO_VAL,
CONVERT_NUM_UNIT_EXACT, 0, NULL);
assoc_mgr_unlock(&locks);
if (get_sched_log_level() >= LOG_LEVEL_INFO) {
char *tmp_str = NULL, *tmp_str_pos = NULL;
char start_time[256], end_time[256];
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->accounts)
xstrfmtcatat(tmp_str, &tmp_str_pos, " accounts=%s",
resv_ptr->accounts);
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);
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
_post_resv_create(resv_ptr);
}
/*
* _license_validate2 - A variant of license_validate which considers the
* licenses used by overlapping reservations
*/
static list_t *_license_validate2(resv_desc_msg_t *resv_desc_ptr, bool *valid)
{
list_t *license_list = NULL, *merged_list = NULL;
list_itr_t *iter;
slurmctld_resv_t *resv_ptr;
char *merged_licenses;
if (xstrchr(resv_desc_ptr->licenses, '|')) {
/* Reservations do not support OR licenses */
*valid = false;
return NULL;
}
license_list = license_validate(resv_desc_ptr->licenses, true, true,
true, NULL, valid);
if (resv_desc_ptr->licenses == NULL)
return license_list;
merged_licenses = xstrdup(resv_desc_ptr->licenses);
iter = list_iterator_create(resv_list);
while ((resv_ptr = list_next(iter))) {
if ((resv_ptr->licenses == NULL) ||
(resv_ptr->end_time <= resv_desc_ptr->start_time) ||
(resv_ptr->start_time >= resv_desc_ptr->end_time))
continue; /* No overlap */
if (resv_desc_ptr->name &&
!xstrcmp(resv_desc_ptr->name, resv_ptr->name))
continue; /* Modifying this reservation */
xstrcat(merged_licenses, ",");
xstrcat(merged_licenses, resv_ptr->licenses);
}
list_iterator_destroy(iter);
merged_list = license_validate(merged_licenses, true, true, true, NULL,
valid);
xfree(merged_licenses);
FREE_NULL_LIST(merged_list);
return license_list;
}
static int _delete_resv_internal(slurmctld_resv_t *resv_ptr,
bitstr_t *node_down_bitmap)
{
if (_is_resv_used(resv_ptr))
return ESLURM_RESERVATION_BUSY;
if (resv_ptr->ctld_flags & RESV_CTLD_NODE_FLAGS_SET) {
time_t now = time(NULL);
resv_ptr->ctld_flags &= (~RESV_CTLD_NODE_FLAGS_SET);
_set_nodes_flags(resv_ptr, now,
(NODE_STATE_RES | NODE_STATE_MAINT), false,
node_down_bitmap);
last_node_update = now;
}
return _post_resv_delete(resv_ptr);
}
static bitstr_t *_get_update_node_bitmap(slurmctld_resv_t *resv_ptr,
char *node_list)
{
char *last = NULL, *tmp, *tok, *node_name;
node_record_t *node_ptr;
bitstr_t *node_bitmap = NULL;
hostlist_t *hl = NULL;
tmp = xstrdup(node_list);
tok = node_conf_nodestr_tokenize(tmp, &last);
while (tok) {
bool minus = false, plus = false;
if (tok[0] == '-') {
minus = true;
tok++;
} else if (tok[0] == '+') {
plus = true;
tok++;
} else if (tok[0] == '\0') {
break;
}
if (!plus && !minus) {
if (node_bitmap) {
info("Reservation %s request has bad nodelist given (%s)",
resv_ptr->name, node_list);
FREE_NULL_BITMAP(node_bitmap);
} else
(void)node_name2bitmap(node_list, false,
&node_bitmap, NULL);
break;
}
/* Create hostlist to handle ranges i.e. tux[0-10] */
hl = hostlist_create(tok);
while ((node_name = hostlist_shift(hl))) {
node_ptr = find_node_record(node_name);
if (!node_ptr) {
info("Reservation %s request has bad node name given (%s)",
resv_ptr->name, node_name);
free(node_name);
FREE_NULL_BITMAP(node_bitmap);
break;
}
free(node_name);
if (!node_bitmap)
node_bitmap = bit_copy(resv_ptr->node_bitmap);
if (plus)
bit_set(node_bitmap, node_ptr->index);
else if (minus)
bit_clear(node_bitmap, node_ptr->index);
}
hostlist_destroy(hl);
if (!node_bitmap)
break;
tok = node_conf_nodestr_tokenize(NULL, &last);
}
xfree(tmp);
return node_bitmap;
}
/* Returns false if only one reoccurring flag is set, true otherwise */
static bool _has_multiple_reoccurring(resv_desc_msg_t *resv_desc_ptr){
int flag_count = 0;
if (resv_desc_ptr->flags & RESERVE_FLAG_HOURLY)
flag_count++;
if (resv_desc_ptr->flags & RESERVE_FLAG_DAILY)
flag_count++;
if (resv_desc_ptr->flags & RESERVE_FLAG_WEEKDAY)
flag_count++;
if (resv_desc_ptr->flags & RESERVE_FLAG_WEEKEND)
flag_count++;
if (resv_desc_ptr->flags & RESERVE_FLAG_WEEKLY)
flag_count++;
return (flag_count > 1);
}
static void _set_tres_err_msg(char **err_msg, int rc)
{
if (!err_msg)
return;
switch (rc) {
case ESLURM_INVALID_BURST_BUFFER_REQUEST:
*err_msg = xstrdup("TRES=<buffer_spec>=<num> and BurstBuffer=<buffer_spec> are mutually exclusive");
break;
case ESLURM_INVALID_CPU_COUNT:
*err_msg = xstrdup("TRES=cpu=<num> and CoreCnt=<num> are mutually exclusive");
break;
case ESLURM_INVALID_LICENSES:
*err_msg = xstrdup("TRES=license/<name>=<num> and Licenses=<name>:<num> are mutually exclusive");
break;
case ESLURM_INVALID_NODE_COUNT:
*err_msg = xstrdup("TRES=node=<num> and Nodes=<num> are mutually exclusive");
break;
}
}
/* Create a resource reservation */
extern int create_resv(resv_desc_msg_t *resv_desc_ptr, char **err_msg)
{
int i, rc = SLURM_SUCCESS;
time_t now = time(NULL);
part_record_t *part_ptr = NULL;
slurmctld_resv_t *resv_ptr = NULL;
int account_cnt = 0, user_cnt = 0;
char **account_list = NULL;
uid_t *user_list = NULL;
list_t *license_list = NULL;
uint32_t total_node_cnt = 0;
bool account_not = false, user_not = false, qos_not = false;
resv_select_t resv_select = { 0 };
list_t *qos_list = NULL;
_create_resv_lists(false);
if (resv_desc_ptr->flags == NO_VAL64)
resv_desc_ptr->flags = 0;
else {
resv_desc_ptr->flags &= RESERVE_FLAG_MAINT |
RESERVE_FLAG_FLEX |
RESERVE_FLAG_OVERLAP |
RESERVE_FLAG_IGN_JOBS |
RESERVE_FLAG_HOURLY |
RESERVE_FLAG_DAILY |
RESERVE_FLAG_WEEKDAY |
RESERVE_FLAG_WEEKEND |
RESERVE_FLAG_WEEKLY |
RESERVE_FLAG_FORCE_START |
RESERVE_FLAG_STATIC |
RESERVE_FLAG_ANY_NODES |
RESERVE_FLAG_PART_NODES |
RESERVE_FLAG_TIME_FLOAT |
RESERVE_FLAG_PURGE_COMP |
RESERVE_FLAG_REPLACE |
RESERVE_FLAG_REPLACE_DOWN |
RESERVE_FLAG_NO_HOLD_JOBS |
RESERVE_FLAG_MAGNETIC |
RESERVE_FLAG_USER_DEL |
RESERVE_TRES_PER_NODE;
}
if ((rc = _parse_tres_str(resv_desc_ptr)) != SLURM_SUCCESS) {
_set_tres_err_msg(err_msg, rc);
return rc;
}
_dump_resv_req(resv_desc_ptr, "create_resv");
if (xstrcasestr(resv_desc_ptr->tres_str, "gres"))
resv_desc_ptr->flags |= RESERVE_FLAG_GRES_REQ;
/* Validate the request */
if ((resv_desc_ptr->core_cnt != NO_VAL) && !running_cons_tres()) {
char *err_str = "CoreCnt only supported with cons_tres.";
info("%s", err_str);
if (err_msg)
*err_msg = xstrdup(err_str);
rc = ESLURM_NOT_SUPPORTED;
goto bad_parse;
}
if (resv_desc_ptr->start_time != (time_t) NO_VAL) {
if (resv_desc_ptr->flags & RESERVE_FLAG_TIME_FLOAT) {
if (resv_desc_ptr->start_time < now)
resv_desc_ptr->start_time = now;
} else if (resv_desc_ptr->flags & RESERVE_FLAG_FORCE_START) {
if ((resv_desc_ptr->start_time +
resv_desc_ptr->duration * 60) <
(now - MAX_RESV_DELAY)) {
info("Reservation request has start and end time in the past");
rc = ESLURM_INVALID_TIME_VALUE;
goto bad_parse;
}
} else if (resv_desc_ptr->start_time < (now - MAX_RESV_DELAY)) {
info("Reservation request has invalid start time");
rc = ESLURM_INVALID_TIME_VALUE;
goto bad_parse;
}
} else
resv_desc_ptr->start_time = now;
if (resv_desc_ptr->end_time != (time_t) NO_VAL) {
if (resv_desc_ptr->end_time < (now - MAX_RESV_DELAY)) {
info("Reservation request has invalid end time");
rc = ESLURM_INVALID_TIME_VALUE;
goto bad_parse;
}
} else if (resv_desc_ptr->duration == INFINITE) {
resv_desc_ptr->end_time = resv_desc_ptr->start_time +
YEAR_SECONDS;
} else if (resv_desc_ptr->duration) {
resv_desc_ptr->end_time = resv_desc_ptr->start_time +
(resv_desc_ptr->duration * 60);
} else
resv_desc_ptr->end_time = INFINITE;
if (resv_desc_ptr->flags & RESERVE_REOCCURRING) {
if (_has_multiple_reoccurring(resv_desc_ptr)) {
info("Reservation has multiple reoccurring flags. Please specify only one reoccurring flag");
if (err_msg)
*err_msg = xstrdup("Reservation has multiple reoccurring flags. Please specify only one reoccurring flag");
rc = ESLURM_NOT_SUPPORTED;
goto bad_parse;
}
}
if ((resv_desc_ptr->flags & RESERVE_FLAG_REPLACE) ||
(resv_desc_ptr->flags & RESERVE_FLAG_REPLACE_DOWN)) {
if (resv_desc_ptr->node_list) {
info("%s: REPLACE or REPLACE_DOWN flags should be used with the NodeCnt reservation option; do not specify Nodes",
__func__);
if (err_msg)
*err_msg = xstrdup("REPLACE or REPLACE_DOWN flags should be used with the NodeCnt reservation option; do not specify Nodes");
rc = ESLURM_INVALID_NODE_NAME;
goto bad_parse;
}
if (resv_desc_ptr->core_cnt != NO_VAL) {
info("%s: REPLACE or REPLACE_DOWN flags should be used with the NodeCnt reservation option; do not specify CoreCnt",
__func__);
if (err_msg)
*err_msg = xstrdup("REPLACE or REPLACE_DOWN flags should be used with the NodeCnt reservation option; do not specify CoreCnt");
rc = ESLURM_INVALID_CPU_COUNT;
goto bad_parse;
}
}
if (((resv_desc_ptr->flags & RESERVE_FLAG_REPLACE) ||
(resv_desc_ptr->flags & RESERVE_FLAG_REPLACE_DOWN)) &&
((resv_desc_ptr->flags & RESERVE_FLAG_STATIC) ||
(resv_desc_ptr->flags & RESERVE_FLAG_MAINT))) {
info("REPLACE and REPLACE_DOWN flags cannot be used with STATIC_ALLOC or MAINT flags");
if (err_msg)
*err_msg = xstrdup("REPLACE and REPLACE_DOWN flags cannot be used with STATIC_ALLOC or MAINT flags");
rc = ESLURM_NOT_SUPPORTED;
goto bad_parse;
}
if (resv_desc_ptr->partition) {
part_ptr = find_part_record(resv_desc_ptr->partition);
if (!part_ptr) {
info("Reservation request has invalid partition %s",
resv_desc_ptr->partition);
rc = ESLURM_INVALID_PARTITION_NAME;
goto bad_parse;
}
} else if (resv_desc_ptr->flags & RESERVE_FLAG_PART_NODES) {
info("Reservation request with Part_Nodes flag lacks "
"partition specification");
rc = ESLURM_INVALID_PARTITION_NAME;
goto bad_parse;
}
if ((resv_desc_ptr->flags & RESERVE_FLAG_PART_NODES) &&
(xstrcasecmp(resv_desc_ptr->node_list, "ALL"))) {
info("Reservation request with Part_Nodes flag lacks nodelist=ALL specification");
rc = ESLURM_INVALID_NODE_NAME;
goto bad_parse;
}
if (resv_desc_ptr->users && resv_desc_ptr->groups) {
info("Reservation request with both users and groups, these are mutually exclusive. You can have one or the other, but not both.");
rc = ESLURM_RESERVATION_USER_GROUP;
goto bad_parse;
} else if (!resv_desc_ptr->accounts &&
!resv_desc_ptr->users &&
!resv_desc_ptr->qos &&
!resv_desc_ptr->groups) {
info("Reservation request lacks users, accounts, QOS, or groups");
rc = ESLURM_RESERVATION_EMPTY;
goto bad_parse;
}
if (resv_desc_ptr->accounts) {
rc = _build_account_list(resv_desc_ptr->accounts,
&account_cnt, &account_list,
&account_not);
if (rc)
goto bad_parse;
}
if (resv_desc_ptr->users) {
rc = _build_uid_list(resv_desc_ptr->users,
&user_cnt, &user_list, &user_not, true);
if (rc)
goto bad_parse;
}
if (resv_desc_ptr->groups) {
user_list =
get_groups_members(resv_desc_ptr->groups, &user_cnt);
if (!user_list) {
rc = ESLURM_GROUP_ID_MISSING;
goto bad_parse;
}
}
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->licenses) {
bool valid = true;
license_list = _license_validate2(resv_desc_ptr, &valid);
if (!valid) {
info("Reservation request has invalid licenses %s",
resv_desc_ptr->licenses);
rc = ESLURM_INVALID_LICENSES;
goto bad_parse;
}
}
if ((resv_desc_ptr->flags & RESERVE_FLAG_TIME_FLOAT) &&
(resv_desc_ptr->flags & RESERVE_REOCCURRING)) {
info("Reservation request has mutually exclusive flags. Repeating floating reservations are not supported.");
if (err_msg)
*err_msg = xstrdup("Reservation request has mutually exclusive flags. Repeating floating reservations are not supported.");
rc = ESLURM_NOT_SUPPORTED;
goto bad_parse;
}
if (resv_desc_ptr->node_list) {
resv_desc_ptr->flags |= RESERVE_FLAG_SPEC_NODES;
if (xstrcasecmp(resv_desc_ptr->node_list, "ALL") == 0) {
if (resv_desc_ptr->partition && part_ptr &&
(resv_desc_ptr->flags & RESERVE_FLAG_PART_NODES)) {
resv_select.node_bitmap =
bit_copy(part_ptr->node_bitmap);
} else {
resv_desc_ptr->flags &=
(~RESERVE_FLAG_PART_NODES);
resv_desc_ptr->flags |= RESERVE_FLAG_ALL_NODES;
resv_select.node_bitmap =
node_conf_get_active_bitmap();
}
xfree(resv_desc_ptr->node_list);
resv_desc_ptr->node_list =
bitmap2node_name(resv_select.node_bitmap);
} else {
resv_desc_ptr->flags &= (~RESERVE_FLAG_PART_NODES);
if (node_name2bitmap(resv_desc_ptr->node_list, false,
&resv_select.node_bitmap, NULL)) {
rc = ESLURM_INVALID_NODE_NAME;
goto bad_parse;
}
xfree(resv_desc_ptr->node_list);
resv_desc_ptr->node_list = bitmap2node_name(resv_select.node_bitmap);
}
if (bit_set_count(resv_select.node_bitmap) == 0) {
info("Reservation node list is empty");
rc = ESLURM_INVALID_NODE_NAME;
goto bad_parse;
}
if (!(resv_desc_ptr->flags & RESERVE_FLAG_OVERLAP) &&
_resv_overlap(resv_desc_ptr, resv_select.node_bitmap, NULL)) {
info("Reservation request overlaps another");
rc = ESLURM_RESERVATION_OVERLAP;
goto bad_parse;
}
total_node_cnt = bit_set_count(resv_select.node_bitmap);
if ((resv_desc_ptr->node_cnt == NO_VAL) ||
(resv_desc_ptr->node_cnt < total_node_cnt)) {
resv_desc_ptr->node_cnt = total_node_cnt;
if ((resv_desc_ptr->flags & RESERVE_TRES_PER_NODE) &&
(resv_desc_ptr->core_cnt != NO_VAL))
resv_desc_ptr->core_cnt *=
resv_desc_ptr->node_cnt;
}
if (!(resv_desc_ptr->flags & RESERVE_FLAG_IGN_JOBS) &&
(resv_desc_ptr->core_cnt == NO_VAL)) {
uint64_t flags = resv_desc_ptr->flags;
/*
* Need to clear this flag before _job_overlap()
* which would otherwise add the current time
* on to the start_time. start_time for floating
* reservations has already been set to now.
*/
flags &= ~RESERVE_FLAG_TIME_FLOAT;
if (_job_overlap(resv_desc_ptr->start_time, flags,
resv_select.node_bitmap, NULL)) {
info("Reservation request overlaps jobs");
rc = ESLURM_NODES_BUSY;
goto bad_parse;
}
}
/* We do allow to request cores with nodelist */
if ((resv_desc_ptr->flags & RESERVE_FLAG_GRES_REQ) ||
(resv_desc_ptr->core_cnt != NO_VAL)) {
if (!resv_desc_ptr->core_cnt) {
info("Core count for reservation nodelist is not consistent!");
rc = ESLURM_INVALID_CORE_CNT;
goto bad_parse;
}
if (resv_desc_ptr->flags & RESERVE_FLAG_GRES_REQ)
log_flag(RESERVATION, "%s: Requesting TRES/GRES '%s' for node_list",
__func__, resv_desc_ptr->tres_str);
else
log_flag(RESERVATION, "%s: Requesting %d cores for node_list",
__func__,
resv_desc_ptr->core_cnt);
resv_desc_ptr->job_ptr =
job_mgr_copy_resv_desc_to_job_record(
resv_desc_ptr);
rc = _select_nodes(resv_desc_ptr, &part_ptr,
&resv_select, NULL);
if (rc != SLURM_SUCCESS)
goto bad_parse;
}
} else if (!(resv_desc_ptr->flags & RESERVE_FLAG_ANY_NODES)) {
resv_desc_ptr->flags &= (~RESERVE_FLAG_PART_NODES);
if ((resv_desc_ptr->node_cnt == NO_VAL) &&
(resv_desc_ptr->core_cnt == NO_VAL) &&
!(resv_desc_ptr->flags & RESERVE_FLAG_GRES_REQ)) {
info("Reservation request lacks node specification");
rc = ESLURM_INVALID_NODE_NAME;
} else {
resv_desc_ptr->job_ptr =
job_mgr_copy_resv_desc_to_job_record(
resv_desc_ptr);
rc = _select_nodes(resv_desc_ptr, &part_ptr,
&resv_select, NULL);
}
if (rc != SLURM_SUCCESS) {
goto bad_parse;
}
/* Get count of allocated nodes, on BlueGene systems, this
* might be more than requested */
total_node_cnt = bit_set_count(resv_select.node_bitmap);
}
if ((resv_desc_ptr->core_cnt != NO_VAL) && !resv_select.core_bitmap) {
info("Attempt to reserve cores not possible with current "
"configuration");
rc = ESLURM_INVALID_CPU_COUNT;
goto bad_parse;
}
/*
* A reservation without nodes/cores should only be possible if the flag
* ANY_NODES is set and it has at least one of licenses or burst buffer.
* So test this here after the checks for the involved options.
*/
if ((resv_desc_ptr->flags & RESERVE_FLAG_ANY_NODES) &&
!total_node_cnt && !resv_select.core_bitmap && !resv_desc_ptr->burst_buffer &&
(!license_list || list_is_empty(license_list))) {
info("%s: reservations without nodes and with ANY_NODES flag are expected to be one of Licenses, BurstBuffer, and/or TRES specification of a License or BurstBuffer", __func__);
rc = ESLURM_RESERVATION_INVALID;
goto bad_parse;
}
rc = _generate_resv_id();
if (rc != SLURM_SUCCESS)
goto bad_parse;
/* If name == NULL or empty string, then generate a name. */
if (resv_desc_ptr->name && (resv_desc_ptr->name[0] != '\0')) {
resv_ptr = find_resv_name(resv_desc_ptr->name);
if (resv_ptr) {
info("Reservation request name duplication (%s)",
resv_desc_ptr->name);
rc = ESLURM_RESERVATION_NAME_DUP;
goto bad_parse;
}
} else {
xfree(resv_desc_ptr->name);
while (1) {
_generate_resv_name(resv_desc_ptr);
resv_ptr = find_resv_name(resv_desc_ptr->name);
if (!resv_ptr)
break;
rc = _generate_resv_id(); /* makes new suffix */
if (rc != SLURM_SUCCESS)
goto bad_parse;
/* Same as previously created name, retry */
}
}
/* Create a new reservation record */
resv_ptr = xmalloc(sizeof(slurmctld_resv_t));
resv_ptr->magic = RESV_MAGIC;
resv_ptr->accounts = resv_desc_ptr->accounts;
resv_desc_ptr->accounts = NULL; /* Nothing left to free */
resv_ptr->account_cnt = account_cnt;
resv_ptr->account_list = account_list;
account_cnt = 0;
account_list = NULL;
resv_ptr->burst_buffer = resv_desc_ptr->burst_buffer;
resv_desc_ptr->burst_buffer = NULL; /* Nothing left to free */
resv_ptr->comment = resv_desc_ptr->comment;
resv_desc_ptr->comment = NULL; /* Nothing left to free */
if (resv_desc_ptr->job_ptr) {
job_record_t *job_ptr = resv_desc_ptr->job_ptr;
resv_ptr->core_resrcs = job_ptr->job_resrcs;
job_ptr->job_resrcs = NULL; /* Nothing left to free */
resv_ptr->gres_list_alloc = job_ptr->gres_list_req;
gres_job_state_log(resv_ptr->gres_list_alloc, 0);
job_ptr->gres_list_req = NULL; /* Nothing left to free */
job_record_delete(resv_desc_ptr->job_ptr);
resv_desc_ptr->job_ptr = NULL; /* Nothing left to free */
}
if (user_not)
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;
resv_ptr->duration = resv_desc_ptr->duration;
if (resv_desc_ptr->purge_comp_time != NO_VAL)
resv_ptr->purge_comp_time = resv_desc_ptr->purge_comp_time;
else
resv_ptr->purge_comp_time = 300; /* default to 5 minutes */
resv_ptr->end_time = resv_desc_ptr->end_time;
if ((resv_desc_ptr->flags & RESERVE_FLAG_FORCE_START) &&
(now > resv_desc_ptr->start_time)) {
/*
* The time_force is needed for correct accounting of
* reservations. Set the time_force to now.
*/
resv_ptr->time_force = now;
}
resv_ptr->features = resv_desc_ptr->features;
resv_desc_ptr->features = NULL; /* Nothing left to free */
resv_ptr->licenses = license_list_to_string(license_list);
resv_ptr->license_list = license_list;
license_list = NULL;
if (resv_desc_ptr->max_start_delay != NO_VAL)
resv_ptr->max_start_delay = resv_desc_ptr->max_start_delay;
resv_ptr->resv_id = top_suffix;
resv_ptr->name = xstrdup(resv_desc_ptr->name);
resv_ptr->node_cnt = total_node_cnt;
resv_ptr->node_list = resv_desc_ptr->node_list;
resv_desc_ptr->node_list = NULL; /* Nothing left to free */
resv_ptr->node_bitmap = resv_select.node_bitmap; /* May be unset */
resv_select.node_bitmap = NULL;
resv_ptr->core_bitmap = resv_select.core_bitmap; /* May be unset */
resv_select.core_bitmap = NULL;
resv_ptr->partition = resv_desc_ptr->partition;
resv_desc_ptr->partition = NULL; /* Nothing left to free */
resv_ptr->part_ptr = part_ptr;
resv_ptr->start_time = resv_desc_ptr->start_time;
resv_ptr->start_time_first = resv_ptr->start_time;
resv_ptr->start_time_prev = resv_ptr->start_time;
resv_ptr->flags = resv_desc_ptr->flags;
resv_ptr->users = resv_desc_ptr->users;
resv_desc_ptr->users = NULL; /* Nothing left to free */
resv_ptr->groups = resv_desc_ptr->groups;
resv_desc_ptr->groups = NULL;
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;
if (!(resv_desc_ptr->flags & RESERVE_FLAG_GRES_REQ) &&
(resv_desc_ptr->core_cnt == NO_VAL)) {
log_flag(RESERVATION, "%s: reservation %s using full nodes",
__func__, resv_ptr->name);
resv_ptr->ctld_flags |= RESV_CTLD_FULL_NODE;
} else {
log_flag(RESERVATION, "%s: reservation %s using partial nodes",
__func__, resv_ptr->name);
resv_ptr->ctld_flags &= (~RESV_CTLD_FULL_NODE);
}
if ((rc = _set_access(resv_ptr)) != SLURM_SUCCESS) {
_del_resv_rec(resv_ptr);
goto bad_parse;
}
if (resv_ptr->flags & RESERVE_FLAG_TIME_FLOAT)
resv_ptr->start_time -= now;
_set_tres_cnt(resv_ptr, NULL);
_add_resv_to_lists(resv_ptr);
last_resv_update = now;
schedule_resv_save();
return SLURM_SUCCESS;
bad_parse:
job_record_delete(resv_desc_ptr->job_ptr);
resv_desc_ptr->job_ptr = NULL;
for (i = 0; i < account_cnt; i++)
xfree(account_list[i]);
xfree(account_list);
FREE_NULL_LIST(license_list);
FREE_NULL_LIST(qos_list);
_free_resv_select_members(&resv_select);
xfree(user_list);
return rc;
}
/* Purge all reservation data structures */
extern void resv_fini(void)
{
FREE_NULL_LIST(magnetic_resv_list);
FREE_NULL_LIST(resv_list);
}
static int _validate_reservation_access_update(void *x, void *y)
{
bool job_use_reservation = false;
job_record_t *job_ptr = (job_record_t *) x;
slurmctld_resv_t *resv_ptr = (slurmctld_resv_t *) y;
if (job_ptr->resv_name == NULL)
return 0;
if (IS_JOB_RUNNING(job_ptr) && !xstrcmp(job_ptr->resv_name,
resv_ptr->name)) {
job_use_reservation = true;
} else if (IS_JOB_PENDING(job_ptr) && job_ptr->resv_list &&
list_find_first(job_ptr->resv_list, _find_resv_name,
resv_ptr->name)) {
job_use_reservation = true;
} else if (IS_JOB_PENDING(job_ptr) &&
!xstrcmp(job_ptr->resv_name, resv_ptr->name)) {
job_use_reservation = true;
}
if (!job_use_reservation)
return 0;
if (_valid_job_access_resv(job_ptr, resv_ptr, false) != SLURM_SUCCESS) {
info("Rejecting update of reservation %s, because it's in use by %pJ",
resv_ptr->name, job_ptr);
return 1;
}
return 0;
}
static int _validate_and_set_partition(part_record_t **part_ptr,
char **partition)
{
if (*part_ptr == NULL) {
*part_ptr = default_part_loc;
if (*part_ptr == NULL)
return ESLURM_DEFAULT_PARTITION_NOT_SET;
}
xfree(*partition);
*partition = xstrdup((*part_ptr)->name);
return SLURM_SUCCESS;
}
/* Update an exiting resource reservation */
extern int update_resv(resv_desc_msg_t *resv_desc_ptr, char **err_msg)
{
time_t now = time(NULL);
slurmctld_resv_t *resv_backup, *resv_ptr;
resv_desc_msg_t resv_desc;
int error_code = SLURM_SUCCESS, rc;
bool skip_it = false;
bool append_magnetic_resv = false, remove_magnetic_resv = false;
job_record_t *job_ptr;
bitstr_t *node_down_bitmap = NULL;
if ((rc = _parse_tres_str(resv_desc_ptr)) != SLURM_SUCCESS) {
_set_tres_err_msg(err_msg, rc);
return rc;
}
_create_resv_lists(false);
_dump_resv_req(resv_desc_ptr, "update_resv");
/* Find the specified reservation */
if (!resv_desc_ptr->name)
return ESLURM_RESERVATION_INVALID;
resv_ptr = find_resv_name(resv_desc_ptr->name);
if (!resv_ptr)
return ESLURM_RESERVATION_INVALID;
if ((resv_desc_ptr->core_cnt != NO_VAL) && !running_cons_tres()) {
char *err_str = "CoreCnt only supported with cons_tres.";
info("%s", err_str);
if (err_msg)
*err_msg = xstrdup(err_str);
return ESLURM_NOT_SUPPORTED;
}
/* FIXME: Support more core based reservation updates */
if ((!(resv_ptr->ctld_flags & RESV_CTLD_FULL_NODE) &&
((resv_desc_ptr->node_cnt != NO_VAL) ||
resv_desc_ptr->node_list)) ||
(resv_desc_ptr->core_cnt != NO_VAL)) {
char *err_str = "Updating core/node TRES not supported for core-based reservations";
info("%s(%s): %s", __func__, resv_desc_ptr->name, err_str);
if (err_msg)
*err_msg = xstrdup(err_str);
return ESLURM_CORE_RESERVATION_UPDATE;
}
/* Make backup to restore state in case of failure */
resv_backup = _copy_resv(resv_ptr);
/* Process the request */
if (resv_desc_ptr->flags != NO_VAL64) {
if (resv_desc_ptr->flags & RESERVE_FLAG_FLEX)
resv_ptr->flags |= RESERVE_FLAG_FLEX;
if (resv_desc_ptr->flags & RESERVE_FLAG_NO_FLEX)
resv_ptr->flags &= (~RESERVE_FLAG_FLEX);
if (resv_desc_ptr->flags & RESERVE_FLAG_NO_MAINT)
resv_ptr->flags &= (~RESERVE_FLAG_MAINT);
if (resv_desc_ptr->flags & RESERVE_FLAG_OVERLAP)
resv_ptr->flags |= RESERVE_FLAG_OVERLAP;
if (resv_desc_ptr->flags & RESERVE_FLAG_IGN_JOBS)
resv_ptr->flags |= RESERVE_FLAG_IGN_JOBS;
if (resv_desc_ptr->flags & RESERVE_FLAG_NO_IGN_JOB)
resv_ptr->flags &= (~RESERVE_FLAG_IGN_JOBS);
if (resv_desc_ptr->flags & RESERVE_FLAG_NO_HOURLY)
resv_ptr->flags &= (~RESERVE_FLAG_HOURLY);
if (resv_desc_ptr->flags & RESERVE_FLAG_NO_DAILY)
resv_ptr->flags &= (~RESERVE_FLAG_DAILY);
if (resv_desc_ptr->flags & RESERVE_FLAG_NO_WEEKDAY)
resv_ptr->flags &= (~RESERVE_FLAG_WEEKDAY);
if (resv_desc_ptr->flags & RESERVE_FLAG_NO_WEEKEND)
resv_ptr->flags &= (~RESERVE_FLAG_WEEKEND);
if (resv_desc_ptr->flags & RESERVE_FLAG_NO_WEEKLY)
resv_ptr->flags &= (~RESERVE_FLAG_WEEKLY);
if (resv_desc_ptr->flags & RESERVE_FLAG_ANY_NODES)
resv_ptr->flags |= RESERVE_FLAG_ANY_NODES;
if (resv_desc_ptr->flags & RESERVE_FLAG_NO_ANY_NODES)
resv_ptr->flags &= (~RESERVE_FLAG_ANY_NODES);
if (resv_desc_ptr->flags & RESERVE_FLAG_NO_STATIC)
resv_ptr->flags &= (~RESERVE_FLAG_STATIC);
if (resv_desc_ptr->flags & RESERVE_REOCCURRING) {
if (resv_ptr->flags & RESERVE_FLAG_TIME_FLOAT) {
info("Cannot add a reoccurring flag to a floating reservation");
if (err_msg)
*err_msg = xstrdup("Cannot add a reoccurring flag to a floating reservation");
error_code = ESLURM_NOT_SUPPORTED;
goto update_failure;
}
/*
* If the reservation already has a reoccurring flag
* that differs from the requested one, or is being
* updated to have multiple reoccurring flags, then
* reject the update
*/
if (((resv_ptr->flags & RESERVE_REOCCURRING) &&
((resv_ptr->flags & RESERVE_REOCCURRING) !=
(resv_desc_ptr->flags & RESERVE_REOCCURRING))) ||
(_has_multiple_reoccurring(resv_desc_ptr))) {
info("Cannot update reservation to have multiple reoccurring flags. Please specify only one reoccurring flag");
if (err_msg)
*err_msg = xstrdup("Cannot update reservation to have multiple reoccurring flags. Please specify only one reoccurring flag");
error_code = ESLURM_NOT_SUPPORTED;
goto update_failure;
}
else if (resv_desc_ptr->flags & RESERVE_FLAG_HOURLY)
resv_ptr->flags |= RESERVE_FLAG_HOURLY;
else if (resv_desc_ptr->flags & RESERVE_FLAG_DAILY)
resv_ptr->flags |= RESERVE_FLAG_DAILY;
else if (resv_desc_ptr->flags & RESERVE_FLAG_WEEKDAY)
resv_ptr->flags |= RESERVE_FLAG_WEEKDAY;
else if (resv_desc_ptr->flags & RESERVE_FLAG_WEEKEND)
resv_ptr->flags |= RESERVE_FLAG_WEEKEND;
else if (resv_desc_ptr->flags & RESERVE_FLAG_WEEKLY)
resv_ptr->flags |= RESERVE_FLAG_WEEKLY;
}
if ((resv_desc_ptr->flags & RESERVE_FLAG_REPLACE) ||
(resv_desc_ptr->flags & RESERVE_FLAG_REPLACE_DOWN)) {
if ((resv_ptr->flags & RESERVE_FLAG_SPEC_NODES) ||
!(resv_ptr->ctld_flags & RESV_CTLD_FULL_NODE)) {
info("%s: reservation %s can't be updated with REPLACE or REPLACE_DOWN flags; they should be updated on a NodeCnt reservation",
__func__, resv_desc_ptr->name);
if (err_msg)
*err_msg = xstrdup("Reservation can't be updated with REPLACE or REPLACE_DOWN flags; they should be updated on a NodeCnt reservation");
error_code = ESLURM_NOT_SUPPORTED;
goto update_failure;
}
/*
* If requesting to add the REPLACE or REPLACE_DOWN
* flags, and the STATIC or MAINT flags were already
* set, then reject the update.
*/
if ((resv_ptr->flags & RESERVE_FLAG_STATIC) ||
(resv_ptr->flags & RESERVE_FLAG_MAINT)) {
info("%s: reservation %s can't be updated: REPLACE and REPLACE_DOWN flags cannot be used with STATIC_ALLOC or MAINT flags",
__func__, resv_desc_ptr->name);
if (err_msg)
*err_msg = xstrdup("REPLACE and REPLACE_DOWN flags cannot be used with STATIC_ALLOC or MAINT flags");
error_code = ESLURM_NOT_SUPPORTED;
goto update_failure;
}
if (resv_desc_ptr->flags & RESERVE_FLAG_REPLACE)
resv_ptr->flags |= RESERVE_FLAG_REPLACE;
else
resv_ptr->flags |= RESERVE_FLAG_REPLACE_DOWN;
}
if ((resv_desc_ptr->flags & RESERVE_FLAG_STATIC) ||
(resv_desc_ptr->flags & RESERVE_FLAG_MAINT)) {
/*
* If requesting to add the MAINT or STATIC flag,
* and the REPLACE or REPLACE_DOWN flags were already
* set, then reject the update.
*/
if ((resv_ptr->flags & RESERVE_FLAG_REPLACE) ||
(resv_ptr->flags & RESERVE_FLAG_REPLACE_DOWN)) {
info("%s: reservation %s can't be updated: REPLACE and REPLACE_DOWN flags cannot be used with STATIC_ALLOC or MAINT flags",
__func__, resv_desc_ptr->name);
if (err_msg)
*err_msg = xstrdup("REPLACE and REPLACE_DOWN flags cannot be used with STATIC_ALLOC or MAINT flags");
error_code = ESLURM_NOT_SUPPORTED;
goto update_failure;
}
if (resv_desc_ptr->flags & RESERVE_FLAG_STATIC)
resv_ptr->flags |= RESERVE_FLAG_STATIC;
else
resv_ptr->flags |= RESERVE_FLAG_MAINT;
}
if (resv_desc_ptr->flags & RESERVE_FLAG_PART_NODES) {
if ((resv_ptr->partition == NULL) &&
(resv_desc_ptr->partition == NULL)) {
info("Reservation %s request can not set "
"Part_Nodes flag without partition",
resv_desc_ptr->name);
error_code = ESLURM_INVALID_PARTITION_NAME;
goto update_failure;
}
if (xstrcasecmp(resv_desc_ptr->node_list, "ALL")) {
info("Reservation %s request can not set Part_Nodes flag without partition and nodes=ALL",
resv_desc_ptr->name);
error_code = ESLURM_INVALID_NODE_NAME;
goto update_failure;
}
if ((resv_ptr->flags & RESERVE_FLAG_REPLACE) ||
(resv_ptr->flags & RESERVE_FLAG_REPLACE_DOWN)) {
info("%s: reservation %s can't be updated with PART_NODES flag; it is incompatible with REPLACE[_DOWN]",
__func__, resv_desc_ptr->name);
error_code = ESLURM_NOT_SUPPORTED;
goto update_failure;
}
resv_ptr->flags |= RESERVE_FLAG_PART_NODES;
/* Explicitly set the node_list to ALL */
xfree(resv_desc_ptr->node_list);
resv_desc_ptr->node_list = xstrdup("ALL");
}
if (resv_desc_ptr->flags & RESERVE_FLAG_NO_PART_NODES)
resv_ptr->flags &= (~RESERVE_FLAG_PART_NODES);
if (resv_desc_ptr->flags & RESERVE_FLAG_TIME_FLOAT) {
info("Reservation %s request to set TIME_FLOAT flag",
resv_desc_ptr->name);
error_code = ESLURM_INVALID_TIME_VALUE;
goto update_failure;
}
if (resv_desc_ptr->flags & RESERVE_FLAG_PURGE_COMP)
resv_ptr->flags |= RESERVE_FLAG_PURGE_COMP;
if (resv_desc_ptr->flags & RESERVE_FLAG_NO_PURGE_COMP) {
resv_ptr->flags &= (~RESERVE_FLAG_PURGE_COMP);
if (resv_desc_ptr->purge_comp_time == NO_VAL)
resv_ptr->purge_comp_time = 300;
}
if (resv_desc_ptr->flags & RESERVE_FLAG_NO_HOLD_JOBS)
resv_ptr->flags |= RESERVE_FLAG_NO_HOLD_JOBS;
if ((resv_desc_ptr->flags & RESERVE_FLAG_MAGNETIC) &&
!(resv_ptr->flags & RESERVE_FLAG_MAGNETIC)) {
resv_ptr->flags |= RESERVE_FLAG_MAGNETIC;
append_magnetic_resv = true;
}
if ((resv_desc_ptr->flags & RESERVE_FLAG_NO_MAGNETIC) &&
(resv_ptr->flags & RESERVE_FLAG_MAGNETIC)) {
resv_ptr->flags &= (~RESERVE_FLAG_MAGNETIC);
remove_magnetic_resv = true;
}
if (resv_desc_ptr->flags & RESERVE_FLAG_USER_DEL)
resv_ptr->flags |= RESERVE_FLAG_USER_DEL;
if (resv_desc_ptr->flags & RESERVE_FLAG_NO_USER_DEL)
resv_ptr->flags &= (~RESERVE_FLAG_USER_DEL);
/* handle skipping later */
if (resv_desc_ptr->flags & RESERVE_FLAG_SKIP) {
if (!(resv_ptr->flags & RESERVE_REOCCURRING)) {
error_code = ESLURM_RESERVATION_NO_SKIP;
goto update_failure;
}
skip_it = true;
}
}
if (resv_desc_ptr->max_start_delay != NO_VAL)
resv_ptr->max_start_delay = resv_desc_ptr->max_start_delay;
if (resv_desc_ptr->purge_comp_time != NO_VAL)
resv_ptr->purge_comp_time = resv_desc_ptr->purge_comp_time;
if (resv_desc_ptr->partition && (resv_desc_ptr->partition[0] == '\0')) {
/* Clear the partition */
xfree(resv_desc_ptr->partition);
xfree(resv_ptr->partition);
resv_ptr->part_ptr = NULL;
}
if (resv_desc_ptr->partition) {
part_record_t *part_ptr = NULL;
part_ptr = find_part_record(resv_desc_ptr->partition);
if (!part_ptr) {
info("Reservation %s request has invalid partition (%s)",
resv_desc_ptr->name, resv_desc_ptr->partition);
error_code = ESLURM_INVALID_PARTITION_NAME;
goto update_failure;
}
xfree(resv_ptr->partition);
resv_ptr->partition = resv_desc_ptr->partition;
resv_desc_ptr->partition = NULL; /* Nothing left to free */
resv_ptr->part_ptr = part_ptr;
}
if (resv_desc_ptr->accounts) {
rc = _update_account_list(resv_ptr, resv_desc_ptr->accounts);
if (rc) {
error_code = rc;
goto update_failure;
}
}
if (resv_desc_ptr->burst_buffer) {
xfree(resv_ptr->burst_buffer);
if (resv_desc_ptr->burst_buffer[0] != '\0') {
resv_ptr->burst_buffer = resv_desc_ptr->burst_buffer;
resv_desc_ptr->burst_buffer = NULL;
}
}
if (resv_desc_ptr->comment) {
xfree(resv_ptr->comment);
if (resv_desc_ptr->comment[0] != '\0') {
resv_ptr->comment = resv_desc_ptr->comment;
resv_desc_ptr->comment = NULL;
}
}
if (resv_desc_ptr->licenses && (resv_desc_ptr->licenses[0] == '\0')) {
if (!resv_desc_ptr->node_cnt ||
((resv_desc_ptr->node_cnt == NO_VAL) &&
(resv_ptr->node_cnt == 0))) {
info("Reservation %s attempt to clear licenses with "
"NodeCount=0", resv_desc_ptr->name);
error_code = ESLURM_INVALID_LICENSES;
goto update_failure;
}
xfree(resv_desc_ptr->licenses); /* clear licenses */
xfree(resv_ptr->licenses);
FREE_NULL_LIST(resv_ptr->license_list);
}
if (resv_desc_ptr->licenses) {
bool valid = true;
list_t *license_list = NULL;
license_list = _license_validate2(resv_desc_ptr, &valid);
if (!valid) {
info("Reservation %s invalid license update (%s)",
resv_desc_ptr->name, resv_desc_ptr->licenses);
error_code = ESLURM_INVALID_LICENSES;
goto update_failure;
}
xfree(resv_ptr->licenses);
resv_ptr->licenses = license_list_to_string(license_list);
FREE_NULL_LIST(resv_ptr->license_list);
resv_ptr->license_list = license_list;
}
if (resv_desc_ptr->features && (resv_desc_ptr->features[0] == '\0')) {
xfree(resv_desc_ptr->features); /* clear features */
xfree(resv_ptr->features);
}
if (resv_desc_ptr->features) {
/* To support in the future, the reservation resources would
* need to be selected again. For now, administrator can
* delete this reservation and create a new one. */
info("Attempt to change features of reservation %s. "
"Delete the reservation and create a new one.",
resv_desc_ptr->name);
error_code = ESLURM_NOT_SUPPORTED;
goto update_failure;
}
/* Groups has to be done before users */
if (resv_desc_ptr->groups) {
rc = _update_group_uid_list(resv_ptr, resv_desc_ptr->groups);
if (rc) {
error_code = rc;
goto update_failure;
}
}
if (resv_desc_ptr->users) {
rc = _update_uid_list(resv_ptr, resv_desc_ptr->users);
if (rc) {
error_code = rc;
goto update_failure;
}
}
if (resv_ptr->users && resv_ptr->groups) {
info("Reservation requested both users and groups, these are mutually exclusive. You can have one or the other, but not both.");
error_code = ESLURM_RESERVATION_USER_GROUP;
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_ptr->users &&
!resv_ptr->accounts &&
!resv_ptr->qos &&
!resv_ptr->groups) {
info("Reservation %s request lacks users, accounts, QOS or groups",
resv_desc_ptr->name);
error_code = ESLURM_RESERVATION_EMPTY;
goto update_failure;
}
if (resv_desc_ptr->start_time != (time_t) NO_VAL) {
if (resv_ptr->start_time <= time(NULL)) {
info("%s: reservation already started", __func__);
error_code = ESLURM_RSV_ALREADY_STARTED;
goto update_failure;
}
if (resv_desc_ptr->start_time < (now - 60)) {
info("Reservation %s request has invalid start time",
resv_desc_ptr->name);
error_code = ESLURM_INVALID_TIME_VALUE;
goto update_failure;
}
resv_ptr->start_time_prev = resv_ptr->start_time;
resv_ptr->start_time = resv_desc_ptr->start_time;
resv_ptr->start_time_first = resv_desc_ptr->start_time;
if (resv_ptr->duration != NO_VAL) {
resv_ptr->end_time = resv_ptr->start_time_first +
(resv_ptr->duration * 60);
}
}
if (resv_desc_ptr->end_time != (time_t) NO_VAL) {
if (resv_desc_ptr->end_time < (now - 60)) {
info("Reservation %s request has invalid end time",
resv_desc_ptr->name);
error_code = ESLURM_INVALID_TIME_VALUE;
goto update_failure;
}
resv_ptr->end_time = resv_desc_ptr->end_time;
resv_ptr->duration = NO_VAL;
}
if (resv_desc_ptr->duration == INFINITE) {
resv_ptr->duration = YEAR_SECONDS / 60;
resv_ptr->end_time = resv_ptr->start_time_first + YEAR_SECONDS;
} else if (resv_desc_ptr->duration != NO_VAL) {
if (resv_desc_ptr->flags == NO_VAL64)
resv_ptr->duration = resv_desc_ptr->duration;
else if (resv_desc_ptr->flags & RESERVE_FLAG_DUR_PLUS)
resv_ptr->duration += resv_desc_ptr->duration;
else if (resv_desc_ptr->flags & RESERVE_FLAG_DUR_MINUS) {
if (resv_ptr->duration >= resv_desc_ptr->duration)
resv_ptr->duration -= resv_desc_ptr->duration;
else
resv_ptr->duration = 0;
} else
resv_ptr->duration = resv_desc_ptr->duration;
resv_ptr->end_time = resv_ptr->start_time_first +
(resv_ptr->duration * 60);
/*
* Since duration is a static number we could put the end time
* in the past if the reservation already started and we are
* removing more time than is left.
*/
if (resv_ptr->end_time < now)
resv_ptr->end_time = now;
}
if (resv_ptr->start_time >= resv_ptr->end_time) {
info("Reservation %s request has invalid times (start > end)",
resv_desc_ptr->name);
error_code = ESLURM_INVALID_TIME_VALUE;
goto update_failure;
}
if (resv_desc_ptr->node_list &&
(resv_desc_ptr->node_list[0] == '\0')) { /* Clear bitmap */
resv_ptr->flags &= (~RESERVE_FLAG_SPEC_NODES);
resv_ptr->flags &= (~RESERVE_FLAG_ALL_NODES);
xfree(resv_desc_ptr->node_list);
xfree(resv_ptr->node_list);
FREE_NULL_BITMAP(resv_ptr->node_bitmap);
FREE_NULL_BITMAP(resv_ptr->core_bitmap);
free_job_resources(&resv_ptr->core_resrcs);
resv_ptr->node_bitmap = bit_alloc(node_record_count);
if (!resv_desc_ptr->node_cnt ||
(resv_desc_ptr->node_cnt == NO_VAL)) {
resv_desc_ptr->node_cnt = resv_ptr->node_cnt;
}
resv_ptr->node_cnt = 0;
}
if (resv_desc_ptr->node_list) { /* Change bitmap last */
if ((resv_ptr->flags & RESERVE_FLAG_REPLACE) ||
(resv_ptr->flags & RESERVE_FLAG_REPLACE_DOWN)) {
info("%s: reservation %s can't be updated with Nodes option; it is incompatible with REPLACE[_DOWN]",
__func__, resv_desc_ptr->name);
if (err_msg)
*err_msg = xstrdup("Reservation can't be updated with Nodes option; it is incompatible with REPLACE[_DOWN]");
error_code = ESLURM_NOT_SUPPORTED;
goto update_failure;
}
bitstr_t *node_bitmap;
resv_ptr->flags |= RESERVE_FLAG_SPEC_NODES;
if (xstrcasecmp(resv_desc_ptr->node_list, "ALL") == 0) {
if ((resv_ptr->partition) &&
(resv_ptr->flags & RESERVE_FLAG_PART_NODES)) {
part_record_t *part_ptr = NULL;
part_ptr = find_part_record(resv_ptr->
partition);
node_bitmap = bit_copy(part_ptr->node_bitmap);
xfree(resv_ptr->node_list);
xfree(resv_desc_ptr->node_list);
resv_ptr->node_list = xstrdup(part_ptr->nodes);
} else {
resv_ptr->flags |= RESERVE_FLAG_ALL_NODES;
node_bitmap = node_conf_get_active_bitmap();
resv_ptr->flags &= (~RESERVE_FLAG_PART_NODES);
xfree(resv_ptr->node_list);
xfree(resv_desc_ptr->node_list);
resv_ptr->node_list =
bitmap2node_name(node_bitmap);
}
} else {
resv_ptr->flags &= (~RESERVE_FLAG_PART_NODES);
resv_ptr->flags &= (~RESERVE_FLAG_ALL_NODES);
if (!(node_bitmap = _get_update_node_bitmap(
resv_ptr, resv_desc_ptr->node_list))) {
info("Reservation %s request has invalid node name (%s)",
resv_desc_ptr->name,
resv_desc_ptr->node_list);
error_code = ESLURM_INVALID_NODE_NAME;
goto update_failure;
}
xfree(resv_desc_ptr->node_list);
xfree(resv_ptr->node_list);
resv_ptr->node_list = bitmap2node_name(node_bitmap);
}
resv_desc_ptr->node_list = NULL; /* Nothing left to free */
FREE_NULL_BITMAP(resv_ptr->node_bitmap);
FREE_NULL_BITMAP(resv_ptr->core_bitmap);
free_job_resources(&resv_ptr->core_resrcs);
resv_ptr->node_bitmap = node_bitmap;
resv_ptr->node_cnt = bit_set_count(resv_ptr->node_bitmap);
}
if (resv_desc_ptr->node_cnt != NO_VAL) {
resv_ptr->flags &= (~RESERVE_FLAG_PART_NODES);
resv_ptr->flags &= (~RESERVE_FLAG_ALL_NODES);
rc = _resize_resv(resv_ptr, resv_desc_ptr->node_cnt);
if (rc) {
error_code = rc;
goto update_failure;
}
/*
* If the reservation was 0 node count before (ANY_NODES) this
* could be NULL, if for some reason someone tried to update the
* node count in this situation we will still not have a
* node_bitmap.
*/
if (resv_ptr->node_bitmap)
resv_ptr->node_cnt =
bit_set_count(resv_ptr->node_bitmap);
}
slurm_init_resv_desc_msg(&resv_desc);
resv_desc.start_time = resv_ptr->start_time;
resv_desc.end_time = resv_ptr->end_time;
resv_desc.flags = resv_ptr->flags;
resv_desc.name = resv_ptr->name;
if (_resv_overlap(&resv_desc, resv_ptr->node_bitmap, resv_ptr)) {
info("Reservation %s request overlaps another",
resv_desc_ptr->name);
error_code = ESLURM_RESERVATION_OVERLAP;
goto update_failure;
}
if (_job_overlap(resv_ptr->start_time, resv_ptr->flags,
resv_ptr->node_bitmap, resv_desc_ptr->name)) {
info("Reservation %s request overlaps jobs",
resv_desc_ptr->name);
error_code = ESLURM_NODES_BUSY;
goto update_failure;
}
/* This needs to be after checks for both account and user changes */
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_access
*/
if ((job_ptr = list_find_first(job_list,
_validate_reservation_access_update,
resv_ptr))) {
if (err_msg)
xstrfmtcat(*err_msg,
"Reservation update rejected because of JobId=%u",
job_ptr->job_id);
error_code = ESLURM_RESERVATION_BUSY;
goto update_failure;
}
/*
* A reservation without nodes/cores should only be possible if the flag
* ANY_NODES is set and it has at least one of licenses or burst buffer.
* So test this here after the checks for the involved options.
*/
if (!resv_ptr->node_bitmap || (bit_ffs(resv_ptr->node_bitmap)) == -1) {
if ((resv_ptr->flags & RESERVE_FLAG_ANY_NODES) == 0) {
info("%s: reservations without nodes are only expected with ANY_NODES flag", __func__);
error_code = ESLURM_RESERVATION_INVALID;
goto update_failure;
} else if ((!resv_ptr->license_list ||
list_is_empty(resv_ptr->license_list)) &&
!resv_ptr->burst_buffer) {
info("%s: reservations without nodes and with ANY_NODES flag are expected to be one of Licenses, and/or BurstBuffer", __func__);
error_code = ESLURM_RESERVATION_INVALID;
goto update_failure;
}
}
_set_tres_cnt(resv_ptr, resv_backup);
node_down_bitmap = bit_alloc(node_record_count);
/* Now check if we are skipping this one */
if (skip_it) {
if ((error_code = _delete_resv_internal(resv_ptr,
node_down_bitmap)) !=
SLURM_SUCCESS)
goto update_failure;
if (resv_ptr->start_time > now) {
resv_ptr->ctld_flags |= RESV_CTLD_EPILOG;
resv_ptr->ctld_flags |= RESV_CTLD_PROLOG;
}
if (_advance_resv_time(resv_ptr) != SLURM_SUCCESS) {
error_code = ESLURM_RESERVATION_NO_SKIP;
error("Couldn't skip reservation %s, this should never happen",
resv_ptr->name);
goto update_failure;
}
}
/*
* The following two checks need to happen once it is guaranteed the
* whole update succeeds, avoiding any path leading to update failure.
*/
if (append_magnetic_resv)
list_append(magnetic_resv_list, resv_ptr);
if (remove_magnetic_resv)
(void) list_remove_first(magnetic_resv_list, _find_resv_ptr,
resv_ptr);
_del_resv_rec(resv_backup);
(void) _set_node_maint_mode(true, node_down_bitmap);
_flush_node_down_cache(node_down_bitmap, now);
FREE_NULL_BITMAP(node_down_bitmap);
last_resv_update = now;
schedule_resv_save();
return error_code;
update_failure:
/* Restore backup reservation data */
FREE_NULL_BITMAP(node_down_bitmap);
_restore_resv(resv_ptr, resv_backup);
_del_resv_rec(resv_backup);
return error_code;
}
/* Determine if a running or pending job is using a reservation */
static bool _is_resv_used(slurmctld_resv_t *resv_ptr)
{
if (list_find_first_ro(job_list,
_find_running_job_with_resv_ptr,
resv_ptr))
return true;
return false;
}
/* Clear the reservation pointers for jobs referencing a defunct reservation */
static void _clear_job_resv(slurmctld_resv_t *resv_ptr)
{
list_for_each(job_list, _foreach_clear_job_resv, resv_ptr);
}
static bool _match_user_assoc(char *assoc_str, list_t *assoc_list, bool deny)
{
list_itr_t *itr;
bool found = 0;
slurmdb_assoc_rec_t *assoc;
char tmp_char[30];
if (!assoc_str || !assoc_list || !list_count(assoc_list))
return false;
itr = list_iterator_create(assoc_list);
while ((assoc = list_next(itr))) {
while (assoc) {
snprintf(tmp_char, sizeof(tmp_char), ",%s%u,",
deny ? "-" : "", assoc->id);
if (xstrstr(assoc_str, tmp_char)) {
found = 1;
goto end_it;
}
assoc = assoc->usage->parent_assoc_ptr;
}
}
end_it:
list_iterator_destroy(itr);
return found;
}
/* Delete an exiting resource reservation */
extern int delete_resv(reservation_name_msg_t *resv_desc_ptr)
{
list_itr_t *iter;
slurmctld_resv_t *resv_ptr;
int rc = SLURM_SUCCESS;
bitstr_t *node_down_bitmap = NULL;
log_flag(RESERVATION, "%s: Name=%s", __func__, resv_desc_ptr->name);
iter = list_iterator_create(resv_list);
while ((resv_ptr = list_next(iter))) {
if (xstrcmp(resv_ptr->name, resv_desc_ptr->name))
continue;
node_down_bitmap = bit_alloc(node_record_count);
if ((rc = _delete_resv_internal(resv_ptr, node_down_bitmap)) !=
ESLURM_RESERVATION_BUSY) {
_clear_job_resv(resv_ptr);
list_delete_item(iter);
}
break;
}
list_iterator_destroy(iter);
if (!resv_ptr) {
xassert(!node_down_bitmap);
info("Reservation %s not found for deletion",
resv_desc_ptr->name);
return ESLURM_RESERVATION_INVALID;
}
last_resv_update = time(NULL);
_flush_node_down_cache(node_down_bitmap, last_resv_update);
FREE_NULL_BITMAP(node_down_bitmap);
schedule_resv_save();
return rc;
}
extern void reservation_delete_resv_exc_parts(resv_exc_t *resv_exc)
{
if (!resv_exc)
return;
/*
* These are pointers into the lists that are about to be freed right
* afterwards.
*/
resv_exc->gres_js_exc = NULL;
resv_exc->gres_js_inc = NULL;
FREE_NULL_LIST(resv_exc->gres_list_exc);
FREE_NULL_LIST(resv_exc->gres_list_inc);
FREE_NULL_BITMAP(resv_exc->core_bitmap);
free_core_array(&resv_exc->exc_cores);
}
extern void reservation_delete_resv_exc(resv_exc_t *resv_exc)
{
if (!resv_exc)
return;
reservation_delete_resv_exc_parts(resv_exc);
xfree(resv_exc);
}
/* Return pointer to the named reservation or NULL if not found */
extern slurmctld_resv_t *find_resv_name(char *resv_name)
{
slurmctld_resv_t *resv_ptr;
resv_ptr = (slurmctld_resv_t *) list_find_first (resv_list,
_find_resv_name, resv_name);
return resv_ptr;
}
/* Dump the reservation records to a buffer */
extern buf_t *show_resv(uid_t uid, uint16_t protocol_version)
{
list_itr_t *iter;
slurmctld_resv_t *resv_ptr;
uint32_t resv_packed;
int tmp_offset;
buf_t *buffer;
time_t now = time(NULL);
list_t *assoc_list = NULL;
bool check_permissions = false;
assoc_mgr_lock_t locks = { .assoc = READ_LOCK };
DEF_TIMERS;
START_TIMER;
_create_resv_lists(false);
buffer = init_buf(BUF_SIZE);
/* write header: version and time */
resv_packed = 0;
pack32(resv_packed, buffer);
pack_time(now, buffer);
/* Create this list once since it will not change during this call. */
if ((slurm_conf.private_data & PRIVATE_DATA_RESERVATIONS)
&& !validate_operator(uid)) {
slurmdb_assoc_rec_t assoc;
check_permissions = true;
memset(&assoc, 0, sizeof(slurmdb_assoc_rec_t));
assoc.uid = uid;
assoc_list = list_create(NULL);
assoc_mgr_lock(&locks);
if (assoc_mgr_get_user_assocs(acct_db_conn, &assoc,
accounting_enforce, assoc_list)
!= SLURM_SUCCESS)
goto no_assocs;
}
/* write individual reservation records */
iter = list_iterator_create(resv_list);
while ((resv_ptr = list_next(iter))) {
if (check_permissions &&
!_validate_user_access(resv_ptr, assoc_list, uid))
continue;
_pack_resv(resv_ptr, buffer, false, protocol_version);
resv_packed++;
}
list_iterator_destroy(iter);
no_assocs:
if (check_permissions) {
FREE_NULL_LIST(assoc_list);
assoc_mgr_unlock(&locks);
}
/* put the real record count in the message body header */
tmp_offset = get_buf_offset(buffer);
set_buf_offset(buffer, 0);
pack32(resv_packed, buffer);
set_buf_offset(buffer, tmp_offset);
END_TIMER2(__func__);
return buffer;
}
/* Save the state of all reservations to file */
extern int dump_all_resv_state(void)
{
list_itr_t *iter;
slurmctld_resv_t *resv_ptr;
int error_code = 0;
/* Locks: Read node */
slurmctld_lock_t resv_read_lock = {
.conf = READ_LOCK,
.node = READ_LOCK,
};
buf_t *buffer = init_buf(BUF_SIZE);
DEF_TIMERS;
START_TIMER;
_create_resv_lists(false);
/* write header: time */
packstr(RESV_STATE_VERSION, buffer);
pack16(SLURM_PROTOCOL_VERSION, buffer);
pack_time(time(NULL), buffer);
pack32(top_suffix, buffer);
/* write reservation records to buffer */
lock_slurmctld(resv_read_lock);
iter = list_iterator_create(resv_list);
while ((resv_ptr = list_next(iter)))
_pack_resv(resv_ptr, buffer, true, SLURM_PROTOCOL_VERSION);
list_iterator_destroy(iter);
unlock_slurmctld(resv_read_lock);
error_code = save_buf_to_state("resv_state", buffer, NULL);
FREE_NULL_BUFFER(buffer);
END_TIMER2(__func__);
return error_code;
}
/* Validate one reservation record, return true if good */
static bool _validate_one_reservation(slurmctld_resv_t *resv_ptr)
{
bool account_not = false, user_not = false;
slurmctld_resv_t old_resv_ptr;
if ((resv_ptr->name == NULL) || (resv_ptr->name[0] == '\0')) {
error("Read reservation without name");
return false;
}
if (_get_core_resrcs(resv_ptr) != SLURM_SUCCESS)
return false;
if (resv_ptr->partition) {
part_record_t *part_ptr = NULL;
part_ptr = find_part_record(resv_ptr->partition);
if (!part_ptr) {
error("Reservation %s has invalid partition (%s)",
resv_ptr->name, resv_ptr->partition);
return false;
}
resv_ptr->part_ptr = part_ptr;
}
if (resv_ptr->accounts) {
int account_cnt = 0, i, rc;
char **account_list;
rc = _build_account_list(resv_ptr->accounts,
&account_cnt, &account_list,
&account_not);
if (rc) {
error("Reservation %s has invalid accounts (%s)",
resv_ptr->name, resv_ptr->accounts);
return false;
}
for (i=0; i<resv_ptr->account_cnt; i++)
xfree(resv_ptr->account_list[i]);
xfree(resv_ptr->account_list);
resv_ptr->account_cnt = account_cnt;
resv_ptr->account_list = account_list;
if (account_not)
resv_ptr->ctld_flags |= RESV_CTLD_ACCT_NOT;
else
resv_ptr->ctld_flags &= (~RESV_CTLD_ACCT_NOT);
}
if (resv_ptr->licenses) {
bool valid = true;
FREE_NULL_LIST(resv_ptr->license_list);
resv_ptr->license_list =
license_validate(resv_ptr->licenses, true, true, true,
NULL, &valid);
if (!valid) {
error("Reservation %s has invalid licenses (%s)",
resv_ptr->name, resv_ptr->licenses);
return false;
}
}
if (resv_ptr->users) {
int rc, user_cnt = 0;
uid_t *user_list = NULL;
rc = _build_uid_list(resv_ptr->users,
&user_cnt, &user_list, &user_not, false);
if (rc == SLURM_ERROR) {
error("Reservation %s has invalid users specification (%s)",
resv_ptr->name, resv_ptr->users);
return false;
} else if (rc == ESLURM_USER_ID_MISSING) {
error("Reservation %s has no valid users (%s), not updating UID list.",
resv_ptr->name, resv_ptr->users);
} else {
xfree(resv_ptr->user_list);
resv_ptr->user_cnt = user_cnt;
resv_ptr->user_list = user_list;
if (user_not)
resv_ptr->ctld_flags |= RESV_CTLD_USER_NOT;
else
resv_ptr->ctld_flags &= (~RESV_CTLD_USER_NOT);
}
}
if (resv_ptr->groups) {
int user_cnt = 0;
uid_t *user_list = get_groups_members(resv_ptr->groups,
&user_cnt);
if (!user_list) {
error("Reservation %s has no valid users from groups (%s), not updating UID list",
resv_ptr->name, resv_ptr->groups);
} else {
xfree(resv_ptr->user_list);
resv_ptr->user_list = user_list;
resv_ptr->user_cnt = user_cnt;
resv_ptr->ctld_flags &= (~RESV_CTLD_USER_NOT);
}
}
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->flags & RESERVE_FLAG_PART_NODES) &&
resv_ptr->part_ptr && resv_ptr->part_ptr->node_bitmap) {
memset(&old_resv_ptr, 0, sizeof(slurmctld_resv_t));
old_resv_ptr.assoc_list = resv_ptr->assoc_list;
old_resv_ptr.flags = resv_ptr->flags;
old_resv_ptr.node_list = resv_ptr->node_list;
resv_ptr->node_list = NULL;
resv_ptr->node_list = xstrdup(resv_ptr->part_ptr->nodes);
FREE_NULL_BITMAP(resv_ptr->node_bitmap);
resv_ptr->node_bitmap = bit_copy(resv_ptr->part_ptr->
node_bitmap);
resv_ptr->node_cnt = bit_set_count(resv_ptr->node_bitmap);
old_resv_ptr.tres_str = resv_ptr->tres_str;
resv_ptr->tres_str = NULL;
_set_tres_cnt(resv_ptr, &old_resv_ptr);
old_resv_ptr.assoc_list = NULL;
xfree(old_resv_ptr.tres_str);
xfree(old_resv_ptr.node_list);
last_resv_update = time(NULL);
} else if (resv_ptr->flags & RESERVE_FLAG_ALL_NODES) {
memset(&old_resv_ptr, 0, sizeof(slurmctld_resv_t));
old_resv_ptr.assoc_list = resv_ptr->assoc_list;
old_resv_ptr.flags = resv_ptr->flags;
old_resv_ptr.node_list = resv_ptr->node_list;
resv_ptr->node_list = NULL;
FREE_NULL_BITMAP(resv_ptr->node_bitmap);
resv_ptr->node_bitmap = node_conf_get_active_bitmap();
resv_ptr->node_list = bitmap2node_name(resv_ptr->node_bitmap);
resv_ptr->node_cnt = bit_set_count(resv_ptr->node_bitmap);
old_resv_ptr.tres_str = resv_ptr->tres_str;
resv_ptr->tres_str = NULL;
_set_tres_cnt(resv_ptr, &old_resv_ptr);
old_resv_ptr.assoc_list = NULL;
xfree(old_resv_ptr.tres_str);
xfree(old_resv_ptr.node_list);
last_resv_update = time(NULL);
} else if (resv_ptr->node_list) { /* Change bitmap last */
/*
* Node bitmap must be recreated in any case, i.e. when
* they grow because adding new nodes to slurm.conf
*/
FREE_NULL_BITMAP(resv_ptr->node_bitmap);
if (node_name2bitmap(resv_ptr->node_list, false,
&resv_ptr->node_bitmap, NULL)) {
char *new_node_list;
resv_ptr->node_cnt = bit_set_count(
resv_ptr->node_bitmap);
if (!resv_ptr->node_cnt) {
error("%s: Reservation %s has no nodes left, deleting it",
__func__, resv_ptr->name);
return false;
}
memset(&old_resv_ptr, 0, sizeof(slurmctld_resv_t));
old_resv_ptr.assoc_list = resv_ptr->assoc_list;
old_resv_ptr.flags = resv_ptr->flags;
old_resv_ptr.node_list = resv_ptr->node_list;
resv_ptr->node_list = NULL;
new_node_list = bitmap2node_name(resv_ptr->node_bitmap);
info("%s: Reservation %s has invalid previous_nodes:%s remaining_nodes[%d/%u]:%s",
__func__, resv_ptr->name, old_resv_ptr.node_list,
bit_set_count(resv_ptr->node_bitmap),
resv_ptr->node_cnt, new_node_list);
resv_ptr->node_list = new_node_list;
new_node_list = NULL;
old_resv_ptr.tres_str = resv_ptr->tres_str;
resv_ptr->tres_str = NULL;
_set_tres_cnt(resv_ptr, &old_resv_ptr);
old_resv_ptr.assoc_list = NULL;
xfree(old_resv_ptr.tres_str);
xfree(old_resv_ptr.node_list);
last_resv_update = time(NULL);
schedule_resv_save();
}
}
return true;
}
extern void validate_all_reservations(bool run_now, bool run_locked)
{
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
bool run;
if (!run_now) {
slurm_mutex_lock(&mutex);
validate_resv_cnt++;
log_flag(RESERVATION, "%s: requests %u",
__func__, validate_resv_cnt);
xassert(validate_resv_cnt != UINT32_MAX);
slurm_mutex_unlock(&mutex);
return;
}
slurm_mutex_lock(&mutex);
run = (validate_resv_cnt > 0);
/* reset requests counter */
validate_resv_cnt = 0;
slurm_mutex_unlock(&mutex);
if (run) {
slurmctld_lock_t lock = {
.conf = READ_LOCK,
.job = WRITE_LOCK,
.node = WRITE_LOCK,
.part = READ_LOCK,
};
if (run_locked)
lock_slurmctld(lock);
_validate_all_reservations();
if (run_locked)
unlock_slurmctld(lock);
}
}
static int _validate_job_resv(void *job, void *y)
{
job_record_t *job_ptr = (job_record_t *)job;
int rc = SLURM_SUCCESS;
if (job_ptr->resv_name == NULL)
return 0;
if ((job_ptr->resv_ptr == NULL) ||
(job_ptr->resv_ptr->magic != RESV_MAGIC))
rc = validate_job_resv(job_ptr);
if (!job_ptr->resv_ptr) {
error("%pJ linked to defunct reservation %s",
job_ptr, job_ptr->resv_name);
job_ptr->resv_id = 0;
xfree(job_ptr->resv_name);
}
if (rc != SLURM_SUCCESS) {
error("%pJ linked to invalid reservation: %s, holding the job.",
job_ptr, job_ptr->resv_name);
job_ptr->state_reason = WAIT_RESV_INVALID;
job_state_set_flag(job_ptr, JOB_RESV_DEL_HOLD);
xstrfmtcat(job_ptr->state_desc,
"Reservation %s is invalid",
job_ptr->resv_name);
}
return 0;
}
/*
* Validate all reservation records, reset bitmaps, etc.
* Purge any invalid reservation.
*/
static void _validate_all_reservations(void)
{
list_itr_t *iter;
slurmctld_resv_t *resv_ptr;
/* Make sure we have node write locks. */
xassert(verify_lock(JOB_LOCK, WRITE_LOCK));
log_flag(RESERVATION, "%s: validating %u reservations and %u jobs",
__func__, list_count(resv_list), list_count(job_list));
iter = list_iterator_create(resv_list);
while ((resv_ptr = list_next(iter))) {
if (!_validate_one_reservation(resv_ptr)) {
error("Purging invalid reservation record %s",
resv_ptr->name);
_post_resv_delete(resv_ptr);
_clear_job_resv(resv_ptr);
list_delete_item(iter);
} else {
_set_access(resv_ptr);
top_suffix = MAX(top_suffix, resv_ptr->resv_id);
_validate_node_choice(resv_ptr);
}
}
list_iterator_destroy(iter);
/* Validate all job reservation pointers */
list_for_each(job_list, _validate_job_resv, NULL);
}
/*
* Replace DOWN, DRAIN or ALLOCATED nodes for reservations with "replace" flag
*/
static void _resv_node_replace(slurmctld_resv_t *resv_ptr)
{
bitstr_t *preserve_bitmap = NULL;
bitstr_t *tmp_bitmap = NULL;
resv_desc_msg_t resv_desc;
int i, add_nodes, new_nodes, preserve_nodes, busy_nodes_needed;
bool log_it = true;
bool replaced = false;
resv_select_t resv_select = { 0 };
/* Identify nodes which can be preserved in this reservation */
preserve_bitmap = bit_copy(resv_ptr->node_bitmap);
bit_and(preserve_bitmap, avail_node_bitmap);
if (resv_ptr->flags & RESERVE_FLAG_REPLACE)
bit_and(preserve_bitmap, idle_node_bitmap);
preserve_nodes = bit_set_count(preserve_bitmap);
/*
* Try to get replacement nodes, first from idle pool then reuse
* busy nodes in the current reservation as needed
*/
add_nodes = resv_ptr->node_cnt - preserve_nodes;
while (add_nodes) {
if (slurm_conf.debug_flags & DEBUG_FLAG_RESERVATION) {
char *pres = bitmap2node_name(preserve_bitmap);
bitstr_t *rem_bitmap = bit_copy(resv_ptr->node_bitmap);
char *rem = NULL;
bit_and_not(rem_bitmap, preserve_bitmap);
rem = bitmap2node_name(rem_bitmap);
log_flag(RESERVATION, "%s: reservation %s replacing %d/%d nodes unavailable[%d/%"PRId64"]:%s preserving[%d]:%s",
__func__, resv_ptr->name, add_nodes,
resv_ptr->node_cnt, bit_set_count(rem_bitmap),
bit_size(rem_bitmap), rem, preserve_nodes,
pres);
xfree(pres);
xfree(rem);
FREE_NULL_BITMAP(rem_bitmap);
}
slurm_init_resv_desc_msg(&resv_desc);
resv_desc.start_time = resv_ptr->start_time;
resv_desc.end_time = resv_ptr->end_time;
resv_desc.features = resv_ptr->features;
resv_desc.flags = resv_ptr->flags;
resv_desc.name = resv_ptr->name;
resv_desc.tres_str = resv_ptr->tres_str;
if (!(resv_ptr->ctld_flags & RESV_CTLD_FULL_NODE)) {
resv_desc.core_cnt = resv_ptr->core_cnt;
}
resv_desc.node_cnt = add_nodes;
resv_desc.job_ptr =
job_mgr_copy_resv_desc_to_job_record(&resv_desc);
/* exclude already reserved nodes from new resv request */
resv_select.node_bitmap =
bit_copy(resv_ptr->part_ptr->node_bitmap);
bit_and_not(resv_select.node_bitmap, resv_ptr->node_bitmap);
i = _select_nodes(&resv_desc, &resv_ptr->part_ptr, &resv_select,
preserve_bitmap);
xfree(resv_desc.node_list);
xfree(resv_desc.partition);
if (i == SLURM_SUCCESS) {
job_record_t *job_ptr = resv_desc.job_ptr;
replaced = true;
new_nodes = bit_set_count(resv_select.node_bitmap);
busy_nodes_needed = resv_ptr->node_cnt - new_nodes
- preserve_nodes;
if (busy_nodes_needed > 0) {
bit_and_not(resv_ptr->node_bitmap,
preserve_bitmap);
tmp_bitmap = bit_pick_cnt(resv_ptr->node_bitmap,
busy_nodes_needed);
bit_and(resv_ptr->node_bitmap, tmp_bitmap);
FREE_NULL_BITMAP(tmp_bitmap);
bit_or(resv_ptr->node_bitmap, preserve_bitmap);
} else {
bit_and(resv_ptr->node_bitmap, preserve_bitmap);
}
bit_or(resv_ptr->node_bitmap, resv_select.node_bitmap);
FREE_NULL_BITMAP(resv_select.node_bitmap);
FREE_NULL_BITMAP(resv_ptr->core_bitmap);
resv_ptr->core_bitmap = resv_select.core_bitmap;
resv_select.core_bitmap = NULL;
free_job_resources(&resv_ptr->core_resrcs);
resv_ptr->core_resrcs = job_ptr->job_resrcs;
job_ptr->job_resrcs = NULL;
xfree(resv_ptr->node_list);
resv_ptr->node_list = bitmap2node_name(resv_ptr->
node_bitmap);
FREE_NULL_LIST(resv_ptr->gres_list_alloc);
resv_ptr->gres_list_alloc = job_ptr->gres_list_req;
gres_job_state_log(resv_ptr->gres_list_alloc, 0);
job_ptr->gres_list_req = NULL;
job_record_delete(resv_desc.job_ptr);
resv_desc.job_ptr = NULL;
if (log_it ||
(slurm_conf.debug_flags & DEBUG_FLAG_RESERVATION)) {
char *kept, *added;
bitstr_t *new_nodes =
bit_copy(resv_ptr->node_bitmap);
bitstr_t *kept_nodes =
bit_copy(resv_ptr->node_bitmap);
bit_and_not(new_nodes, preserve_bitmap);
bit_and(kept_nodes, preserve_bitmap);
added = bitmap2node_name(new_nodes);
kept = bitmap2node_name(kept_nodes);
verbose("%s: modified reservation %s with added[%d/%"PRId64"]:%s kept[%d/%"PRId64"]:%s",
__func__, resv_ptr->name,
bit_set_count(new_nodes),
bit_size(new_nodes), added,
bit_set_count(kept_nodes),
bit_size(kept_nodes), kept);
xfree(kept);
xfree(added);
FREE_NULL_BITMAP(new_nodes);
FREE_NULL_BITMAP(kept_nodes);
}
break;
}
job_record_delete(resv_desc.job_ptr);
add_nodes /= 2; /* Try to get idle nodes as possible */
if (log_it ||
(slurm_conf.debug_flags & DEBUG_FLAG_RESERVATION)) {
verbose("%s: unable to replace all allocated nodes in reservation %s at this time",
__func__, resv_ptr->name);
log_it = false;
}
_free_resv_select_members(&resv_select);
}
FREE_NULL_BITMAP(preserve_bitmap);
if (replaced) {
last_resv_update = time(NULL);
schedule_resv_save();
}
}
/*
* Replace DOWN or DRAINED in an advanced reservation, also replaces nodes
* in use for reservations with the "replace" flag.
*/
static void _validate_node_choice(slurmctld_resv_t *resv_ptr)
{
int i;
resv_desc_msg_t resv_desc;
resv_select_t resv_select = { 0 };
if ((resv_ptr->node_bitmap == NULL) ||
(!(resv_ptr->ctld_flags & RESV_CTLD_FULL_NODE) &&
(resv_ptr->node_cnt > 1)) ||
(resv_ptr->flags & RESERVE_FLAG_SPEC_NODES) ||
(resv_ptr->flags & RESERVE_FLAG_STATIC) ||
(resv_ptr->flags & RESERVE_FLAG_MAINT))
return;
if ((resv_ptr->flags & RESERVE_FLAG_REPLACE) ||
(resv_ptr->flags & RESERVE_FLAG_REPLACE_DOWN)) {
_resv_node_replace(resv_ptr);
return;
}
i = bit_overlap(resv_ptr->node_bitmap, avail_node_bitmap);
if (i == resv_ptr->node_cnt) {
return;
}
/* Reservation includes DOWN, DRAINED/DRAINING, FAILING or
* NO_RESPOND nodes. Generate new request using _select_nodes()
* in attempt to replace these nodes */
slurm_init_resv_desc_msg(&resv_desc);
resv_desc.start_time = resv_ptr->start_time;
resv_desc.end_time = resv_ptr->end_time;
resv_desc.features = resv_ptr->features;
resv_desc.flags = resv_ptr->flags;
resv_desc.name = resv_ptr->name;
if (!(resv_ptr->ctld_flags & RESV_CTLD_FULL_NODE)) {
resv_desc.core_cnt = resv_ptr->core_cnt;
}
resv_desc.node_cnt = resv_ptr->node_cnt - i;
resv_desc.tres_str = resv_ptr->tres_str;
resv_desc.job_ptr = job_mgr_copy_resv_desc_to_job_record(&resv_desc);
/* Exclude self reserved nodes only if reservation contains any nodes */
if (resv_ptr->node_bitmap) {
resv_select.node_bitmap = bit_copy(avail_node_bitmap);
bit_and(resv_select.node_bitmap, resv_ptr->part_ptr->node_bitmap);
bit_and_not(resv_select.node_bitmap, resv_ptr->node_bitmap);
}
i = _select_nodes(&resv_desc, &resv_ptr->part_ptr, &resv_select, NULL);
xfree(resv_desc.node_list);
xfree(resv_desc.partition);
if (i == SLURM_SUCCESS) {
job_record_t *job_ptr = resv_desc.job_ptr;
bit_and(resv_ptr->node_bitmap, avail_node_bitmap);
bit_or(resv_ptr->node_bitmap, resv_select.node_bitmap);
FREE_NULL_BITMAP(resv_ptr->core_bitmap);
resv_ptr->core_bitmap = resv_select.core_bitmap;
resv_select.core_bitmap = NULL;
free_job_resources(&resv_ptr->core_resrcs);
resv_ptr->core_resrcs = job_ptr->job_resrcs;
job_ptr->job_resrcs = NULL;
xfree(resv_ptr->node_list);
resv_ptr->node_list = bitmap2node_name(resv_ptr->node_bitmap);
FREE_NULL_LIST(resv_ptr->gres_list_alloc);
resv_ptr->gres_list_alloc = job_ptr->gres_list_req;
gres_job_state_log(resv_ptr->gres_list_alloc, 0);
job_ptr->gres_list_req = NULL;
job_record_delete(resv_desc.job_ptr);
resv_desc.job_ptr = NULL;
info("modified reservation %s due to unusable nodes, "
"new nodes: %s", resv_ptr->name, resv_ptr->node_list);
} else if (difftime(resv_ptr->start_time, time(NULL)) < 600) {
info("reservation %s contains unusable nodes, "
"can't reallocate now", resv_ptr->name);
} else {
debug("reservation %s contains unusable nodes, "
"can't reallocate now", resv_ptr->name);
}
job_record_delete(resv_desc.job_ptr);
_free_resv_select_members(&resv_select);
}
/*
* Validate if the user has access to this reservation.
*/
static bool _validate_user_access(slurmctld_resv_t *resv_ptr,
list_t *user_assoc_list, uid_t uid)
{
/* Determine if we have access */
if ((accounting_enforce & ACCOUNTING_ENFORCE_ASSOCS) &&
resv_ptr->assoc_list) {
xassert(user_assoc_list);
/*
* Check to see if the association is
* here or the parent association is
* listed in the valid associations.
*/
if (xstrchr(resv_ptr->assoc_list, '-')) {
if (_match_user_assoc(resv_ptr->assoc_list,
user_assoc_list,
true))
return 0;
}
if (xstrstr(resv_ptr->assoc_list, ",1") ||
xstrstr(resv_ptr->assoc_list, ",2") ||
xstrstr(resv_ptr->assoc_list, ",3") ||
xstrstr(resv_ptr->assoc_list, ",4") ||
xstrstr(resv_ptr->assoc_list, ",5") ||
xstrstr(resv_ptr->assoc_list, ",6") ||
xstrstr(resv_ptr->assoc_list, ",7") ||
xstrstr(resv_ptr->assoc_list, ",8") ||
xstrstr(resv_ptr->assoc_list, ",9") ||
xstrstr(resv_ptr->assoc_list, ",0")) {
if (!_match_user_assoc(resv_ptr->assoc_list,
user_assoc_list,
false))
return 0;
}
} else {
for (int i = 0; i < resv_ptr->user_cnt; i++) {
if (resv_ptr->user_list[i] == uid)
return 1;
}
return 0;
}
return 1;
}
/*
* Load the reservation state from file, recover on slurmctld restart.
* Reset reservation pointers for all jobs.
* Execute this after loading the configuration file data.
* IN recover - 0 = validate current reservations ONLY if already recovered,
* otherwise recover from disk
* 1+ = recover all reservation state from disk
* RET SLURM_SUCCESS or error code
* NOTE: READ lock_slurmctld config before entry
*/
extern int load_all_resv_state(int recover)
{
char *state_file, *ver_str = NULL;
time_t now;
int error_code = 0;
buf_t *buffer;
slurmctld_resv_t *resv_ptr = NULL;
uint16_t protocol_version = NO_VAL16;
last_resv_update = time(NULL);
if ((recover == 0) && resv_list) {
_validate_all_reservations();
return SLURM_SUCCESS;
}
/* Read state file and validate */
_create_resv_lists(true);
/* read the file */
if (!(buffer = state_save_open("resv_state", &state_file))) {
if ((clustername_existed == 1) && (!ignore_state_errors))
fatal("No reservation state file (%s) to recover",
state_file);
info("No reservation state file (%s) to recover",
state_file);
xfree(state_file);
return ENOENT;
}
xfree(state_file);
safe_unpackstr(&ver_str, buffer);
debug3("Version string in resv_state header is %s", ver_str);
if (ver_str && !xstrcmp(ver_str, RESV_STATE_VERSION))
safe_unpack16(&protocol_version, buffer);
if (protocol_version == NO_VAL16) {
if (!ignore_state_errors)
fatal("Can not recover reservation state, data version incompatible, start with '-i' to ignore this. Warning: using -i will lose the data that can't be recovered.");
error("************************************************************");
error("Can not recover reservation state, data version incompatible");
error("************************************************************");
xfree(ver_str);
FREE_NULL_BUFFER(buffer);
schedule_resv_save(); /* Schedule save with new format */
return EFAULT;
}
xfree(ver_str);
safe_unpack_time(&now, buffer);
safe_unpack32(&top_suffix, buffer);
while (remaining_buf(buffer) > 0) {
resv_ptr = _load_reservation_state(buffer, protocol_version);
if (!resv_ptr)
break;
_add_resv_to_lists(resv_ptr);
info("Recovered state of reservation %s", resv_ptr->name);
}
_validate_all_reservations();
info("Recovered state of %d reservations", list_count(resv_list));
FREE_NULL_BUFFER(buffer);
return error_code;
unpack_error:
if (!ignore_state_errors)
fatal("Incomplete reservation data checkpoint file, start with '-i' to ignore this. Warning: using -i will lose the data that can't be recovered.");
error("Incomplete reservation data checkpoint file");
_validate_all_reservations();
info("Recovered state of %d reservations", list_count(resv_list));
FREE_NULL_BUFFER(buffer);
return EFAULT;
}
static int _validate_job_resv_internal(job_record_t *job_ptr,
slurmctld_resv_t *resv_ptr)
{
int rc = _valid_job_access_resv(job_ptr, resv_ptr, true);
if (rc == SLURM_SUCCESS) {
if ((resv_ptr->flags & RESERVE_FLAG_PURGE_COMP)
&& resv_ptr->idle_start_time) {
log_flag(RESERVATION, "Resetting idle start time to zero on PURGE_COMP reservation %s due to associated %pJ",
resv_ptr->name, job_ptr);
}
resv_ptr->idle_start_time = 0;
_validate_node_choice(resv_ptr);
}
return rc;
}
/*
* get_resv_list - find record for named reservation(s)
* IN name - reservation name(s) in a comma separated char
* OUT err_part - The first invalid reservation name.
* RET list of pointers to the reservations or NULL if not found
* NOTE: Caller must free the returned list
* NOTE: Caller must free err_part
*/
static int _get_resv_list(job_record_t *job_ptr, char **err_resv)
{
slurmctld_resv_t *resv_ptr;
char *token, *last = NULL, *tmp_name;
int rc = SLURM_SUCCESS;
xassert(job_ptr);
if (!xstrchr(job_ptr->resv_name, ','))
return rc;
tmp_name = xstrdup(job_ptr->resv_name);
token = strtok_r(tmp_name, ",", &last);
if (!token) {
rc = ESLURM_RESERVATION_INVALID;
FREE_NULL_LIST(job_ptr->resv_list);
xfree(*err_resv);
*err_resv = xstrdup(job_ptr->resv_name);
}
while (token) {
resv_ptr = find_resv_name(token);
if (resv_ptr) {
rc = _validate_job_resv_internal(job_ptr, resv_ptr);
if (rc != SLURM_SUCCESS) {
FREE_NULL_LIST(job_ptr->resv_list);
xfree(*err_resv);
*err_resv = xstrdup(token);
break;
}
if (!job_ptr->resv_list)
job_ptr->resv_list = list_create(NULL);
if (!list_find_first(job_ptr->resv_list, _find_resv_ptr,
resv_ptr))
list_append(job_ptr->resv_list, resv_ptr);
} else {
FREE_NULL_LIST(job_ptr->resv_list);
rc = ESLURM_RESERVATION_INVALID;
if (err_resv) {
xfree(*err_resv);
*err_resv = xstrdup(token);
}
break;
}
token = strtok_r(NULL, ",", &last);
}
xfree(tmp_name);
if (rc == SLURM_SUCCESS)
list_sort(job_ptr->resv_list, _cmp_resv_id);
return rc;
}
/*
* Determine if a job request can use the specified reservations
*
* IN/OUT job_ptr - job to validate, set its resv_id
* RET SLURM_SUCCESS or error code (not found or access denied)
*/
extern int validate_job_resv(job_record_t *job_ptr)
{
slurmctld_resv_t *resv_ptr = NULL;
int rc;
xassert(job_ptr);
if ((job_ptr->resv_name == NULL) || (job_ptr->resv_name[0] == '\0')) {
xfree(job_ptr->resv_name);
job_ptr->resv_id = 0;
job_ptr->resv_ptr = NULL;
return SLURM_SUCCESS;
}
if (!resv_list)
return ESLURM_RESERVATION_INVALID;
/* Check to see if we have multiple reservations requested */
if (xstrchr(job_ptr->resv_name, ',')) {
char *tmp_str = NULL;
rc = _get_resv_list(job_ptr, &tmp_str);
if (tmp_str) {
error("%pJ requested reservation (%s): %s",
job_ptr, tmp_str, slurm_strerror(rc));
xfree(tmp_str);
} else /* grab the first on the list to use */
resv_ptr = list_peek(job_ptr->resv_list);
} else {
/* Find the named reservation */
resv_ptr = find_resv_name(job_ptr->resv_name);
rc = _validate_job_resv_internal(job_ptr, resv_ptr);
}
if (resv_ptr) {
job_ptr->resv_id = resv_ptr->resv_id;
job_ptr->resv_ptr = resv_ptr;
} else {
job_ptr->resv_id = 0;
job_ptr->resv_ptr = NULL;
}
return rc;
}
static int _resize_resv(slurmctld_resv_t *resv_ptr, uint32_t node_cnt)
{
bitstr_t *tmp2_bitmap = NULL;
int delta_node_cnt, i, rc;
resv_desc_msg_t resv_desc;
resv_select_t resv_select = { 0 };
delta_node_cnt = resv_ptr->node_cnt - node_cnt;
if (delta_node_cnt == 0) /* Already correct node count */
return SLURM_SUCCESS;
if (delta_node_cnt > 0) { /* Must decrease node count */
if (bit_overlap_any(resv_ptr->node_bitmap, idle_node_bitmap)) {
/* Start by eliminating idle nodes from reservation */
resv_select.node_bitmap = bit_copy(resv_ptr->node_bitmap);
bit_and(resv_select.node_bitmap, idle_node_bitmap);
i = bit_set_count(resv_select.node_bitmap);
if (i > delta_node_cnt) {
tmp2_bitmap = bit_pick_cnt(resv_select.node_bitmap,
delta_node_cnt);
bit_and_not(resv_ptr->node_bitmap, tmp2_bitmap);
FREE_NULL_BITMAP(resv_select.node_bitmap);
FREE_NULL_BITMAP(tmp2_bitmap);
delta_node_cnt = 0; /* ALL DONE */
} else if (i) {
bit_and_not(resv_ptr->node_bitmap,
idle_node_bitmap);
resv_ptr->node_cnt = bit_set_count(
resv_ptr->node_bitmap);
delta_node_cnt = resv_ptr->node_cnt -
node_cnt;
}
FREE_NULL_BITMAP(resv_select.node_bitmap);
}
if (delta_node_cnt > 0) {
/* Now eliminate allocated nodes from reservation */
resv_select.node_bitmap =
bit_pick_cnt(resv_ptr->node_bitmap, node_cnt);
FREE_NULL_BITMAP(resv_ptr->node_bitmap);
resv_ptr->node_bitmap = resv_select.node_bitmap;
}
xfree(resv_ptr->node_list);
resv_ptr->node_list = bitmap2node_name(resv_ptr->node_bitmap);
resv_ptr->node_cnt = node_cnt;
return SLURM_SUCCESS;
}
/* Ensure if partition exists in reservation otherwise use default */
if ((rc = _validate_and_set_partition(&resv_ptr->part_ptr,
&resv_ptr->partition))) {
return rc;
}
/* Must increase node count. Make this look like new request so
* we can use _select_nodes() for selecting the nodes */
slurm_init_resv_desc_msg(&resv_desc);
resv_desc.start_time = resv_ptr->start_time;
resv_desc.end_time = resv_ptr->end_time;
resv_desc.features = resv_ptr->features;
resv_desc.flags = resv_ptr->flags;
resv_desc.node_cnt = 0 - delta_node_cnt;
resv_desc.name = resv_ptr->name;
resv_desc.tres_str = resv_ptr->tres_str;
resv_desc.job_ptr = job_mgr_copy_resv_desc_to_job_record(&resv_desc);
/* Exclude self reserved nodes only if reservation contains any nodes */
if (resv_ptr->node_bitmap) {
resv_select.node_bitmap =
bit_copy(resv_ptr->part_ptr->node_bitmap);
bit_and_not(resv_select.node_bitmap, resv_ptr->node_bitmap);
}
rc = _select_nodes(&resv_desc, &resv_ptr->part_ptr, &resv_select, NULL);
xfree(resv_desc.node_list);
xfree(resv_desc.partition);
if (rc == SLURM_SUCCESS) {
job_record_t *job_ptr = resv_desc.job_ptr;
/*
* If the reservation was 0 node count before (ANY_NODES) this
* could be NULL, if for some reason someone tried to update the
* node count in this situation we will still not have a
* node_bitmap.
*/
if (resv_ptr->node_bitmap)
bit_or(resv_ptr->node_bitmap, resv_select.node_bitmap);
else
resv_ptr->node_bitmap = bit_copy(
resv_select.node_bitmap);
FREE_NULL_BITMAP(resv_select.node_bitmap);
FREE_NULL_BITMAP(resv_ptr->core_bitmap);
resv_ptr->core_bitmap = resv_select.core_bitmap;
resv_select.core_bitmap = NULL;
free_job_resources(&resv_ptr->core_resrcs);
resv_ptr->core_resrcs = job_ptr->job_resrcs;
job_ptr->job_resrcs = NULL;
xfree(resv_ptr->node_list);
resv_ptr->node_list = bitmap2node_name(resv_ptr->node_bitmap);
resv_ptr->node_cnt = node_cnt;
FREE_NULL_LIST(resv_ptr->gres_list_alloc);
resv_ptr->gres_list_alloc = job_ptr->gres_list_req;
gres_job_state_log(resv_ptr->gres_list_alloc, 0);
job_ptr->gres_list_req = NULL;
job_record_delete(resv_desc.job_ptr);
resv_desc.job_ptr = NULL;
}
job_record_delete(resv_desc.job_ptr);
return rc;
}
static int _feature_has_node_cnt(void *x, void *key)
{
job_feature_t *feat_ptr = (job_feature_t *) x;
if (feat_ptr->count > 0)
return 1;
return 0;
}
static int _have_mor_feature(void *x, void *key)
{
job_feature_t *feat_ptr = (job_feature_t *) x;
if (feat_ptr->op_code == FEATURE_OP_MOR)
return 1;
return 0;
}
static int _combine_gres_list_exc(void *object, void *arg)
{
gres_state_t *gres_state_job_in = object;
list_t *gres_list_exc = arg;
gres_job_state_t *gres_js_in = gres_state_job_in->gres_data;
gres_key_t job_search_key = {
.config_flags = gres_state_job_in->config_flags,
.plugin_id = gres_state_job_in->plugin_id,
.type_id = gres_js_in->type_id,
};
gres_state_t *gres_state_job =
list_find_first(gres_list_exc,
gres_find_job_by_key_exact_type,
&job_search_key);
if (!gres_state_job) {
gres_state_job = gres_create_state(
gres_state_job_in,
GRES_STATE_SRC_STATE_PTR,
GRES_STATE_TYPE_JOB,
gres_job_state_dup(gres_js_in));
list_append(gres_list_exc, gres_state_job);
} else {
gres_job_state_t *gres_js = gres_state_job->gres_data;
gres_js->total_gres += gres_js_in->total_gres;
/*
* At the moment we only care about gres_js->gres_bit_alloc and
* gres_js->gres_cnt_node_alloc.
*/
if (gres_js_in->gres_bit_alloc) {
if (!gres_js->gres_bit_alloc)
gres_js->gres_bit_alloc =
xcalloc(gres_js->node_cnt,
sizeof(bitstr_t *));
for (int i = 0; i < gres_js_in->node_cnt; i++) {
if (!gres_js_in->gres_bit_alloc[i])
continue;
if (!gres_js->gres_bit_alloc[i])
gres_js->gres_bit_alloc[i] =
bit_copy(gres_js_in->
gres_bit_alloc[i]);
else
bit_or(gres_js->gres_bit_alloc[i],
gres_js_in->gres_bit_alloc[i]);
}
}
if (gres_js_in->gres_cnt_node_alloc) {
if (!gres_js->gres_cnt_node_alloc)
gres_js->gres_cnt_node_alloc =
xcalloc(gres_js->node_cnt,
sizeof(uint64_t));
for (int i = 0; i < gres_js_in->node_cnt; i++) {
gres_js->gres_cnt_node_alloc[i] +=
gres_js_in->gres_cnt_node_alloc[i];
}
}
}
return 1;
}
static void _addto_gres_list_exc(list_t **total_list, list_t *sub_list)
{
if (!sub_list)
return;
if (!*total_list) {
*total_list = gres_job_state_list_dup(sub_list);
} else {
/* Here we have to combine the lists */
(void) list_for_each(sub_list,
_combine_gres_list_exc,
*total_list);
}
}
/*
* Filter out nodes and cores from reservation based on existing
* reservations.
*/
static void _filter_resv(resv_desc_msg_t *resv_desc_ptr,
slurmctld_resv_t *resv_ptr,
resv_select_t *resv_select, bool filter_overlap)
{
if (!filter_overlap &&
((resv_ptr->flags & RESERVE_FLAG_MAINT) ||
(resv_ptr->flags & RESERVE_FLAG_OVERLAP))) {
log_flag(RESERVATION,
"%s: skipping reservation %s filter for reservation %s",
__func__, resv_ptr->name, resv_desc_ptr->name);
return;
}
if (resv_ptr->node_bitmap == NULL) {
log_flag(RESERVATION,
"%s: reservation %s has no nodes to filter for reservation %s",
__func__, resv_ptr->name, resv_desc_ptr->name);
return;
}
if (!_resv_time_overlap(resv_desc_ptr, resv_ptr)) {
log_flag(RESERVATION,
"%s: reservation %s does not overlap in time to filter for reservation %s",
__func__, resv_ptr->name, resv_desc_ptr->name);
return;
}
if (!resv_ptr->core_bitmap &&
!(resv_ptr->flags & RESERVE_FLAG_GRES_REQ) &&
!(resv_ptr->ctld_flags & RESV_CTLD_FULL_NODE)) {
error("%s: Reservation %s has no core_bitmap and full_nodes is not set",
__func__, resv_ptr->name);
resv_ptr->ctld_flags |= RESV_CTLD_FULL_NODE;
}
if (resv_ptr->ctld_flags & RESV_CTLD_FULL_NODE) {
if (slurm_conf.debug_flags & DEBUG_FLAG_RESERVATION) {
char *nodes[2] = {
bitmap2node_name(resv_ptr->node_bitmap),
bitmap2node_name(resv_select->node_bitmap)
};
log_flag(RESERVATION,
"%s: reservation %s filtered nodes:%s from reservation %s nodes:%s",
__func__, resv_ptr->name, nodes[0],
resv_desc_ptr->name, nodes[1]);
xfree(nodes[0]);
xfree(nodes[1]);
}
bit_and_not(resv_select->node_bitmap, resv_ptr->node_bitmap);
}
if (resv_select->core_bitmap && resv_ptr->core_bitmap) {
if (slurm_conf.debug_flags & DEBUG_FLAG_RESERVATION) {
char *cores[2] = {
bit_fmt_full(resv_ptr->core_bitmap),
bit_fmt_full(resv_select->core_bitmap)
};
log_flag(RESERVATION,
"%s: reservation %s filtered cores:%s from reservation %s cores:%s",
__func__, resv_ptr->name, cores[0],
resv_desc_ptr->name, cores[1]);
xfree(cores[0]);
xfree(cores[1]);
}
bit_or(resv_select->core_bitmap, resv_ptr->core_bitmap);
}
_addto_gres_list_exc(&resv_select->gres_list_exc,
resv_ptr->gres_list_alloc);
}
/*
* Select nodes using given node bitmap and/or core_bitmap
* Given a reservation create request, select appropriate nodes for use
* resv_desc_ptr IN - Reservation request, node_list field set on exit
* part_ptr IN/OUT - Desired partition, if NULL then set to default part
* resv_bitmap IN/OUT - nodes to use, if points to NULL then used nodes in
* specified partition. Set to selected nodes on output.
* core_bitmap OUT - cores allocated to reservation
*/
static int _select_nodes(resv_desc_msg_t *resv_desc_ptr,
part_record_t **part_ptr,
resv_select_t *resv_select_ret,
bitstr_t *preserve_bitmap)
{
slurmctld_resv_t *resv_ptr;
resv_select_t resv_select[MAX_BITMAPS] = {{0}};
int max_bitmap = SELECT_ALL_RSVD;
time_t now = time(NULL);
int rc = SLURM_SUCCESS;
bool have_xand = false;
list_itr_t *itr;
job_record_t *job_ptr;
if ((rc = _validate_and_set_partition(part_ptr,
&resv_desc_ptr->partition))) {
return rc;
}
xassert(resv_desc_ptr->job_ptr);
job_ptr = resv_desc_ptr->job_ptr;
if (job_ptr->details->min_nodes > job_ptr->details->min_cpus) {
info("Core count for reservation is less than node count!");
return ESLURM_INVALID_CORE_CNT;
}
xfree(job_ptr->partition);
job_ptr->partition = xstrdup(resv_desc_ptr->partition);
job_ptr->part_ptr = *part_ptr;
if (resv_select_ret->node_bitmap) {
resv_select[SELECT_ALL_RSVD].node_bitmap =
resv_select_ret->node_bitmap;
resv_select_ret->node_bitmap = NULL;
} else {
/* Start with all nodes in the partition */
resv_select[SELECT_ALL_RSVD].node_bitmap =
bit_copy((*part_ptr)->node_bitmap);
}
/* clone online from ALL and then filter down nodes */
resv_select[SELECT_ONL_RSVD].node_bitmap =
bit_copy(resv_select[SELECT_ALL_RSVD].node_bitmap);
bit_and(resv_select[SELECT_ONL_RSVD].node_bitmap, up_node_bitmap);
/* clone available from ONL and then filter unavailable nodes */
resv_select[SELECT_AVL_RSVD].node_bitmap =
bit_copy(resv_select[SELECT_ONL_RSVD].node_bitmap);
bit_and(resv_select[SELECT_AVL_RSVD].node_bitmap, avail_node_bitmap);
/* populate other node bitmaps from available (AVL) */
resv_select[SELECT_NOT_RSVD].node_bitmap =
bit_copy(resv_select[SELECT_AVL_RSVD].node_bitmap);
resv_select[SELECT_OVR_RSVD].node_bitmap =
bit_copy(resv_select[SELECT_AVL_RSVD].node_bitmap);
/* create core bitmap if cores are requested */
if (resv_desc_ptr->core_cnt != NO_VAL) {
node_conf_create_cluster_core_bitmap(
&resv_select[SELECT_ALL_RSVD].core_bitmap);
for (int i = 0; i < SELECT_ALL_RSVD; i++)
resv_select[i].core_bitmap = bit_copy(
resv_select[SELECT_ALL_RSVD].core_bitmap);
}
/*
* Filter bitmaps based on selection types.
* This needs to be an iterator since _advance_resv_time() may
* eventually call _generate_resv_id() which will deadlock the
* resv_list lock.
*/
itr = list_iterator_create(resv_list);
while ((resv_ptr = list_next(itr))) {
if (resv_ptr->end_time <= now)
(void)_advance_resv_time(resv_ptr);
_filter_resv(resv_desc_ptr, resv_ptr,
&resv_select[SELECT_NOT_RSVD], true);
_filter_resv(resv_desc_ptr, resv_ptr,
&resv_select[SELECT_OVR_RSVD], false);
}
list_iterator_destroy(itr);
if (!(resv_desc_ptr->flags & RESERVE_FLAG_MAINT) &&
!(resv_desc_ptr->flags & RESERVE_FLAG_OVERLAP)) {
/*
* Remove reserve red and down nodes unless
* MAINT or OVERLAP
*/
_free_resv_select_members(&resv_select[SELECT_AVL_RSVD]);
_free_resv_select_members(&resv_select[SELECT_ONL_RSVD]);
_free_resv_select_members(&resv_select[SELECT_ALL_RSVD]);
max_bitmap = SELECT_OVR_RSVD;
}
if (!(resv_desc_ptr->flags & RESERVE_FLAG_MAINT) &&
(resv_desc_ptr->flags & RESERVE_FLAG_OVERLAP)) {
/*
* Overlap can not select from online/all
*/
_free_resv_select_members(&resv_select[SELECT_ONL_RSVD]);
_free_resv_select_members(&resv_select[SELECT_ALL_RSVD]);
max_bitmap = SELECT_AVL_RSVD;
}
/* Satisfy feature specification */
if (resv_desc_ptr->features) {
job_record_t *job_ptr = resv_desc_ptr->job_ptr;
bool dummy = false;
int total_node_cnt = 0;
if (!job_ptr->details->feature_list)
rc = ESLURM_INVALID_FEATURE;
else if (list_find_first(job_ptr->details->feature_list,
_have_mor_feature, &dummy)) {
rc = ESLURM_INVALID_FEATURE;
} else {
find_feature_nodes(job_ptr->details->feature_list,
true);
if (resv_desc_ptr->node_cnt != NO_VAL) {
total_node_cnt = resv_desc_ptr->node_cnt;
}
}
if (rc != SLURM_SUCCESS) {
;
} else if (list_find_first(job_ptr->details->feature_list,
_feature_has_node_cnt, &dummy)) {
/* take the core_bitmap */
FREE_NULL_BITMAP(resv_select_ret->core_bitmap);
resv_select_ret->core_bitmap =
resv_select[max_bitmap].core_bitmap;
resv_select[max_bitmap].core_bitmap = NULL;
/* Accumulate resources by feature type/count */
have_xand = true;
_pick_nodes_by_feature_node_cnt(
resv_select[max_bitmap].node_bitmap,
preserve_bitmap,
resv_desc_ptr,
resv_select_ret,
total_node_cnt,
job_ptr->details->feature_list);
} else {
/*
* Simple AND/OR node filtering.
* First try to use nodes with the feature active.
* If that fails, use nodes with the feature available.
*/
bitstr_t *tmp_bitmap;
tmp_bitmap = bit_copy(resv_select[max_bitmap].
node_bitmap);
rc = valid_feature_counts(job_ptr, true, tmp_bitmap,
&dummy);
if ((rc == SLURM_SUCCESS) &&
(bit_set_count(tmp_bitmap) < total_node_cnt)) {
/* reset tmp_bitmap and try with available */
bit_clear_all(tmp_bitmap);
bit_or(tmp_bitmap,
resv_select[max_bitmap].node_bitmap);
rc = valid_feature_counts(job_ptr, false,
tmp_bitmap, &dummy);
}
if ((rc == SLURM_SUCCESS) &&
bit_set_count(tmp_bitmap) < total_node_cnt)
rc = ESLURM_REQUESTED_NODE_CONFIG_UNAVAILABLE;
/* filter nodes that won't work from all bitmaps */
for (size_t i = 0; (i < MAX_BITMAPS) &&
resv_select[i].node_bitmap;
i++)
bit_and(resv_select[i].node_bitmap, tmp_bitmap);
FREE_NULL_BITMAP(tmp_bitmap);
}
}
if (!have_xand && (rc == SLURM_SUCCESS)) {
rc = _pick_nodes_ordered(resv_desc_ptr,
resv_select, MAX_BITMAPS,
resv_select_ret,
select_node_bitmap_tags);
}
/* release all the resv_select */
for (size_t i = 0; (i < MAX_BITMAPS); i++)
_free_resv_select_members(&resv_select[i]);
/* No idle nodes found */
if ((resv_select_ret->node_bitmap == NULL) && (rc == SLURM_SUCCESS))
rc = ESLURM_NODES_BUSY;
if (!resv_desc_ptr->node_list)
resv_desc_ptr->node_list =
bitmap2node_name(resv_select_ret->node_bitmap);
return rc;
}
static void _pick_nodes_by_feature_node_cnt(bitstr_t *avail_bitmap,
bitstr_t *preserve_bitmap,
resv_desc_msg_t *resv_desc_ptr,
resv_select_t *resv_select_ret,
int total_node_cnt,
list_t *feature_list)
{
bitstr_t *tmp_bitmap = NULL;
bitstr_t *feature_bitmap;
uint32_t save_core_cnt;
uint32_t save_node_cnt;
uint32_t save_min_cpus, save_min_nodes, save_max_nodes;
job_feature_t *feat_ptr;
list_itr_t *feat_iter;
int paren = 0;
bool test_active = true;
job_record_t *job_ptr = resv_desc_ptr->job_ptr;
job_details_t *detail_ptr = job_ptr->details;
resv_select_t resv_select = { 0 };
save_min_cpus = detail_ptr->min_cpus;
save_min_nodes = detail_ptr->min_nodes;
save_max_nodes = detail_ptr->max_nodes;
save_core_cnt = resv_desc_ptr->core_cnt;
resv_desc_ptr->core_cnt = NO_VAL;
save_node_cnt = resv_desc_ptr->node_cnt;
resv_desc_ptr->node_cnt = NO_VAL;
TRY_AVAIL:
/*
* In the first pass, we try to satisfy the resource requirements using
* currently active features. If that fails, use available features
* and require a reboot to satisfy the request
*/
feat_iter = list_iterator_create(feature_list);
while ((feat_ptr = list_next(feat_iter))) {
feature_bitmap = test_active ?
feat_ptr->node_bitmap_active :
feat_ptr->node_bitmap_avail;
if (feat_ptr->paren > paren) { /* Start parenthesis */
paren = feat_ptr->paren;
tmp_bitmap = feature_bitmap;
continue;
}
if ((feat_ptr->paren == 1) || /* Continue parenthesis */
(feat_ptr->paren < paren)) { /* End of parenthesis */
paren = feat_ptr->paren;
bit_and(feature_bitmap, tmp_bitmap);
tmp_bitmap = feature_bitmap;
if (feat_ptr->paren == 1)
continue;
}
detail_ptr->orig_min_cpus =
detail_ptr->num_tasks =
detail_ptr->min_cpus =
detail_ptr->max_nodes =
detail_ptr->min_nodes =
resv_desc_ptr->node_cnt =
feat_ptr->count ? feat_ptr->count : 1;
/*
* If we have a list of preserved nodes, select from those
* first.
*/
if (preserve_bitmap) {
/*
* Set these = 1 to allow less than
* feature count number of nodes to
* be selected
*/
detail_ptr->orig_min_cpus =
detail_ptr->min_cpus =
detail_ptr->min_nodes = 1;
int node_count = 0;
if (resv_select_ret->node_bitmap) {
node_count = bit_set_count(
resv_select_ret->node_bitmap);
}
resv_select.node_bitmap = bit_copy(preserve_bitmap);
bit_and(resv_select.node_bitmap, feature_bitmap);
resv_select.core_bitmap = resv_select_ret->core_bitmap;
_pick_nodes(resv_desc_ptr, &resv_select,
resv_select_ret);
FREE_NULL_BITMAP(resv_select.node_bitmap);
/* decrement needed node count */
if (resv_select_ret->node_bitmap) {
/* calculate the number of nodes added */
int found_count = bit_set_count(
resv_select_ret->node_bitmap) -
node_count;
/* reduce to number still needed */
detail_ptr->num_tasks =
detail_ptr->max_nodes =
resv_desc_ptr->node_cnt =
feat_ptr->count - found_count;
}
}
/* Search for replacements if needed */
if (resv_desc_ptr->node_cnt > 0) {
resv_select.node_bitmap = bit_copy(avail_bitmap);
bit_and(resv_select.node_bitmap, feature_bitmap);
resv_select.core_bitmap = resv_select_ret->core_bitmap;
_pick_nodes(resv_desc_ptr, &resv_select,
resv_select_ret);
FREE_NULL_BITMAP(resv_select.node_bitmap);
}
if (!resv_select_ret->node_bitmap)
break;
}
list_iterator_destroy(feat_iter);
if (!resv_select_ret->node_bitmap && test_active) {
/* Test failed for active features, test available features */
test_active = false;
goto TRY_AVAIL;
}
/*
* We have picked all the featured nodes, if we requested more nodes we
* will now pick those non-featured nodes.
*/
if (resv_select_ret->node_bitmap &&
(bit_set_count(resv_select_ret->node_bitmap) < total_node_cnt)) {
detail_ptr->orig_min_cpus =
detail_ptr->num_tasks =
detail_ptr->min_cpus =
detail_ptr->max_nodes =
detail_ptr->min_nodes =
resv_desc_ptr->node_cnt =
total_node_cnt -
bit_set_count(resv_select_ret->node_bitmap);
resv_select.node_bitmap = bit_copy(avail_bitmap);
resv_select.core_bitmap = resv_select_ret->core_bitmap;
_pick_nodes(resv_desc_ptr, &resv_select, resv_select_ret);
FREE_NULL_BITMAP(resv_select.node_bitmap);
}
detail_ptr->orig_min_cpus =
detail_ptr->num_tasks =
detail_ptr->min_cpus =
save_min_cpus;
detail_ptr->min_nodes = save_min_nodes;
detail_ptr->max_nodes = save_max_nodes;
resv_desc_ptr->core_cnt = save_core_cnt;
resv_desc_ptr->node_cnt = save_node_cnt;
}
/*
* Build core_resrcs based upon node_bitmap and core_bitmap as needed.
* This translates a global core_bitmap (including all nodes) to a
* core_bitmap for only those nodes in the reservation. This is needed to
* handle nodes being added or removed from the system or their core count
* changing.
*/
static void _validate_core_resrcs(resv_desc_msg_t *resv_desc_ptr,
bitstr_t *node_bitmap,
bitstr_t *core_bitmap)
{
job_record_t *job_ptr = resv_desc_ptr->job_ptr;
node_record_t *node_ptr;
int node_inx, rc;
int core_offset_local, core_offset_global, core_end;
xassert(job_ptr);
/*
* In most cases if we have a core_bitmap the core_resrcs will already
* be correct. In that case just continue.
*/
if (!core_bitmap ||
!job_ptr->job_resrcs ||
!job_ptr->job_resrcs->core_bitmap ||
(bit_set_count(job_ptr->job_resrcs->core_bitmap) ==
bit_set_count(core_bitmap)) ||
!bit_set_count(node_bitmap))
return;
free_job_resources(&job_ptr->job_resrcs);
job_ptr->job_resrcs = create_job_resources();
job_ptr->job_resrcs->nodes = bitmap2node_name(node_bitmap);
job_ptr->job_resrcs->node_bitmap = bit_copy(node_bitmap);
job_ptr->job_resrcs->nhosts = bit_set_count(node_bitmap);
rc = build_job_resources(job_ptr->job_resrcs);
if (rc != SLURM_SUCCESS) {
free_job_resources(&job_ptr->job_resrcs);
return;
}
job_ptr->job_resrcs->cpus =
xcalloc(job_ptr->job_resrcs->nhosts, sizeof(uint16_t));
core_offset_local = -1;
node_inx = -1;
for (int i = 0; (node_ptr = next_node_bitmap(node_bitmap, &i)); i++) {
node_inx++;
core_offset_global = cr_get_coremap_offset(i);
core_end = cr_get_coremap_offset(i + 1);
for (int c = core_offset_global; c < core_end; c++) {
core_offset_local++;
if (!bit_test(core_bitmap, c))
continue;
if (job_ptr->job_resrcs->core_bitmap)
bit_set(job_ptr->job_resrcs->core_bitmap,
core_offset_local);
job_ptr->job_resrcs->cpus[node_inx] +=
node_ptr->threads;
job_ptr->job_resrcs->ncpus += node_ptr->threads;
}
}
}
/*
* Pick nodes based on ordered list of bitmaps
* IN/OUT resv_desc_ptr - Reservation requesting nodes.
* node_list will be updated every run.
* IN/OUT resv_select - array of size MAX_BITMAPS, last pointer must be NULL.
* IN .node_bitmap - Ordered list of nodes that could be used
* for the reservation. Will attempt to use
* nodes from low ordered bitmaps first.
* IN/OUT .core_bitmap - Ordered list of cores that could be used
* for the reservation. Will attempt to use
* cores from low ordered bitmaps
* first. Cores must match nodes in same
* node avail_bitmap. Cores will be updated
* as chosen.
* OUT resv_select_ret - on success set new bitmaps of allocation.
* IN bitmap_tags - NULL, or array of cstrings giving a tag for each array index
* in the bitmaps
* RET SLURM_SUCCESS or error
*/
static int _pick_nodes_ordered(resv_desc_msg_t *resv_desc_ptr,
resv_select_t *resv_select,
int resv_select_cnt,
resv_select_t *resv_select_ret,
const char **bitmap_tags)
{
bitstr_t *selected_bitmap =
bit_alloc(bit_size(resv_select[0].node_bitmap));
bitstr_t *selected_core_bitmap = NULL;
size_t remain_nodes = (resv_desc_ptr->node_cnt == NO_VAL) ?
0 : resv_desc_ptr->node_cnt;
size_t remain_cores = (resv_desc_ptr->core_cnt == NO_VAL) ?
0 : resv_desc_ptr->core_cnt;
if (resv_select[0].core_bitmap)
selected_core_bitmap =
bit_alloc(bit_size(resv_select[0].core_bitmap));
if (slurm_conf.debug_flags & DEBUG_FLAG_RESERVATION) {
char *cores = NULL, *nodes = NULL, *pos = NULL;
size_t max_bitmap = 0;
for (size_t b = 0; (b < resv_select_cnt) &&
resv_select[b].node_bitmap;
b++) {
char *tmp = bitmap2node_name(
resv_select[b].node_bitmap);
xstrfmtcatat(nodes, &pos, "%s%s[%zu]=%s",
(b == 0 ? "" : ","),
(bitmap_tags ? bitmap_tags[b] : ""),
b,
((!tmp || !tmp[0]) ? "(NONE)" : tmp));
xfree(tmp);
max_bitmap = MAX(max_bitmap, (b + 1));
}
pos = NULL;
for (size_t b = 0; (b < resv_select_cnt) &&
resv_select[b].core_bitmap; b++) {
char *tmp = bit_fmt_full(resv_select[b].core_bitmap);
xstrfmtcatat(cores, &pos, "%s%s[%zu]=%s",
(b == 0 ? "" : ","),
(bitmap_tags ? bitmap_tags[b] : ""),
b,
((!tmp || !tmp[0]) ? "(NONE)" : tmp));
xfree(tmp);
max_bitmap = MAX(max_bitmap, (b + 1));
}
pos = NULL;
log_flag(RESERVATION, "%s: reservation %s picking from %zu bitmaps avail_nodes_bitmaps[%u]:%s used_cores_bitmaps[%u]:%s",
__func__, resv_desc_ptr->name, max_bitmap,
resv_desc_ptr->node_cnt,
nodes, resv_desc_ptr->core_cnt,
(cores ? cores : "(NONE)"));
xfree(cores);
xfree(nodes);
}
/* Free node_list here, it could be filled in by the select plugin. */
xfree(resv_desc_ptr->node_list);
if (resv_desc_ptr->flags & RESERVE_FLAG_GRES_REQ) {
remain_cores = 1;
}
for (size_t b = 0; (remain_nodes || remain_cores) &&
(b < resv_select_cnt) && resv_select[b].node_bitmap; b++) {
bitstr_t *tmp_bitmap;
size_t nodes_picked, cores_picked = 0;
/* Avoid picking already picked nodes */
bit_and_not(resv_select[b].node_bitmap, selected_bitmap);
if (selected_core_bitmap)
bit_and_not(resv_select[b].core_bitmap,
selected_core_bitmap);
if (!bit_set_count(resv_select[b].node_bitmap)) {
log_flag(RESERVATION, "%s: reservation %s skipping empty bitmap:%s[%zu]",
__func__, resv_desc_ptr->name,
(bitmap_tags ? bitmap_tags[b] : ""),
b);
continue;
}
tmp_bitmap = _pick_node_cnt(
resv_desc_ptr, &resv_select[b], remain_nodes);
if (tmp_bitmap == NULL) { /* allocation failure */
log_flag(RESERVATION, "%s: reservation %s of 0/%zu nodes with bitmap:%s[%zu]",
__func__, resv_desc_ptr->name,
remain_nodes,
(bitmap_tags ? bitmap_tags[b] : ""),
b);
continue;
}
/* avoid counting already reserved nodes */
bit_and_not(tmp_bitmap, selected_bitmap);
/* grab counts of picked resources */
nodes_picked = bit_set_count(tmp_bitmap);
if (resv_select[b].core_bitmap)
cores_picked = bit_set_count(resv_select[b].core_bitmap);
if (slurm_conf.debug_flags & DEBUG_FLAG_RESERVATION) {
char *nodes = bitmap2node_name(tmp_bitmap);
char *cores = NULL;
if (resv_select[b].core_bitmap)
cores = bit_fmt_full(resv_select[b].core_bitmap);
log_flag(RESERVATION, "%s: reservation %s picked from bitmap:%s[%zu] nodes[%zu/%zu]:%s cores[%zu]:%s",
__func__, resv_desc_ptr->name,
(bitmap_tags ? bitmap_tags[b] : ""), b,
remain_nodes, nodes_picked, nodes,
cores_picked, cores);
xfree(nodes);
xfree(cores);
}
if (nodes_picked <= remain_nodes)
remain_nodes -= nodes_picked;
else
remain_nodes = 0;
if (resv_select[b].core_bitmap) {
if (cores_picked <= remain_cores)
remain_cores -= cores_picked;
else
remain_cores = 0;
if (!selected_core_bitmap) {
/*
* select plugin made a core bitmap, use
* it for selected cores instead
*/
selected_core_bitmap =
resv_select[b].core_bitmap;
resv_select[b].core_bitmap = NULL;
} else
bit_or(selected_core_bitmap,
resv_select[b].core_bitmap);
}
bit_or(selected_bitmap, tmp_bitmap);
bit_and_not(resv_select[b].node_bitmap, tmp_bitmap);
FREE_NULL_BITMAP(tmp_bitmap);
if (!remain_nodes) {
log_flag(RESERVATION, "%s: reservation %s selected sufficient nodes by bitmap:%s[%zu]",
__func__, resv_desc_ptr->name,
(bitmap_tags ? bitmap_tags[b] : ""),
b);
} else if (selected_core_bitmap && !remain_cores) {
log_flag(RESERVATION, "%s: reservation %s selected sufficient cores by bitmap:%s[%zu]",
__func__, resv_desc_ptr->name,
(bitmap_tags ? bitmap_tags[b] : ""),
b);
} else {
log_flag(RESERVATION, "%s: reservation %s requires nodes:%zu cores:%zu after bitmap:%s[%zu]",
__func__, resv_desc_ptr->name,
remain_nodes, remain_cores,
(bitmap_tags ? bitmap_tags[b] : ""),
b);
}
}
/* If nothing selected, return a NULL pointer instead */
if (!selected_bitmap || !bit_set_count(selected_bitmap)) {
log_flag(RESERVATION, "%s: reservation %s unable to pick any nodes",
__func__, resv_desc_ptr->name);
FREE_NULL_BITMAP(selected_bitmap);
FREE_NULL_BITMAP(selected_core_bitmap);
return ESLURM_NODES_BUSY;
} else {
_validate_core_resrcs(resv_desc_ptr, selected_bitmap,
selected_core_bitmap);
if (slurm_conf.debug_flags & DEBUG_FLAG_RESERVATION) {
char *nodes = NULL;
int node_cnt = 0;
char *cores = NULL;
int core_cnt = 0;
if (selected_bitmap) {
nodes = bitmap2node_name(selected_bitmap);
node_cnt = bit_set_count(selected_bitmap);
}
if (selected_core_bitmap) {
cores = bit_fmt_full(selected_core_bitmap);
core_cnt = bit_set_count(selected_core_bitmap);
}
log_flag(RESERVATION, "%s: reservation %s picked nodes[%u]:%s cores[%u]:%s",
__func__, resv_desc_ptr->name, node_cnt, nodes,
core_cnt, cores);
xfree(nodes);
xfree(cores);
}
if (resv_select_ret->node_bitmap) {
bit_or(resv_select_ret->node_bitmap, selected_bitmap);
FREE_NULL_BITMAP(selected_bitmap);
} else {
resv_select_ret->node_bitmap = selected_bitmap;
}
resv_select_ret->core_bitmap = selected_core_bitmap;
return SLURM_SUCCESS;
}
}
/*
* Select nodes using given a single node bitmap and/or core_bitmap
*/
static void _pick_nodes(resv_desc_msg_t *resv_desc_ptr,
resv_select_t *resv_select,
resv_select_t *resv_select_ret)
{
xassert(resv_select);
if (resv_select_ret->node_bitmap)
bit_and_not(resv_select->node_bitmap,
resv_select_ret->node_bitmap);
if (slurm_conf.debug_flags & DEBUG_FLAG_RESERVATION) {
char *nodes = NULL;
int node_cnt = 0;
char *cores = NULL;
int core_cnt = 0;
if (resv_select->node_bitmap) {
nodes = bitmap2node_name(resv_select->node_bitmap);
node_cnt = bit_set_count(resv_select->node_bitmap);
}
if (resv_select->core_bitmap) {
cores = bit_fmt_full(resv_select->core_bitmap);
core_cnt = bit_set_count(resv_select->core_bitmap);
}
log_flag(RESERVATION, "%s: reservation %s picking nodes[%u]:%s cores[%u]:%s",
__func__, resv_desc_ptr->name, node_cnt, nodes,
core_cnt, cores);
xfree(nodes);
xfree(cores);
}
if (_pick_nodes_ordered(resv_desc_ptr,
resv_select, 1,
resv_select_ret,
(select_node_bitmap_tags + SELECT_ALL_RSVD))) {
/* If picking nodes failed clear ret_node_bitmap */
_free_resv_select_members(resv_select_ret);
}
}
static void _check_job_compatibility(job_record_t *job_ptr,
resv_select_t *resv_select)
{
uint32_t total_nodes;
bitstr_t *full_node_bitmap;
int i_core, i_node, res_inx;
int start = 0;
int rep_count = 0;
job_resources_t *job_res = job_ptr->job_resrcs;
if (!job_res->core_bitmap)
return;
total_nodes = bit_set_count(job_res->node_bitmap);
if (slurm_conf.debug_flags & DEBUG_FLAG_RESERVATION) {
char str[200];
bit_fmt(str, sizeof(str), job_res->core_bitmap);
log_flag(RESERVATION, "%s: Checking %d nodes (of %"PRIu64") for %pJ, core_bitmap:%s core_bitmap_size:%"PRIu64,
__func__, total_nodes, bit_size(job_res->node_bitmap),
job_ptr, str, bit_size(job_res->core_bitmap));
}
full_node_bitmap = bit_copy(job_res->node_bitmap);
node_conf_create_cluster_core_bitmap(&resv_select->core_bitmap);
i_node = 0;
res_inx = 0;
while (i_node < total_nodes) {
int cores_in_a_node = (job_res->sockets_per_node[res_inx] *
job_res->cores_per_socket[res_inx]);
int repeat_node_conf = job_res->sock_core_rep_count[rep_count++];
int node_bitmap_inx;
log_flag(RESERVATION, "%s: Working with %d cores per node. Same node conf repeated %d times (start core offset %d)",
__func__, cores_in_a_node, repeat_node_conf, start);
i_node += repeat_node_conf;
res_inx++;
while (repeat_node_conf--) {
int allocated;
int global_core_start;
node_bitmap_inx = bit_ffs(full_node_bitmap);
if (node_bitmap_inx < 0)
break; /* No more nodes */
global_core_start =
cr_get_coremap_offset(node_bitmap_inx);
allocated = 0;
for (i_core = 0; i_core < cores_in_a_node; i_core++) {
log_flag(RESERVATION, "%s: %pJ i_core: %d, start: %d, allocated: %d",
__func__, job_ptr, i_core, start,
allocated);
if (bit_test(job_ptr->job_resrcs->core_bitmap,
i_core + start)) {
allocated++;
bit_set(resv_select->core_bitmap,
global_core_start + i_core);
}
}
log_flag(RESERVATION, "%s: Checking node %d, allocated: %d, cores_in_a_node: %d",
__func__, node_bitmap_inx, allocated,
cores_in_a_node);
if (allocated == cores_in_a_node) {
/* We can exclude this node */
log_flag(RESERVATION, "%s: %pJ excluding node %d",
__func__, job_ptr, node_bitmap_inx);
bit_clear(resv_select->node_bitmap,
node_bitmap_inx);
}
start += cores_in_a_node;
bit_clear(full_node_bitmap, node_bitmap_inx);
}
}
FREE_NULL_BITMAP(full_node_bitmap);
}
static bitstr_t *_pick_node_cnt(resv_desc_msg_t *resv_desc_ptr,
resv_select_t *resv_select,
uint32_t node_cnt)
{
list_itr_t *job_iterator;
job_record_t *job_ptr;
bitstr_t *orig_bitmap = NULL, *save_bitmap = NULL;
bitstr_t *ret_bitmap = NULL, *tmp_bitmap = NULL;
int total_node_cnt;
resv_select_t orig_resv_select = { 0 };
if (slurm_conf.debug_flags & DEBUG_FLAG_RESERVATION) {
orig_resv_select.node_bitmap =
bit_copy(resv_select->node_bitmap);
if (resv_select->core_bitmap)
orig_resv_select.core_bitmap =
bit_copy(resv_select->core_bitmap);
}
total_node_cnt = bit_set_count(resv_select->node_bitmap);
if (total_node_cnt < node_cnt) {
verbose("%s: reservation %s requests %d of %d nodes. Reducing requested node count.",
__func__, resv_desc_ptr->name, node_cnt,
total_node_cnt);
node_cnt = total_node_cnt;
}
if ((total_node_cnt == node_cnt) &&
(resv_desc_ptr->flags & RESERVE_FLAG_IGN_JOBS)) {
log_flag(RESERVATION, "%s: reservation %s requests all %d nodes",
__func__, resv_desc_ptr->name, total_node_cnt);
ret_bitmap = _resv_select(resv_desc_ptr, resv_select);
goto fini;
} else if ((node_cnt == 0) &&
(resv_desc_ptr->core_cnt == NO_VAL) &&
(resv_desc_ptr->flags & RESERVE_FLAG_ANY_NODES)) {
log_flag(RESERVATION, "%s: reservation %s requests any of all %d nodes",
__func__, resv_desc_ptr->name, total_node_cnt);
ret_bitmap = bit_alloc(bit_size(resv_select->node_bitmap));
goto fini;
}
orig_bitmap = bit_copy(resv_select->node_bitmap);
job_iterator = list_iterator_create(job_list);
while ((job_ptr = list_next(job_iterator))) {
if (!IS_JOB_RUNNING(job_ptr) && !IS_JOB_SUSPENDED(job_ptr))
continue;
if (job_ptr->end_time < resv_desc_ptr->start_time)
continue;
if (resv_desc_ptr->core_cnt == NO_VAL) {
bit_and_not(resv_select->node_bitmap, job_ptr->node_bitmap);
} else if (!(resv_desc_ptr->flags & RESERVE_FLAG_IGN_JOBS)) {
/*
* _check_job_compatibility will remove nodes and cores
* from the available bitmaps if those resources are
* being used by jobs. Don't do this if the IGNORE_JOBS
* flag is set.
*/
_check_job_compatibility(job_ptr, resv_select);
}
}
list_iterator_destroy(job_iterator);
total_node_cnt = bit_set_count(resv_select->node_bitmap);
if (total_node_cnt >= node_cnt) {
/*
* NOTE: _resv_select() does NOT preserve
* resv_select->node_bitmap,
* so we do that here and other calls to that function.
*/
save_bitmap = bit_copy(resv_select->node_bitmap);
ret_bitmap = _resv_select(resv_desc_ptr, resv_select);
if (ret_bitmap)
goto fini;
bit_or(resv_select->node_bitmap, save_bitmap);
FREE_NULL_BITMAP(save_bitmap);
}
/* Next: Try to reserve nodes that will be allocated to a limited
* number of running jobs. We could sort the jobs by priority, QOS,
* size or other criterion if desired. Right now we just go down
* the unsorted job list. */
if (resv_desc_ptr->flags & RESERVE_FLAG_IGN_JOBS) {
job_iterator = list_iterator_create(job_list);
while ((job_ptr = list_next(job_iterator))) {
if (!IS_JOB_RUNNING(job_ptr) &&
!IS_JOB_SUSPENDED(job_ptr))
continue;
if (job_ptr->end_time < resv_desc_ptr->start_time)
continue;
tmp_bitmap = bit_copy(orig_bitmap);
bit_and(tmp_bitmap, job_ptr->node_bitmap);
if (bit_set_count(tmp_bitmap) > 0)
bit_or(resv_select->node_bitmap, tmp_bitmap);
total_node_cnt = bit_set_count(
resv_select->node_bitmap);
if (total_node_cnt >= node_cnt) {
save_bitmap = bit_copy(
resv_select->node_bitmap);
ret_bitmap = _resv_select(
resv_desc_ptr, resv_select);
if (!ret_bitmap) {
bit_or(resv_select->node_bitmap,
save_bitmap);
FREE_NULL_BITMAP(save_bitmap);
}
}
FREE_NULL_BITMAP(tmp_bitmap);
if (ret_bitmap)
break;
}
list_iterator_destroy(job_iterator);
}
fini: FREE_NULL_BITMAP(orig_bitmap);
FREE_NULL_BITMAP(save_bitmap);
if (slurm_conf.debug_flags & DEBUG_FLAG_RESERVATION) {
char *nodes[2] = {
(ret_bitmap ? bitmap2node_name(ret_bitmap) : NULL),
bitmap2node_name(orig_resv_select.node_bitmap),
};
char *cores[2] = {0};
if (resv_select->core_bitmap)
cores[0] = bit_fmt_full(resv_select->core_bitmap);
if (orig_resv_select.core_bitmap)
cores[1] = bit_fmt_full(orig_resv_select.core_bitmap);
log_flag(RESERVATION, "%s: reservation %s picked nodes:%s cores:%s from possible_nodes:%s used_cores:%s",
__func__, resv_desc_ptr->name,
((nodes[0] && nodes[0][0]) ? nodes[0] : "(NONE)"),
((cores[0] && cores[0][0]) ? cores[0] : "(NONE)"),
((nodes[1] && nodes[1][0]) ? nodes[1] : "(NONE)"),
((cores[1] && cores[1][0]) ? cores[1] : "(NONE)"));
xfree(nodes[0]);
xfree(nodes[1]);
xfree(cores[0]);
xfree(cores[1]);
_free_resv_select_members(&orig_resv_select);
}
return ret_bitmap;
}
/* Determine if a job has access to a reservation
* RET SLURM_SUCCESS if true, some error code otherwise */
static int _valid_job_access_resv(job_record_t *job_ptr,
slurmctld_resv_t *resv_ptr,
bool show_security_violation_error)
{
bool account_good = false, user_good = false;
int i;
if (!resv_ptr) {
info("Reservation name not found (%s)", job_ptr->resv_name);
return ESLURM_RESERVATION_INVALID;
}
if (resv_ptr->flags & RESERVE_FLAG_TIME_FLOAT) {
verbose("%s: %pJ attempting to use reservation %s with floating start time",
__func__, job_ptr, resv_ptr->name);
return ESLURM_RESERVATION_ACCESS;
}
if (validate_slurm_user(job_ptr->user_id))
return SLURM_SUCCESS;
/* Determine if we have access */
if (accounting_enforce & ACCOUNTING_ENFORCE_ASSOCS) {
char tmp_char[30];
slurmdb_assoc_rec_t *assoc;
if (!resv_ptr->assoc_list) {
if (resv_ptr->qos_list)
return SLURM_SUCCESS;
error("Reservation %s has no association list. "
"Checking user/account lists",
resv_ptr->name);
goto no_assocs;
}
if (!job_ptr->assoc_ptr) {
slurmdb_assoc_rec_t assoc_rec;
/* This should never be called, but just to be
* safe we will try to fill it in. */
memset(&assoc_rec, 0,
sizeof(slurmdb_assoc_rec_t));
assoc_rec.id = job_ptr->assoc_id;
if (assoc_mgr_fill_in_assoc(
acct_db_conn, &assoc_rec,
accounting_enforce,
(slurmdb_assoc_rec_t **)
&job_ptr->assoc_ptr, false))
goto end_it;
}
/* Check to see if the association is here or the parent
* association is listed in the valid associations. */
if (strchr(resv_ptr->assoc_list, '-')) {
assoc = job_ptr->assoc_ptr;
while (assoc) {
snprintf(tmp_char, sizeof(tmp_char), ",-%u,",
assoc->id);
if (xstrstr(resv_ptr->assoc_list, tmp_char))
goto end_it; /* explicitly denied */
assoc = assoc->usage->parent_assoc_ptr;
}
}
if (xstrstr(resv_ptr->assoc_list, ",1") ||
xstrstr(resv_ptr->assoc_list, ",2") ||
xstrstr(resv_ptr->assoc_list, ",3") ||
xstrstr(resv_ptr->assoc_list, ",4") ||
xstrstr(resv_ptr->assoc_list, ",5") ||
xstrstr(resv_ptr->assoc_list, ",6") ||
xstrstr(resv_ptr->assoc_list, ",7") ||
xstrstr(resv_ptr->assoc_list, ",8") ||
xstrstr(resv_ptr->assoc_list, ",9") ||
xstrstr(resv_ptr->assoc_list, ",0")) {
assoc = job_ptr->assoc_ptr;
while (assoc) {
snprintf(tmp_char, sizeof(tmp_char), ",%u,",
assoc->id);
if (xstrstr(resv_ptr->assoc_list, tmp_char))
return SLURM_SUCCESS;
assoc = assoc->usage->parent_assoc_ptr;
}
} else {
return SLURM_SUCCESS;
}
} else {
no_assocs: if ((resv_ptr->user_cnt == 0) ||
(resv_ptr->ctld_flags & RESV_CTLD_USER_NOT))
user_good = true;
for (i = 0; i < resv_ptr->user_cnt; i++) {
if (job_ptr->user_id == resv_ptr->user_list[i]) {
if (resv_ptr->ctld_flags & RESV_CTLD_USER_NOT)
user_good = false;
else
user_good = true;
break;
}
}
if (!user_good)
goto end_it;
if ((resv_ptr->user_cnt != 0) && (resv_ptr->account_cnt == 0))
return SLURM_SUCCESS;
if ((resv_ptr->account_cnt == 0) ||
(resv_ptr->ctld_flags & RESV_CTLD_ACCT_NOT))
account_good = true;
for (i=0; (i<resv_ptr->account_cnt) && job_ptr->account; i++) {
if (resv_ptr->account_list[i] &&
(xstrcmp(job_ptr->account,
resv_ptr->account_list[i]) == 0)) {
if (resv_ptr->ctld_flags & RESV_CTLD_ACCT_NOT)
account_good = false;
else
account_good = true;
break;
}
}
if (!account_good)
goto end_it;
return SLURM_SUCCESS;
}
end_it:
if (show_security_violation_error)
info("Security violation, uid=%u account=%s attempt to use reservation %s",
job_ptr->user_id, job_ptr->account, resv_ptr->name);
return ESLURM_RESERVATION_ACCESS;
}
/*
* 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;
}
}
return rc;
}
/*
* Determine if a job can start now based only upon reservations
*
* IN job_ptr - job to test
* RET SLURM_SUCCESS if runable now, otherwise an error code
*/
extern int job_test_resv_now(job_record_t *job_ptr)
{
slurmctld_resv_t * resv_ptr;
time_t now;
int rc;
if (job_ptr->resv_name == NULL)
return SLURM_SUCCESS;
if (!job_ptr->resv_ptr) {
rc = validate_job_resv(job_ptr);
return rc;
}
resv_ptr = job_ptr->resv_ptr;
rc = _valid_job_access_resv(job_ptr, resv_ptr, true);
if (rc != SLURM_SUCCESS)
return rc;
if (resv_ptr->flags & RESERVE_FLAG_FLEX)
return SLURM_SUCCESS;
now = time(NULL);
if (now < resv_ptr->start_time) {
/* reservation starts later */
return ESLURM_INVALID_TIME_VALUE;
}
if (now > resv_ptr->end_time) {
/* reservation ended earlier */
return ESLURM_RESERVATION_INVALID;
}
if ((resv_ptr->node_cnt == 0) &&
!(resv_ptr->flags & RESERVE_FLAG_ANY_NODES)) {
/* empty reservation treated like it will start later */
return ESLURM_INVALID_TIME_VALUE;
}
return SLURM_SUCCESS;
}
/*
* Note that a job is starting or finishing execution. If that job is associated
* with a reservation having the "Replace" flag, then remove that job's nodes
* from the reservation. Additional nodes will be added to the reservation from
* those currently available.
*/
extern void resv_replace_update(job_record_t *job_ptr)
{
slurmctld_resv_t *resv_ptr;
if (job_ptr->resv_name == NULL)
return;
if (!job_ptr->resv_ptr)
/* Don't check for error here, we are ok ignoring it */
(void)validate_job_resv(job_ptr);
resv_ptr = job_ptr->resv_ptr;
if (!resv_ptr || !resv_ptr->node_bitmap ||
(!(resv_ptr->ctld_flags & RESV_CTLD_FULL_NODE) &&
(resv_ptr->node_cnt > 1)) ||
!(resv_ptr->flags & RESERVE_FLAG_REPLACE) ||
(resv_ptr->flags & RESERVE_FLAG_SPEC_NODES) ||
(resv_ptr->flags & RESERVE_FLAG_STATIC) ||
(resv_ptr->flags & RESERVE_FLAG_MAINT))
return;
_resv_node_replace(resv_ptr);
}
/*
* Adjust a job's time_limit and end_time as needed to avoid using
* reserved resources. Don't go below job's time_min value.
*/
extern void job_time_adj_resv(job_record_t *job_ptr)
{
list_itr_t *iter;
slurmctld_resv_t * resv_ptr;
time_t now = time(NULL);
int32_t resv_begin_time;
iter = list_iterator_create(resv_list);
while ((resv_ptr = list_next(iter))) {
if (resv_ptr->end_time <= now)
(void)_advance_resv_time(resv_ptr);
if (job_ptr->resv_ptr == resv_ptr)
continue; /* authorized user of reservation */
if (resv_ptr->start_time <= now)
continue; /* already validated */
if (resv_ptr->start_time >= job_ptr->end_time)
continue; /* reservation starts after job ends */
if (!license_list_overlap(job_ptr->license_list,
resv_ptr->license_list) &&
((resv_ptr->node_bitmap == NULL) ||
(bit_overlap_any(resv_ptr->node_bitmap,
job_ptr->node_bitmap) == 0)))
continue; /* disjoint resources */
resv_begin_time = difftime(resv_ptr->start_time, now) / 60;
job_ptr->time_limit = MIN(job_ptr->time_limit,resv_begin_time);
}
list_iterator_destroy(iter);
job_ptr->time_limit = MAX(job_ptr->time_limit, job_ptr->time_min);
job_end_time_reset(job_ptr);
}
/*
* For a given license_list, return the total count of licenses of the
* specified id
*/
static int _license_cnt(list_t *license_list, licenses_id_t id)
{
int lic_cnt = 0;
licenses_t *license_ptr;
if (license_list == NULL)
return lic_cnt;
license_ptr = license_find_rec_by_id(license_list, id);
if (license_ptr)
lic_cnt = license_ptr->total;
return lic_cnt;
}
/*
* get the run time of a job, in seconds
* job_ptr IN - pointer to the job record
* reboot IN - true if node reboot required
*/
static uint32_t _get_job_duration(job_record_t *job_ptr, bool reboot)
{
uint32_t duration;
uint16_t time_slices = 1;
if (job_ptr->time_limit == INFINITE)
duration = YEAR_SECONDS;
else if (job_ptr->time_limit != NO_VAL)
duration = (job_ptr->time_limit * 60);
else { /* partition time limit */
if (job_ptr->part_ptr->max_time == INFINITE)
duration = YEAR_SECONDS;
else
duration = (job_ptr->part_ptr->max_time * 60);
}
if (job_ptr->part_ptr)
time_slices = job_ptr->part_ptr->max_share & ~SHARED_FORCE;
if ((duration != YEAR_SECONDS) && (time_slices > 1) &&
(slurm_conf.preempt_mode & PREEMPT_MODE_GANG)) {
/* FIXME: Ideally we figure out how many jobs are actually
* time-slicing on each node rather than using the maximum
* value. */
duration *= time_slices;
}
/* FIXME: reboot and sending it to this function needs to be removed */
/* if (reboot) */
/* duration += node_features_g_boot_time(); */
return duration;
}
static void _add_bb_resv(burst_buffer_info_msg_t **bb_resv, char *plugin,
char *type, uint64_t cnt)
{
burst_buffer_info_t *bb_array;
burst_buffer_pool_t *pool_ptr;
int i;
if (*bb_resv == NULL)
*bb_resv = xmalloc(sizeof(burst_buffer_info_msg_t));
for (i = 0, bb_array = (*bb_resv)->burst_buffer_array;
i < (*bb_resv)->record_count; i++) {
if (!xstrcmp(plugin, bb_array->name))
break;
}
if (i >= (*bb_resv)->record_count) {
(*bb_resv)->record_count++;
(*bb_resv)->burst_buffer_array = xrealloc(
(*bb_resv)->burst_buffer_array,
sizeof(burst_buffer_info_t) * (*bb_resv)->record_count);
bb_array = (*bb_resv)->burst_buffer_array +
(*bb_resv)->record_count - 1;
bb_array->name = xstrdup(plugin);
}
if (type == NULL) {
bb_array->used_space += cnt;
return;
}
for (i = 0, pool_ptr = bb_array->pool_ptr; i < bb_array->pool_cnt; i++){
if ((pool_ptr->name == NULL) || !xstrcmp(type, pool_ptr->name))
break;
}
if (i >= bb_array->pool_cnt) {
bb_array->pool_cnt++;
bb_array->pool_ptr = xrealloc(bb_array->pool_ptr,
sizeof(burst_buffer_pool_t) *
bb_array->pool_cnt);
pool_ptr = bb_array->pool_ptr + bb_array->pool_cnt - 1;
pool_ptr->name = xstrdup(type);
}
pool_ptr->used_space += cnt;
}
static void _update_bb_resv(burst_buffer_info_msg_t **bb_resv, char *bb_spec)
{
uint64_t cnt, mult;
char *end_ptr = NULL, *unit = NULL;
char *sep, *tmp_spec, *tok, *plugin, *type;
if ((bb_spec == NULL) || (bb_spec[0] == '\0'))
return;
tmp_spec = xstrdup(bb_spec);
tok = strtok_r(tmp_spec, ",", &end_ptr);
while (tok) {
if (!xstrncmp(tok, "datawarp:", 9)) {
plugin = "datawarp";
tok +=9;
} else if (!xstrncmp(tok, "generic:", 8)) {
plugin = "generic";
tok += 8;
} else
plugin = NULL;
sep = strchr(tok, ':');
if (sep) {
type = tok;
sep[0] = '\0';
tok = sep + 1;
} else {
type = NULL;
}
cnt = (uint64_t) strtoull(tok, &unit, 10);
if (!xstrcasecmp(unit, "n") ||
!xstrcasecmp(unit, "node") ||
!xstrcasecmp(unit, "nodes")) {
type = "nodes"; /* Cray node spec format */
} else if ((mult = suffix_mult(unit)) != NO_VAL64) {
cnt *= mult;
}
if (cnt)
_add_bb_resv(bb_resv, plugin, type, cnt);
tok = strtok_r(NULL, ",", &end_ptr);
}
xfree(tmp_spec);
}
/*
* Determine how many burst buffer resources the specified job is prevented
* from using due to reservations
*
* IN job_ptr - job to test
* IN when - when the job is expected to start
* IN reboot - true if node reboot required to start job
* RET burst buffer reservation structure, call
* slurm_free_burst_buffer_info_msg() to free
*/
extern burst_buffer_info_msg_t *job_test_bb_resv(job_record_t *job_ptr,
time_t when, bool reboot)
{
slurmctld_resv_t * resv_ptr;
time_t job_start_time, job_end_time, now = time(NULL);
time_t job_end_time_use;
burst_buffer_info_msg_t *bb_resv = NULL;
list_itr_t *iter;
if ((job_ptr->burst_buffer == NULL) ||
(job_ptr->burst_buffer[0] == '\0'))
return bb_resv;
job_start_time = when;
job_end_time = when + _get_job_duration(job_ptr, reboot);
iter = list_iterator_create(resv_list);
while ((resv_ptr = list_next(iter))) {
if (resv_ptr->end_time <= now)
(void)_advance_resv_time(resv_ptr);
if (reboot)
job_end_time_use =
job_end_time + resv_ptr->boot_time;
else
job_end_time_use = job_end_time;
if ((resv_ptr->start_time >= job_end_time_use) ||
(resv_ptr->end_time <= job_start_time))
continue; /* reservation at different time */
if ((resv_ptr->burst_buffer == NULL) ||
(resv_ptr->burst_buffer[0] == '\0'))
continue; /* reservation has no burst buffers */
if (!xstrcmp(job_ptr->resv_name, resv_ptr->name))
continue; /* job can use this reservation */
_update_bb_resv(&bb_resv, resv_ptr->burst_buffer);
}
list_iterator_destroy(iter);
return bb_resv;
}
/*
* Determine how many licenses of the give type the specified job is
* prevented from using due to reservations
*
* IN job_ptr - job to test
* IN id - id of license
* IN when - when the job is expected to start
* IN reboot - true if node reboot required to start job
* RET number of licenses of this type the job is prevented from using
*/
extern int job_test_lic_resv(job_record_t *job_ptr, licenses_id_t id,
time_t when, bool reboot)
{
slurmctld_resv_t * resv_ptr;
time_t job_start_time, job_end_time, now = time(NULL);
time_t job_end_time_use;
list_itr_t *iter;
int resv_cnt = 0;
job_start_time = when;
job_end_time = when + _get_job_duration(job_ptr, reboot);
iter = list_iterator_create(resv_list);
while ((resv_ptr = list_next(iter))) {
if (resv_ptr->end_time <= now)
(void)_advance_resv_time(resv_ptr);
if (reboot)
job_end_time_use =
job_end_time + resv_ptr->boot_time;
else
job_end_time_use = job_end_time;
if ((resv_ptr->start_time >= job_end_time_use) ||
(resv_ptr->end_time <= job_start_time))
continue; /* reservation at different time */
if (job_ptr->resv_name &&
(xstrcmp(job_ptr->resv_name, resv_ptr->name) == 0))
continue; /* job can use this reservation */
resv_cnt += _license_cnt(resv_ptr->license_list, id);
}
list_iterator_destroy(iter);
/* info("%pJ blocked from %d licenses:%u",
job_ptr, resv_cnt, id.lic_id); */
return resv_cnt;
}
static void _get_rel_start_end(slurmctld_resv_t *resv_ptr, time_t now,
time_t *start_relative, time_t *end_relative)
{
xassert(resv_ptr);
xassert(start_relative);
xassert(end_relative);
if (resv_ptr->flags & RESERVE_FLAG_TIME_FLOAT) {
*start_relative = resv_ptr->start_time + now;
if (resv_ptr->duration == INFINITE)
*end_relative = *start_relative + YEAR_SECONDS;
else if (resv_ptr->duration && (resv_ptr->duration != NO_VAL)) {
*end_relative =
*start_relative + resv_ptr->duration * 60;
} else {
*end_relative = resv_ptr->end_time;
if (*start_relative > *end_relative)
*start_relative = *end_relative;
}
} else {
if (resv_ptr->end_time <= now)
(void)_advance_resv_time(resv_ptr);
*start_relative = resv_ptr->start_time_first;
*end_relative = resv_ptr->end_time;
}
}
extern int job_test_resv(job_record_t *job_ptr, time_t *when,
bool move_time, bitstr_t **node_bitmap,
resv_exc_t *resv_exc_ptr, bool *resv_overlap,
bool reboot)
{
slurmctld_resv_t *resv_ptr = NULL, *res2_ptr;
time_t job_start_time, job_end_time, job_end_time_use, lic_resv_time;
time_t start_relative, end_relative;
time_t now = time(NULL);
list_itr_t *iter;
int i, rc = SLURM_SUCCESS, rc2;
*resv_overlap = false; /* initialize to false */
job_start_time = *when;
job_end_time = *when + _get_job_duration(job_ptr, reboot);
*node_bitmap = (bitstr_t *) NULL;
if (job_ptr->resv_name) {
if (!job_ptr->resv_ptr) {
rc2 = validate_job_resv(job_ptr);
if (rc2 != SLURM_SUCCESS)
return rc2;
}
resv_ptr = job_ptr->resv_ptr;
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.
*/
if (job_ptr->resv_id != resv_ptr->resv_id) {
job_ptr->resv_id = resv_ptr->resv_id;
/*
* Update the database if not using a magnetic
* reservation
*/
if (!(job_ptr->bit_flags & JOB_MAGNETIC))
jobacct_storage_g_job_start(
acct_db_conn, job_ptr);
}
if (resv_ptr->flags & RESERVE_FLAG_FLEX) {
/* Job not bound to reservation nodes or time */
*node_bitmap = node_conf_get_active_bitmap();
} else {
if (resv_ptr->end_time <= now)
(void)_advance_resv_time(resv_ptr);
if (*when < resv_ptr->start_time) {
/* reservation starts later */
*when = resv_ptr->start_time;
return ESLURM_INVALID_TIME_VALUE;
}
if ((resv_ptr->node_cnt == 0) &&
(!(resv_ptr->flags & RESERVE_FLAG_ANY_NODES))) {
/*
* empty reservation treated like it will
* start later
*/
*when = now + 600;
return ESLURM_INVALID_TIME_VALUE;
}
if (*when > resv_ptr->end_time) {
/* reservation ended earlier */
*when = resv_ptr->end_time;
if ((now > resv_ptr->end_time) ||
((job_ptr->details) &&
(job_ptr->details->begin_time >
resv_ptr->end_time))) {
debug("%s: Holding %pJ, expired reservation %s",
__func__, job_ptr, resv_ptr->name);
job_ptr->priority = 0; /* admin hold */
}
return ESLURM_RESERVATION_INVALID;
}
if (job_ptr->details->req_node_bitmap &&
(!(resv_ptr->flags & RESERVE_FLAG_ANY_NODES)) &&
!bit_super_set(job_ptr->details->req_node_bitmap,
resv_ptr->node_bitmap)) {
return ESLURM_RESERVATION_INVALID;
}
if (resv_ptr->flags & RESERVE_FLAG_ANY_NODES) {
*node_bitmap = node_conf_get_active_bitmap();
} else {
*node_bitmap = bit_copy(resv_ptr->node_bitmap);
}
}
/* The job can only run on nodes in the partition */
bit_and(*node_bitmap, job_ptr->part_ptr->node_bitmap);
/*
* if there are any overlapping reservations, we need to
* prevent the job from using those nodes (e.g. MAINT nodes)
*/
iter = list_iterator_create(resv_list);
while ((res2_ptr = list_next(iter))) {
if (reboot)
job_end_time_use =
job_end_time + res2_ptr->boot_time;
else
job_end_time_use = job_end_time;
_get_rel_start_end(
res2_ptr, now, &start_relative, &end_relative);
if ((resv_ptr->flags & RESERVE_FLAG_MAINT) ||
((resv_ptr->flags & RESERVE_FLAG_OVERLAP) &&
!(res2_ptr->flags & RESERVE_FLAG_MAINT)) ||
(res2_ptr == resv_ptr) ||
(res2_ptr->node_bitmap == NULL) ||
(start_relative >= job_end_time_use) ||
(end_relative <= job_start_time) ||
(!(res2_ptr->ctld_flags & RESV_CTLD_FULL_NODE)))
continue;
if (bit_overlap_any(*node_bitmap,
res2_ptr->node_bitmap)) {
log_flag(RESERVATION, "%s: reservation %s overlaps %s with %u nodes",
__func__, resv_ptr->name,
res2_ptr->name,
bit_overlap(*node_bitmap,
res2_ptr->node_bitmap));
*resv_overlap = true;
bit_and_not(*node_bitmap,res2_ptr->node_bitmap);
}
}
list_iterator_destroy(iter);
if (slurm_conf.debug_flags & DEBUG_FLAG_RESERVATION) {
char *nodes = bitmap2node_name(*node_bitmap);
verbose("%s: %pJ reservation:%s nodes:%s",
__func__, job_ptr, job_ptr->resv_name, nodes);
xfree(nodes);
}
/*
* if reservation is using just partial nodes, this returns
* coremap to exclude
*/
if (resv_ptr->core_bitmap && resv_exc_ptr &&
!(resv_ptr->flags & RESERVE_FLAG_FLEX) ) {
free_core_array(&resv_exc_ptr->exc_cores);
resv_exc_ptr->core_bitmap =
bit_copy(resv_ptr->core_bitmap);
bit_not(resv_exc_ptr->core_bitmap);
resv_exc_ptr->exc_cores =
core_bitmap_to_array(resv_exc_ptr->core_bitmap);
resv_exc_ptr->gres_list_inc =
gres_job_state_list_dup(
resv_ptr->gres_list_alloc);
resv_exc_ptr->gres_list_exc = NULL;
resv_exc_ptr->gres_js_exc = NULL;
resv_exc_ptr->gres_js_inc = NULL;
}
return SLURM_SUCCESS;
}
job_ptr->resv_ptr = NULL; /* should be redundant */
*node_bitmap = node_conf_get_active_bitmap();
if (list_count(resv_list) == 0)
return SLURM_SUCCESS;
/*
* Job has no reservation, try to find time when this can
* run and get it's required nodes (if any)
*/
for (i = 0; ; i++) {
lic_resv_time = (time_t) 0;
iter = list_iterator_create(resv_list);
while ((resv_ptr = list_next(iter))) {
_get_rel_start_end(
resv_ptr, now, &start_relative, &end_relative);
if (reboot)
job_end_time_use =
job_end_time + resv_ptr->boot_time;
else
job_end_time_use = job_end_time;
if ((start_relative >= job_end_time_use) ||
(end_relative <= job_start_time))
continue;
/*
* FIXME: This only tracks when ANY licenses required
* by the job are freed by any reservation without
* counting them, so the results are not accurate.
*/
if (license_list_overlap(job_ptr->license_list,
resv_ptr->license_list)) {
if ((lic_resv_time == (time_t) 0) ||
(lic_resv_time > resv_ptr->end_time))
lic_resv_time = resv_ptr->end_time;
}
if (resv_ptr->node_bitmap == NULL)
continue;
/*
* Check if we are able to use this reservation's
* resources even though we didn't request it.
*/
if (resv_ptr->max_start_delay &&
(job_ptr->warn_time <= resv_ptr->max_start_delay) &&
(job_ptr->warn_flags & KILL_JOB_RESV)) {
continue;
}
if (resv_ptr->flags & RESERVE_FLAG_ALL_NODES ||
((resv_ptr->flags & RESERVE_FLAG_PART_NODES) &&
job_ptr->part_ptr == resv_ptr->part_ptr) ||
((resv_ptr->flags & RESERVE_FLAG_MAINT) &&
job_ptr->part_ptr &&
(bit_super_set(job_ptr->part_ptr->node_bitmap,
resv_ptr->node_bitmap)))) {
rc = ESLURM_RESERVATION_MAINT;
if (move_time)
*when = resv_ptr->end_time;
break;
}
if (job_ptr->details->req_node_bitmap &&
bit_overlap_any(job_ptr->details->req_node_bitmap,
resv_ptr->node_bitmap) &&
(!resv_ptr->tres_str ||
job_ptr->details->whole_node &
WHOLE_NODE_REQUIRED)) {
if (move_time)
*when = resv_ptr->end_time;
rc = ESLURM_NODES_BUSY;
break;
}
if (IS_JOB_WHOLE_TOPO(job_ptr)) {
bitstr_t *efctv_bitmap =
bit_copy(resv_ptr->node_bitmap);
topology_g_whole_topo(efctv_bitmap,
job_ptr->part_ptr
->topology_idx);
log_flag(RESERVATION, "%s: %pJ will can not share topology with %s",
__func__, job_ptr, resv_ptr->name);
bit_and_not(*node_bitmap, efctv_bitmap);
FREE_NULL_BITMAP(efctv_bitmap);
} else if ((resv_ptr->ctld_flags &
RESV_CTLD_FULL_NODE) ||
(job_ptr->details->whole_node &
WHOLE_NODE_REQUIRED)) {
log_flag(RESERVATION, "%s: reservation %s uses full nodes or %pJ will not share nodes",
__func__, resv_ptr->name, job_ptr);
bit_and_not(*node_bitmap, resv_ptr->node_bitmap);
} else {
log_flag(RESERVATION, "%s: reservation %s uses partial nodes",
__func__, resv_ptr->name);
if (resv_ptr->core_bitmap == NULL) {
;
} else if (!resv_exc_ptr) {
error("%s: resv_exc_ptr is NULL",
__func__);
} else if (!resv_exc_ptr->core_bitmap) {
resv_exc_ptr->core_bitmap =
bit_copy(resv_ptr->core_bitmap);
} else {
bit_or(resv_exc_ptr->core_bitmap,
resv_ptr->core_bitmap);
}
}
if (resv_exc_ptr)
_addto_gres_list_exc(
&resv_exc_ptr->gres_list_exc,
resv_ptr->gres_list_alloc);
if(!job_ptr->part_ptr ||
bit_overlap_any(job_ptr->part_ptr->node_bitmap,
resv_ptr->node_bitmap)) {
*resv_overlap = true;
continue;
}
}
list_iterator_destroy(iter);
if (resv_exc_ptr) {
free_core_array(&resv_exc_ptr->exc_cores);
if (resv_exc_ptr->core_bitmap) {
resv_exc_ptr->exc_cores = core_bitmap_to_array(
resv_exc_ptr->core_bitmap);
}
}
if ((rc == SLURM_SUCCESS) && move_time) {
if (license_job_test(job_ptr, job_start_time, reboot)
== EAGAIN) {
/*
* Need to postpone for licenses. Time returned
* is best case; first reservation with those
* licenses ends.
*/
rc = ESLURM_NODES_BUSY;
if (lic_resv_time > *when)
*when = lic_resv_time;
}
}
if (rc == SLURM_SUCCESS)
break;
/*
* rc == ESLURM_NODES_BUSY or rc == ESLURM_RESERVATION_MAINT
* above "break"
*/
if (move_time && (i < 10)) { /* Retry for later start time */
job_start_time = *when;
job_end_time = *when +
_get_job_duration(job_ptr, reboot);
node_conf_set_all_active_bits(*node_bitmap);
rc = SLURM_SUCCESS;
continue;
}
FREE_NULL_BITMAP(*node_bitmap);
break; /* Give up */
}
return rc;
}
static int _update_resv_group_uid_access_list(void *x, void *arg)
{
slurmctld_resv_t *resv_ptr = (slurmctld_resv_t *)x;
int *updated = (int *)arg;
int user_cnt = 0;
uid_t *tmp_uids;
if (!resv_ptr->groups)
return 0;
tmp_uids = get_groups_members(resv_ptr->groups, &user_cnt);
/*
* If the lists are different sizes clearly we are different.
* If the memory isn't the same they are different as well
* as the lists will be in the same order.
*/
if ((resv_ptr->user_cnt != user_cnt) ||
memcmp(tmp_uids, resv_ptr->user_list,
sizeof(*tmp_uids) * user_cnt)) {
char *old_assocs = xstrdup(resv_ptr->assoc_list);
resv_ptr->user_cnt = user_cnt;
xfree(resv_ptr->user_list);
resv_ptr->user_list = tmp_uids;
tmp_uids = NULL;
/* Now update the associations to match */
(void) _set_access(resv_ptr);
/* Now see if something really did change */
if (!slurm_with_slurmdbd() ||
xstrcmp(old_assocs, resv_ptr->assoc_list))
*updated = 1;
xfree(old_assocs);
}
xfree(tmp_uids);
return 0;
}
/*
* Determine the time of the first reservation to end after some time.
* return zero of no reservation ends after that time.
* IN start_time - look for reservations ending after this time
* IN resolution - return end_time with the given resolution, this is important
* to avoid additional try_later attempts from backfill when we have multiple
* reservations with very close end time.
* RET the reservation end time or zero of none found
*/
extern time_t find_resv_end(time_t start_time, int resolution)
{
list_itr_t *iter;
slurmctld_resv_t *resv_ptr;
time_t end_time = 0;
if (!resv_list)
return end_time;
iter = list_iterator_create(resv_list);
while ((resv_ptr = list_next(iter))) {
if (start_time > resv_ptr->end_time)
continue;
if ((end_time == 0) || (resv_ptr->end_time < end_time))
end_time = resv_ptr->end_time;
}
list_iterator_destroy(iter);
/* Round-up returned time to given resolution */
if (resolution > 0) {
end_time = ROUNDUP(end_time, resolution);
end_time *= resolution;
}
return end_time;
}
/* Test a particular job for valid reservation
* and refill job_run_cnt/job_pend_cnt */
static int _job_resv_check(void *x, void *arg)
{
job_record_t *job_ptr = (job_record_t *) x;
if (!job_ptr->resv_ptr && !job_ptr->resv_list)
return SLURM_SUCCESS;
if (IS_JOB_PENDING(job_ptr)) {
if (job_ptr->resv_list) {
list_for_each(job_ptr->resv_list, _update_resv_pend_cnt,
NULL);
} else {
xassert(job_ptr->resv_ptr->magic == RESV_MAGIC);
job_ptr->resv_ptr->job_pend_cnt++;
}
} else if (!IS_JOB_FINISHED(job_ptr) && job_ptr->resv_ptr) {
xassert(job_ptr->resv_ptr->magic == RESV_MAGIC);
job_ptr->resv_ptr->job_run_cnt++;
}
return SLURM_SUCCESS;
}
static int _set_job_resvid(void *object, void *arg)
{
job_record_t *job_ptr = (job_record_t *) object;
slurmctld_resv_t *resv_ptr = (slurmctld_resv_t *)arg;
if ((job_ptr->resv_ptr != resv_ptr) || !IS_JOB_PENDING(job_ptr))
return SLURM_SUCCESS;
log_flag(RESERVATION, "updating %pJ to correct resv_id (%u->%u) of reoccurring reservation '%s'",
job_ptr, job_ptr->resv_id, resv_ptr->resv_id, resv_ptr->name);
job_ptr->resv_id = resv_ptr->resv_id;
/* Update the database */
jobacct_storage_g_job_start(acct_db_conn, job_ptr);
return SLURM_SUCCESS;
}
static void *_update_resv_jobs(void *arg)
{
slurmctld_resv_t *resv_ptr;
uint32_t resv_id = *(uint32_t *)arg;
/* get the job write lock and node and config read lock */
slurmctld_lock_t job_write_lock = {
.conf = READ_LOCK,
.job = WRITE_LOCK,
.node = READ_LOCK,
};
lock_slurmctld(job_write_lock);
if (!resv_list) {
unlock_slurmctld(job_write_lock);
return NULL;
}
resv_ptr = list_find_first(resv_list, _find_resv_id, &resv_id);
if (!resv_ptr) {
unlock_slurmctld(job_write_lock);
return NULL;
}
list_for_each(job_list, _set_job_resvid, resv_ptr);
unlock_slurmctld(job_write_lock);
return NULL;
}
/* Advance a expired reservation's time stamps one day or one week
* as appropriate. */
static int _advance_resv_time(slurmctld_resv_t *resv_ptr)
{
time_t now;
struct tm tm;
int day_cnt = 0, hour_cnt = 0;
int rc = SLURM_ERROR;
/* Make sure we have node write locks. */
xassert(verify_lock(NODE_LOCK, WRITE_LOCK));
if (resv_ptr->flags & RESERVE_FLAG_TIME_FLOAT)
return rc; /* Not applicable */
if (resv_ptr->flags & RESERVE_FLAG_HOURLY) {
hour_cnt = 1;
} else if (resv_ptr->flags & RESERVE_FLAG_DAILY) {
day_cnt = 1;
} else if (resv_ptr->flags & RESERVE_FLAG_WEEKDAY) {
now = time(NULL);
localtime_r(&now, &tm);
if (tm.tm_wday == 5) /* Friday */
day_cnt = 3;
else if (tm.tm_wday == 6) /* Saturday */
day_cnt = 2;
else
day_cnt = 1;
} else if (resv_ptr->flags & RESERVE_FLAG_WEEKEND) {
now = time(NULL);
localtime_r(&now, &tm);
if (tm.tm_wday == 0) /* Sunday */
day_cnt = 6;
else if (tm.tm_wday == 6) /* Saturday */
day_cnt = 1;
else
day_cnt = 6 - tm.tm_wday;
} else if (resv_ptr->flags & RESERVE_FLAG_WEEKLY) {
day_cnt = 7;
}
if (day_cnt || hour_cnt) {
char *tmp_str = NULL;
if (!(resv_ptr->ctld_flags & RESV_CTLD_PROLOG))
_run_script(slurm_conf.resv_prolog, resv_ptr, false);
if (!(resv_ptr->ctld_flags & RESV_CTLD_EPILOG))
_run_script(slurm_conf.resv_epilog, resv_ptr, true);
/*
* Repeated reservations need a new reservation id. Try to get a
* new one and update the ID if successful.
*/
if (_generate_resv_id()) {
error("%s, Recurring reservation %s is being "
"rescheduled but has the same ID.",
__func__, resv_ptr->name);
} else {
resv_ptr->resv_id = top_suffix;
/*
* Update pending jobs for this reservation with the new
* reservation ID out of band.
*/
slurm_thread_create_detached(_update_resv_jobs,
&resv_ptr->resv_id);
}
if (hour_cnt)
xstrfmtcat(tmp_str, "%d hour%s",
hour_cnt, ((hour_cnt > 1) ? "s" : ""));
else if (day_cnt)
xstrfmtcat(tmp_str, "%d day%s",
day_cnt, ((day_cnt > 1) ? "s" : ""));
verbose("%s: reservation %s advanced by %s",
__func__, resv_ptr->name, tmp_str);
xfree(tmp_str);
resv_ptr->idle_start_time = 0;
resv_ptr->start_time = resv_ptr->start_time_first;
_advance_time(&resv_ptr->start_time, day_cnt, hour_cnt);
resv_ptr->start_time_prev = resv_ptr->start_time;
resv_ptr->start_time_first = resv_ptr->start_time;
_advance_time(&resv_ptr->end_time, day_cnt, hour_cnt);
resv_ptr->ctld_flags &= (~RESV_CTLD_PROLOG);
resv_ptr->ctld_flags &= (~RESV_CTLD_EPILOG);
_post_resv_create(resv_ptr);
last_resv_update = time(NULL);
schedule_resv_save();
rc = SLURM_SUCCESS;
} else {
log_flag(RESERVATION, "%s: skipping reservation %s for being advanced in time",
__func__, resv_ptr->name);
}
return rc;
}
static void _run_script(char *script, slurmctld_resv_t *resv_ptr,
bool is_resv_epilog)
{
uint32_t argc = 2;
char **argv;
char *name = is_resv_epilog ? "ResvEpilog" : "ResvProlog";
int timeout = is_resv_epilog ?
slurm_conf.epilog_timeout : slurm_conf.prolog_timeout;
if (!script || !script[0])
return;
if (access(script, X_OK) < 0) {
error("Invalid %s(%s): %m", name, script);
return;
}
argv = xcalloc(argc + 1, sizeof(*argv)); /* +1 to NULL-terminate */
argv[0] = script;
argv[1] = resv_ptr->name;
slurmscriptd_run_resv(script, argc, argv, timeout, name);
xfree(argv);
}
static int _resv_list_reset_cnt(void *x, void *arg)
{
slurmctld_resv_t *resv_ptr = (slurmctld_resv_t *) x;
resv_ptr->job_pend_cnt = 0;
resv_ptr->job_run_cnt = 0;
return 0;
}
/* Finish scan of all jobs for valid reservations
*
* Purge vestigial reservation records.
* Advance daily or weekly reservations that are no longer
* being actively used.
*/
extern void job_resv_check(void)
{
list_itr_t *iter;
slurmctld_resv_t *resv_ptr;
time_t now = time(NULL);
if (!resv_list)
return;
list_for_each(resv_list, _resv_list_reset_cnt, NULL);
list_for_each(job_list, _job_resv_check, NULL);
iter = list_iterator_create(resv_list);
while ((resv_ptr = list_next(iter))) {
if (resv_ptr->start_time <= now) {
if (resv_ptr->job_run_cnt || resv_ptr->job_pend_cnt) {
if ((resv_ptr->flags & RESERVE_FLAG_PURGE_COMP)
&& resv_ptr->idle_start_time)
log_flag(RESERVATION, "Resetting idle start time to zero on PURGE_COMP reservation %s due to active associated jobs",
resv_ptr->name);
resv_ptr->idle_start_time = 0;
} else if (!resv_ptr->idle_start_time) {
if (resv_ptr->flags & RESERVE_FLAG_PURGE_COMP)
log_flag(RESERVATION, "Marking idle start time to now on PURGE_COMP reservation %s",
resv_ptr->name);
resv_ptr->idle_start_time = now;
}
}
if ((resv_ptr->flags & RESERVE_FLAG_PURGE_COMP) &&
resv_ptr->idle_start_time &&
(resv_ptr->end_time > now) &&
(resv_ptr->purge_comp_time <=
(now - resv_ptr->idle_start_time))) {
char tmp_pct[40];
secs2time_str(resv_ptr->purge_comp_time,
tmp_pct, sizeof(tmp_pct));
info("Reservation %s has no more jobs for %s, ending it",
resv_ptr->name, tmp_pct);
(void)_post_resv_delete(resv_ptr);
/*
* If we are ending a reoccurring reservation advance
* it, otherwise delete it.
*/
if (!(resv_ptr->flags & RESERVE_REOCCURRING)) {
/*
* Reset time here for reoccurring reservations
* so we don't continually keep running this.
*/
resv_ptr->idle_start_time = 0;
if (!(resv_ptr->ctld_flags & RESV_CTLD_PROLOG))
_run_script(slurm_conf.resv_prolog,
resv_ptr, false);
if (!(resv_ptr->ctld_flags & RESV_CTLD_EPILOG))
_run_script(slurm_conf.resv_epilog,
resv_ptr, true);
/*
* Clear resv ptrs on finished jobs still
* pointing to this reservation.
*/
_clear_job_resv(resv_ptr);
list_delete_item(iter);
} else if (resv_ptr->start_time <= now) {
_advance_resv_time(resv_ptr);
}
last_resv_update = now;
schedule_resv_save();
continue;
}
if ((resv_ptr->end_time >= now) ||
(resv_ptr->duration && (resv_ptr->duration != NO_VAL) &&
(resv_ptr->flags & RESERVE_FLAG_TIME_FLOAT))) {
_validate_node_choice(resv_ptr);
continue;
}
if (!(resv_ptr->ctld_flags & RESV_CTLD_PROLOG) ||
!(resv_ptr->ctld_flags & RESV_CTLD_EPILOG))
continue;
(void)_advance_resv_time(resv_ptr);
if ((!resv_ptr->job_run_cnt ||
(resv_ptr->flags & RESERVE_FLAG_FLEX)) &&
!(resv_ptr->flags & RESERVE_REOCCURRING)) {
if (resv_ptr->job_pend_cnt) {
info("Purging vestigial reservation %s "
"with %u pending jobs",
resv_ptr->name, resv_ptr->job_pend_cnt);
} else {
debug("Purging vestigial reservation %s",
resv_ptr->name);
}
_clear_job_resv(resv_ptr);
list_delete_item(iter);
last_resv_update = now;
schedule_resv_save();
}
}
list_iterator_destroy(iter);
}
/*
* Send all reservations to accounting. Only needed at first registration
*/
extern int send_resvs_to_accounting(int db_rc)
{
list_itr_t *itr = NULL;
slurmctld_resv_t *resv_ptr;
slurmctld_lock_t node_write_lock = {
.node = WRITE_LOCK,
.part = READ_LOCK,
};
if (!resv_list)
return SLURM_SUCCESS;
lock_slurmctld(node_write_lock);
itr = list_iterator_create(resv_list);
while ((resv_ptr = list_next(itr))) {
if (db_rc == ACCOUNTING_FIRST_REG)
_post_resv_create(resv_ptr);
else if (db_rc == ACCOUNTING_NODES_CHANGE_DB) {
/*
* This makes it so we always get the correct node
* indexes in the database.
*/
slurmctld_resv_t tmp_resv = {0};
_post_resv_update(resv_ptr, &tmp_resv);
} else {
error("%s: unknown db_rc %d", __func__, db_rc);
break;
}
}
list_iterator_destroy(itr);
unlock_slurmctld(node_write_lock);
return SLURM_SUCCESS;
}
/*
* Set or clear NODE_STATE_MAINT for node_state as needed
* IN reset_all - if true, then re-initialize all node information for all
* reservations, but do not run any prologs or epilogs or count started
* reservations
* RET count of newly started reservations
*/
static int _set_node_maint_mode(bool reset_all, bitstr_t *node_down_bitmap)
{
int i, res_start_cnt = 0;
node_record_t *node_ptr;
uint32_t flags;
list_itr_t *iter;
slurmctld_resv_t *resv_ptr;
time_t now = time(NULL);
xassert(node_down_bitmap);
if (!resv_list)
return res_start_cnt;
flags = NODE_STATE_RES;
if (reset_all)
flags |= NODE_STATE_MAINT;
for (i = 0; (node_ptr = next_node(&i)); i++) {
node_ptr->node_state &= (~flags);
xfree(node_ptr->resv_name);
}
if (!reset_all) {
/* NODE_STATE_RES already cleared above,
* clear RESERVE_FLAG_MAINT for expired reservations */
iter = list_iterator_create(resv_list);
while ((resv_ptr = list_next(iter))) {
if ((resv_ptr->ctld_flags & RESV_CTLD_NODE_FLAGS_SET) &&
(resv_ptr->flags & RESERVE_FLAG_MAINT) &&
((now < resv_ptr->start_time) ||
(now >= resv_ptr->end_time ))) {
flags = NODE_STATE_MAINT;
resv_ptr->ctld_flags &=
(~RESV_CTLD_NODE_FLAGS_SET);
_set_nodes_flags(resv_ptr, now, flags,
reset_all, node_down_bitmap);
last_node_update = now;
}
}
list_iterator_destroy(iter);
}
/* Set NODE_STATE_RES and possibly NODE_STATE_MAINT for nodes in all
* currently active reservations */
iter = list_iterator_create(resv_list);
while ((resv_ptr = list_next(iter))) {
if ((now >= resv_ptr->start_time) &&
(now < resv_ptr->end_time )) {
flags = NODE_STATE_RES;
if (resv_ptr->flags & RESERVE_FLAG_MAINT)
flags |= NODE_STATE_MAINT;
resv_ptr->ctld_flags |= RESV_CTLD_NODE_FLAGS_SET;
_set_nodes_flags(resv_ptr, now, flags, reset_all,
node_down_bitmap);
last_node_update = now;
}
if (reset_all) /* Defer reservation prolog/epilog */
continue;
if ((resv_ptr->start_time <= now) &&
!(resv_ptr->ctld_flags & RESV_CTLD_PROLOG)) {
res_start_cnt++;
resv_ptr->ctld_flags |= RESV_CTLD_PROLOG;
_run_script(slurm_conf.resv_prolog, resv_ptr, false);
}
if ((resv_ptr->end_time <= now) &&
!(resv_ptr->ctld_flags & RESV_CTLD_EPILOG)) {
resv_ptr->ctld_flags |= RESV_CTLD_EPILOG;
_run_script(slurm_conf.resv_epilog, resv_ptr, true);
}
}
list_iterator_destroy(iter);
return res_start_cnt;
}
/*
* Set or clear NODE_STATE_MAINT for node_state as needed
* RET count of newly started reservations
*/
extern int set_node_maint_mode(void)
{
time_t now = time(NULL);
int result = 0;
bitstr_t *node_down_bitmap = bit_alloc(node_record_count);
result = _set_node_maint_mode(false, node_down_bitmap);
_flush_node_down_cache(node_down_bitmap, now);
FREE_NULL_BITMAP(node_down_bitmap);
return result;
}
/* checks if node within node_record_table_ptr is in maint reservation */
extern bool is_node_in_maint_reservation(int nodenum)
{
bool res = false;
list_itr_t *iter;
slurmctld_resv_t *resv_ptr;
time_t t;
if (nodenum < 0 || nodenum >= node_record_count || !resv_list)
return false;
t = time(NULL);
iter = list_iterator_create(resv_list);
while ((resv_ptr = list_next(iter))) {
if ((resv_ptr->flags & RESERVE_FLAG_MAINT) == 0)
continue;
if (! (t >= resv_ptr->start_time
&& t <= resv_ptr->end_time))
continue;
if (resv_ptr->node_bitmap &&
bit_test(resv_ptr->node_bitmap, nodenum)) {
res = true;
break;
}
}
list_iterator_destroy(iter);
return res;
}
extern void update_assocs_in_resvs(void)
{
slurmctld_resv_t *resv_ptr = NULL;
list_itr_t *iter = NULL;
slurmctld_lock_t node_write_lock = {
.node = WRITE_LOCK,
.part = READ_LOCK,
};
if (!resv_list) {
error("No reservation list given for updating associations");
return;
}
lock_slurmctld(node_write_lock);
iter = list_iterator_create(resv_list);
while ((resv_ptr = list_next(iter)))
_set_access(resv_ptr);
list_iterator_destroy(iter);
unlock_slurmctld(node_write_lock);
}
extern void update_part_nodes_in_resv(part_record_t *part_ptr)
{
list_itr_t *iter = NULL;
slurmctld_resv_t *resv_ptr = NULL;
xassert(part_ptr);
iter = list_iterator_create(resv_list);
while ((resv_ptr = list_next(iter))) {
if ((resv_ptr->flags & RESERVE_FLAG_PART_NODES) &&
(resv_ptr->partition != NULL) &&
(xstrcmp(resv_ptr->partition, part_ptr->name) == 0)) {
slurmctld_resv_t old_resv_ptr;
memset(&old_resv_ptr, 0, sizeof(slurmctld_resv_t));
old_resv_ptr.assoc_list = resv_ptr->assoc_list;
old_resv_ptr.flags = resv_ptr->flags;
old_resv_ptr.node_list = resv_ptr->node_list;
resv_ptr->node_list = NULL;
FREE_NULL_BITMAP(resv_ptr->node_bitmap);
resv_ptr->node_bitmap = bit_copy(part_ptr->node_bitmap);
resv_ptr->node_cnt = bit_set_count(resv_ptr->
node_bitmap);
resv_ptr->node_list = xstrdup(part_ptr->nodes);
old_resv_ptr.tres_str = resv_ptr->tres_str;
resv_ptr->tres_str = NULL;
_set_tres_cnt(resv_ptr, &old_resv_ptr);
old_resv_ptr.assoc_list = NULL;
xfree(old_resv_ptr.tres_str);
xfree(old_resv_ptr.node_list);
last_resv_update = time(NULL);
_set_boot_time(resv_ptr);
}
}
list_iterator_destroy(iter);
}
extern bool job_borrow_from_resv_check(job_record_t *job_ptr,
job_record_t *preemptor_ptr)
{
/*
* If this job is running in a reservation, but not belonging to the
* reservation directly.
*/
if (job_uses_max_start_delay_resv(preemptor_ptr) &&
(job_ptr->warn_flags & KILL_JOB_RESV) &&
job_ptr->node_bitmap &&
bit_overlap_any(job_ptr->node_bitmap,
preemptor_ptr->resv_ptr->node_bitmap))
return true;
return false;
}
extern bool job_uses_max_start_delay_resv(job_record_t *job_ptr)
{
if (job_ptr->resv_ptr && job_ptr->resv_ptr->max_start_delay &&
job_ptr->resv_ptr->node_bitmap)
return true;
return false;
}
static void _set_nodes_flags(slurmctld_resv_t *resv_ptr, time_t now,
uint32_t flags, bool reset_all,
bitstr_t *node_down_bitmap)
{
node_record_t *node_ptr;
uint32_t old_state;
bitstr_t *maint_node_bitmap = NULL;
slurmctld_resv_t *resv2_ptr;
xassert(node_down_bitmap);
if (!resv_ptr->node_bitmap) {
if ((resv_ptr->flags & RESERVE_FLAG_ANY_NODES) == 0) {
error("%s: reservation %s lacks a bitmap",
__func__, resv_ptr->name);
}
return;
}
if (!bit_set_count(resv_ptr->node_bitmap)) {
if ((resv_ptr->flags & RESERVE_FLAG_ANY_NODES) == 0) {
error("%s: reservation %s includes no nodes",
__func__, resv_ptr->name);
}
return;
}
if (!(resv_ptr->ctld_flags & RESV_CTLD_NODE_FLAGS_SET) && !reset_all &&
(resv_ptr->flags & RESERVE_FLAG_MAINT)) {
maint_node_bitmap = bit_alloc(node_record_count);
list_itr_t *iter = list_iterator_create(resv_list);
while ((resv2_ptr = list_next(iter))) {
if (resv_ptr != resv2_ptr &&
resv2_ptr->ctld_flags & RESV_CTLD_NODE_FLAGS_SET &&
resv2_ptr->flags & RESERVE_FLAG_MAINT &&
resv2_ptr->node_bitmap) {
bit_or(maint_node_bitmap,
resv2_ptr->node_bitmap);
}
}
list_iterator_destroy(iter);
}
for (int i = 0;
(node_ptr = next_node_bitmap(resv_ptr->node_bitmap, &i)); i++) {
old_state = node_ptr->node_state;
if (resv_ptr->ctld_flags & RESV_CTLD_NODE_FLAGS_SET)
node_ptr->node_state |= flags;
else if (!maint_node_bitmap || !bit_test(maint_node_bitmap, i))
node_ptr->node_state &= (~flags);
/* mark that this node is now down if maint mode flag changed */
bool state_change = ((old_state ^ node_ptr->node_state) &
NODE_STATE_MAINT) || reset_all;
if (state_change && (IS_NODE_DOWN(node_ptr) ||
IS_NODE_DRAIN(node_ptr) ||
IS_NODE_FAIL(node_ptr))) {
bit_set(node_down_bitmap, i);
}
xfree(node_ptr->resv_name);
if (IS_NODE_RES(node_ptr))
node_ptr->resv_name = xstrdup(resv_ptr->name);
}
FREE_NULL_BITMAP(maint_node_bitmap);
}
extern void job_resv_append_magnetic(job_queue_req_t *job_queue_req)
{
if (!magnetic_resv_list || !list_count(magnetic_resv_list))
return;
list_for_each(magnetic_resv_list, _queue_magnetic_resv,
job_queue_req);
}
extern void job_resv_clear_magnetic_flag(job_record_t *job_ptr)
{
if (!(job_ptr->bit_flags & JOB_MAGNETIC) ||
(job_ptr->job_state & JOB_RUNNING))
return;
xfree(job_ptr->resv_name);
job_ptr->resv_id = 0;
job_ptr->resv_ptr = NULL;
job_ptr->bit_flags &= (~JOB_MAGNETIC);
}
extern bool validate_resv_uid(char *resv_name, uid_t uid)
{
static time_t sched_update = 0;
static bool user_resv_delete = false;
slurmdb_assoc_rec_t assoc;
list_t *assoc_list = NULL;
assoc_mgr_lock_t locks = { .assoc = READ_LOCK };
bool found_it = false;
slurmctld_resv_t *resv_ptr;
/* Make sure we have node write locks. */
xassert(verify_lock(NODE_LOCK, WRITE_LOCK));
if (!resv_name)
return false;
if (sched_update != slurm_conf.last_update) {
if (xstrcasestr(slurm_conf.slurmctld_params,
"user_resv_delete"))
user_resv_delete = true;
else
user_resv_delete = false;
sched_update = slurm_conf.last_update;
}
if (!(resv_ptr = find_resv_name(resv_name)))
return false;
if ((!user_resv_delete) && !(resv_ptr->flags & RESERVE_FLAG_USER_DEL))
return false;
memset(&assoc, 0, sizeof(slurmdb_assoc_rec_t));
assoc.uid = uid;
assoc_list = list_create(NULL);
assoc_mgr_lock(&locks);
if (assoc_mgr_get_user_assocs(acct_db_conn, &assoc,
accounting_enforce, assoc_list)
!= SLURM_SUCCESS)
goto end_it;
if (_validate_user_access(resv_ptr, assoc_list, uid))
found_it = true;
end_it:
FREE_NULL_LIST(assoc_list);
assoc_mgr_unlock(&locks);
return found_it;
}
/*
* reservation_update_groups - reload the user_list of reservations
* with groups set
* IN force - if set then always reload the user_list
*/
extern void reservation_update_groups(int force)
{
static time_t last_update_time;
int updated = 0;
time_t temp_time;
DEF_TIMERS;
START_TIMER;
temp_time = get_group_tlm();
if (!force && (temp_time == last_update_time))
return;
debug2("Updating reservations group's uid access lists");
last_update_time = temp_time;
list_for_each(resv_list, _update_resv_group_uid_access_list, &updated);
/*
* Only update last_resv_update when changes made
*/
if (updated) {
debug2("%s: list updated, resetting last_resv_update time",
__func__);
last_resv_update = time(NULL);
}
END_TIMER2(__func__);
}
/*
* The following functions all deal with calculating the count of reserved
* licenses for a given license. (Iterating across all licenses is handled
* upstream of this function.)
* This is done by iterating across all reservations, checking if the
* reservation is currently active, and then if it matches the license to
* update we add the reserved license count in. This is O(reservations *
* licenses) which is not ideal, but the count of reservation and licenses on a
* system tends to be low enough to ignore this overhead, and go with the
* straightforward iterative solution presented here.
*/
static int _foreach_reservation_license(void *x, void *key)
{
licenses_t *resv_license = (licenses_t *) x;
licenses_t *license = (licenses_t *) key;
if (resv_license->id.lic_id == license->id.lic_id)
license->reserved += resv_license->total;
return 0;
}
static int _foreach_reservation_license_list(void *x, void *key)
{
slurmctld_resv_t *reservation = (slurmctld_resv_t *) x;
time_t now = time(NULL);
if (!reservation->license_list) {
/* reservation without licenses */
return 0;
} else if (reservation->flags & RESERVE_FLAG_FLEX) {
/*
* Treat FLEX reservations as always active
* and skip time bounds checks.
*/
;
} else if (now < reservation->start_time) {
/* reservation starts later */
return 0;
} else if (now > reservation->end_time) {
/* reservation ended earlier */
return 0;
}
list_for_each(reservation->license_list, _foreach_reservation_license,
key);
return 0;
}
extern void set_reserved_license_count(licenses_t *license)
{
license->reserved = 0;
list_for_each(resv_list, _foreach_reservation_license_list,
license);
}
extern int get_magnetic_resv_count(void)
{
xassert(magnetic_resv_list);
return list_count(magnetic_resv_list);
}