/*****************************************************************************\
 *  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 <limits.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/threadpool.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 nodes in overlapping reservations*/
#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
};

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 {
	list_t *allowed_parts_list;
	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;

typedef struct {
	char *access_str;
	bool check_denied;
	bool check_allowed;
	bool found_denied;
	bool found_allowed;
} foreach_check_assoc_access_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 _resv_node_replace(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;
	will_run_data_t will_run_data = {
		.start = resv_desc_ptr->start_time,
		.end = resv_desc_ptr->end_time,
	};

	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;

	/*
	 * Simple whole-node IGNORE_JOBS: resv_select->node_bitmap already
	 * reflects partition, features, and other filters. Any min_nodes
	 * of those bits is sufficient.
	 *
	 * Skip select_g_job_test(WILL_RUN). The code below
	 * rejects when job_ptr->start_time is after reservation start/now;
	 * WILL_RUN sets that when nodes are busy, but IGNORE_JOBS explicitly
	 * allows overlapping running work. Core- or GRES-based reservations
	 * must keep WILL_RUN for correct plugin accounting.
	 *
	 * Guards: RESERVE_FLAG_IGN_JOBS, whole-node (core_cnt unset), not a
	 * GRES reservation request, no per-select core bitmap (core resvs use
	 * the normal path with add_job_to_cores below).
	 */
	if ((resv_desc_ptr->flags & RESERVE_FLAG_IGN_JOBS) &&
	    (resv_desc_ptr->core_cnt == NO_VAL) &&
	    !(resv_desc_ptr->flags & RESERVE_FLAG_GRES_REQ) &&
	    !resv_select->core_bitmap) {
		/* Same count as NodeCnt / synthetic job min_nodes. */
		uint32_t need = job_ptr->details->min_nodes;

		/* Usual path frees this after job_test; not entered here. */
		free_core_array(&resv_exc.exc_cores);
		if (bit_set_count(resv_select->node_bitmap) < need)
			return NULL;
		/* No job_test; do not leave stale job_resrcs on synthetic job. */
		free_job_resources(&job_ptr->job_resrcs);
		/* New bitmap: first need set bits, ascending node index. */
		return bit_pick_cnt(resv_select->node_bitmap, need);
	}

	/*
	 * 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,
		&will_run_data);

	free_core_array(&resv_exc.exc_cores);

	if (rc != SLURM_SUCCESS ||
	    job_ptr->start_time > MAX(resv_desc_ptr->start_time, time(NULL))) {
		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 AllowedPartitions=%s QOS=%s BurstBuffer=%s TRES=%s Comment=%s",
	     mode, resv_ptr->name, start_str, end_str, duration,
	     flag_str, resv_ptr->node_cnt, resv_ptr->core_cnt,
	     resv_ptr->node_list,
	     resv_ptr->features, resv_ptr->partition,
	     resv_ptr->users, resv_ptr->groups, resv_ptr->accounts,
	     resv_ptr->licenses, resv_ptr->allowed_parts, 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 && !resv_ptr->allowed_parts &&
		   (accounting_enforce & ACCOUNTING_ENFORCE_ASSOCS)) {
		error("We need at least 1 user or 1 account to "
		      "create a reservtion.");
		rc = SLURM_ERROR;
	}

	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_allowed_parts_name_str(void *x, void *arg)
{
	part_record_t *part_ptr = x;
	foreach_set_allow_str_t *set_allow_str = arg;

	_addto_name_str(set_allow_str, part_ptr->name);
	return 0;
}

extern int _sort_allowed_parts_list_asc(void *v1, void *v2)
{
	part_record_t *part_a = *(part_record_t **) v1;
	part_record_t *part_b = *(part_record_t **) v2;

	return slurm_sort_char_list_asc(&part_a->name, &part_b->name);
}

/*
 * Since the returned allowed_parts_list is full of pointers from the
 * allowed_parts_list slurmctld_lock_t READ_LOCK on PART must be set before
 * calling this function and while handling it after a return.
 */
static int _append_to_allowed_parts_list(list_t *allowed_parts_list,
					 char *part_name)
{
	part_record_t *part_ptr = NULL;

	xassert(allowed_parts_list);
	xassert(part_name);
	xassert(verify_lock(PART_LOCK, READ_LOCK));

	if (!(part_ptr = list_find_first(part_list, &list_find_part,
					 part_name))) {
		error("No Partition by name %s", part_name);
		return ESLURM_INVALID_PARTITION_NAME;
	}

	if (!list_find_first(allowed_parts_list, slurm_find_ptr_in_list,
			     part_ptr))
		list_append(allowed_parts_list, part_ptr);

	return SLURM_SUCCESS;
}

/*
 * Since the returned allowed_parts_list is full of pointers from the
 * allowed_parts_list slurmctld_lock_t READ_LOCK on PART must be set before
 * calling this function and while handling it after a return.
 */
static int _remove_from_allowed_parts_list(list_t *allowed_parts_list,
					   char *part_name)
{
	part_record_t *part_ptr = NULL;

	xassert(allowed_parts_list);
	xassert(part_name);
	xassert(verify_lock(PART_LOCK, READ_LOCK));

	if (!(part_ptr = list_find_first(part_list, &list_find_part,
					 part_name))) {
		error("No Partition by name %s", part_name);
		return ESLURM_INVALID_PARTITION_NAME;
	}

	(void) list_delete_first(allowed_parts_list, slurm_find_ptr_in_list,
				 part_ptr);

	return SLURM_SUCCESS;
}

/*
 * Validate a comma delimited list of account names and build an array of them
 *
 * IN part_name - a list of partition names
 * OUT allowed_parts_list - list of the partitions names,
 *		            CALLER MUST XFREE this plus each individual record
 * OUT part_not - true if allowed_parts_list is that of partitions to be blocked
 *                from reservation access.
 * RETURN 0 on success
 */
static int _build_allowed_parts_list(char *part_names,
				     list_t **allowed_parts_list,
				     bool *part_not,
				     bool break_on_failure)
{
	char *last = NULL, *tmp, *tok;
	int rc = SLURM_SUCCESS;

	xassert(allowed_parts_list);

	*part_not = false;

	if (!part_names)
		return ESLURM_INVALID_PARTITION_NAME;

	xassert(verify_lock(PART_LOCK, READ_LOCK));

	tmp = xstrdup(part_names);
	tok = strtok_r(tmp, ",", &last);
	while (tok) {
		if (tok[0] == '-') {
			if (!*allowed_parts_list) {
				*part_not = true;
			} else if (*part_not != true) {
				info("Reservation request has some not/partitions");
				rc = ESLURM_INVALID_PARTITION_NAME;
				break;
			}
			tok++;
		} else if (*part_not != false) {
			info("Reservation request has some not/partitions");
			rc = ESLURM_INVALID_PARTITION_NAME;
			break;
		}

		if (!*allowed_parts_list)
			*allowed_parts_list = list_create(NULL);
		if (((rc = _append_to_allowed_parts_list(
			      *allowed_parts_list, tok)) != SLURM_SUCCESS) &&
		    break_on_failure)
			break;

		tok = strtok_r(NULL, ",", &last);
	}

	if (rc != SLURM_SUCCESS)
		FREE_NULL_LIST(*allowed_parts_list);

	if (*allowed_parts_list) {
		list_sort(*allowed_parts_list, _sort_allowed_parts_list_asc);
	}

	xfree(tmp);

	return rc;
}

/*
 * Update a part list for an existing reservation based upon an
 *	update comma delimited specification of part to add (+name),
 *	remove (-name), or set value of
 * IN/OUT resv_ptr - pointer to reservation structure being updated
 * IN part_names - a list of part names, to set, add, or remove
 * RETURN 0 on success
 */
static int _update_allowed_parts_list(slurmctld_resv_t *resv_ptr,
				      char *part_names)
{
	int rc = SLURM_SUCCESS;
	char *last = NULL, *tmp, *tok;
	bool minus_part = false, cleared = false;
	list_t *allowed_parts_list = NULL;

	if (!part_names)
		return ESLURM_INVALID_PARTITION_NAME;

	xassert(verify_lock(PART_LOCK, READ_LOCK));

	if (!resv_ptr->allowed_parts_list)
		resv_ptr->allowed_parts_list = list_create(NULL);
	allowed_parts_list = list_shallow_copy(resv_ptr->allowed_parts_list);


	tmp = xstrdup(part_names);
	tok = strtok_r(tmp, ",", &last);
	while (tok) {
		if (tok[0] == '-') {
			minus_part = 1;
			tok++;
		} else if (tok[0] == '+') {
			minus_part = 0;
			tok++;
		} else if (tok[0] == '\0') {
			continue;
		} else {
			/* The request is a completely new list */
			if (!cleared) {
				list_flush(allowed_parts_list);
				resv_ptr->ctld_flags &=
					(~RESV_CTLD_ALLOWED_PARTS_NOT);
				cleared = true;
			}
		}

		if (resv_ptr->ctld_flags & RESV_CTLD_ALLOWED_PARTS_NOT) {
			if (minus_part)
				rc = _append_to_allowed_parts_list(
					allowed_parts_list, tok);
			else
				rc = _remove_from_allowed_parts_list(
					allowed_parts_list, tok);
		} else {
			if (minus_part)
				rc = _remove_from_allowed_parts_list(
					allowed_parts_list, tok);
			else
				rc = _append_to_allowed_parts_list(
					allowed_parts_list, tok);
		}

		if (rc != SLURM_SUCCESS)
			break;

		tok = strtok_r(NULL, ",", &last);
	}

	if ((rc == SLURM_SUCCESS) && list_count(allowed_parts_list)) {
		foreach_set_allow_str_t set_allow_str = {
			.prefix = (resv_ptr->ctld_flags &
				   RESV_CTLD_ALLOWED_PARTS_NOT) ?
			"-" : "",
			.str = &resv_ptr->allowed_parts,
		};

		list_sort(allowed_parts_list, _sort_allowed_parts_list_asc);
		xfree(resv_ptr->allowed_parts);
		(void) list_for_each(allowed_parts_list,
				     _foreach_set_allowed_parts_name_str,
				     &set_allow_str);
		FREE_NULL_LIST(resv_ptr->allowed_parts_list);
		resv_ptr->allowed_parts_list = allowed_parts_list;
		allowed_parts_list = NULL;
	} else if (rc == SLURM_SUCCESS)
		rc = ESLURM_INVALID_PARTITION_NAME;

	FREE_NULL_LIST(allowed_parts_list);
	xfree(tmp);

	return rc;
}

static int _foreach_set_qos_name_str(void *x, void *arg)
{
	slurmdb_qos_rec_t *qos_ptr = x;
	foreach_set_allow_str_t *set_allow_str = arg;

	_addto_name_str(set_allow_str, qos_ptr->name);
	return 0;
}

extern int _sort_qos_list_asc(void *v1, void *v2)
{
	slurmdb_qos_rec_t *qos_a = *(slurmdb_qos_rec_t **) v1;
	slurmdb_qos_rec_t *qos_b = *(slurmdb_qos_rec_t **) v2;

	return slurm_sort_char_list_asc(&qos_a->name, &qos_b->name);
}

/*
 * Since the returned qos_list is full of pointers from the
 * assoc_mgr_qos_list assoc_mgr_lock_t READ_LOCK on
 * qos must be set before calling this function and while
 * handling it after a return.
 */
static int _append_to_qos_list(list_t *qos_list, char *qos_name)
{
	int rc = ESLURM_INVALID_QOS;
	slurmdb_qos_rec_t *qos_ptr = NULL;
	slurmdb_qos_rec_t qos = {
		.name = qos_name,
	};

	xassert(qos_list);
	xassert(qos.name);
	xassert(verify_assoc_lock(QOS_LOCK, READ_LOCK));

	if (assoc_mgr_fill_in_qos(acct_db_conn, &qos, accounting_enforce,
				  &qos_ptr, true)) {
		if (accounting_enforce & ACCOUNTING_ENFORCE_QOS) {
			error("No QOS by name %s", qos.name);
		} else {
			verbose("No QOS by name %s", qos.name);
			rc = SLURM_SUCCESS;
		}
	}

	if (qos_ptr) {
		if (!list_find_first(qos_list, slurm_find_ptr_in_list, qos_ptr))
			list_append(qos_list, qos_ptr);
		rc = SLURM_SUCCESS;
	}

	return rc;
}

/*
 * Since the returned qos_list is full of pointers from the
 * assoc_mgr_qos_list assoc_mgr_lock_t READ_LOCK on
 * qos must be set before calling this function and while
 * handling it after a return.
 */
static int _remove_from_qos_list(list_t *qos_list, char *qos_name)
{
	int rc = ESLURM_INVALID_QOS;
	slurmdb_qos_rec_t *qos_ptr = NULL;
	slurmdb_qos_rec_t qos = {
		.name = qos_name,
	};

	xassert(qos_list);
	xassert(qos.name);
	xassert(verify_assoc_lock(QOS_LOCK, READ_LOCK));

	if (assoc_mgr_fill_in_qos(acct_db_conn, &qos, accounting_enforce,
				  &qos_ptr, true)) {
		if (accounting_enforce & ACCOUNTING_ENFORCE_QOS) {
			error("No QOS by name %s", qos.name);
		} else {
			verbose("No QOS by name %s", qos.name);
			rc = SLURM_SUCCESS;
		}
	}

	if (qos_ptr) {
		(void) list_delete_first(qos_list, slurm_find_ptr_in_list,
					 qos_ptr);
		rc = SLURM_SUCCESS;
	}

	return rc;
}

/*
 * Validate a comma delimited list of account names and build an array of
 *	them
 * IN account       - a list of account names
 * OUT account_cnt  - number of accounts in the list
 * OUT account_list - list of the account names,
 *		      CALLER MUST XFREE this plus each individual record
 * OUT account_not  - true of account_list is that of accounts to be blocked
 *                    from reservation access
 * RETURN 0 on success
 */
static int _build_qos_list(char *qos, list_t **qos_list, bool *qos_not,
			   bool break_on_failure)
{
	char *last = NULL, *tmp, *tok;
	int rc = SLURM_SUCCESS;
	assoc_mgr_lock_t locks = {
		.qos = READ_LOCK,
	};

	xassert(qos_list);

	*qos_not = false;

	if (!qos)
		return ESLURM_INVALID_QOS;

	assoc_mgr_lock(&locks);

	tmp = xstrdup(qos);
	tok = strtok_r(tmp, ",", &last);
	while (tok) {
		if (tok[0] == '-') {
			if (!*qos_list) {
				*qos_not = true;
			} else if (*qos_not != true) {
				info("Reservation request has some not/qos");
				rc = ESLURM_INVALID_QOS;
				break;
			}
			tok++;
		} else if (*qos_not != false) {
			info("Reservation request has some not/qos");
			rc = ESLURM_INVALID_QOS;
			break;
		}

		if (!*qos_list)
			*qos_list = list_create(NULL);
		if (((rc = _append_to_qos_list(*qos_list, tok)) !=
		     SLURM_SUCCESS) && break_on_failure)
			break;

		tok = strtok_r(NULL, ",", &last);
	}

	if (rc != SLURM_SUCCESS)
		FREE_NULL_LIST(*qos_list);

	if (*qos_list) {
		list_sort(*qos_list, _sort_qos_list_asc);
	}

	assoc_mgr_unlock(&locks);

	xfree(tmp);

	return rc;
}

/*
 * Update a qos list for an existing reservation based upon an
 *	update comma delimited specification of qos to add (+name),
 *	remove (-name), or set value of
 * IN/OUT resv_ptr - pointer to reservation structure being updated
 * IN qos     - a list of qos names, to set, add, or remove
 * RETURN 0 on success
 */
static int _update_qos_list(slurmctld_resv_t *resv_ptr, char *qos)
{
	int rc = SLURM_SUCCESS;
	char *last = NULL, *tmp, *tok;
	bool minus_qos = false, cleared = false;
	list_t *qos_list = NULL;
	assoc_mgr_lock_t locks = {
		.qos = READ_LOCK,
	};

	if (!qos)
		return ESLURM_INVALID_QOS;

	if (!resv_ptr->qos_list)
		resv_ptr->qos_list = list_create(NULL);
	qos_list = list_shallow_copy(resv_ptr->qos_list);

	assoc_mgr_lock(&locks);

	tmp = xstrdup(qos);
	tok = strtok_r(tmp, ",", &last);
	while (tok) {
		if (tok[0] == '-') {
			minus_qos = 1;
			tok++;
		} else if (tok[0] == '+') {
			minus_qos = 0;
			tok++;
		} else if (tok[0] == '\0') {
			continue;
		} else {
			/* The request is a completely new list */
			if (!cleared) {
				list_flush(qos_list);
				resv_ptr->ctld_flags &= (~RESV_CTLD_QOS_NOT);
				cleared = true;
			}
		}

		if (resv_ptr->ctld_flags & RESV_CTLD_QOS_NOT) {
			if (minus_qos)
				rc = _append_to_qos_list(qos_list, tok);
			else
				rc = _remove_from_qos_list(qos_list, tok);
		} else {
			if (minus_qos)
				rc = _remove_from_qos_list(qos_list, tok);
			else
				rc = _append_to_qos_list(qos_list, tok);
		}

		if (rc != SLURM_SUCCESS)
			break;

		tok = strtok_r(NULL, ",", &last);
	}

	if ((rc == SLURM_SUCCESS) && list_count(qos_list)) {
		foreach_set_allow_str_t set_allow_str = {
			.prefix = (resv_ptr->ctld_flags & RESV_CTLD_QOS_NOT) ?
			"-" : "",
			.str = &resv_ptr->qos,
		};

		list_sort(qos_list, _sort_qos_list_asc);
		xfree(resv_ptr->qos);
		(void) list_for_each(qos_list, _foreach_set_qos_name_str,
				     &set_allow_str);
		FREE_NULL_LIST(resv_ptr->qos_list);
		resv_ptr->qos_list = qos_list;
		qos_list = NULL;
	} else if (rc == SLURM_SUCCESS)
		rc = ESLURM_INVALID_QOS;

	assoc_mgr_unlock(&locks);

	FREE_NULL_LIST(qos_list);
	xfree(tmp);

	return rc;
}

static int _set_access(slurmctld_resv_t *resv_ptr)
{
	int rc = SLURM_SUCCESS;

	if ((rc = _set_assoc_list(resv_ptr)) != SLURM_SUCCESS)
		return rc;

	return rc;
}

/* Post reservation create */
static int _post_resv_create(slurmctld_resv_t *resv_ptr)
{
	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);
	}

	xfree(tmp);

	if (u_cnt > 0) {
		*user_cnt  = u_cnt;
		*user_list = u_list;
		return SLURM_SUCCESS;
	}

	xfree(u_list);
	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;
}

/*
 * Advance a reservation slot to the next reocurrence using calendar-aware
 * logic (localtime_r + slurm_mktime), matching _advance_time().
 * This correctly handles Daylight Saving Time transitions.
 */
static int _advance_slot(constraint_slot_t *slot)
{
	struct tm tm;
	time_t new_start, delta;
	int day_cnt = 0, hour_cnt = 0;

	if (!slot) {
		error("%s: Reservation slot is NULL and it shouldn't happen",
		      __func__);
		return SLURM_ERROR;
	}

	if (!(slot->flags & RESERVE_REOCCURRING))
		return SLURM_ERROR;

	localtime_r(&(slot->start), &tm);

	if (slot->flags & RESERVE_FLAG_WEEKDAY) {
		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 (slot->flags & RESERVE_FLAG_WEEKEND) {
		if (tm.tm_wday == 6) /* Saturday */
			day_cnt = 1;
		else
			day_cnt = 6 - tm.tm_wday;
	} else if (slot->flags & RESERVE_FLAG_WEEKLY) {
		day_cnt = 7;
	} else if (slot->flags & RESERVE_FLAG_DAILY) {
		day_cnt = 1;
	} else if (slot->flags & RESERVE_FLAG_HOURLY) {
		hour_cnt = 1;
	} else {
		error("%s: Unknown recurring reservation flags",
		      __func__);
		return SLURM_ERROR;
	}

	tm.tm_mday += day_cnt;
	tm.tm_hour += hour_cnt;
	new_start = slurm_mktime(&tm);
	if (new_start == (time_t) (-1)) {
		error("%s: Could not compute reservation time %lu",
		      __func__, (long unsigned int) slot->start);
		return SLURM_ERROR;
	}
	if (new_start <= slot->start) {
		error("%s: Computed time did not advance from %lu",
		      __func__, (long unsigned int) slot->start);
		return SLURM_ERROR;
	}

	delta = new_start - slot->start;
	slot->start = new_start;
	slot->end += delta;

	return SLURM_SUCCESS;
}

static void _advance_slot_until(constraint_slot_t *slot, time_t end)
{
	constraint_slot_t slot_advanced;
	struct tm tm_start;
	time_t new_start, delta, days;
	int jump_days;

	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;
	}

	/*
	 * Jump close to end to avoid iterating through long time spans.
	 * Leave a 7-day margin so the loop below handles the final
	 * alignment for all recurrence types (including weekly).
	 */
	days = (end - slot->start) / (24 * 3600);

	if (days > 7) {
		/*
		 * tm_mday is int and will be incremented by jump_days below.
		 * Bound days so neither the narrowing cast nor the addition
		 * to tm_mday (max starting value 31) can overflow int.
		 */
		if (days > INT_MAX - 31) {
			error("%s: Absurd reservation time gap between reservations; aborting",
			      __func__);
			return;
		}
		jump_days = (int) (days - 7);

		if (slot->flags & RESERVE_FLAG_WEEKLY)
			jump_days -= jump_days % 7;

		localtime_r(&(slot->start), &tm_start);
		tm_start.tm_mday += jump_days;
		new_start = slurm_mktime(&tm_start);
		if (new_start == (time_t) (-1)) {
			error("%s: Could not compute reservation time %lu",
			      __func__, (long unsigned int) slot->start);
			return;
		}

		delta = new_start - slot->start;
		slot->start = new_start;
		slot->end += delta;
	}

	/* Loop the remaining days to find the last occurrence before end */
	slot_advanced = *slot;
	while (slot_advanced.start < end) {
		*slot = slot_advanced;
		if (_advance_slot(&slot_advanced) != SLURM_SUCCESS)
			break;
	}
}

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) {
		if ((slot[1]->flags & RESERVE_FLAG_MAINT) &&
		    !(slot[1]->flags & RESERVE_REOCCURRING)) {
			/*
			 * Evaluate against reoccurring resv start, or now() if
			 * resv already started.
			 */
			time_t eval_time = MAX(now, slot[0]->start);
			/*
			 * Do not check overlaps against maintenance
			 * reservations starting more than 7 days after the
			 * reservation being evaluated.
			 */
			if ((slot[1]->start - eval_time) > (7 * DAY_SECONDS)) {
				log_flag(RESERVATION, "%s: Not filtering out nodes from maintenance reservation %s starting more than 7 days later",
					 __func__, resv_ptr->name);
				return false;
			}
		}
		/*
		 * 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]->start > 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
		 */
		if (_advance_slot(slot[0]) != SLURM_SUCCESS)
			return true;

		if (slot[0]->start < 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]->start > 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->allowed_parts)
			xstrfmtcatat(tmp_str, &tmp_str_pos, " allowed_parts=%s",
				     resv_ptr->allowed_parts);
		if (resv_ptr->core_cnt)
			xstrfmtcatat(tmp_str, &tmp_str_pos, " cores=%u",
				     resv_ptr->core_cnt);
		if (resv_ptr->groups)
			xstrfmtcatat(tmp_str, &tmp_str_pos, " groups=%s",
				     resv_ptr->groups);
		if (resv_ptr->licenses)
			xstrfmtcatat(tmp_str, &tmp_str_pos, " licenses=%s",
				     resv_ptr->licenses);

		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, NULL);
	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, NULL);
	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,
		allowed_parts_not = false;
	resv_select_t resv_select = { 0 };
	list_t *qos_list = NULL;
	list_t *allowed_parts_list = NULL;

	_create_resv_lists(false);

	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->allowed_parts &&
		   !resv_desc_ptr->groups) {
		info("Reservation request lacks users, accounts, QOS, AllowedPartition 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->allowed_parts) {
		foreach_set_allow_str_t set_allow_str = {
			.str = &resv_desc_ptr->allowed_parts,
		};

		rc = _build_allowed_parts_list(
			resv_desc_ptr->allowed_parts,
			&allowed_parts_list,
			&allowed_parts_not,
			true);
		if (rc != SLURM_SUCCESS)
			goto bad_parse;

		set_allow_str.prefix = allowed_parts_not ? "-" : "";
		xfree(resv_desc_ptr->allowed_parts);
		(void) list_for_each(allowed_parts_list,
				     _foreach_set_allowed_parts_name_str,
				     &set_allow_str);
		resv_select.allowed_parts_list = allowed_parts_list;
	}

	if (resv_desc_ptr->licenses) {
		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 */
		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;
	if (allowed_parts_not)
		resv_ptr->ctld_flags |= RESV_CTLD_ALLOWED_PARTS_NOT;

	resv_ptr->duration      = resv_desc_ptr->duration;
	if (resv_desc_ptr->purge_comp_time != NO_VAL)
		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;
	resv_ptr->allowed_parts = resv_desc_ptr->allowed_parts;
	resv_desc_ptr->allowed_parts = NULL;
	resv_ptr->allowed_parts_list = allowed_parts_list;
	allowed_parts_list = NULL;

	if (!(resv_desc_ptr->flags & RESERVE_FLAG_GRES_REQ) &&
	    (resv_desc_ptr->core_cnt == NO_VAL)) {
		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_NULL_LIST(allowed_parts_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,
				       list_t *allowed_parts_list)
{
	if (*part_ptr == NULL) {
		if (allowed_parts_list && list_count(allowed_parts_list))
			*part_ptr = list_peek(allowed_parts_list);
		else
			*part_ptr = default_part_loc;
		if (*part_ptr == NULL)
			return ESLURM_DEFAULT_PARTITION_NOT_SET;
	}
	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')) {
		/*
		 * Removing the partition from a reservation which already has
		 * a partition may cause slurmctld to crash. Deny the request.
		 */
		info("Reservation %s request to clear partition is not allowed",
		     resv_desc_ptr->name);
		if (err_msg) {
			xfree(*err_msg);
			*err_msg = xstrdup("Reservation partition cannot be removed");
		}
		error_code = ESLURM_INVALID_PARTITION_NAME;
		goto update_failure;
	}
	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_desc_ptr->allowed_parts) {
		rc = _update_allowed_parts_list(resv_ptr,
						resv_desc_ptr->allowed_parts);
		if (rc) {
			error_code = rc;
			goto update_failure;
		}
	}

	if (!resv_ptr->users &&
	    !resv_ptr->accounts &&
	    !resv_ptr->qos &&
	    !resv_ptr->allowed_parts &&
	    !resv_ptr->groups) {
		info("Reservation %s request lacks users, accounts, AllowedPartitions, 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);
}

/* 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);
			/* Only update timestamps when resv is deleted */
			last_resv_update = time(NULL);
			_flush_node_down_cache(node_down_bitmap,
					       last_resv_update);
			schedule_resv_save();
		}
		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;
	}

	FREE_NULL_BITMAP(node_down_bitmap);
	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;
		bool fuzzy_match = false;

		FREE_NULL_LIST(resv_ptr->license_list);
		resv_ptr->license_list =
			license_validate(resv_ptr->licenses, true, true, true,
					 NULL, &valid, &fuzzy_match);
		if (!valid) {
			error("Reservation %s has invalid licenses (%s)",
			      resv_ptr->name, resv_ptr->licenses);
			return false;
		} else if (fuzzy_match) {
			xfree(resv_ptr->licenses);
			resv_ptr->licenses =
				license_list_to_string(resv_ptr->license_list);
		}
	}
	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->allowed_parts) {
		bool allowed_parts_not; /* we don't care about this */
		(void) _build_allowed_parts_list(resv_ptr->allowed_parts,
						 &resv_ptr->allowed_parts_list,
						 &allowed_parts_not, false);

		if (!resv_ptr->allowed_parts_list ||
		    !list_count(resv_ptr->allowed_parts_list)) {
			error("Reservation %s has invalid AllowedPartitions (%s)",
			      resv_ptr->name, resv_ptr->allowed_parts);
			return false;
		}
	}

	if ((resv_ptr->flags & RESERVE_FLAG_PART_NODES) &&
	    resv_ptr->part_ptr && resv_ptr->part_ptr->node_bitmap) {
		memset(&old_resv_ptr, 0, sizeof(slurmctld_resv_t));
		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();
		}
	}

	/*
	 * Reservation GRES arrays are indexed by global node_inx.
	 * After a restart the node table may have been re-sorted, so remap
	 * GRES per-node arrays from old indices to current ones and
	 * expand to cover the current node_record_count.
	 */
	if (resv_ptr->gres_list_alloc && is_node_table_changed)
		gres_resv_list_remap_global_indices(resv_ptr->gres_list_alloc);

	return true;
}

extern int resv_cache_update_qos_list(void *x, void *arg)
{
	slurmctld_resv_t *resv_ptr = x;
	char *last = NULL, *tmp = NULL, *tok = NULL;

	xassert(verify_assoc_lock(QOS_LOCK, READ_LOCK));

	if (!resv_ptr->qos_list)
		return 0;

	/*
	 * resv_ptr->qos_list caches pointers into assoc_mgr_qos_list with a
	 * NULL destructor. After the qos list has been refreshed those
	 * pointers are dangling, so rebuild from the canonical resv_ptr->qos
	 * string.
	 */
	list_flush(resv_ptr->qos_list);
	if (!resv_ptr->qos)
		return 0;

	tmp = xstrdup(resv_ptr->qos);
	tok = strtok_r(tmp, ",", &last);
	while (tok) {
		if ((tok[0] == '-') || (tok[0] == '+'))
			tok++;
		(void) _append_to_qos_list(resv_ptr->qos_list, tok);
		tok = strtok_r(NULL, ",", &last);
	}
	xfree(tmp);

	return 0;
}

extern void validate_all_reservations(bool run_now, bool run_locked)
{
	static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
	static uint32_t requests = 0;
	bool run;

	if (!run_now) {
		slurm_mutex_lock(&mutex);
		requests++;
		log_flag(RESERVATION, "%s: requests %u",
			 __func__, requests);
		xassert(requests != UINT32_MAX);
		slurm_mutex_unlock(&mutex);
		return;
	}

	slurm_mutex_lock(&mutex);
	run = (requests > 0);
	/* reset requests counter */
	requests = 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(job_record_t *job_ptr)
{
	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->priority = 0;
		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;
	job_record_t *job_ptr = NULL;

	/* 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);
			_resv_node_replace(resv_ptr);
		}
	}
	list_iterator_destroy(iter);

	/*
	 * Validate all job reservation pointers.
	 * This needs to be an iterator since _validate_job_resv() may
	 * eventually call _pick_node_cnt() which will deadlock the
	 * job_list lock.
	 */
	iter = list_iterator_create(job_list);
	while ((job_ptr = list_next(iter)))
		_validate_job_resv(job_ptr);
	list_iterator_destroy(iter);
}

/*
 * Replace DOWN, DRAIN (or ALLOCATED) nodes for reservations. Behavior varies
 * depending on the configured flags:
 * No flags     -> Replace DOWN/DRAIN
 * REPLACE_DOWN -> Replace DOWN/DRAIN
 * REPLACE      -> Replace DOWN/DRAIN/ALLOC nodes
 */
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 };
	slurmctld_resv_t *resv_backup;

	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_SPEC_NODES) ||
	    (resv_ptr->flags & RESERVE_FLAG_STATIC) ||
	    (resv_ptr->flags & RESERVE_FLAG_MAINT))
		return;

	resv_select.allowed_parts_list = resv_ptr->allowed_parts_list;

	/* Identify nodes which can be preserved in this reservation */
	preserve_bitmap = bit_copy(avail_node_bitmap);
	if (resv_ptr->flags & RESERVE_FLAG_REPLACE)
		bit_and(preserve_bitmap, idle_node_bitmap);
	/* Only nodes in the reservation's partition are valid candidates */
	if (resv_ptr->part_ptr && resv_ptr->part_ptr->node_bitmap)
		bit_and(preserve_bitmap, resv_ptr->part_ptr->node_bitmap);
	bit_and(preserve_bitmap, resv_ptr->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;
	if (!add_nodes) {
		FREE_NULL_BITMAP(preserve_bitmap);
		return;
	}

	/* Required for accounting update if node list changes */
	resv_backup = _copy_resv(resv_ptr);

	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) {
		_set_tres_cnt(resv_ptr, resv_backup);
		last_resv_update = time(NULL);
		schedule_resv_save();
	}
	_del_resv_rec(resv_backup);
}

static bool _access_str_any_allowed(char *access_str)
{
	char c;
	int i = 0;

	if (!access_str || *access_str == '\0')
		return false;

	while (access_str[i + 1]) {
		if (access_str[i] == ',') {
			c = access_str[i + 1];
			if ('0' <= c && c <= '9')
				return true;
		}
		i++;
	}
	return false;
}

static int _foreach_check_assoc_access(void *x, void *arg)
{
	slurmdb_assoc_rec_t *assoc = x;
	foreach_check_assoc_access_t *data = arg;

	char *access_str = data->access_str;
	char deny_id_str[30];
	char *allow_id_str;

	while (assoc) {
		snprintf(deny_id_str, sizeof(deny_id_str), ",-%u,", assoc->id);

		/* Check if ,-%u, is in list */
		if (data->check_denied && xstrstr(access_str, deny_id_str)) {
			data->found_denied = true;
			return -1;
		}

		/* Check if ,%u, is in list */
		if (data->check_allowed) {
			/*
			 * Remove '-' to change "deny" string to "allow" string
			 */
			allow_id_str = deny_id_str + 1;
			allow_id_str[0] = ',';
			if (xstrstr(access_str, allow_id_str)) {
				data->found_allowed = true;
				if (data->check_denied) {
					/* Keep checking for denied only */
					data->check_allowed = false;
				} else {
					return -1;
				}
			}
		}
		assoc = assoc->usage->parent_assoc_ptr;
	}

	return 0;
}

static bool _check_assoc_access(char *access_list, slurmdb_assoc_rec_t *assoc)
{
	xassert(access_list);
	xassert(assoc);

	/*
	 * Reject if the association or a parent association is denied.
	 * Otherwise, check if explicitly allowed.
	 */
	bool any_denied = xstrchr(access_list, '-');
	bool any_allowed = _access_str_any_allowed(access_list);
	foreach_check_assoc_access_t args = {
		.access_str = access_list,
		.check_denied = any_denied,
		.check_allowed = any_allowed,
		.found_denied = false,
		.found_allowed = false,
	};
	(void) _foreach_check_assoc_access(assoc, &args);

	return !args.found_denied && (!any_allowed || args.found_allowed);
}

static bool _check_assoc_access_each(char *access_list, list_t *assoc_list)
{
	xassert(access_list);
	xassert(assoc_list);

	/*
	 * Reject if any association or parent association is denied.
	 * Otherwise, check if any is explicitly allowed.
	 */
	bool any_denied = xstrchr(access_list, '-');
	bool any_allowed = _access_str_any_allowed(access_list);
	foreach_check_assoc_access_t args = {
		.access_str = access_list,
		.check_denied = any_denied,
		.check_allowed = any_allowed,
		.found_denied = false,
		.found_allowed = false,
	};
	(void) list_for_each(assoc_list, _foreach_check_assoc_access, &args);

	return !args.found_denied && (!any_allowed || args.found_allowed);
}

static bool _check_uid_access(slurmctld_resv_t *resv_ptr, uid_t uid)
{
	bool user_found = false;
	bool is_allow_list = !(resv_ptr->ctld_flags & RESV_CTLD_USER_NOT);

	if (resv_ptr->user_cnt == 0)
		return true;

	for (int i = 0; i < resv_ptr->user_cnt; i++) {
		if (resv_ptr->user_list[i] == uid) {
			user_found = true;
			break;
		}
	}
	return user_found == is_allow_list;
}

/*
 * 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) {
		return _check_assoc_access_each(resv_ptr->assoc_list,
						user_assoc_list);
	}

	return _check_uid_access(resv_ptr, uid);
}

/*
 * 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;
		_resv_node_replace(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 = {
		.allowed_parts_list = resv_ptr->allowed_parts_list,
	};

	delta_node_cnt = resv_ptr->node_cnt - node_cnt;
	if (delta_node_cnt == 0)	/* Already correct node count */
		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,
					      resv_ptr->allowed_parts_list))) {
		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_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,
					      resv_select_ret->
					      allowed_parts_list))) {
		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;
	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) {
		if (!resv_ptr->assoc_list) {
			if (resv_ptr->qos_list || resv_ptr->allowed_parts_list)
				return SLURM_SUCCESS;

			error("Reservation %s has no association list. "
			      "Checking user/account lists",
			      resv_ptr->name);
			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;
		}

		if (_check_assoc_access(resv_ptr->assoc_list,
					job_ptr->assoc_ptr))
			return SLURM_SUCCESS;
	} else {
no_assocs:
		if (!_check_uid_access(resv_ptr, job_ptr->user_id))
			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;
		}
	}

	/* Check AllowedPartitions */
	if (resv_ptr->allowed_parts_list) {
		part_record_t *part_ptr = NULL;

		xassert(job_ptr->part_ptr);

		/*
		 * Since we do not allow mixed state check the list's pointers.
		 */
		part_ptr = list_find_first(resv_ptr->allowed_parts_list,
					   slurm_find_ptr_in_list,
					   job_ptr->part_ptr);

		if (resv_ptr->ctld_flags & RESV_CTLD_ALLOWED_PARTS_NOT) {
			if (part_ptr) { /* explicitly denied */
				rc = ESLURM_INVALID_PARTITION_NAME;
			}
		} else if (!part_ptr) { /* not allowed */
			rc = ESLURM_INVALID_PARTITION_NAME;
		}

		if (rc != SLURM_SUCCESS) {
			debug2("%pJ attempted to use reservation '%s' with partition '%s' not allowed in reservation (%s)",
			       job_ptr,
			       resv_ptr->name,
			       job_ptr->part_ptr->name,
			       resv_ptr->allowed_parts);
			return rc;
		}
	}

	return rc;
}

/*
 * Determine if a job can start now based only upon reservations
 *
 * IN job_ptr      - job to test
 * 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->flags & RESERVE_FLAG_REPLACE))
		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;
	}
}

static void _addto_resv_exc(bitstr_t *core_bitmap, resv_exc_t *resv_exc_ptr)
{
	bitstr_t **tmp_bitstr;

	if (!resv_exc_ptr || !core_bitmap)
		return;

	tmp_bitstr = core_bitmap_to_array(core_bitmap);

	if (!resv_exc_ptr->exc_cores) {
		resv_exc_ptr->exc_cores = tmp_bitstr;
	} else {
		core_array_or(resv_exc_ptr->exc_cores, tmp_bitstr);
		free_core_array(&tmp_bitstr);
	}
}

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;

	/*
	 * Prevent jobs that exceed the end time of the reservation from being
	 * attracted to a magnetic reservation
	 */
	if ((job_ptr->bit_flags & JOB_MAGNETIC) && (job_ptr->resv_name) &&
	    (job_ptr->resv_ptr) &&
	    !(job_ptr->resv_ptr->flags & RESERVE_FLAG_FLEX) &&
	    (job_end_time > job_ptr->resv_ptr->end_time)) {
		return ESLURM_RESERVATION_INVALID;
	}

	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)) {
				continue;
			}

			if (!(res2_ptr->ctld_flags & RESV_CTLD_FULL_NODE)) {
				/*
				 * Flex reservations steal other reservations
				 * resources if they are not on the full node.
				 * This removes any cores that belong to other
				 * reservations.
				 */
				if (resv_ptr->flags & RESERVE_FLAG_FLEX) {
					_addto_resv_exc(res2_ptr->core_bitmap,
							resv_exc_ptr);
				}

				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(NULL, _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))) {
			_resv_node_replace(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);
}
