/*****************************************************************************\
 *  slurmdb_defs.c - definitions used by slurmdb api
 ******************************************************************************
 *  Copyright (C) 2010 Lawrence Livermore National Security.
 *  Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
 *  Written by Danny Auble da@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 <stdlib.h>

#include "src/common/assoc_mgr.h"
#include "src/common/log.h"
#include "src/common/parse_time.h"
#include "src/interfaces/select.h"
#include "src/interfaces/auth.h"
#include "src/common/slurm_protocol_defs.h"
#include "src/interfaces/jobacct_gather.h"
#include "src/common/slurm_time.h"
#include "src/common/slurmdb_defs.h"
#include "src/common/xmalloc.h"
#include "src/common/xstring.h"
#include "src/slurmdbd/read_config.h"
#include "src/common/print_fields.h"

#define FORMAT_STRING_SIZE 34

slurmdb_cluster_rec_t *working_cluster_rec = NULL;

static void _free_res_cond_members(slurmdb_res_cond_t *res_cond);
static void _free_res_rec_members(slurmdb_res_rec_t *res);

strong_alias(get_qos_complete_str_bitstr, slurmdb_get_qos_complete_str_bitstr);

static void _free_clus_res_rec_members(slurmdb_clus_res_rec_t *clus_res)
{
	if (clus_res) {
		xfree(clus_res->cluster);
	}
}

static void _free_cluster_rec_members(slurmdb_cluster_rec_t *cluster)
{
	if (cluster) {
		FREE_NULL_LIST(cluster->accounting_list);
		xfree(cluster->control_host);
		xfree(cluster->dim_size);
		FREE_NULL_LIST(cluster->fed.feature_list);
		xfree(cluster->fed.name);
		slurm_persist_conn_destroy(cluster->fed.recv);
		slurm_persist_conn_destroy(cluster->fed.send);
		slurm_mutex_destroy(&cluster->lock);
		xfree(cluster->name);
		xfree(cluster->nodes);
		slurmdb_destroy_assoc_rec(cluster->root_assoc);
		FREE_NULL_LIST(cluster->send_rpc);
		xfree(cluster->tres_str);
	}
}

static void _free_federation_rec_members(slurmdb_federation_rec_t *federation)
{
	if (federation) {
		xfree(federation->name);
		FREE_NULL_LIST(federation->cluster_list);
	}
}

static void _free_wckey_rec_members(slurmdb_wckey_rec_t *wckey)
{
	if (wckey) {
		FREE_NULL_LIST(wckey->accounting_list);
		xfree(wckey->cluster);
		xfree(wckey->name);
		xfree(wckey->user);
	}
}

static void _free_cluster_cond_members(slurmdb_cluster_cond_t *cluster_cond)
{
	if (cluster_cond) {
		FREE_NULL_LIST(cluster_cond->cluster_list);
		FREE_NULL_LIST(cluster_cond->federation_list);
		FREE_NULL_LIST(cluster_cond->format_list);
		FREE_NULL_LIST(cluster_cond->rpc_version_list);
	}
}

static void _free_federation_cond_members(slurmdb_federation_cond_t *fed_cond)
{
	if (fed_cond) {
		FREE_NULL_LIST(fed_cond->cluster_list);
		FREE_NULL_LIST(fed_cond->federation_list);
	}
}

static void _free_tres_cond_members(slurmdb_tres_cond_t *tres_cond)
{
	if (tres_cond) {
		FREE_NULL_LIST(tres_cond->id_list);
		FREE_NULL_LIST(tres_cond->name_list);
		FREE_NULL_LIST(tres_cond->type_list);
	}
}

static void _free_res_cond_members(slurmdb_res_cond_t *res_cond)
{
	if (res_cond) {
		FREE_NULL_LIST(res_cond->allowed_list);
		FREE_NULL_LIST(res_cond->cluster_list);
		FREE_NULL_LIST(res_cond->description_list);
		FREE_NULL_LIST(res_cond->id_list);
		FREE_NULL_LIST(res_cond->manager_list);
		FREE_NULL_LIST(res_cond->name_list);
		FREE_NULL_LIST(res_cond->server_list);
		FREE_NULL_LIST(res_cond->type_list);
	}
}

static void _free_res_rec_members(slurmdb_res_rec_t *res)
{
	if (res) {
		FREE_NULL_LIST(res->clus_res_list);
		slurmdb_destroy_clus_res_rec(res->clus_res_rec);
		xfree(res->description);
		xfree(res->manager);
		xfree(res->name);
		xfree(res->server);
	}
}


/*
 * Comparator used for sorting immediate children of acct_hierarchical_recs
 *
 * returns: -1 assoc_a < assoc_b   0: assoc_a == assoc_b   1: assoc_a > assoc_b
 *
 */

static int _sort_children_list(void *v1, void *v2)
{
	int diff = 0;
	slurmdb_hierarchical_rec_t *assoc_a;
	slurmdb_hierarchical_rec_t *assoc_b;

	assoc_a = *(slurmdb_hierarchical_rec_t **)v1;
	assoc_b = *(slurmdb_hierarchical_rec_t    **)v2;

	/* Since all these associations are on the same level we don't
	 * have to check the lineage
	 */

	/* check to see if this is a user association or an account.
	 * We want the accounts at the bottom
	 */
	if (assoc_a->assoc->user && !assoc_b->assoc->user)
		return -1;
	else if (!assoc_a->assoc->user && assoc_b->assoc->user)
		return 1;

	/* Sort by alpha */
	diff = xstrcmp(assoc_a->sort_name, assoc_b->sort_name);

	if (diff < 0)
		return -1;
	else if (diff > 0)
		return 1;

	return 0;

}

/*
 * Comparator used for sorting immediate children of acct_hierarchical_recs by
 * lineage
 *
 * returns: -1 assoc_a < assoc_b   0: assoc_a == assoc_b   1: assoc_a > assoc_b
 *
 */
static int _sort_assoc_by_lineage_asc(void *v1, void *v2)
{
	slurmdb_assoc_rec_t *assoc_a = *(slurmdb_assoc_rec_t **)v1;
	slurmdb_assoc_rec_t *assoc_b = *(slurmdb_assoc_rec_t **)v2;
	int diff = slurm_sort_char_list_asc(&assoc_a->cluster,
					    &assoc_b->cluster);
	if (diff)
		return diff;
	return slurm_sort_char_list_asc(&assoc_a->lineage, &assoc_b->lineage);
}

static int _sort_slurmdb_hierarchical_rec_list(
	list_t *slurmdb_hierarchical_rec_list)
{
	slurmdb_hierarchical_rec_t *slurmdb_hierarchical_rec = NULL;
	list_itr_t *itr;

	if (!list_count(slurmdb_hierarchical_rec_list))
		return SLURM_SUCCESS;

	list_sort(slurmdb_hierarchical_rec_list, (ListCmpF)_sort_children_list);

	itr = list_iterator_create(slurmdb_hierarchical_rec_list);
	while((slurmdb_hierarchical_rec = list_next(itr))) {
		if (list_count(slurmdb_hierarchical_rec->children))
			_sort_slurmdb_hierarchical_rec_list(
				slurmdb_hierarchical_rec->children);
	}
	list_iterator_destroy(itr);

	return SLURM_SUCCESS;
}

static int _append_hierarchical_children_ret_list(
	list_t *ret_list, list_t *slurmdb_hierarchical_rec_list)
{
	slurmdb_hierarchical_rec_t *slurmdb_hierarchical_rec = NULL;
	list_itr_t *itr;

	if (!ret_list)
		return SLURM_ERROR;

	if (!list_count(slurmdb_hierarchical_rec_list))
		return SLURM_SUCCESS;

	itr = list_iterator_create(slurmdb_hierarchical_rec_list);
	while((slurmdb_hierarchical_rec = list_next(itr))) {
		list_append(ret_list, slurmdb_hierarchical_rec->assoc);

		if (list_count(slurmdb_hierarchical_rec->children))
			_append_hierarchical_children_ret_list(
				ret_list, slurmdb_hierarchical_rec->children);
	}
	list_iterator_destroy(itr);

	return SLURM_SUCCESS;
}

static char *_get_qos_list_str(list_t *qos_list)
{
	char *qos_char = NULL;
	list_itr_t *itr = NULL;
	slurmdb_qos_rec_t *qos = NULL;

	if (!qos_list)
		return NULL;

	itr = list_iterator_create(qos_list);
	while((qos = list_next(itr))) {
		if (qos_char)
			xstrfmtcat(qos_char, ",%s", qos->name);
		else
			xstrcat(qos_char, qos->name);
	}
	list_iterator_destroy(itr);

	return qos_char;
}

static int _unset_amending_tres(void *x, void *key)
{
	slurmdb_tres_rec_t *tres_rec = x;

	if (tres_rec->modifier == '-')
		tres_rec->count = 0;

	tres_rec->modifier = '\0';

	return 0;
}

extern int slurmdb_setup_cluster_rec(slurmdb_cluster_rec_t *cluster_rec)
{
	xassert(cluster_rec);

	if (!cluster_rec->control_port) {
		debug("Slurmctld on '%s' hasn't registered yet.",
		      cluster_rec->name);
		return SLURM_ERROR;
	}

	slurm_set_addr(&cluster_rec->control_addr,
		       cluster_rec->control_port,
		       cluster_rec->control_host);
	if (slurm_addr_is_unspec(&cluster_rec->control_addr)) {
		error("Unable to establish control "
		      "machine address for '%s'(%s:%u)",
		      cluster_rec->name,
		      cluster_rec->control_host,
		      cluster_rec->control_port);
		return SLURM_ERROR;
	}

	if (cluster_rec->dimensions > 1) {
		int number, i, len;
		char *nodes = cluster_rec->nodes;

		cluster_rec->dim_size = xmalloc(sizeof(int) *
						cluster_rec->dimensions);
		len = strlen(nodes);
		i = len - cluster_rec->dimensions;
		if (nodes[len-1] == ']')
			i--;

		if (i > 0) {
			number = xstrntol(nodes + i, NULL,
					  cluster_rec->dimensions, 36);
			hostlist_parse_int_to_array(
				number, cluster_rec->dim_size,
				cluster_rec->dimensions, 36);
			/* all calculations this is for should
			 * be expecting 0 not to count as a
			 * number so add 1 to it. */
			for (i=0; i<cluster_rec->dimensions; i++)
				cluster_rec->dim_size[i]++;
		}
	}

	return SLURM_SUCCESS;
}

extern void slurmdb_job_cond_def_start_end(slurmdb_job_cond_t *job_cond)
{
	time_t now = time(NULL);

	if (!job_cond ||
	    (job_cond->flags & JOBCOND_FLAG_RUNAWAY) ||
	    (job_cond->flags & JOBCOND_FLAG_NO_DEFAULT_USAGE))
		return;
	/*
	 * Defaults for start (S) and end (E) times:
	 * - with -j and -s:
	 *   -S defaults to Epoch 0
	 *   -E defaults to -S (unless no -S then Now)
	 * - with only -j (NOT -s)
	 *   -S defaults to Epoch 0
	 *   -E defaults to Now
	 * - with only -s (NOT -j):
	 *   -S defaults to Now
	 *   -E defaults to -S
	 * - without either -j nor -s:
	 *   -S defaults to Midnight
	 *   -E defaults to Now
	 */
	if (job_cond->state_list && list_count(job_cond->state_list)) {
		if (!job_cond->usage_start &&
		    (!job_cond->step_list || !list_count(job_cond->step_list)))
			job_cond->usage_start = now;

		if (job_cond->usage_start && !job_cond->usage_end)
			job_cond->usage_end = job_cond->usage_start;
	} else if (!job_cond->step_list || !list_count(job_cond->step_list)) {
		if (!job_cond->usage_start) {
			struct tm start_tm;
			job_cond->usage_start = now;
			if (!localtime_r(&job_cond->usage_start, &start_tm)) {
				error("Couldn't get localtime from %ld",
				      (long)job_cond->usage_start);
			} else {
				start_tm.tm_sec = 0;
				start_tm.tm_min = 0;
				start_tm.tm_hour = 0;
				job_cond->usage_start = slurm_mktime(&start_tm);
			}
		}
	}

	if (!job_cond->usage_end)
		job_cond->usage_end = now;

	/*
	 * The query will be exclusive of the end time, that is [S,E).
	 * We must adjust E when E==S to include S, and when E==Now to
	 * include the current second.
	 */
	if ((job_cond->usage_end == job_cond->usage_start) ||
	    (job_cond->usage_end == now))
		job_cond->usage_end++;
}

#define T(flag, str) { flag, XSTRINGIFY(flag), str }
static const struct {
	slurmdb_qos_flags_t flag;
	char *flag_str;
	char *str;
} slurmdb_qos_flags_map[] = {
	T(QOS_FLAG_DELETED, "Deleted"),
	T(QOS_FLAG_DENY_LIMIT, "DenyOnLimit"),
	T(QOS_FLAG_ENFORCE_USAGE_THRES, "EnforceUsageThreshold"),
	T(QOS_FLAG_PART_MIN_NODE, "PartitionMinNodes"),
	T(QOS_FLAG_PART_MAX_NODE, "PartitionMaxNodes"),
	T(QOS_FLAG_PART_TIME_LIMIT, "PartitionTimeLimit"),
	T(QOS_FLAG_REQ_RESV, "RequiresReservation"),
	T(QOS_FLAG_OVER_PART_QOS, "OverPartQOS"),
	T(QOS_FLAG_PART_QOS, "PartQOS"),
	T(QOS_FLAG_NO_RESERVE, "NoReserve"),
	T(QOS_FLAG_NO_DECAY, "NoDecay"),
	T(QOS_FLAG_RELATIVE, "Relative"),
	T(QOS_FLAG_USAGE_FACTOR_SAFE, "UsageFactorSafe"),
};
#undef T

static int _str_2_qos_flags(char *flag_in, slurmdb_qos_flags_t *flags_ptr)
{
	if (!flag_in || !flag_in[0])
		return SLURM_SUCCESS;

	for (int i = 0; i < ARRAY_SIZE(slurmdb_qos_flags_map); i++) {
		if (!xstrncasecmp(flag_in, slurmdb_qos_flags_map[i].str,
				  strlen(flag_in))) {
			*flags_ptr |= slurmdb_qos_flags_map[i].flag;
			return SLURM_SUCCESS;
		}
	}

	debug("%s: Unable to match %s to a slurmdbd_qos_flags_t flag",
	      __func__, flag_in);
	return EINVAL;
}

static uint32_t _str_2_res_flags(char *flags)
{

	if (xstrcasestr(flags, "Absolute"))
		return SLURMDB_RES_FLAG_ABSOLUTE;

	return 0;
}

static uint32_t _str_2_job_flags(char *flags)
{
	if (xstrcasestr(flags, "None"))
		return SLURMDB_JOB_FLAG_NONE;

	if (xstrcasestr(flags, "SchedSubmit"))
		return SLURMDB_JOB_FLAG_SUBMIT;

	if (xstrcasestr(flags, "SchedMain"))
		return SLURMDB_JOB_FLAG_SCHED;

	if (xstrcasestr(flags, "SchedBackfill"))
		return SLURMDB_JOB_FLAG_BACKFILL;

	if (xstrcasestr(flags, "StartReceived"))
		return SLURMDB_JOB_FLAG_START_R;

	return SLURMDB_JOB_FLAG_NOTSET;
}

static will_run_response_msg_t *_job_will_run(job_desc_msg_t *req)
{
	will_run_response_msg_t *will_run_resp = NULL;
	int rc;

	rc = slurm_job_will_run2(req, &will_run_resp);

	if (rc >= 0) {
		will_run_resp->cluster_name =
			xstrdup(working_cluster_rec->name);
		if (get_log_level() >= LOG_LEVEL_DEBUG) {
			char buf[256];
			slurm_make_time_str(&will_run_resp->start_time, buf,
					    sizeof(buf));
			debug("%pI to start at %s on cluster %s using %u processors on nodes %s in partition %s",
			      &will_run_resp->step_id, buf,
			      working_cluster_rec->name,
			      will_run_resp->proc_cnt, will_run_resp->node_list,
			      will_run_resp->part_name);

			if (will_run_resp->preemptee_job_id) {
				list_itr_t *itr;
				uint32_t *job_id_ptr;
				char *job_list = NULL, *sep = "";
				itr = list_iterator_create(
					will_run_resp->preemptee_job_id);
				while ((job_id_ptr = list_next(itr))) {
					if (job_list)
						sep = ",";
					xstrfmtcat(job_list, "%s%u",
						   sep, *job_id_ptr);
				}
				list_iterator_destroy(itr);
				debug("  Preempts: %s", job_list);
				xfree(job_list);
			}
		}
	}

	return will_run_resp;
}

static int _set_qos_bit_from_string(bitstr_t *valid_qos, char *name)
{
	void (*my_function) (bitstr_t *b, bitoff_t bit);
	bitoff_t bit = 0;

	xassert(valid_qos);

	if (!name)
		return SLURM_ERROR;

	if (name[0] == '-') {
		name++;
		my_function = bit_clear;
	} else if (name[0] == '+') {
		name++;
		my_function = bit_set;
	} else
		my_function = bit_set;

	if ((bit = atoi(name)) >= bit_size(valid_qos))
		return SLURM_ERROR;

	(*(my_function))(valid_qos, bit);

	return SLURM_SUCCESS;
}

static char *_create_hash_rec_id(slurmdb_assoc_rec_t *assoc, bool parent)
{
	/*
	 * Add comma to key to distinguish between id and cluster name
	 * .e.g
	 * associd: 11 cluster: foo
	 * associd: 1  cluster: 1foo
	 */
	return xstrdup_printf("%u,%s",
			      parent ? assoc->parent_id : assoc->id,
			      assoc->cluster);
}

static void _arch_hash_rec_id(void *item, const char **key, uint32_t *key_len)
{
	slurmdb_hierarchical_rec_t *arch_rec = item;

	xfree(arch_rec->key);
	arch_rec->key = _create_hash_rec_id(arch_rec->assoc, false);
	*key = arch_rec->key;
	*key_len = strlen(*key);
}

static int _list_copy_coord(void *x, void *key)
{
	slurmdb_coord_rec_t *coord_in = x;
	list_t **ret_list = key;
	slurmdb_coord_rec_t *coord = xmalloc(sizeof(*coord));

	if (!*ret_list)
		*ret_list = list_create(slurmdb_destroy_coord_rec);
	list_append(*ret_list, coord);
	coord->name = xstrdup(coord_in->name);
	coord->direct = coord_in->direct;

	return 0;
}

extern slurmdb_job_rec_t *slurmdb_create_job_rec(void)
{
	slurmdb_job_rec_t *job = xmalloc(sizeof(slurmdb_job_rec_t));
	job->array_task_id = NO_VAL;
	job->derived_ec = NO_VAL;
	job->state = JOB_PENDING;
	job->steps = list_create(slurmdb_destroy_step_rec);
	job->requid = -1;
	job->resvid = NO_VAL;

      	return job;
}

extern slurmdb_step_rec_t *slurmdb_create_step_rec(void)
{
	slurmdb_step_rec_t *step = xmalloc(sizeof(slurmdb_step_rec_t));
	memset(&step->stats, 0, sizeof(slurmdb_stats_t));
	step->step_id.step_id = NO_VAL;
	step->step_id.step_het_comp = NO_VAL;
	step->state = NO_VAL;
	step->exitcode = NO_VAL;
	step->elapsed = NO_VAL;
	step->tot_cpu_sec = NO_VAL;
	step->tot_cpu_usec = NO_VAL;
	step->requid = -1;

	return step;
}

extern slurmdb_assoc_usage_t *slurmdb_create_assoc_usage(int tres_cnt)
{
	slurmdb_assoc_usage_t *usage;
	int alloc_size;

	if (!tres_cnt)
		fatal("%s: You need to give a tres_cnt to call this function",
		      __func__);

	usage =	xmalloc(sizeof(slurmdb_assoc_usage_t));

	usage->level_shares = NO_VAL;
	usage->shares_norm = (double)NO_VAL64;
	usage->usage_efctv = 0;
	usage->usage_norm = (long double)NO_VAL;
	usage->usage_raw = 0;
	usage->level_fs = 0;
	usage->fs_factor = 0;

	usage->tres_cnt = tres_cnt;

	alloc_size = sizeof(uint64_t) * tres_cnt;
	usage->grp_used_tres = xmalloc(alloc_size);
	usage->grp_used_tres_run_secs = xmalloc(alloc_size);

	usage->usage_tres_raw = xmalloc(sizeof(long double) * tres_cnt);

	return usage;
}

extern slurmdb_qos_usage_t *slurmdb_create_qos_usage(int tres_cnt)
{
	slurmdb_qos_usage_t *usage =
		xmalloc(sizeof(slurmdb_qos_usage_t));

	if (tres_cnt) {
		int alloc_size = sizeof(uint64_t) * tres_cnt;
		usage->tres_cnt = tres_cnt;
		usage->grp_used_tres_run_secs = xmalloc(alloc_size);
		usage->grp_used_tres = xmalloc(alloc_size);
		usage->usage_tres_raw = xmalloc(sizeof(long double) * tres_cnt);
	}

	return usage;
}

extern void slurmdb_destroy_assoc_usage(void *object)
{
	slurmdb_assoc_usage_t *usage =
		(slurmdb_assoc_usage_t *)object;

	if (usage) {
		FREE_NULL_LIST(usage->children_list);
		FREE_NULL_BITMAP(usage->grp_node_bitmap);
		xfree(usage->grp_node_job_cnt);
		xfree(usage->grp_used_tres_run_secs);
		xfree(usage->grp_used_tres);
		xfree(usage->usage_tres_raw);
		FREE_NULL_BITMAP(usage->valid_qos);
		xfree(usage);
	}
}

extern void slurmdb_destroy_bf_usage(void *object)
{
	slurmdb_destroy_bf_usage_members(object);
	xfree(object);
}

extern void slurmdb_destroy_bf_usage_members(void *object)
{
	return;
}

extern void slurmdb_destroy_qos_usage(void *object)
{
	slurmdb_qos_usage_t *usage =
		(slurmdb_qos_usage_t *)object;

	if (usage) {
		FREE_NULL_LIST(usage->acct_limit_list);
		FREE_NULL_BITMAP(usage->grp_node_bitmap);
		xfree(usage->grp_node_job_cnt);
		xfree(usage->grp_used_tres_run_secs);
		xfree(usage->grp_used_tres);
		FREE_NULL_LIST(usage->job_list);
		xfree(usage->usage_tres_raw);
		FREE_NULL_LIST(usage->user_limit_list);
		xfree(usage);
	}
}

extern void slurmdb_free_user_rec_members(slurmdb_user_rec_t *slurmdb_user)
{
	if (!slurmdb_user)
		return;

	FREE_NULL_LIST(slurmdb_user->assoc_list);
	FREE_NULL_LIST(slurmdb_user->coord_accts);
	xfree(slurmdb_user->default_acct);
	xfree(slurmdb_user->default_wckey);
	xfree(slurmdb_user->name);
	xfree(slurmdb_user->old_name);
	FREE_NULL_LIST(slurmdb_user->wckey_list);
	slurmdb_destroy_bf_usage(slurmdb_user->bf_usage);
}

extern void slurmdb_destroy_user_rec(void *object)
{
	slurmdb_user_rec_t *slurmdb_user = (slurmdb_user_rec_t *)object;

	if (!slurmdb_user)
		return;

	slurmdb_free_user_rec_members(slurmdb_user);
	xfree(slurmdb_user);
}

extern void slurmdb_destroy_account_rec(void *object)
{
	slurmdb_account_rec_t *slurmdb_account =
		(slurmdb_account_rec_t *)object;

	if (slurmdb_account) {
		FREE_NULL_LIST(slurmdb_account->assoc_list);
		FREE_NULL_LIST(slurmdb_account->coordinators);
		xfree(slurmdb_account->description);
		xfree(slurmdb_account->name);
		xfree(slurmdb_account->organization);
		xfree(slurmdb_account);
	}
}

extern void slurmdb_destroy_coord_rec(void *object)
{
	slurmdb_coord_rec_t *slurmdb_coord =
		(slurmdb_coord_rec_t *)object;

	if (slurmdb_coord) {
		xfree(slurmdb_coord->name);
		xfree(slurmdb_coord);
	}
}

extern void slurmdb_destroy_cluster_accounting_rec(void *object)
{
	slurmdb_cluster_accounting_rec_t *clusteracct_rec =
		(slurmdb_cluster_accounting_rec_t *)object;

	if (clusteracct_rec) {
		slurmdb_destroy_tres_rec_noalloc(
			&clusteracct_rec->tres_rec);
		xfree(clusteracct_rec);
	}
}

extern void slurmdb_destroy_clus_res_rec(void *object)
{
	slurmdb_clus_res_rec_t *slurmdb_clus_res =
		(slurmdb_clus_res_rec_t *)object;

	if (slurmdb_clus_res) {
		_free_clus_res_rec_members(slurmdb_clus_res);
		xfree(slurmdb_clus_res);
	}
}

extern void slurmdb_destroy_cluster_rec(void *object)
{
	slurmdb_cluster_rec_t *slurmdb_cluster =
		(slurmdb_cluster_rec_t *)object;

	if (slurmdb_cluster) {
		_free_cluster_rec_members(slurmdb_cluster);
		xfree(slurmdb_cluster);
	}
}

extern void slurmdb_destroy_federation_rec(void *object)
{
	slurmdb_federation_rec_t *slurmdb_federation =
		(slurmdb_federation_rec_t *)object;

	if (slurmdb_federation) {
		_free_federation_rec_members(slurmdb_federation);
		xfree(slurmdb_federation);
	}
}

extern void slurmdb_destroy_accounting_rec(void *object)
{
	slurmdb_accounting_rec_t *slurmdb_accounting =
		(slurmdb_accounting_rec_t *)object;

	if (slurmdb_accounting) {
		slurmdb_destroy_tres_rec_noalloc(
			&slurmdb_accounting->tres_rec);
		xfree(slurmdb_accounting);
	}
}

extern void slurmdb_free_assoc_rec_members(slurmdb_assoc_rec_t *assoc)
{
	if (assoc) {
		FREE_NULL_LIST(assoc->accounting_list);
		xfree(assoc->acct);
		xfree(assoc->cluster);
		xfree(assoc->comment);
		xfree(assoc->grp_tres);
		xfree(assoc->grp_tres_ctld);
		xfree(assoc->grp_tres_mins);
		xfree(assoc->grp_tres_mins_ctld);
		xfree(assoc->grp_tres_run_mins);
		xfree(assoc->grp_tres_run_mins_ctld);
		xfree(assoc->lineage);
		xfree(assoc->max_tres_mins_pj);
		xfree(assoc->max_tres_mins_ctld);
		xfree(assoc->max_tres_run_mins);
		xfree(assoc->max_tres_run_mins_ctld);
		xfree(assoc->max_tres_pj);
		xfree(assoc->max_tres_ctld);
		xfree(assoc->max_tres_pn);
		xfree(assoc->max_tres_pn_ctld);
		xfree(assoc->parent_acct);
		xfree(assoc->partition);
		FREE_NULL_LIST(assoc->qos_list);
		xfree(assoc->user);

		/* Account with previously deleted users */
		if (assoc->leaf_usage != assoc->usage)
			slurmdb_destroy_assoc_usage(assoc->leaf_usage);
		/*
		 * Be crazy safe and set this to NULL as it should never be used
		 * again!
		 */
		assoc->leaf_usage = NULL;

		slurmdb_destroy_assoc_usage(assoc->usage);
		/* NOTE assoc->user_rec is a soft reference, do not free here */
		assoc->user_rec = NULL;
		slurmdb_destroy_bf_usage(assoc->bf_usage);
	}
}

extern void slurmdb_destroy_assoc_rec(void *object)
{
	slurmdb_assoc_rec_t *slurmdb_assoc =
		(slurmdb_assoc_rec_t *)object;

	if (slurmdb_assoc) {
		slurmdb_free_assoc_rec_members(slurmdb_assoc);
		xfree(slurmdb_assoc);
	}
}

extern void slurmdb_destroy_event_rec(void *object)
{
	slurmdb_event_rec_t *slurmdb_event =
		(slurmdb_event_rec_t *)object;

	if (slurmdb_event) {
		xfree(slurmdb_event->cluster);
		xfree(slurmdb_event->cluster_nodes);
		xfree(slurmdb_event->node_name);
		xfree(slurmdb_event->reason);
		xfree(slurmdb_event->tres_str);

		xfree(slurmdb_event);
	}
}

extern void slurmdb_destroy_instance_rec(void *object)
{
	slurmdb_instance_rec_t *slurmdb_instance = object;

	if (slurmdb_instance) {
		xfree(slurmdb_instance->cluster);
		xfree(slurmdb_instance->extra);
		xfree(slurmdb_instance->instance_id);
		xfree(slurmdb_instance->instance_type);
		xfree(slurmdb_instance->node_name);

		xfree(slurmdb_instance);
	}
	return;
}

extern void slurmdb_destroy_job_rec(void *object)
{
	slurmdb_job_rec_t *job = (slurmdb_job_rec_t *)object;
	if (job) {
		xfree(job->account);
		xfree(job->admin_comment);
		xfree(job->array_task_str);
		xfree(job->blockid);
		xfree(job->cluster);
		xfree(job->constraints);
		xfree(job->container);
		xfree(job->derived_es);
		xfree(job->env);
		xfree(job->extra);
		xfree(job->failed_node);
		xfree(job->jobname);
		xfree(job->licenses);
		xfree(job->lineage);
		xfree(job->mcs_label);
		xfree(job->partition);
		xfree(job->qos_req);
		xfree(job->nodes);
		xfree(job->resv_name);
		xfree(job->resv_req);
		xfree(job->script);
		FREE_NULL_LIST(job->steps);
		xfree(job->std_err);
		xfree(job->std_in);
		xfree(job->std_out);
		xfree(job->submit_line);
		xfree(job->system_comment);
		xfree(job->tres_alloc_str);
		xfree(job->tres_req_str);
		xfree(job->user);
		xfree(job->wckey);
		xfree(job->work_dir);
		xfree(job);
	}
}

extern void slurmdb_free_qos_rec_members(slurmdb_qos_rec_t *qos)
{
	if (qos) {
		xfree(qos->description);
		xfree(qos->grp_tres);
		xfree(qos->grp_tres_ctld);
		xfree(qos->grp_tres_mins);
		xfree(qos->grp_tres_mins_ctld);
		xfree(qos->grp_tres_run_mins);
		xfree(qos->grp_tres_run_mins_ctld);
		xfree(qos->max_tres_mins_pj);
		xfree(qos->max_tres_mins_pj_ctld);
		xfree(qos->max_tres_run_mins_pa);
		xfree(qos->max_tres_run_mins_pa_ctld);
		xfree(qos->max_tres_run_mins_pu);
		xfree(qos->max_tres_run_mins_pu_ctld);
		xfree(qos->max_tres_pa);
		xfree(qos->max_tres_pa_ctld);
		xfree(qos->max_tres_pj);
		xfree(qos->max_tres_pj_ctld);
		xfree(qos->max_tres_pn);
		xfree(qos->max_tres_pn_ctld);
		xfree(qos->max_tres_pu);
		xfree(qos->max_tres_pu_ctld);
		xfree(qos->min_tres_pj);
		xfree(qos->min_tres_pj_ctld);
		xfree(qos->name);
		FREE_NULL_BITMAP(qos->preempt_bitstr);
		FREE_NULL_LIST(qos->preempt_list);
		xfree(qos->relative_tres_cnt);
		slurmdb_destroy_qos_usage(qos->usage);
	}
}

extern void slurmdb_destroy_qos_rec(void *object)
{
	slurmdb_qos_rec_t *slurmdb_qos = (slurmdb_qos_rec_t *)object;
	if (slurmdb_qos) {
		slurmdb_free_qos_rec_members(slurmdb_qos);
		xfree(slurmdb_qos);
	}
}

extern void slurmdb_destroy_reservation_rec(void *object)
{
	slurmdb_reservation_rec_t *slurmdb_resv =
		(slurmdb_reservation_rec_t *)object;
	if (slurmdb_resv) {
		xfree(slurmdb_resv->assocs);
		xfree(slurmdb_resv->cluster);
		xfree(slurmdb_resv->comment);
		xfree(slurmdb_resv->name);
		xfree(slurmdb_resv->nodes);
		xfree(slurmdb_resv->node_inx);
		xfree(slurmdb_resv->tres_str);
		xfree(slurmdb_resv);
	}
}

extern void slurmdb_destroy_step_rec(void *object)
{
	slurmdb_step_rec_t *step = (slurmdb_step_rec_t *)object;
	if (step) {
		xfree(step->container);
		xfree(step->nodes);
		xfree(step->pid_str);
		slurmdb_free_slurmdb_stats_members(&step->stats);
		xfree(step->stepname);
		xfree(step->cwd);
		xfree(step->std_err);
		xfree(step->std_in);
		xfree(step->std_out);
		xfree(step->submit_line);
		xfree(step->tres_alloc_str);
		xfree(step);
	}
}

extern void slurmdb_destroy_res_rec(void *object)
{
	slurmdb_res_rec_t *slurmdb_res =
		(slurmdb_res_rec_t *)object;

	if (slurmdb_res) {
		_free_res_rec_members(slurmdb_res);
		xfree(slurmdb_res);
	}
}

extern void slurmdb_destroy_txn_rec(void *object)
{
	slurmdb_txn_rec_t *slurmdb_txn = (slurmdb_txn_rec_t *)object;
	if (slurmdb_txn) {
		xfree(slurmdb_txn->accts);
		xfree(slurmdb_txn->actor_name);
		xfree(slurmdb_txn->clusters);
		xfree(slurmdb_txn->set_info);
		xfree(slurmdb_txn->users);
		xfree(slurmdb_txn->where_query);
		xfree(slurmdb_txn);
	}
}

extern void slurmdb_destroy_wckey_rec(void *object)
{
	slurmdb_wckey_rec_t *wckey = (slurmdb_wckey_rec_t *)object;

	if (wckey) {
		_free_wckey_rec_members(wckey);
		xfree(wckey);
	}
}

extern void slurmdb_destroy_archive_rec(void *object)
{
	slurmdb_archive_rec_t *arch_rec = (slurmdb_archive_rec_t *)object;

	if (arch_rec) {
		xfree(arch_rec->archive_file);
		xfree(arch_rec->insert);
		xfree(arch_rec);
	}
}

extern void slurmdb_destroy_tres_rec_noalloc(void *object)
{
	slurmdb_tres_rec_t *tres_rec = (slurmdb_tres_rec_t *)object;

	if (!tres_rec)
		return;

	xfree(tres_rec->name);
	xfree(tres_rec->type);
}

extern void slurmdb_destroy_tres_rec(void *object)
{
	slurmdb_tres_rec_t *tres_rec = (slurmdb_tres_rec_t *)object;

	if (tres_rec) {
		slurmdb_destroy_tres_rec_noalloc(tres_rec);
		xfree(tres_rec);
	}
}

extern void slurmdb_destroy_report_assoc_rec(void *object)
{
	slurmdb_report_assoc_rec_t *slurmdb_report_assoc =
		(slurmdb_report_assoc_rec_t *)object;
	if (slurmdb_report_assoc) {
		xfree(slurmdb_report_assoc->acct);
		xfree(slurmdb_report_assoc->cluster);
		xfree(slurmdb_report_assoc->parent_acct);
		FREE_NULL_LIST(slurmdb_report_assoc->tres_list);
		xfree(slurmdb_report_assoc->user);
		xfree(slurmdb_report_assoc);
	}
}

extern void slurmdb_destroy_report_user_rec(void *object)
{
	slurmdb_report_user_rec_t *slurmdb_report_user =
		(slurmdb_report_user_rec_t *)object;
	if (slurmdb_report_user) {
		xfree(slurmdb_report_user->acct);
		FREE_NULL_LIST(slurmdb_report_user->acct_list);
		FREE_NULL_LIST(slurmdb_report_user->assoc_list);
		xfree(slurmdb_report_user->name);
		FREE_NULL_LIST(slurmdb_report_user->tres_list);
		xfree(slurmdb_report_user);
	}
}

extern void slurmdb_destroy_report_cluster_rec(void *object)
{
	slurmdb_report_cluster_rec_t *slurmdb_report_cluster =
		(slurmdb_report_cluster_rec_t *)object;
	if (slurmdb_report_cluster) {
		FREE_NULL_LIST(slurmdb_report_cluster->assoc_list);
		xfree(slurmdb_report_cluster->name);
		FREE_NULL_LIST(slurmdb_report_cluster->tres_list);
		FREE_NULL_LIST(slurmdb_report_cluster->user_list);
		xfree(slurmdb_report_cluster);
	}
}

extern void slurmdb_destroy_user_cond(void *object)
{
	slurmdb_user_cond_t *slurmdb_user = (slurmdb_user_cond_t *)object;

	if (slurmdb_user) {
		slurmdb_destroy_assoc_cond(slurmdb_user->assoc_cond);
		FREE_NULL_LIST(slurmdb_user->def_acct_list);
		FREE_NULL_LIST(slurmdb_user->def_wckey_list);
		xfree(slurmdb_user);
	}
}

extern void slurmdb_destroy_account_cond(void *object)
{
	slurmdb_account_cond_t *slurmdb_account =
		(slurmdb_account_cond_t *)object;

	if (slurmdb_account) {
		slurmdb_destroy_assoc_cond(slurmdb_account->assoc_cond);
		FREE_NULL_LIST(slurmdb_account->description_list);
		FREE_NULL_LIST(slurmdb_account->organization_list);
		xfree(slurmdb_account);
	}
}

extern void slurmdb_destroy_cluster_cond(void *object)
{
	slurmdb_cluster_cond_t *slurmdb_cluster =
		(slurmdb_cluster_cond_t *)object;

	if (slurmdb_cluster) {
		_free_cluster_cond_members(slurmdb_cluster);
		xfree(slurmdb_cluster);
	}
}

extern void slurmdb_destroy_federation_cond(void *object)
{
	slurmdb_federation_cond_t *slurmdb_federation =
		(slurmdb_federation_cond_t *)object;

	if (slurmdb_federation) {
		_free_federation_cond_members(slurmdb_federation);
		xfree(slurmdb_federation);
	}
}

extern void slurmdb_destroy_tres_cond(void *object)
{
	slurmdb_tres_cond_t *slurmdb_tres =
		(slurmdb_tres_cond_t *)object;

	if (slurmdb_tres) {
		_free_tres_cond_members(slurmdb_tres);
		xfree(slurmdb_tres);
	}
}

extern void slurmdb_destroy_assoc_cond(void *object)
{
	slurmdb_assoc_cond_t *slurmdb_assoc =
		(slurmdb_assoc_cond_t *)object;

	if (slurmdb_assoc) {
		FREE_NULL_LIST(slurmdb_assoc->acct_list);
		FREE_NULL_LIST(slurmdb_assoc->cluster_list);
		FREE_NULL_LIST(slurmdb_assoc->def_qos_id_list);
		FREE_NULL_LIST(slurmdb_assoc->id_list);
		FREE_NULL_LIST(slurmdb_assoc->partition_list);
		FREE_NULL_LIST(slurmdb_assoc->parent_acct_list);
		FREE_NULL_LIST(slurmdb_assoc->qos_list);
		FREE_NULL_LIST(slurmdb_assoc->user_list);
		xfree(slurmdb_assoc);
	}
}

extern void slurmdb_destroy_event_cond(void *object)
{
	slurmdb_event_cond_t *slurmdb_event =
		(slurmdb_event_cond_t *)object;

	if (slurmdb_event) {
		FREE_NULL_LIST(slurmdb_event->cluster_list);
		FREE_NULL_LIST(slurmdb_event->format_list);
		FREE_NULL_LIST(slurmdb_event->reason_list);
		FREE_NULL_LIST(slurmdb_event->reason_uid_list);
		FREE_NULL_LIST(slurmdb_event->state_list);
		xfree(slurmdb_event->node_list);
		xfree(slurmdb_event);
	}
}

extern void slurmdb_destroy_instance_cond(void *object)
{
	slurmdb_instance_cond_t *slurmdb_instance = object;

	if (slurmdb_instance) {
		FREE_NULL_LIST(slurmdb_instance->cluster_list);
		FREE_NULL_LIST(slurmdb_instance->extra_list);
		FREE_NULL_LIST(slurmdb_instance->format_list);
		FREE_NULL_LIST(slurmdb_instance->instance_id_list);
		FREE_NULL_LIST(slurmdb_instance->instance_type_list);
		xfree(slurmdb_instance->node_list);

		xfree(slurmdb_instance);
	}
}

extern void slurmdb_destroy_job_cond_members(slurmdb_job_cond_t *job_cond)
{
	if (!job_cond)
		return;
	FREE_NULL_LIST(job_cond->acct_list);
	FREE_NULL_LIST(job_cond->associd_list);
	FREE_NULL_LIST(job_cond->cluster_list);
	FREE_NULL_LIST(job_cond->constraint_list);
	FREE_NULL_LIST(job_cond->groupid_list);
	FREE_NULL_LIST(job_cond->jobname_list);
	FREE_NULL_LIST(job_cond->partition_list);
	FREE_NULL_LIST(job_cond->qos_list);
	FREE_NULL_LIST(job_cond->reason_list);
	FREE_NULL_LIST(job_cond->resv_list);
	FREE_NULL_LIST(job_cond->resvid_list);
	FREE_NULL_LIST(job_cond->step_list);
	FREE_NULL_LIST(job_cond->state_list);
	xfree(job_cond->used_nodes);
	FREE_NULL_LIST(job_cond->userid_list);
	FREE_NULL_LIST(job_cond->wckey_list);
}

extern void slurmdb_destroy_job_cond(void *object)
{
	slurmdb_job_cond_t *job_cond =
		(slurmdb_job_cond_t *)object;

	if (job_cond) {
		slurmdb_destroy_job_cond_members(job_cond);
		xfree(job_cond);
	}
}

extern void slurmdb_destroy_qos_cond(void *object)
{
	slurmdb_qos_cond_t *slurmdb_qos = (slurmdb_qos_cond_t *)object;
	if (slurmdb_qos) {
		FREE_NULL_LIST(slurmdb_qos->id_list);
		FREE_NULL_LIST(slurmdb_qos->name_list);
		xfree(slurmdb_qos);
	}
}

extern void slurmdb_destroy_res_cond(void *object)
{
	slurmdb_res_cond_t *slurmdb_res =
		(slurmdb_res_cond_t *)object;
	if (slurmdb_res) {
		_free_res_cond_members(slurmdb_res);
		xfree(slurmdb_res);
	}
}

extern void slurmdb_destroy_reservation_cond(void *object)
{
	slurmdb_reservation_cond_t *slurmdb_resv =
		(slurmdb_reservation_cond_t *)object;
	if (slurmdb_resv) {
		FREE_NULL_LIST(slurmdb_resv->cluster_list);
		FREE_NULL_LIST(slurmdb_resv->id_list);
		FREE_NULL_LIST(slurmdb_resv->name_list);
		xfree(slurmdb_resv->nodes);
		xfree(slurmdb_resv);
	}
}

extern void slurmdb_destroy_txn_cond(void *object)
{
	slurmdb_txn_cond_t *slurmdb_txn = (slurmdb_txn_cond_t *)object;
	if (slurmdb_txn) {
		FREE_NULL_LIST(slurmdb_txn->acct_list);
		FREE_NULL_LIST(slurmdb_txn->action_list);
		FREE_NULL_LIST(slurmdb_txn->actor_list);
		FREE_NULL_LIST(slurmdb_txn->cluster_list);
		FREE_NULL_LIST(slurmdb_txn->id_list);
		FREE_NULL_LIST(slurmdb_txn->info_list);
		FREE_NULL_LIST(slurmdb_txn->name_list);
		FREE_NULL_LIST(slurmdb_txn->user_list);
		xfree(slurmdb_txn);
	}
}

extern void slurmdb_destroy_wckey_cond(void *object)
{
	slurmdb_wckey_cond_t *wckey = (slurmdb_wckey_cond_t *)object;

	if (wckey) {
		FREE_NULL_LIST(wckey->cluster_list);
		FREE_NULL_LIST(wckey->id_list);
		FREE_NULL_LIST(wckey->name_list);
		FREE_NULL_LIST(wckey->user_list);
		xfree(wckey);
	}
}

extern void slurmdb_destroy_archive_cond(void *object)
{
	slurmdb_archive_cond_t *arch_cond = (slurmdb_archive_cond_t *)object;

	if (arch_cond) {
		xfree(arch_cond->archive_dir);
		xfree(arch_cond->archive_script);
		slurmdb_destroy_job_cond(arch_cond->job_cond);
		xfree(arch_cond);

	}
}

extern void slurmdb_free_add_assoc_cond_members(
	slurmdb_add_assoc_cond_t *add_assoc)
{
	if (!add_assoc)
		return;

	FREE_NULL_LIST(add_assoc->acct_list);
	slurmdb_free_assoc_rec_members(&add_assoc->assoc);
	FREE_NULL_LIST(add_assoc->cluster_list);
	xfree(add_assoc->default_acct);
	FREE_NULL_LIST(add_assoc->partition_list);
	FREE_NULL_LIST(add_assoc->user_list);
	FREE_NULL_LIST(add_assoc->wckey_list);
}

extern void slurmdb_destroy_add_assoc_cond(void *object)
{
	slurmdb_add_assoc_cond_t *add_assoc = object;

	if (!add_assoc)
		return;

	slurmdb_free_add_assoc_cond_members(add_assoc);
	xfree(add_assoc);
}

extern void slurmdb_destroy_update_object(void *object)
{
	slurmdb_update_object_t *slurmdb_update =
		(slurmdb_update_object_t *) object;

	if (slurmdb_update) {
		FREE_NULL_LIST(slurmdb_update->objects);
		xfree(slurmdb_update);
	}
}

extern void slurmdb_destroy_used_limits(void *object)
{
	slurmdb_used_limits_t *slurmdb_used_limits =
		(slurmdb_used_limits_t *)object;

	if (slurmdb_used_limits) {
		xfree(slurmdb_used_limits->acct);
		FREE_NULL_BITMAP(slurmdb_used_limits->node_bitmap);
		xfree(slurmdb_used_limits->node_job_cnt);
		xfree(slurmdb_used_limits->tres);
		xfree(slurmdb_used_limits->tres_run_secs);
		xfree(slurmdb_used_limits);
	}
}

extern void slurmdb_destroy_print_tree(void *object)
{
	slurmdb_print_tree_t *slurmdb_print_tree =
		(slurmdb_print_tree_t *)object;

	if (slurmdb_print_tree) {
		xfree(slurmdb_print_tree->name);
		xfree(slurmdb_print_tree->print_name);
		xfree(slurmdb_print_tree->spaces);
		xfree(slurmdb_print_tree);
	}
}

extern void slurmdb_destroy_hierarchical_rec(void *object)
{
	/* Most of this is pointers to something else that will be
	 * destroyed elsewhere.
	 */
	slurmdb_hierarchical_rec_t *slurmdb_hierarchical_rec =
		(slurmdb_hierarchical_rec_t *)object;
	if (slurmdb_hierarchical_rec) {
		xfree(slurmdb_hierarchical_rec->key);
		FREE_NULL_LIST(slurmdb_hierarchical_rec->children);
		xfree(slurmdb_hierarchical_rec);
	}
}

extern void slurmdb_destroy_report_job_grouping(void *object)
{
	slurmdb_report_job_grouping_t *job_grouping =
		(slurmdb_report_job_grouping_t *)object;
	if (job_grouping) {
		FREE_NULL_LIST(job_grouping->jobs);
		FREE_NULL_LIST(job_grouping->tres_list);
		xfree(job_grouping);
	}
}

extern void slurmdb_destroy_report_acct_grouping(void *object)
{
	slurmdb_report_acct_grouping_t *acct_grouping =
		(slurmdb_report_acct_grouping_t *)object;
	if (acct_grouping) {
		xfree(acct_grouping->acct);
		FREE_NULL_LIST(acct_grouping->groups);
		xfree(acct_grouping->lineage);
		FREE_NULL_LIST(acct_grouping->tres_list);
		xfree(acct_grouping);
	}
}

extern void slurmdb_destroy_report_cluster_grouping(void *object)
{
	slurmdb_report_cluster_grouping_t *cluster_grouping =
		(slurmdb_report_cluster_grouping_t *)object;
	if (cluster_grouping) {
		xfree(cluster_grouping->cluster);
		FREE_NULL_LIST(cluster_grouping->acct_list);
		FREE_NULL_LIST(cluster_grouping->tres_list);
		xfree(cluster_grouping);
	}
}

extern list_t *slurmdb_get_info_cluster(char *cluster_names)
{
	slurmdb_cluster_rec_t *cluster_rec = NULL;
	slurmdb_cluster_cond_t cluster_cond;
	list_t *temp_list = NULL;
	char *cluster_name = NULL;
	void *db_conn = NULL;
	list_itr_t *itr, *itr2;
	bool all_clusters = 0;

	if (cluster_names && !xstrcasecmp(cluster_names, "all"))
		all_clusters = 1;

	db_conn = acct_storage_g_get_connection(0, NULL, 1,
						slurm_conf.cluster_name);

	slurmdb_init_cluster_cond(&cluster_cond, 0);
	if (cluster_names && !all_clusters) {
		cluster_cond.cluster_list = list_create(xfree_ptr);
		slurm_addto_char_list(cluster_cond.cluster_list, cluster_names);
	}

	if (!(temp_list = acct_storage_g_get_clusters(db_conn, getuid(),
						      &cluster_cond))) {
		error("Problem talking to database");
		goto end_it;
	}
	itr = list_iterator_create(temp_list);
	if (!cluster_names || all_clusters) {
		while ((cluster_rec = list_next(itr))) {
			if (slurmdb_setup_cluster_rec(cluster_rec) !=
			    SLURM_SUCCESS) {
				list_delete_item(itr);
			}
		}
	} else {
		itr2 = list_iterator_create(cluster_cond.cluster_list);
		while ((cluster_name = list_next(itr2))) {
			while ((cluster_rec = list_next(itr))) {
				if (!xstrcmp(cluster_name, cluster_rec->name))
					break;
			}
			if (!cluster_rec) {
				error("No cluster '%s' known by database.",
				      cluster_name);
				goto next;
			}

			if (slurmdb_setup_cluster_rec(cluster_rec) !=
			    SLURM_SUCCESS) {
				list_delete_item(itr);
			}
		next:
			list_iterator_reset(itr);
		}
		list_iterator_destroy(itr2);
	}
	list_iterator_destroy(itr);

end_it:
	FREE_NULL_LIST(cluster_cond.cluster_list);
	acct_storage_g_close_connection(&db_conn);

	if (temp_list && !list_count(temp_list)) {
		FREE_NULL_LIST(temp_list);
	}

	return temp_list;
}

extern void slurmdb_init_assoc_rec(slurmdb_assoc_rec_t *assoc,
					 bool free_it)
{
	if (!assoc)
		return;

	if (free_it)
		slurmdb_free_assoc_rec_members(assoc);
	memset(assoc, 0, sizeof(slurmdb_assoc_rec_t));

	assoc->def_qos_id = NO_VAL;
	assoc->is_def = NO_VAL16;

	/* assoc->grp_tres_mins = NULL; */
	/* assoc->grp_tres_run_mins = NULL; */
	/* assoc->grp_tres = NULL; */
	assoc->grp_jobs = NO_VAL;
	assoc->grp_jobs_accrue = NO_VAL;
	assoc->grp_submit_jobs = NO_VAL;
	assoc->grp_wall = NO_VAL;

	/* assoc->level_shares = NO_VAL; */

	/* assoc->max_tres_mins_pj = NULL; */
	/* assoc->max_tres_run_mins = NULL; */
	/* assoc->max_tres_pj = NULL; */
	assoc->max_jobs = NO_VAL;
	assoc->max_jobs_accrue = NO_VAL;
	assoc->min_prio_thresh = NO_VAL;
	assoc->max_submit_jobs = NO_VAL;
	assoc->max_wall_pj = NO_VAL;

	assoc->priority = NO_VAL;

	/* assoc->shares_norm = NO_VAL64; */
	assoc->shares_raw = NO_VAL;

	/* assoc->usage_efctv = 0; */
	/* assoc->usage_norm = (long double)NO_VAL; */
	/* assoc->usage_raw = 0; */
}

extern void slurmdb_init_clus_res_rec(slurmdb_clus_res_rec_t *clus_res,
				      bool free_it)
{
	if (!clus_res)
		return;

	if (free_it)
		_free_clus_res_rec_members(clus_res);
	memset(clus_res, 0, sizeof(slurmdb_clus_res_rec_t));
	clus_res->allowed = NO_VAL;
}

extern void slurmdb_init_cluster_rec(slurmdb_cluster_rec_t *cluster,
				     bool free_it)
{
	if (!cluster)
		return;

	if (free_it)
		_free_cluster_rec_members(cluster);
	memset(cluster, 0, sizeof(slurmdb_cluster_rec_t));
	cluster->flags      = NO_VAL;
	cluster->fed.state  = NO_VAL;
	slurm_mutex_init(&cluster->lock);
}

extern void slurmdb_init_federation_rec(slurmdb_federation_rec_t *federation,
					bool free_it)
{
	if (!federation)
		return;

	if (free_it)
		_free_federation_rec_members(federation);
	memset(federation, 0, sizeof(slurmdb_federation_rec_t));
	federation->flags = FEDERATION_FLAG_NOTSET;
}

extern void slurmdb_init_instance_rec(slurmdb_instance_rec_t *instance)
{
	if (!instance)
		return;

	memset(instance, 0, sizeof(slurmdb_instance_rec_t));

	/* instance->cluster = NULL; */
	/* instance->extra = NULL; */
	/* instance->instance_id = NULL; */
	/* instance->instance_type = NULL; */
	/* instance->node_name = NULL; */
	instance->time_end = NO_VAL;
	instance->time_start = NO_VAL;
}

extern void slurmdb_init_qos_rec(slurmdb_qos_rec_t *qos, bool free_it,
				 uint32_t init_val)
{
	if (!qos)
		return;

	if (free_it)
		slurmdb_free_qos_rec_members(qos);
	memset(qos, 0, sizeof(slurmdb_qos_rec_t));

	qos->flags = QOS_FLAG_NOTSET;

	qos->grace_time = init_val;
	qos->preempt_mode = (uint16_t)init_val;
	qos->preempt_exempt_time = init_val;
	qos->priority = init_val;

	/* qos->grp_tres_mins = NULL; */
	/* qos->grp_tres_run_mins = NULL; */
	/* qos->grp_tres = NULL; */
	qos->grp_jobs = init_val;
	qos->grp_jobs_accrue = init_val;
	qos->grp_submit_jobs = init_val;
	qos->grp_wall = init_val;

	qos->limit_factor = (double)init_val;

	/* qos->max_tres_mins_pj = NULL; */
	/* qos->max_tres_run_mins_pa = NULL; */
	/* qos->max_tres_run_mins_pu = NULL; */
	/* qos->max_tres_pa = NULL; */
	/* qos->max_tres_pj = NULL; */
	/* qos->max_tres_pu = NULL; */
	qos->max_jobs_pa = init_val;
	qos->max_jobs_pu = init_val;
	qos->max_jobs_accrue_pa = init_val;
	qos->max_jobs_accrue_pu = init_val;
	qos->min_prio_thresh = init_val;
	qos->max_submit_jobs_pa = init_val;
	qos->max_submit_jobs_pu = init_val;
	qos->max_wall_pj = init_val;

	/* qos->min_tres_pj = NULL; */

	qos->usage_factor = (double)init_val;
	qos->usage_thres = (double)init_val;
}

extern void slurmdb_init_res_rec(slurmdb_res_rec_t *res,
				 bool free_it)
{
	if (!res)
		return;

	if (free_it)
		_free_res_rec_members(res);
	memset(res, 0, sizeof(slurmdb_res_rec_t));
	res->count = NO_VAL;
	res->flags = SLURMDB_RES_FLAG_NOTSET;
	res->id = NO_VAL;
	res->last_consumed = NO_VAL;
	res->allocated = NO_VAL;
	res->type = SLURMDB_RESOURCE_NOTSET;
}

extern void slurmdb_init_wckey_rec(slurmdb_wckey_rec_t *wckey, bool free_it)
{
	if (!wckey)
		return;

	if (free_it)
		_free_wckey_rec_members(wckey);
	memset(wckey, 0, sizeof(slurmdb_wckey_rec_t));
	wckey->is_def = NO_VAL16;
}

extern void slurmdb_init_add_assoc_cond(slurmdb_add_assoc_cond_t *add_assoc,
					bool free_it)
{
	if (!add_assoc)
		return;

	if (free_it)
		slurmdb_free_add_assoc_cond_members(add_assoc);
	memset(add_assoc, 0, sizeof(*add_assoc));
	slurmdb_init_assoc_rec(&add_assoc->assoc, free_it);
}

extern void slurmdb_init_tres_cond(slurmdb_tres_cond_t *tres,
				    bool free_it)
{
	if (!tres)
		return;

	if (free_it)
		_free_tres_cond_members(tres);
	memset(tres, 0, sizeof(slurmdb_tres_cond_t));
	tres->count = NO_VAL;
}

extern void slurmdb_init_cluster_cond(slurmdb_cluster_cond_t *cluster,
				      bool free_it)
{
	if (!cluster)
		return;

	if (free_it)
		_free_cluster_cond_members(cluster);
	memset(cluster, 0, sizeof(slurmdb_cluster_cond_t));
	cluster->flags = NO_VAL;
}

extern void slurmdb_init_federation_cond(slurmdb_federation_cond_t *federation,
					 bool free_it)
{
	if (!federation)
		return;

	if (free_it)
		_free_federation_cond_members(federation);
	memset(federation, 0, sizeof(slurmdb_federation_cond_t));
}

extern void slurmdb_init_res_cond(slurmdb_res_cond_t *res,
				  bool free_it)
{
	if (!res)
		return;

	if (free_it)
		_free_res_cond_members(res);
	memset(res, 0, sizeof(slurmdb_res_cond_t));
	res->flags = SLURMDB_RES_FLAG_NOTSET;
}

extern char *slurmdb_qos_str(list_t *qos_list, uint32_t level)
{
	slurmdb_qos_rec_t *qos = NULL;

	if (!qos_list) {
		error("We need a qos list to translate");
		return NULL;
	} else if (!level) {
		debug2("no level");
		return "";
	}

	qos = list_find_first(qos_list, slurmdb_find_qos_in_list, &level);
	if (qos)
		return qos->name;
	else
		return NULL;
}

extern uint32_t str_2_slurmdb_qos(list_t *qos_list, char *level)
{
	list_itr_t *itr = NULL;
	slurmdb_qos_rec_t *qos = NULL;
	char *working_level = NULL;

	if (!qos_list) {
		error("We need a qos list to translate");
		return NO_VAL;
	} else if (!level) {
		debug2("no level");
		return 0;
	}
	if (level[0] == '+' || level[0] == '-')
		working_level = level+1;
	else
		working_level = level;

	itr = list_iterator_create(qos_list);
	while((qos = list_next(itr))) {
		if (!xstrcasecmp(working_level, qos->name))
			break;
	}
	list_iterator_destroy(itr);
	if (qos)
		return qos->id;
	else
		return NO_VAL;
}

extern char *slurmdb_federation_flags_str(uint32_t flags)
{
	char *federation_flags = NULL;

	if (flags & FEDERATION_FLAG_NOTSET)
		return xstrdup("NotSet");

#if 0
	/* Remove when there are actually flags since the flags will be
	 * comma-separated. */
	if (federation_flags)
		federation_flags[strlen(federation_flags)-1] = '\0';
#endif

	return federation_flags;
}

#define T(flag, str) { flag, XSTRINGIFY(flag), str }
static const struct {
	slurmdb_acct_flags_t flag;
	char *flag_str;
	char *str;
} slurmdb_acct_flags_map[] = {
	T(SLURMDB_ACCT_FLAG_DELETED, "Deleted"),
	T(SLURMDB_ACCT_FLAG_WASSOC, "WithAssociations"),
	T(SLURMDB_ACCT_FLAG_WCOORD, "WithCoordinators"),
	T(SLURMDB_ACCT_FLAG_USER_COORD_NO, "NoUsersAreCoords"),
	T(SLURMDB_ACCT_FLAG_USER_COORD, "UsersAreCoords"),
};
#undef T

static int _str_2_acct_flag(const char *flag_in,
			    slurmdb_acct_flags_t *flags_ptr)
{
	if (!flag_in || !flag_in[0])
		return SLURM_SUCCESS;

	for (int i = 0; i < ARRAY_SIZE(slurmdb_acct_flags_map); i++) {
		if (!xstrncasecmp(flag_in, slurmdb_acct_flags_map[i].str,
				  strlen(flag_in))) {
			*flags_ptr |= slurmdb_acct_flags_map[i].flag;
			return SLURM_SUCCESS;
		}
	}

	debug("%s: Unable to match %s to a slurmdbd_acct_flags_t flag",
	      __func__, flag_in);
	return EINVAL;
}

extern int str_2_slurmdb_acct_flags(const char *str,
				    slurmdb_acct_flags_t *flags_ptr)
{
	char *token, *my_flags, *last = NULL;
	int rc = SLURM_SUCCESS;

	/* Always reset flags */
	*flags_ptr = SLURMDB_ACCT_FLAG_NONE;

	my_flags = xstrdup(str);
	token = strtok_r(my_flags, ",", &last);
	while (token && !(rc = _str_2_acct_flag(token, flags_ptr)))
		token = strtok_r(NULL, ",", &last);

	xfree(my_flags);
	return rc;
}

extern char *slurmdb_acct_flags_2_str(slurmdb_acct_flags_t flags)
{
	char *acct_flags = NULL, *at = NULL;

	if (flags == SLURMDB_ACCT_FLAG_NONE)
		return xstrdup("None");

	for (int i = 0; i < ARRAY_SIZE(slurmdb_acct_flags_map); i++) {
		if ((slurmdb_acct_flags_map[i].flag & flags) ==
		    slurmdb_acct_flags_map[i].flag)
			xstrfmtcatat(acct_flags, &at, "%s%s",
				     (acct_flags ? "," : ""),
				     slurmdb_acct_flags_map[i].str);
	}

	return acct_flags;
}

#define T(flag, str) { flag, XSTRINGIFY(flag), str }
static const struct {
	slurmdb_assoc_flags_t flag;
	char *flag_str;
	char *str;
} slurmdb_assoc_flags_map[] = {
	T(ASSOC_FLAG_DELETED, "Deleted"),
	T(ASSOC_FLAG_NO_UPDATE, "NoUpdate"),
	T(ASSOC_FLAG_EXACT, "Exact"),
	T(ASSOC_FLAG_USER_COORD_NO, "NoUsersAreCoords"),
	T(ASSOC_FLAG_USER_COORD, "UsersAreCoords"),
};
#undef T

static int _str_2_assoc_flag(const char *flag_in,
			     slurmdb_assoc_flags_t *flags_ptr)
{
	if (!flag_in || !flag_in[0])
		return SLURM_SUCCESS;

	for (int i = 0; i < ARRAY_SIZE(slurmdb_assoc_flags_map); i++) {
		if (!xstrncasecmp(flag_in, slurmdb_assoc_flags_map[i].str,
				  strlen(flag_in))) {
			*flags_ptr |= slurmdb_assoc_flags_map[i].flag;
			return slurmdb_assoc_flags_map[i].flag;
		}
	}

	debug("%s: Unable to match %s to a slurmdbd_assoc_flags_t flag",
	      __func__, flag_in);
	return EINVAL;
}

extern int str_2_slurmdb_assoc_flags(const char *str,
				     slurmdb_assoc_flags_t *flags_ptr)
{
	char *token, *my_flags, *last = NULL;
	int rc = SLURM_SUCCESS;

	/* Always reset flags */
	*flags_ptr = ASSOC_FLAG_NONE;

	my_flags = xstrdup(str);
	token = strtok_r(my_flags, ",", &last);
	while (token && !(rc = _str_2_assoc_flag(token, flags_ptr)))
		token = strtok_r(NULL, ",", &last);

	xfree(my_flags);
	return rc;
}

extern char *slurmdb_assoc_flags_2_str(slurmdb_assoc_flags_t flags)
{
	char *assoc_flags = NULL, *at = NULL;

	if (flags == ASSOC_FLAG_NONE)
		return xstrdup("None");

	for (int i = 0; i < ARRAY_SIZE(slurmdb_assoc_flags_map); i++) {
		if ((slurmdb_assoc_flags_map[i].flag & flags) ==
		    slurmdb_assoc_flags_map[i].flag)
			xstrfmtcatat(assoc_flags, &at, "%s%s",
				     (assoc_flags ? "," : ""),
				     slurmdb_assoc_flags_map[i].str);
	}

	return assoc_flags;
}

static uint32_t _str_2_federation_flags(char *flags)
{
	return 0;
}

extern uint32_t str_2_federation_flags(char *flags, int option)
{
	uint32_t federation_flags = 0;
	char *token, *my_flags, *last = NULL;

	if (!flags) {
		error("We need a federation flags string to translate");
		return FEDERATION_FLAG_NOTSET;
	} else if (atoi(flags) == -1) {
		/* clear them all */
		federation_flags = INFINITE;
		federation_flags &= (~FEDERATION_FLAG_NOTSET &
				     ~FEDERATION_FLAG_ADD);
		return federation_flags;
	}

	my_flags = xstrdup(flags);
	token = strtok_r(my_flags, ",", &last);
	while (token) {
		federation_flags |= _str_2_federation_flags(token);
		token = strtok_r(NULL, ",", &last);
	}
	xfree(my_flags);

	if (!federation_flags)
		federation_flags = FEDERATION_FLAG_NOTSET;
	else if (option == '+')
		federation_flags |= FEDERATION_FLAG_ADD;
	else if (option == '-')
		federation_flags |= FEDERATION_FLAG_REMOVE;

	return federation_flags;
}

extern char *slurmdb_cluster_fed_states_str(uint32_t state)
{
	int  base        = (state & CLUSTER_FED_STATE_BASE);
	bool drain_flag  = (state & CLUSTER_FED_STATE_DRAIN);
	bool remove_flag = (state & CLUSTER_FED_STATE_REMOVE);

	if (base == CLUSTER_FED_STATE_ACTIVE) {
		if (remove_flag && drain_flag)
			return "DRAIN+REMOVE";
		else if (drain_flag)
			return "DRAIN";
		else
			return "ACTIVE";
	} else if (base == CLUSTER_FED_STATE_INACTIVE) {
		if (remove_flag && drain_flag)
			return "DRAINED+REMOVE";
		else if (drain_flag)
			return "DRAINED";
		else
			return "INACTIVE";
	} else if (base == CLUSTER_FED_STATE_NA)
		return "NA";

	return "?";
}

extern uint32_t str_2_cluster_fed_states(char *state)
{
	uint32_t fed_state = 0;

	if (!state) {
		error("We need a cluster federation state string to translate");
		return SLURM_ERROR;
	}

	if (!xstrncasecmp(state, "Active", strlen(state)))
		fed_state = CLUSTER_FED_STATE_ACTIVE;
	else if (!xstrncasecmp(state, "Inactive", strlen(state)))
		fed_state = CLUSTER_FED_STATE_INACTIVE;
	else if (!xstrncasecmp(state, "DRAIN", strlen(state))) {
		fed_state = CLUSTER_FED_STATE_ACTIVE;
		fed_state |= CLUSTER_FED_STATE_DRAIN;
	} else if (!xstrncasecmp(state, "DRAIN+REMOVE", strlen(state))) {
		fed_state = CLUSTER_FED_STATE_ACTIVE;
		fed_state |= (CLUSTER_FED_STATE_DRAIN |
			      CLUSTER_FED_STATE_REMOVE);
	}

	return fed_state;
}

extern char *slurmdb_job_flags_str(uint32_t flags)
{
	char *job_flags = NULL;

	if (flags == SLURMDB_JOB_FLAG_NONE)
		return xstrdup("None");

	if (flags & SLURMDB_JOB_FLAG_NOTSET)
		xstrcat(job_flags, "SchedNotSet");
	else if (flags & SLURMDB_JOB_FLAG_SUBMIT)
		xstrcat(job_flags, "SchedSubmit");
	else if (flags & SLURMDB_JOB_FLAG_SCHED)
		xstrcat(job_flags, "SchedMain");
	else if (flags & SLURMDB_JOB_FLAG_BACKFILL)
		xstrcat(job_flags, "SchedBackfill");

	if (flags & SLURMDB_JOB_FLAG_START_R)
		xstrfmtcat(job_flags, "%sStartReceived", job_flags ? "," : "");

	return job_flags;
}

extern uint32_t str_2_job_flags(char *flags)
{
	uint32_t job_flags = 0;
	char *token, *my_flags, *last = NULL;

	if (!flags) {
		error("We need a server job flags string to translate");
		return SLURMDB_JOB_FLAG_NONE;
	}

	my_flags = xstrdup(flags);
	token = strtok_r(my_flags, ",", &last);
	while (token) {
		job_flags |= _str_2_job_flags(token);
		if (job_flags & SLURMDB_JOB_FLAG_NOTSET) {
			error("%s: Invalid job flag %s", __func__, token);
			xfree(my_flags);
			return SLURMDB_JOB_FLAG_NOTSET;
		}
		token = strtok_r(NULL, ",", &last);
	}
	xfree(my_flags);

	return job_flags;
}

extern char *slurmdb_qos_flags_str(slurmdb_qos_flags_t flags)
{
	char *qos_flags = NULL, *at = NULL;

	if (flags == QOS_FLAG_NOTSET)
		return xstrdup("NotSet");

	for (int i = 0; i < ARRAY_SIZE(slurmdb_qos_flags_map); i++) {
		if ((slurmdb_qos_flags_map[i].flag & flags) ==
		    slurmdb_qos_flags_map[i].flag)
			xstrfmtcatat(qos_flags, &at, "%s%s",
				     (qos_flags ? "," : ""),
				     slurmdb_qos_flags_map[i].str);
	}

	return qos_flags;
}

extern slurmdb_qos_flags_t str_2_qos_flags(char *flags, int option)
{
	slurmdb_qos_flags_t qos_flags = 0;
	char *token, *my_flags, *last = NULL;
	int rc = SLURM_SUCCESS;

	if (!flags) {
		error("We need a qos flags string to translate");
		return QOS_FLAG_NOTSET;
	} else if (atoi(flags) == -1) {
		/* clear them all */
		qos_flags = INFINITE;
		qos_flags &= (~QOS_FLAG_NOTSET &
			      ~QOS_FLAG_ADD);
		return qos_flags;
	}

	my_flags = xstrdup(flags);
	token = strtok_r(my_flags, ",", &last);
	while (token && !(rc = _str_2_qos_flags(token, &qos_flags)))
		token = strtok_r(NULL, ",", &last);
	xfree(my_flags);

	if (rc || !qos_flags)
		qos_flags = QOS_FLAG_NOTSET;
	else if (option == '+')
		qos_flags |= QOS_FLAG_ADD;
	else if (option == '-')
		qos_flags |= QOS_FLAG_REMOVE;


	return qos_flags;
}

extern char *slurmdb_res_flags_str(uint32_t flags)
{
	char *res_flags = NULL;

	if (flags & SLURMDB_RES_FLAG_NOTSET)
		return xstrdup("NotSet");

	if (flags & SLURMDB_RES_FLAG_ADD)
		xstrcat(res_flags, "Add,");
	if (flags & SLURMDB_RES_FLAG_REMOVE)
		xstrcat(res_flags, "Remove,");
	if (flags & SLURMDB_RES_FLAG_ABSOLUTE)
		xstrcat(res_flags, "Absolute,");

	if (res_flags)
		res_flags[strlen(res_flags)-1] = '\0';

	return res_flags;
}

extern uint32_t str_2_res_flags(char *flags, int option)
{
	uint32_t res_flags = 0;
	char *token, *my_flags, *last = NULL;

	if (!flags) {
		error("We need a server resource flags string to translate");
		return SLURMDB_RES_FLAG_NOTSET;
	} else if (atoi(flags) == -1) {
		/* clear them all */
		res_flags = INFINITE;
		res_flags &= (SLURMDB_RES_FLAG_NOTSET &
			      ~SLURMDB_RES_FLAG_ADD);
		return res_flags;
	}

	my_flags = xstrdup(flags);
	token = strtok_r(my_flags, ",", &last);
	while (token) {
		res_flags |= _str_2_res_flags(token);
		token = strtok_r(NULL, ",", &last);
	}
	xfree(my_flags);

	if (!res_flags)
		res_flags = SLURMDB_RES_FLAG_NOTSET;
	else if (option == '+')
		res_flags |= SLURMDB_RES_FLAG_ADD;
	else if (option == '-')
		res_flags |= SLURMDB_RES_FLAG_REMOVE;


	return res_flags;
}

extern char *slurmdb_res_type_str(slurmdb_resource_type_t type)
{
	switch (type) {
	case SLURMDB_RESOURCE_NOTSET:
		return "Not Set";
		break;
	case SLURMDB_RESOURCE_LICENSE:
		return "License";
		break;
	}
	return "Unknown";
}

extern char *slurmdb_admin_level_str(slurmdb_admin_level_t level)
{
	switch(level) {
	case SLURMDB_ADMIN_NOTSET:
		return "Not Set";
		break;
	case SLURMDB_ADMIN_NONE:
		return "None";
		break;
	case SLURMDB_ADMIN_OPERATOR:
		return "Operator";
		break;
	case SLURMDB_ADMIN_SUPER_USER:
		return "Administrator";
		break;
	}
	return "Unknown";
}

extern slurmdb_admin_level_t str_2_slurmdb_admin_level(char *level)
{
	if (!level) {
		return SLURMDB_ADMIN_NOTSET;
	} else if (!xstrncasecmp(level, "None", 1)) {
		return SLURMDB_ADMIN_NONE;
	} else if (!xstrncasecmp(level, "Operator", 1)) {
		return SLURMDB_ADMIN_OPERATOR;
	} else if (!xstrncasecmp(level, "SuperUser", 1)
		   || !xstrncasecmp(level, "Admin", 1)) {
		return SLURMDB_ADMIN_SUPER_USER;
	} else {
		return SLURMDB_ADMIN_NOTSET;
	}
}

extern int slurmdb_ping(char *rem_host)
{
	int rc;
	persist_conn_t *persist_conn = xmalloc(sizeof(*persist_conn));

	persist_conn->cluster_name = xstrdup(slurm_conf.cluster_name);
	persist_conn->flags = PERSIST_FLAG_DBD | PERSIST_FLAG_SUPPRESS_ERR;
	persist_conn->r_uid = SLURM_AUTH_UID_ANY;
	persist_conn->rem_host = xstrdup(rem_host);
	persist_conn->rem_port = slurm_conf.accounting_storage_port;
	persist_conn->timeout = slurm_conf.msg_timeout * 1000;
	persist_conn->version = SLURM_PROTOCOL_VERSION;

	rc = slurm_persist_conn_open(persist_conn);
	slurm_persist_conn_destroy(persist_conn);

	return rc;
}

static void _ping_slurmdbd(slurmdbd_ping_t *ping, int offset)
{
	DEF_TIMERS;

	ping->offset = offset;

	START_TIMER;
	ping->pinged = !slurmdb_ping(ping->hostname);
	END_TIMER;

	ping->latency = DELTA_TIMER;
}

extern slurmdbd_ping_t *slurmdb_ping_all(void)
{
	slurmdbd_ping_t *pings = NULL;
	int slurmdbd_cnt = 0;
	int i = 0;

	if (slurm_conf.accounting_storage_host)
		slurmdbd_cnt++;
	else
		return NULL;

	if (slurm_conf.accounting_storage_backup_host)
		slurmdbd_cnt++;

	/* Allocate enough space for backup ping if needed */
	pings = xcalloc(slurmdbd_cnt + 1, sizeof(*pings));

	pings[i].hostname = slurm_conf.accounting_storage_host;
	_ping_slurmdbd(&pings[i], i);

	/*
	 * Don't ping backup if primary is good. slurmdbd in background mode
	 * doesn't handle RPCs currently so it would appear down anyways.
	 */
	if (pings[i].pinged)
		return pings;

	if (!slurm_conf.accounting_storage_backup_host)
		return pings;

	i++;
	pings[i].hostname = slurm_conf.accounting_storage_backup_host;
	_ping_slurmdbd(&pings[i], i);

	return pings;
}

/* This reorders the list into a alphabetical hierarchy returned in a
 * separate list. The original list is not affected. */
extern list_t *slurmdb_get_hierarchical_sorted_assoc_list(
	list_t *assoc_list)
{
	list_t *slurmdb_hierarchical_rec_list;
	list_t *ret_list = list_create(NULL);

	slurmdb_hierarchical_rec_list =
		slurmdb_get_acct_hierarchical_rec_list(assoc_list);

	_append_hierarchical_children_ret_list(ret_list,
					       slurmdb_hierarchical_rec_list);
	FREE_NULL_LIST(slurmdb_hierarchical_rec_list);

	return ret_list;
}

/* This reorders the list into a alphabetical hierarchy. */
extern void slurmdb_sort_hierarchical_assoc_list(list_t *assoc_list)
{
	(void) list_sort(assoc_list, (ListCmpF)_sort_assoc_by_lineage_asc);
}

extern list_t *slurmdb_get_acct_hierarchical_rec_list(list_t *assoc_list)
{
	slurmdb_hierarchical_rec_t *par_arch_rec = NULL;
	slurmdb_hierarchical_rec_t *last_acct_parent = NULL;
	slurmdb_hierarchical_rec_t *last_parent = NULL;
	slurmdb_hierarchical_rec_t *arch_rec = NULL;
	slurmdb_assoc_rec_t *assoc = NULL;

	xhash_t *all_parents = xhash_init(_arch_hash_rec_id, NULL);
	list_t *arch_rec_list =
		list_create(slurmdb_destroy_hierarchical_rec);
	list_itr_t *itr;

	/*
	 * The list should already be sorted by lineage, do it anyway
	 * just to make sure it is correct.
	 */
	slurmdb_sort_hierarchical_assoc_list(assoc_list);
	itr = list_iterator_create(assoc_list);

	while((assoc = list_next(itr))) {
		arch_rec = xmalloc(sizeof(slurmdb_hierarchical_rec_t));
		arch_rec->children =
			list_create(slurmdb_destroy_hierarchical_rec);
		arch_rec->assoc = assoc;

		/*
		 * To speed things up we are first looking if we have
		 * a parent_id to look for.  If that doesn't work see
		 * if the last parent we had was what we are looking
		 * for.  Then if that isn't panning out look at the
		 * last account parent.  If still we don't have it we
		 * will look for it in the list.  If it isn't there we
		 * will just add it to the parent and call it good
		 */
		if (!assoc->parent_id) {
			arch_rec->sort_name = assoc->cluster;
			list_append(arch_rec_list, arch_rec);
			xhash_add(all_parents, arch_rec);
			continue;
		}

		if (assoc->user)
			arch_rec->sort_name = assoc->user;
		else
			arch_rec->sort_name = assoc->acct;

		if (last_parent &&
		    (assoc->parent_id == last_parent->assoc->id) &&
		    !xstrcmp(assoc->cluster, last_parent->assoc->cluster)) {
			par_arch_rec = last_parent;
		} else if (last_acct_parent &&
			   (assoc->parent_id == last_acct_parent->assoc->id) &&
			   !xstrcmp(assoc->cluster,
				    last_acct_parent->assoc->cluster)) {
			par_arch_rec = last_acct_parent;
		} else {
			char *key = _create_hash_rec_id(assoc, true);
			par_arch_rec = xhash_get(all_parents, key, strlen(key));
			xfree(key);
			if (par_arch_rec) {
				last_parent = par_arch_rec;
				if (!assoc->user)
					last_acct_parent = par_arch_rec;
			}
		}

		if (!par_arch_rec) {
			list_append(arch_rec_list, arch_rec);
			last_parent = last_acct_parent = arch_rec;
		} else
			list_append(par_arch_rec->children, arch_rec);

		if (!assoc->user) /* Users are never parent assocs */
			xhash_add(all_parents, arch_rec);
	}
	list_iterator_destroy(itr);

	xhash_free(all_parents);
/*	info("got %d", list_count(arch_rec_list)); */
	_sort_slurmdb_hierarchical_rec_list(arch_rec_list);

	return arch_rec_list;
}

/* IN/OUT: tree_list a list of slurmdb_print_tree_t's */
extern char *slurmdb_tree_name_get(char *name, char *parent, list_t *tree_list)
{
	list_itr_t *itr = NULL;
	slurmdb_print_tree_t *slurmdb_print_tree = NULL;
	slurmdb_print_tree_t *par_slurmdb_print_tree = NULL;

	if (!tree_list)
		return NULL;

	itr = list_iterator_create(tree_list);
	while((slurmdb_print_tree = list_next(itr))) {
		/* we don't care about users in this list.  They are
		   only there so we don't leak memory */
		if (slurmdb_print_tree->user)
			continue;

		if (!xstrcmp(name, slurmdb_print_tree->name))
			break;
		else if (parent && !xstrcmp(parent, slurmdb_print_tree->name))
			par_slurmdb_print_tree = slurmdb_print_tree;

	}
	list_iterator_destroy(itr);

	if (parent && slurmdb_print_tree)
		return slurmdb_print_tree->print_name;

	slurmdb_print_tree = xmalloc(sizeof(slurmdb_print_tree_t));
	slurmdb_print_tree->name = xstrdup(name);
	if (par_slurmdb_print_tree)
		slurmdb_print_tree->spaces =
			xstrdup_printf(" %s", par_slurmdb_print_tree->spaces);
	else
		slurmdb_print_tree->spaces = xstrdup("");

	/* user account */
	if (name[0] == '|') {
		slurmdb_print_tree->print_name = xstrdup_printf(
			"%s%s", slurmdb_print_tree->spaces, parent);
		slurmdb_print_tree->user = 1;
	} else
		slurmdb_print_tree->print_name = xstrdup_printf(
			"%s%s", slurmdb_print_tree->spaces, name);

	list_append(tree_list, slurmdb_print_tree);

	return slurmdb_print_tree->print_name;
}

extern int set_qos_bitstr_from_list(bitstr_t *valid_qos, list_t *qos_list)
{
	list_itr_t *itr = NULL;
	int rc = SLURM_SUCCESS;
	char *temp_char = NULL;

	xassert(valid_qos);

	if (!qos_list)
		return SLURM_ERROR;

	itr = list_iterator_create(qos_list);
	while((temp_char = list_next(itr)))
		_set_qos_bit_from_string(valid_qos, temp_char);
	list_iterator_destroy(itr);

	return rc;
}

extern const char *rollup_interval_to_string(int interval)
{
	switch (interval) {
	case DBD_ROLLUP_HOUR:
		return "Hour";
	case DBD_ROLLUP_DAY:
		return "Day";
	case DBD_ROLLUP_MONTH:
		return "Month";
	default:
		return "Unknown";
	}
}

extern int set_qos_bitstr_from_string(bitstr_t *valid_qos, char *names)
{
	int rc = SLURM_SUCCESS;
	int i=0, start=0;
	char *name = NULL;

	xassert(valid_qos);

	if (!names)
		return SLURM_ERROR;

	/* skip the first comma if it is one */
	if (names[i] == ',')
		i++;

	start = i;
	while (names[i]) {
		//info("got %d - %d = %d", i, start, i-start);
		if (names[i] == ',') {
			/* If there is a comma at the end just
			   ignore it */
			if (!names[i+1])
				break;

			name = xstrndup(names+start, (i-start));
			/* info("got %s %d", name, i-start); */
			_set_qos_bit_from_string(valid_qos, name);
			xfree(name);
			i++;
			start = i;
		}
		i++;
	}

	name = xstrndup(names+start, (i-start));
	/* info("got %s %d", name, i-start); */
	_set_qos_bit_from_string(valid_qos, name);
	xfree(name);

	return rc;
}

extern char *get_qos_complete_str_bitstr(list_t *qos_list, bitstr_t *valid_qos)
{
	list_t *temp_list = NULL;
	char *temp_char = NULL;
	char *print_this = NULL;
	int i = 0;

	if (!qos_list || !list_count(qos_list)
	    || !valid_qos || (bit_ffs(valid_qos) == -1))
		return xstrdup("");

	temp_list = list_create(NULL);

	for(i=0; i<bit_size(valid_qos); i++) {
		if (!bit_test(valid_qos, i))
			continue;
		if ((temp_char = slurmdb_qos_str(qos_list, i)))
			list_append(temp_list, temp_char);
	}
	print_this = slurm_char_list_to_xstr(temp_list);
	FREE_NULL_LIST(temp_list);

	if (!print_this)
		return xstrdup("");

	return print_this;
}

extern list_t *get_qos_name_list(list_t *qos_list, list_t *num_qos_list)
{
	list_t *temp_list;
	char *temp_char;
	list_itr_t *itr;
	int option;

	if (!qos_list || !list_count(qos_list)
	    || !num_qos_list || !list_count(num_qos_list))
		return NULL;

	temp_list = list_create(xfree_ptr);

	itr = list_iterator_create(num_qos_list);
	while((temp_char = list_next(itr))) {
		option = 0;
		if (temp_char[0] == '+' || temp_char[0] == '-') {
			option = temp_char[0];
			temp_char++;
		}
		temp_char = slurmdb_qos_str(qos_list, atoi(temp_char));
		if (temp_char) {
			if (option)
				list_append(temp_list, xstrdup_printf(
						    "%c%s", option, temp_char));
			else
				list_append(temp_list, xstrdup(temp_char));
		}
	}
	list_iterator_destroy(itr);
	return temp_list;
}

extern char *get_qos_complete_str(list_t *qos_list, list_t *num_qos_list)
{
	list_t *temp_list;
	char *print_this;

	if (!qos_list || !list_count(qos_list) || !num_qos_list ||
	    !list_count(num_qos_list))
		return xstrdup("");

	temp_list = get_qos_name_list(qos_list, num_qos_list);

	print_this = slurm_char_list_to_xstr(temp_list);
	FREE_NULL_LIST(temp_list);

	if (!print_this)
		return xstrdup("");

	return print_this;
}

extern char *get_classification_str(uint16_t class)
{
	bool classified = class & SLURMDB_CLASSIFIED_FLAG;
	slurmdb_classification_type_t type = class & SLURMDB_CLASS_BASE;

	switch(type) {
	case SLURMDB_CLASS_NONE:
		return NULL;
		break;
	case SLURMDB_CLASS_CAPACITY:
		if (classified)
			return "*Capacity";
		else
			return "Capacity";
		break;
	case SLURMDB_CLASS_CAPABILITY:
		if (classified)
			return "*Capability";
		else
			return "Capability";
		break;
	case SLURMDB_CLASS_CAPAPACITY:
		if (classified)
			return "*Capapacity";
		else
			return "Capapacity";
		break;
	default:
		if (classified)
			return "*Unknown";
		else
			return "Unknown";
		break;
	}
}

extern uint16_t str_2_classification(char *class)
{
	uint16_t type = 0;
	if (!class)
		return type;

	if (xstrcasestr(class, "capac"))
		type = SLURMDB_CLASS_CAPACITY;
	else if (xstrcasestr(class, "capab"))
		type = SLURMDB_CLASS_CAPABILITY;
	else if (xstrcasestr(class, "capap"))
		type = SLURMDB_CLASS_CAPAPACITY;

	if (xstrcasestr(class, "*"))
		type |= SLURMDB_CLASSIFIED_FLAG;
	else if (xstrcasestr(class, "class"))
		type |= SLURMDB_CLASSIFIED_FLAG;

	return type;
}

extern char *slurmdb_problem_str_get(uint16_t problem)
{
	slurmdb_problem_type_t type = problem;

	switch(type) {
	case SLURMDB_PROBLEM_NOT_SET:
		return NULL;
		break;
	case SLURMDB_PROBLEM_ACCT_NO_ASSOC:
		return "Account has no Associations";
		break;
	case SLURMDB_PROBLEM_ACCT_NO_USERS:
		return "Account has no users";
		break;
	case SLURMDB_PROBLEM_USER_NO_ASSOC:
		return "User has no Associations";
		break;
	case SLURMDB_PROBLEM_USER_NO_UID:
		return "User does not have a uid";
		break;
	default:
		return "Unknown";
		break;
	}
}

extern uint16_t str_2_slurmdb_problem(char *problem)
{
	uint16_t type = 0;

	if (!problem)
		return type;

	if (xstrcasestr(problem, "account no assocs"))
		type = SLURMDB_PROBLEM_USER_NO_ASSOC;
	else if (xstrcasestr(problem, "account no users"))
		type = SLURMDB_PROBLEM_ACCT_NO_USERS;
	else if (xstrcasestr(problem, "user no assocs"))
		type = SLURMDB_PROBLEM_USER_NO_ASSOC;
	else if (xstrcasestr(problem, "user no uid"))
		type = SLURMDB_PROBLEM_USER_NO_UID;

	return type;
}

extern void log_assoc_rec(slurmdb_assoc_rec_t *assoc_ptr,
			  list_t *qos_list)
{
	char *tmp_char = NULL;

	xassert(assoc_ptr);

	if (get_log_level() < LOG_LEVEL_DEBUG2)
		return;

	debug2("association rec id : %u", assoc_ptr->id);
	debug2("  acct             : %s", assoc_ptr->acct);
	debug2("  cluster          : %s", assoc_ptr->cluster);
	debug2("  comment          : %s", assoc_ptr->comment);

	if (assoc_ptr->shares_raw == INFINITE)
		debug2("  RawShares        : NONE");
	else if (assoc_ptr->shares_raw != NO_VAL)
		debug2("  RawShares        : %u", assoc_ptr->shares_raw);

	if (assoc_ptr->def_qos_id)
		debug2("  Default QOS      : %s",
		       slurmdb_qos_str(qos_list, assoc_ptr->def_qos_id));
	else
		debug2("  Default QOS      : NONE");

	debug2("  GrpTRESMins      : %s",
	       assoc_ptr->grp_tres_mins ?
	       assoc_ptr->grp_tres_mins : "NONE");
	debug2("  GrpTRESRunMins   : %s",
	       assoc_ptr->grp_tres_run_mins ?
	       assoc_ptr->grp_tres_run_mins : "NONE");
	debug2("  GrpTRES          : %s",
	       assoc_ptr->grp_tres ?
	       assoc_ptr->grp_tres : "NONE");

	if (assoc_ptr->grp_jobs == INFINITE)
		debug2("  GrpJobs          : NONE");
	else if (assoc_ptr->grp_jobs != NO_VAL)
		debug2("  GrpJobs          : %u", assoc_ptr->grp_jobs);

	if (assoc_ptr->grp_jobs_accrue == INFINITE)
		debug2("  GrpJobsAccrue    : NONE");
	else if (assoc_ptr->grp_jobs_accrue != NO_VAL)
		debug2("  GrpJobsAccrue    : %u", assoc_ptr->grp_jobs_accrue);

	if (assoc_ptr->grp_submit_jobs == INFINITE)
		debug2("  GrpSubmitJobs    : NONE");
	else if (assoc_ptr->grp_submit_jobs != NO_VAL)
		debug2("  GrpSubmitJobs    : %u", assoc_ptr->grp_submit_jobs);

	if (assoc_ptr->grp_wall == INFINITE)
		debug2("  GrpWall          : NONE");
	else if (assoc_ptr->grp_wall != NO_VAL) {
		char time_buf[32];
		mins2time_str((time_t) assoc_ptr->grp_wall,
			      time_buf, sizeof(time_buf));
		debug2("  GrpWall          : %s", time_buf);
	}

	tmp_char = slurmdb_assoc_flags_2_str(assoc_ptr->flags);
	debug2("  Flags            : %s", tmp_char);
	xfree(tmp_char);

	debug2("  Lineage          : %s", assoc_ptr->lineage);

	debug2("  MaxTRESMins      : %s",
	       assoc_ptr->max_tres_mins_pj ?
	       assoc_ptr->max_tres_mins_pj : "NONE");
	debug2("  MaxTRESRunMins   : %s",
	       assoc_ptr->max_tres_run_mins ?
	       assoc_ptr->max_tres_run_mins : "NONE");
	debug2("  MaxTRESPerJob    : %s",
	       assoc_ptr->max_tres_pj ?
	       assoc_ptr->max_tres_pj : "NONE");
	debug2("  MaxTRESPerNode   : %s",
	       assoc_ptr->max_tres_pn ?
	       assoc_ptr->max_tres_pn : "NONE");

	if (assoc_ptr->max_jobs == INFINITE)
		debug2("  MaxJobs          : NONE");
	else if (assoc_ptr->max_jobs != NO_VAL)
		debug2("  MaxJobs          : %u", assoc_ptr->max_jobs);

	if (assoc_ptr->max_jobs_accrue == INFINITE)
		debug2("  MaxJobsAccrue    : NONE");
	else if (assoc_ptr->max_jobs_accrue != NO_VAL)
		debug2("  MaxJobsAccrue    : %u", assoc_ptr->max_jobs_accrue);

	if (assoc_ptr->min_prio_thresh == INFINITE)
		debug2("  MinPrioThresh    : NONE");
	else if (assoc_ptr->min_prio_thresh != NO_VAL)
		debug2("  MinPrioThresh    : %u", assoc_ptr->min_prio_thresh);

	if (assoc_ptr->max_submit_jobs == INFINITE)
		debug2("  MaxSubmitJobs    : NONE");
	else if (assoc_ptr->max_submit_jobs != NO_VAL)
		debug2("  MaxSubmitJobs    : %u", assoc_ptr->max_submit_jobs);

	if (assoc_ptr->max_wall_pj == INFINITE)
		debug2("  MaxWall          : NONE");
	else if (assoc_ptr->max_wall_pj != NO_VAL) {
		char time_buf[32];
		mins2time_str((time_t) assoc_ptr->max_wall_pj,
			      time_buf, sizeof(time_buf));
		debug2("  MaxWall          : %s", time_buf);
	}

	if (assoc_ptr->qos_list) {
		char *temp_char = get_qos_complete_str(qos_list,
						       assoc_ptr->qos_list);
		if (temp_char) {
			debug2("  Qos              : %s", temp_char);
			xfree(temp_char);
			if (assoc_ptr->usage && assoc_ptr->usage->valid_qos) {
				temp_char = get_qos_complete_str_bitstr(
					qos_list, assoc_ptr->usage->valid_qos);
				debug3("  Valid Qos        : %s", temp_char);
				xfree(temp_char);
			}
		}
	} else {
		debug2("  Qos              : %s", "Normal");
	}

	if (assoc_ptr->parent_acct)
		debug2("  ParentAccount    : %s", assoc_ptr->parent_acct);
	if (assoc_ptr->partition)
		debug2("  Partition        : %s", assoc_ptr->partition);
	if (assoc_ptr->user)
		debug2("  User             : %s(%u)",
		       assoc_ptr->user, assoc_ptr->uid);

	if (assoc_ptr->usage) {
		if (!fuzzy_equal(assoc_ptr->usage->shares_norm, NO_VAL))
			debug2("  NormalizedShares : %f",
			       assoc_ptr->usage->shares_norm);

		if (assoc_ptr->usage->level_shares != NO_VAL)
			debug2("  LevelShares      : %u",
			       assoc_ptr->usage->level_shares);


		debug2("  UsedJobs         : %u", assoc_ptr->usage->used_jobs);
		debug2("  RawUsage         : %Lf", assoc_ptr->usage->usage_raw);
	}
}

extern int slurmdb_report_set_start_end_time(time_t *start, time_t *end)
{
	time_t my_time = time(NULL);
	time_t temp_time;
	struct tm start_tm;
	struct tm end_tm;
	int sent_start = (*start), sent_end = (*end);

//	info("now got %d and %d sent", (*start), (*end));
	/* Default is going to be the last day */
	if (!sent_end) {
		if (!localtime_r(&my_time, &end_tm)) {
			error("Couldn't get localtime from end %ld",
			      (long)my_time);
			return SLURM_ERROR;
		}
		end_tm.tm_hour = 0;
		//(*end) = slurm_mktime(&end_tm);
	} else {
		temp_time = sent_end;
		if (!localtime_r(&temp_time, &end_tm)) {
			error("Couldn't get localtime from user end %ld",
			      (long)my_time);
			return SLURM_ERROR;
		}
	}

	if (end_tm.tm_sec != 0)
		end_tm.tm_min++;
	if (end_tm.tm_min != 0)
		end_tm.tm_hour++;
	end_tm.tm_sec = 0;
	end_tm.tm_min = 0;
	(*end) = slurm_mktime(&end_tm);

	if (!sent_start) {
		if (!localtime_r(&my_time, &start_tm)) {
			error("Couldn't get localtime from start %ld",
			      (long)my_time);
			return SLURM_ERROR;
		}
		start_tm.tm_hour = 0;
		start_tm.tm_mday--;
		//(*start) = slurm_mktime(&start_tm);
	} else {
		temp_time = sent_start;
		if (!localtime_r(&temp_time, &start_tm)) {
			error("Couldn't get localtime from user start %ld",
			      (long)my_time);
			return SLURM_ERROR;
		}
	}
	start_tm.tm_sec = 0;
	start_tm.tm_min = 0;
	(*start) = slurm_mktime(&start_tm);

	if ((*end)-(*start) < 3600)
		(*end) = (*start) + 3600;
/* 	info("now got %d and %d sent", (*start), (*end)); */
/* 	char start_char[20]; */
/* 	char end_char[20]; */
/* 	time_t my_start = (*start); */
/* 	time_t my_end = (*end); */

/* 	slurm_make_time_str(&my_start,  */
/* 			    start_char, sizeof(start_char)); */
/* 	slurm_make_time_str(&my_end, */
/* 			    end_char, sizeof(end_char)); */
/* 	info("which is %s - %s", start_char, end_char); */
	return SLURM_SUCCESS;
}

/* Convert a string to a duration in Months or Days
 * input formats:
 *   <integer>                defaults to Months
 *   <integer>Months
 *   <integer>Days
 *   <integer>Hours
 *
 * output:
 *   SLURMDB_PURGE_MONTHS | <integer>  if input is in Months
 *   SLURMDB_PURGE_DAYS   | <integer>  if input is in Days
 *   SLURMDB_PURGE_HOURS  | <integer>  if input in in Hours
 *   0 on error
 */
extern uint32_t slurmdb_parse_purge(char *string)
{
	int i = 0;
	uint32_t purge = NO_VAL;

	xassert(string);

	while(string[i]) {
		if ((string[i] >= '0') && (string[i] <= '9')) {
			if (purge == NO_VAL)
				purge = 0;
                        purge = (purge * 10) + (string[i] - '0');
                } else
			break;
		i++;
	}

	if (purge != NO_VAL) {
		int len = strlen(string+i);
		if (!len || !xstrncasecmp("months", string+i, MAX(len, 1))) {
			purge |= SLURMDB_PURGE_MONTHS;
		} else if (!xstrncasecmp("hours", string+i, MAX(len, 1))) {
			purge |= SLURMDB_PURGE_HOURS;
		} else if (!xstrncasecmp("days", string+i, MAX(len, 1))) {
			purge |= SLURMDB_PURGE_DAYS;
		} else {
			error("Invalid purge unit '%s', valid options "
			      "are hours, days, or months", string+i);
			purge = NO_VAL;
		}
	} else
		error("Invalid purge string '%s'", string);

	return purge;
}

extern char *slurmdb_purge_string(uint32_t purge, char *string, int len,
				  bool with_archive)
{
	uint32_t units;

	if (purge == NO_VAL) {
		snprintf(string, len, "NONE");
		return string;
	}

	units = SLURMDB_PURGE_GET_UNITS(purge);
	if (SLURMDB_PURGE_IN_HOURS(purge)) {
		if (with_archive && SLURMDB_PURGE_ARCHIVE_SET(purge))
			snprintf(string, len, "%u hours*", units);
		else
			snprintf(string, len, "%u hours", units);
	} else if (SLURMDB_PURGE_IN_DAYS(purge)) {
		if (with_archive && SLURMDB_PURGE_ARCHIVE_SET(purge))
			snprintf(string, len, "%u days*", units);
		else
			snprintf(string, len, "%u days", units);
	} else {
		if (with_archive && SLURMDB_PURGE_ARCHIVE_SET(purge))
			snprintf(string, len, "%u months*", units);
		else
			snprintf(string, len, "%u months", units);
	}

	return string;
}

typedef struct {
	bool add_set;
	bool equal_set;
	int option;
	list_t *qos_list;
} qos_char_list_args_t;

static int _slurmdb_addto_qos_char_list_internal(list_t *char_list, char *name,
						 void *args_in)
{
	char *tmp_name;
	uint32_t id;
	qos_char_list_args_t *args = args_in;

	int tmp_option = args->option;
	if ((name[0] == '+') || (name[0] == '-')) {
		tmp_option = name[0];
		name++;
	}

	id = str_2_slurmdb_qos(args->qos_list, name);
	if (id == NO_VAL) {
		char *tmp = _get_qos_list_str(args->qos_list);
		error("You gave a bad qos '%s'. Valid QOS's are %s", name, tmp);
		xfree(tmp);
		list_flush(char_list);
		return SLURM_ERROR;
	}

	if (tmp_option) {
		if (args->equal_set) {
			error("You can't set qos equal to something and then add or subtract from it in the same line");
			list_flush(char_list);
			return SLURM_ERROR;
		}
		args->add_set = 1;
		tmp_name = xstrdup_printf("%c%u", tmp_option, id);
	} else {
		if (args->add_set) {
			error("You can't set qos equal to something and then add or subtract from it in the same line");
			list_flush(char_list);
			return SLURM_ERROR;
		}
		args->equal_set = 1;
		tmp_name = xstrdup_printf("%u", id);
	}

	if (!list_find_first(char_list, slurm_find_char_in_list, tmp_name)) {
		list_append(char_list, tmp_name);
		return 1;
	} else {
		xfree(tmp_name);
		return 0;
	}
}

extern int slurmdb_addto_qos_char_list(list_t *char_list, list_t *qos_list,
				       char *names, int option)
{
	int count;
	qos_char_list_args_t args = {0};

	if (!char_list) {
		error("No list was given to fill in");
		return 0;
	}

	if (!xstrcmp(names, "")) {
		list_append(char_list, xstrdup(""));
		return 1;
	}

	args.option = option;
	args.qos_list = qos_list;

	count = slurm_parse_char_list(char_list, names, &args,
				      _slurmdb_addto_qos_char_list_internal);
	if (!count) {
		error("You gave me an empty qos list");
	}

	return count;
}

extern int slurmdb_send_accounting_update_persist(list_t *update_list,
						  persist_conn_t *persist_conn)
{
	slurm_msg_t req;
	slurm_msg_t resp;
	accounting_update_msg_t msg = {0};
	int rc;

	xassert(persist_conn);

	if (!persist_conn->conn) {
		if (slurm_persist_conn_open(persist_conn) !=
		    SLURM_SUCCESS) {
			error("slurmdb_send_accounting_update_persist: Unable to open connection to registered cluster %s.",
			      persist_conn->cluster_name);
			persist_conn->conn = NULL;
			rc = SLURM_ERROR;
			goto end;
		}
	}

	msg.update_list = update_list;
	msg.rpc_version = req.protocol_version = persist_conn->version;
	slurm_msg_t_init(&req);
	req.msg_type = ACCOUNTING_UPDATE_MSG;
	req.pcon = persist_conn;
	req.data = &msg;

	/* resp is inited in slurm_send_recv_msg */
	rc = slurm_send_recv_msg(persist_conn->conn, &req, &resp, 0);

end:
	if (rc != SLURM_SUCCESS) {
		error("update cluster: %s at %s(%hu): %m",
		      persist_conn->cluster_name,
		      persist_conn->rem_host,
		      persist_conn->rem_port);
	} else {
		rc = slurm_get_return_code(resp.msg_type, resp.data);
		slurm_free_return_code_msg(resp.data);
	}
	//info("got rc of %d", rc);
	return rc;
}

/*
 * send_accounting_update - send update to controller of cluster
 * IN update_list: updates to send
 * IN cluster: name of cluster
 * IN host: control host of cluster
 * IN port: control port of cluster
 * IN rpc_version: rpc version of cluster
 * RET:  error code
 */
extern int slurmdb_send_accounting_update(list_t *update_list, char *cluster,
					  char *host, uint16_t port,
					  uint16_t rpc_version)
{
	accounting_update_msg_t msg;
	slurm_msg_t req;
	slurm_msg_t resp;
	int i, rc;

	// Set highest version that we can use
	if (rpc_version > SLURM_PROTOCOL_VERSION) {
		rpc_version = SLURM_PROTOCOL_VERSION;
	}
	memset(&msg, 0, sizeof(accounting_update_msg_t));
	msg.rpc_version = rpc_version;
	msg.update_list = update_list;

	debug("sending updates to %s at %s(%hu) ver %hu",
	      cluster, host, port, rpc_version);

	slurm_msg_t_init(&req);
	slurm_set_addr(&req.address, port, host);

	req.protocol_version = rpc_version;
	slurm_msg_set_r_uid(&req, SLURM_AUTH_UID_ANY);

	req.msg_type = ACCOUNTING_UPDATE_MSG;
	if (slurmdbd_conf)
		req.flags = SLURM_GLOBAL_AUTH_KEY;
	req.data = &msg;
	slurm_msg_t_init(&resp);

	for (i = 0; i < 4; i++) {
		/* Retry if the slurmctld can connect, but is not responding */
		rc = slurm_send_recv_node_msg(&req, &resp, 0);
		if ((rc == SLURM_SUCCESS) ||
		    (errno != SLURM_PROTOCOL_SOCKET_IMPL_TIMEOUT))
			break;
	}
	if (rc != SLURM_SUCCESS) {
		error("update cluster: %m to %s at %s(%hu)",
		      cluster, host, port);
		rc = SLURM_ERROR;
	} else
		rc = slurm_get_return_code(resp.msg_type, resp.data);

	if (resp.auth_cred)
		auth_g_destroy(resp.auth_cred);

	slurm_free_return_code_msg(resp.data);

	//info("got rc of %d", rc);
	return rc;
}

extern slurmdb_report_cluster_rec_t *slurmdb_cluster_rec_2_report(
	slurmdb_cluster_rec_t *cluster)
{
	slurmdb_report_cluster_rec_t *slurmdb_report_cluster;
	slurmdb_cluster_accounting_rec_t *accting = NULL;
	slurmdb_tres_rec_t *tres_rec;
	list_itr_t *itr = NULL;
	int count;

	xassert(cluster);
	slurmdb_report_cluster = xmalloc(sizeof(slurmdb_report_cluster_rec_t));
	slurmdb_report_cluster->name = xstrdup(cluster->name);

	if (!(count = list_count(cluster->accounting_list)))
		return slurmdb_report_cluster;

	/* get the amount of time and the average count
	   during the time we are looking at */
	itr = list_iterator_create(cluster->accounting_list);
	while ((accting = list_next(itr)))
		slurmdb_add_cluster_accounting_to_tres_list(
			accting, &slurmdb_report_cluster->tres_list);
	list_iterator_destroy(itr);

	itr = list_iterator_create(slurmdb_report_cluster->tres_list);
	while ((tres_rec = list_next(itr)))
		tres_rec->count /= tres_rec->rec_count;
	list_iterator_destroy(itr);

	return slurmdb_report_cluster;
}

/*
 * get the first cluster that will run a job
 * IN: req - description of resource allocation request
 * IN: cluster_names - comma separated string of cluster names
 * OUT: cluster_rec - record of selected cluster or NULL if none found or
 * 		      cluster_names is NULL
 * RET: SLURM_SUCCESS on success SLURM_ERROR else
 *
 * Note: Cluster_rec needs to be freed with slurmdb_destroy_cluster_rec() when
 * called
 * Note: The will_runs are not threaded. Currently it relies on the
 * working_cluster_rec to pack the job_desc's jobinfo. See previous commit for
 * an example of how to thread this.
 */
extern int slurmdb_get_first_avail_cluster(job_desc_msg_t *req,
	char *cluster_names, slurmdb_cluster_rec_t **cluster_rec)
{
	will_run_response_msg_t *will_run_resp;
	int rc = SLURM_SUCCESS;
	char local_hostname[HOST_NAME_MAX];
	list_itr_t *itr;
	list_t *cluster_list = NULL;
	list_t *ret_list = NULL;

	*cluster_rec = NULL;

	if (slurm_get_cluster_info(&(cluster_list), cluster_names, 0)) {
		return SLURM_ERROR;
	}

	/* return if we only have 1 or less clusters here */
	if (!cluster_list || !list_count(cluster_list)) {
		rc = SLURM_ERROR;
		goto end_it;
	} else if (list_count(cluster_list) == 1) {
		*cluster_rec = list_pop(cluster_list);
		goto end_it;
	}

	if ((req->alloc_node == NULL) &&
	    (gethostname_short(local_hostname, sizeof(local_hostname)) == 0)) {
		req->alloc_node = local_hostname;
	}

	if (working_cluster_rec)
		*cluster_rec = working_cluster_rec;

	ret_list = list_create(slurm_free_will_run_response_msg);
	itr = list_iterator_create(cluster_list);
	while ((working_cluster_rec = list_next(itr))) {
		if ((will_run_resp = _job_will_run(req))) {
			list_append(ret_list, will_run_resp);
		} else {
			error("Problem with submit to cluster %s: %m",
			      working_cluster_rec->name);
		}
	}
	list_iterator_destroy(itr);

	/* restore working_cluster_rec in case it was already set */
	if (*cluster_rec) {
		working_cluster_rec = *cluster_rec;
		*cluster_rec = NULL;
	}

	if (req->alloc_node == local_hostname)
		req->alloc_node = NULL;

	if (!list_count(ret_list)) {
		error("Can't run on any of the specified clusters");
		rc = SLURM_ERROR;
		goto end_it;
	}

	/* sort the list so the first spot is on top */
	list_sort(ret_list, slurm_sort_will_run_resp);
	will_run_resp = list_peek(ret_list);
	/* prevent cluster_rec from being freed when cluster_list is destroyed */
	working_cluster_rec = list_remove_first(cluster_list,
						slurmdb_find_cluster_in_list,
						will_run_resp->cluster_name);
end_it:
	FREE_NULL_LIST(ret_list);
	FREE_NULL_LIST(cluster_list);

	return rc;
}

/* Report the latest start time for any hetjob component on this cluster.
 * Return NULL if any component can not run here */
static will_run_response_msg_t *_het_job_will_run(list_t *job_req_list)
{
	will_run_response_msg_t *ret_resp = NULL, *tmp_resp;
	job_desc_msg_t *req;
	list_itr_t *iter;

	iter = list_iterator_create(job_req_list);
	while ((req = (job_desc_msg_t *) list_next(iter))) {
		tmp_resp = _job_will_run(req);
		if (!tmp_resp) { /* Some het component can't run here */
			slurm_free_will_run_response_msg(ret_resp);
			ret_resp = NULL;
			break;
		}
		if (!ret_resp) {
			ret_resp = tmp_resp;
			tmp_resp = NULL;
		} else if (ret_resp->start_time < tmp_resp->start_time)
			ret_resp->start_time = tmp_resp->start_time;
		slurm_free_will_run_response_msg(tmp_resp);
	}
	list_iterator_destroy(iter);

	return ret_resp;
}

/*
 * get the first cluster that will run a heterogeneous job
 * IN: req - description of resource allocation request
 * IN: cluster_names - comma separated string of cluster names
 * OUT: cluster_rec - record of selected cluster or NULL if none found or
 * 		      cluster_names is NULL
 * RET: SLURM_SUCCESS on success SLURM_ERROR else
 *
 * Note: Cluster_rec needs to be freed with slurmdb_destroy_cluster_rec() when
 * called
 * Note: The will_runs are not threaded. Currently it relies on the
 * working_cluster_rec to pack the job_desc's jobinfo. See previous commit for
 * an example of how to thread this.
 */
extern int slurmdb_get_first_het_job_cluster(list_t *job_req_list,
	char *cluster_names, slurmdb_cluster_rec_t **cluster_rec)
{
	job_desc_msg_t *req;
	will_run_response_msg_t *will_run_resp = NULL;
	int rc = SLURM_SUCCESS;
	char local_hostname[HOST_NAME_MAX] = "";
	list_itr_t *itr;
	list_t *cluster_list = NULL;
	list_t *ret_list = NULL;

	*cluster_rec = NULL;

	if (slurm_get_cluster_info(&(cluster_list), cluster_names, 0)) {
		return SLURM_ERROR;
	}

	/* return if we only have 1 or less clusters here */
	if (!cluster_list || !list_count(cluster_list)) {
		rc = SLURM_ERROR;
		goto end_it;
	} else if (list_count(cluster_list) == 1) {
		*cluster_rec = list_pop(cluster_list);
		goto end_it;
	}

	(void) gethostname_short(local_hostname, sizeof(local_hostname));
	itr = list_iterator_create(job_req_list);
	while ((req = (job_desc_msg_t *) list_next(itr))) {
		if ((req->alloc_node == NULL) && local_hostname[0])
			req->alloc_node = local_hostname;
	}
	list_iterator_destroy(itr);

	if (working_cluster_rec)
		*cluster_rec = working_cluster_rec;

	ret_list = list_create(xfree_ptr);
	itr = list_iterator_create(cluster_list);
	while ((working_cluster_rec = list_next(itr))) {
		if ((will_run_resp = _het_job_will_run(job_req_list))) {
			list_append(ret_list, will_run_resp);
		} else {
			error("Problem with submit to cluster %s: %m",
			      working_cluster_rec->name);
		}
	}
	list_iterator_destroy(itr);

	/* restore working_cluster_rec in case it was already set */
	if (*cluster_rec) {
		working_cluster_rec = *cluster_rec;
		*cluster_rec = NULL;
	}

	itr = list_iterator_create(job_req_list);
	while ((req = (job_desc_msg_t *) list_next(itr))) {
		if (req->alloc_node == local_hostname)
			req->alloc_node = NULL;
	}
	list_iterator_destroy(itr);

	if (!list_count(ret_list)) {
		error("Can't run on any of the specified clusters");
		rc = SLURM_ERROR;
		goto end_it;
	}

	/* sort the list so the first spot is on top */
	list_sort(ret_list, slurm_sort_will_run_resp);
	will_run_resp = list_peek(ret_list);
	/* prevent cluster_rec from being freed when cluster_list is destroyed */
	working_cluster_rec = list_remove_first(cluster_list,
						slurmdb_find_cluster_in_list,
						will_run_resp->cluster_name);
end_it:
	FREE_NULL_LIST(ret_list);
	FREE_NULL_LIST(cluster_list);

	return rc;
}

extern void slurmdb_copy_assoc_rec_limits(slurmdb_assoc_rec_t *out,
					  slurmdb_assoc_rec_t *in)
{
	out->grp_jobs = in->grp_jobs;
	out->grp_jobs_accrue = in->grp_jobs_accrue;
	out->grp_submit_jobs = in->grp_submit_jobs;
	xfree(out->grp_tres);
	out->grp_tres = xstrdup(in->grp_tres);
	xfree(out->grp_tres_mins);
	out->grp_tres_mins = xstrdup(in->grp_tres_mins);
	xfree(out->grp_tres_run_mins);
	out->grp_tres_run_mins = xstrdup(in->grp_tres_run_mins);
	out->grp_wall = in->grp_wall;

	out->max_jobs = in->max_jobs;
	out->max_jobs_accrue = in->max_jobs_accrue;
	out->min_prio_thresh = in->min_prio_thresh;
	out->max_submit_jobs = in->max_submit_jobs;
	xfree(out->max_tres_pj);
	out->max_tres_pj = xstrdup(in->max_tres_pj);
	xfree(out->max_tres_pn);
	out->max_tres_pn = xstrdup(in->max_tres_pn);
	xfree(out->max_tres_mins_pj);
	out->max_tres_mins_pj =	xstrdup(in->max_tres_mins_pj);
	xfree(out->max_tres_run_mins);
	out->max_tres_run_mins = xstrdup(in->max_tres_run_mins);
	out->max_wall_pj = in->max_wall_pj;

	out->priority = in->priority;
	out->comment = xstrdup(in->comment);

	FREE_NULL_LIST(out->qos_list);
	out->qos_list = slurm_copy_char_list(in->qos_list);
}

extern void slurmdb_copy_cluster_rec(slurmdb_cluster_rec_t *out,
				     slurmdb_cluster_rec_t *in)
{
	out->classification   = in->classification;
	xfree(out->control_host);
	out->control_host     = xstrdup(in->control_host);
	out->control_port     = in->control_port;
	out->dimensions       = in->dimensions;
	xfree(out->fed.name);
	out->fed.name         = xstrdup(in->fed.name);
	out->fed.id           = in->fed.id;
	out->fed.state        = in->fed.state;
	out->flags            = in->flags;
	xfree(out->name);
	out->name             = xstrdup(in->name);
	xfree(out->nodes);
	out->nodes            = xstrdup(in->nodes);
	out->rpc_version      = in->rpc_version;
	xfree(out->tres_str);
	out->tres_str         = xstrdup(in->tres_str);

	slurmdb_destroy_assoc_rec(out->root_assoc);
	if (in->root_assoc) {
		out->root_assoc = xmalloc(sizeof(slurmdb_assoc_rec_t));
		slurmdb_init_assoc_rec(out->root_assoc, 0);
		slurmdb_copy_assoc_rec_limits( out->root_assoc, in->root_assoc);
	}

	FREE_NULL_LIST(out->fed.feature_list);
	if (in->fed.feature_list) {
		out->fed.feature_list = list_create(xfree_ptr);
		slurm_char_list_copy(out->fed.feature_list,
				     in->fed.feature_list);
	}

	/* Not copied currently:
	 * accounting_list
	 * control_addr
	 * dim_size
	 * fed.recv
	 * fed.send
	 */
}

extern void slurmdb_copy_federation_rec(slurmdb_federation_rec_t *out,
					slurmdb_federation_rec_t *in)
{
	xfree(out->name);
	out->name     = xstrdup(in->name);
	out->flags    = in->flags;

	FREE_NULL_LIST(out->cluster_list);
	if (in->cluster_list) {
		slurmdb_cluster_rec_t *cluster_in = NULL;
		list_itr_t *itr  = list_iterator_create(in->cluster_list);
		out->cluster_list = list_create(slurmdb_destroy_cluster_rec);
		while ((cluster_in = list_next(itr))) {
			slurmdb_cluster_rec_t *cluster_out =
				xmalloc(sizeof(slurmdb_cluster_rec_t));
			slurmdb_init_cluster_rec(cluster_out, 0);
			slurmdb_copy_cluster_rec(cluster_out, cluster_in);
			list_append(out->cluster_list, cluster_out);
		}
		list_iterator_destroy(itr);
	}
}

extern void slurmdb_copy_qos_rec_limits(slurmdb_qos_rec_t *out,
					slurmdb_qos_rec_t *in)
{
	out->flags = in->flags;
	out->grace_time = in->grace_time;
	out->grp_jobs = in->grp_jobs;
	out->grp_jobs_accrue = in->grp_jobs_accrue;
	out->grp_submit_jobs = in->grp_submit_jobs;
	xfree(out->grp_tres);
	out->grp_tres = xstrdup(in->grp_tres);
	xfree(out->grp_tres_mins);
	out->grp_tres_mins = xstrdup(in->grp_tres_mins);
	xfree(out->grp_tres_run_mins);
	out->grp_tres_run_mins = xstrdup(in->grp_tres_run_mins);
	out->grp_wall = in->grp_wall;

	out->limit_factor = in->limit_factor;

	out->max_jobs_pa = in->max_jobs_pa;
	out->max_jobs_pu = in->max_jobs_pu;
	out->max_jobs_accrue_pa = in->max_jobs_accrue_pa;
	out->max_jobs_accrue_pu = in->max_jobs_accrue_pu;
	out->max_submit_jobs_pa = in->max_submit_jobs_pa;
	out->max_submit_jobs_pu = in->max_submit_jobs_pu;
	xfree(out->max_tres_mins_pj);
	out->max_tres_mins_pj =	xstrdup(in->max_tres_mins_pj);
	xfree(out->max_tres_pa);
	out->max_tres_pa = xstrdup(in->max_tres_pa);
	xfree(out->max_tres_pj);
	out->max_tres_pj = xstrdup(in->max_tres_pj);
	xfree(out->max_tres_pn);
	out->max_tres_pn = xstrdup(in->max_tres_pn);
	xfree(out->max_tres_pu);
	out->max_tres_pu = xstrdup(in->max_tres_pu);
	xfree(out->max_tres_run_mins_pa);
	out->max_tres_run_mins_pa = xstrdup(in->max_tres_run_mins_pa);
	xfree(out->max_tres_run_mins_pu);
	out->max_tres_run_mins_pu = xstrdup(in->max_tres_run_mins_pu);
	out->max_wall_pj = in->max_wall_pj;
	out->min_prio_thresh = in->min_prio_thresh;
	xfree(out->min_tres_pj);
	out->min_tres_pj = xstrdup(in->min_tres_pj);

	FREE_NULL_LIST(out->preempt_list);
	out->preempt_list = slurm_copy_char_list(in->preempt_list);

	out->preempt_mode = in->preempt_mode;
	out->preempt_exempt_time = in->preempt_exempt_time;

	out->priority = in->priority;

	out->usage_factor = in->usage_factor;
	out->usage_thres = in->usage_thres;
}

extern slurmdb_tres_rec_t *slurmdb_copy_tres_rec(slurmdb_tres_rec_t *tres)
{
	slurmdb_tres_rec_t *tres_out = NULL;

	if (!tres)
		return tres_out;

	tres_out = xmalloc_nz(sizeof(slurmdb_tres_rec_t));
	memcpy(tres_out, tres, sizeof(slurmdb_tres_rec_t));
	tres_out->name = xstrdup(tres->name);
	tres_out->type = xstrdup(tres->type);

	return tres_out;
}

extern list_t *slurmdb_copy_tres_list(list_t *tres)
{
	slurmdb_tres_rec_t *tres_rec = NULL;
	list_itr_t *itr;
	list_t *tres_out;

	if (!tres)
		return NULL;

	tres_out = list_create(slurmdb_destroy_tres_rec);

	itr = list_iterator_create(tres);
	while ((tres_rec = list_next(itr)))
		list_append(tres_out, slurmdb_copy_tres_rec(tres_rec));
	list_iterator_destroy(itr);

	return tres_out;
}

extern list_t *slurmdb_list_copy_coord(list_t *coord_accts)
{
	list_t *ret_list = NULL;

	if (!coord_accts || !list_count(coord_accts))
		return NULL;

	list_for_each(coord_accts, _list_copy_coord, &ret_list);

	return ret_list;
}

extern list_t *slurmdb_diff_tres_list(list_t *tres_list_old,
				      list_t *tres_list_new)
{
	slurmdb_tres_rec_t *tres_rec = NULL, *tres_rec_old;
	list_itr_t *itr;
	list_t *tres_out;

	if (!tres_list_new || !list_count(tres_list_new))
		return NULL;

	tres_out = slurmdb_copy_tres_list(tres_list_new);

	itr = list_iterator_create(tres_out);
	while ((tres_rec = list_next(itr))) {
		if (!(tres_rec_old = list_find_first(tres_list_old,
						     slurmdb_find_tres_in_list,
						     &tres_rec->id)))
			continue;
		if (tres_rec_old->count == tres_rec->count)
			list_delete_item(itr);
	}
	list_iterator_destroy(itr);

	return tres_out;
}

extern char *slurmdb_tres_string_combine_lists(
	list_t *tres_list_old, list_t *tres_list_new)
{
	slurmdb_tres_rec_t *tres_rec = NULL, *tres_rec_old;
	list_itr_t *itr;
	char *tres_str = NULL;

	if (!tres_list_new || !list_count(tres_list_new))
		return NULL;

	itr = list_iterator_create(tres_list_new);
	while ((tres_rec = list_next(itr))) {
		if (!(tres_rec_old = list_find_first(tres_list_old,
						     slurmdb_find_tres_in_list,
						     &tres_rec->id))
		    || (tres_rec_old->count == INFINITE64))
			continue;
		if (tres_str)
			xstrcat(tres_str, ",");
		xstrfmtcat(tres_str, "%u=%"PRIu64,
			   tres_rec->id, tres_rec->count);
	}
	list_iterator_destroy(itr);

	return tres_str;
}

/* caller must xfree this char * returned */
extern char *slurmdb_make_tres_string(list_t *tres, uint32_t flags)
{
	char *tres_str = NULL;
	list_itr_t *itr;
	slurmdb_tres_rec_t *tres_rec;
	bool allow_amend = flags & TRES_STR_FLAG_ALLOW_AMEND;

	if (!tres)
		return tres_str;

	itr = list_iterator_create(tres);
	while ((tres_rec = list_next(itr))) {
		if ((flags & TRES_STR_FLAG_REMOVE) &&
		    (tres_rec->count == INFINITE64))
			continue;

		if ((flags & TRES_STR_FLAG_SIMPLE) || !tres_rec->type)
			xstrfmtcat(tres_str, "%s%u=",
				   (tres_str ||
				    (flags & TRES_STR_FLAG_COMMA1)) ? "," : "",
				   tres_rec->id);

		else
			xstrfmtcat(tres_str, "%s%s%s%s=",
				   (tres_str ||
				    (flags & TRES_STR_FLAG_COMMA1)) ? "," : "",
				   tres_rec->type,
				   tres_rec->name ? "/" : "",
				   tres_rec->name ? tres_rec->name : "");

		if (allow_amend && tres_rec->modifier)
			xstrcatchar(tres_str, tres_rec->modifier);

		xstrfmtcat(tres_str, "%"PRIu64, tres_rec->count);
	}
	list_iterator_destroy(itr);

	return tres_str;
}

extern char *slurmdb_make_tres_string_from_arrays(char **tres_names,
						  uint64_t *tres_cnts,
						  uint32_t tres_cnt,
						  uint32_t flags)
{
	char *tres_str = NULL;
	int i;

	if (!tres_names || !tres_cnts)
		return tres_str;

	for (i=0; i<tres_cnt; i++) {
		if ((tres_cnts[i] == INFINITE64) &&
		    (flags & TRES_STR_FLAG_REMOVE))
			continue;
		xstrfmtcat(tres_str, "%s%s=%"PRIu64,
			   tres_str ? "," : "", tres_names[i], tres_cnts[i]);
	}

	return tres_str;
}

extern char *slurmdb_make_tres_string_from_simple(
	char *tres_in, list_t *full_tres_list, int spec_unit,
	uint32_t convert_flags, uint32_t tres_str_flags, char *nodes)
{
	char *tres_str = NULL;
	char *tmp_str = tres_in;
	int id;
	bool allow_amend = tres_str_flags & TRES_STR_FLAG_ALLOW_AMEND;
	uint64_t count;
	slurmdb_tres_rec_t *tres_rec;
	char *node_name = NULL;
	char sign;
	list_t *char_list = NULL;

	if (!full_tres_list || !tmp_str || !tmp_str[0]
	    || tmp_str[0] < '0' || tmp_str[0] > '9')
		return tres_str;

	while (tmp_str) {
		id = atoi(tmp_str);
		if (id <= 0) {
			error("slurmdb_make_tres_string_from_simple: no id "
			      "found at %s instead", tmp_str);
			goto get_next;
		}

		if (!(tres_rec = list_find_first(
			      full_tres_list, slurmdb_find_tres_in_list,
			      &id))) {
			debug("No tres known by id %d", id);
			goto get_next;
		}

		if (!(tmp_str = strchr(tmp_str, '='))) {
			error("slurmdb_make_tres_string_from_simple: "
			      "no value found");
			break;
		}

		sign = *(++tmp_str);

		if ((sign != '+') && (sign != '-'))
			sign = '\0';
		else
			tmp_str++;
		count = slurm_atoull(tmp_str);

		if (count == NO_VAL64)
			goto get_next;

		if (tres_str)
			xstrcat(tres_str, ",");
		if (!tres_rec->type)
			xstrfmtcat(tres_str, "%u=", tres_rec->id);

		else
			xstrfmtcat(tres_str, "%s%s%s=",
				   tres_rec->type,
				   tres_rec->name ? "/" : "",
				   tres_rec->name ? tres_rec->name : "");

		if (allow_amend && sign)
			xstrcatchar(tres_str, sign);

		if (count != INFINITE64) {
			if (nodes) {
				node_name = find_hostname(count, nodes);
				xstrfmtcat(tres_str, "%s", node_name);
				xfree(node_name);
			} else if (tres_str_flags & TRES_STR_FLAG_BYTES) {
				/* This mean usage */
				char outbuf[FORMAT_STRING_SIZE];
				if (tres_rec->id == TRES_CPU) {
					count /= CPU_TIME_ADJ;
					secs2time_str((time_t)count, outbuf,
						      FORMAT_STRING_SIZE);
				} else if (!xstrcasecmp(tres_rec->name,
							"gpuutil")) {
					snprintf(outbuf, sizeof(outbuf),
						 "%"PRIu64, count);
				} else
					convert_num_unit((double)count, outbuf,
							 sizeof(outbuf),
							 UNIT_NONE,
							 spec_unit,
							 convert_flags);
				xstrfmtcat(tres_str, "%s", outbuf);
			} else if ((tres_rec->id == TRES_MEM) ||
				   !xstrcasecmp(tres_rec->name, "gpumem") ||
				   !xstrcasecmp(tres_rec->type, "bb")) {
				char outbuf[FORMAT_STRING_SIZE];
				convert_num_unit((double)count, outbuf,
						 sizeof(outbuf), UNIT_MEGA,
						 spec_unit, convert_flags);
				xstrfmtcat(tres_str, "%s", outbuf);
			} else {
				xstrfmtcat(tres_str, "%"PRIu64, count);
			}
		} else
			xstrfmtcat(tres_str, "NONE");

		if (!(tres_str_flags & TRES_STR_FLAG_SORT_ID)) {
			if (!char_list)
				char_list = list_create(xfree_ptr);
			list_append(char_list, tres_str);
			tres_str = NULL;
		}
	get_next:
		if (!(tmp_str = strchr(tmp_str, ',')))
			break;
		tmp_str++;
	}

	if (char_list) {
		tres_str = slurm_char_list_to_xstr(char_list);
		FREE_NULL_LIST(char_list);
	}

	return tres_str;
}

extern char *slurmdb_format_tres_str(
	char *tres_in, list_t *full_tres_list, bool simple)
{
	char *tres_str = NULL;
	char *val_unit = NULL;
	char *tmp_str = tres_in;
	char *sign;
	uint64_t count;
	slurmdb_tres_rec_t *tres_rec;

	if (!full_tres_list || !tmp_str || !tmp_str[0])
		return tres_str;

	if (tmp_str[0] == ',')
		tmp_str++;

	while (tmp_str) {
		if (tmp_str[0] >= '0' && tmp_str[0] <= '9') {
			int id = atoi(tmp_str);
			if (id <= 0) {
				error("%s: cannot convert %s to ID.",
				      __func__, tmp_str);
				return NULL;
			}

			if (!(tres_rec = list_find_first(
				      full_tres_list, slurmdb_find_tres_in_list,
				      &id))) {
				error("%s: no TRES known by id %d",
				      __func__, id);
				return NULL;
			}
		} else {
			int end = 0;
			char *tres_name;

			while (tmp_str[end]) {
				if ((tmp_str[end] == '=') ||
				    (tmp_str[end] == '+') ||
				    (tmp_str[end] == '-'))
					break;
				end++;
			}
			if (!tmp_str[end]) {
				error("%s: no TRES id found for %s",
				      __func__, tmp_str);
				return NULL;
			}
			tres_name = xstrndup(tmp_str, end);
			if (!(tres_rec = list_find_first(
				      full_tres_list,
				      slurmdb_find_tres_in_list_by_type,
				      tres_name))) {
				error("%s: no TRES known by type %s",
				      __func__, tres_name);
				xfree(tres_name);
				return NULL;
			}
			xfree(tres_name);
		}

		if (!(tmp_str = strchr(tmp_str, '='))) {
			error("%s: no value given as TRES type/id.", __func__);
			return NULL;
		}

		sign = tmp_str - 1;
		if ((*sign != '+') && (*sign != '-'))
			sign = "";

		count = strtoull(++tmp_str, &val_unit, 10);
		if (val_unit && *val_unit != ',' && *val_unit != '\0' &&
		    tres_rec->type) {
			int base_unit =
				slurmdb_get_tres_base_unit(tres_rec->type);
			int convert_val =
				get_convert_unit_val(base_unit, *val_unit);
			if (convert_val > 0)
				count *= convert_val;
		}

		if (tres_str)
			xstrcat(tres_str, ",");
		if (simple || !tres_rec->type)
			xstrfmtcat(tres_str, "%u=%.1s%"PRIu64"",
				   tres_rec->id, sign, count);

		else
			xstrfmtcat(tres_str, "%s%s%s=%.1s%"PRIu64"",
				   tres_rec->type,
				   tres_rec->name ? "/" : "",
				   tres_rec->name ? tres_rec->name : "", sign,
				   count);
		if (!(tmp_str = strchr(tmp_str, ',')))
			break;
		tmp_str++;
	}

	return tres_str;
}

/*
 * Comparator used for sorting tres by id
 *
 * returns: -1 tres_a < tres_b   0: tres_a == tres_b   1: tres_a > tres_b
 *
 */
extern int slurmdb_sort_tres_by_id_asc(void *v1, void *v2)
{
	slurmdb_tres_rec_t *tres_a = *(slurmdb_tres_rec_t **)v1;
	slurmdb_tres_rec_t *tres_b = *(slurmdb_tres_rec_t **)v2;

	if ((tres_a->id > TRES_STATIC_CNT) &&
	    (tres_b->id > TRES_STATIC_CNT)) {
		int diff = xstrcmp(tres_a->type, tres_b->type);

		if (diff < 0)
			return -1;
		else if (diff > 0)
			return 1;

		diff = xstrcmp(tres_a->name, tres_b->name);

		if (diff < 0)
			return -1;
		else if (diff > 0)
			return 1;
	}

	if (tres_a->id < tres_b->id)
		return -1;
	else if (tres_a->id > tres_b->id)
		return 1;

	return 0;
}

extern void slurmdb_tres_list_from_string(list_t **tres_list, const char *tres,
					  uint32_t flags, list_t *sub_tres_list)
{
	const char *tmp_str = tres;
	char sign;
	int id;
	uint64_t count;
	slurmdb_tres_rec_t *tres_rec;
	int remove_found = 0;
	list_t *mgr_tres_list =
		sub_tres_list ? sub_tres_list : assoc_mgr_tres_list;
	bool allow_amend = flags & TRES_STR_FLAG_ALLOW_AMEND;
	xassert(tres_list);

	if (!tres || !tres[0])
		return;

	if (tmp_str[0] == ',')
		tmp_str++;

	while (tmp_str) {
		if (tmp_str[0] >= '0' && tmp_str[0] <= '9') {
			id = atoi(tmp_str);
		} else {
			int end = 0;
			char *tres_name;
			assoc_mgr_lock_t locks = { .tres = READ_LOCK };

			while (tmp_str[end]) {
				if (tmp_str[end] == '=')
					break;
				end++;
			}
			if (!tmp_str[end]) {
				error("%s: no TRES id found for %s",
				      __func__, tmp_str);
				break;
			}
			tres_name = xstrndup(tmp_str, end);
			if (!sub_tres_list)
				assoc_mgr_lock(&locks);
			if (!mgr_tres_list) {
				error("%s: No assoc_mgr_tres_list, this function can't be used here with a formatted tres list.", __func__);
				break;
			}
			tres_rec = list_find_first(
				mgr_tres_list,
				slurmdb_find_tres_in_list_by_type, tres_name);
			if (!sub_tres_list)
				assoc_mgr_unlock(&locks);
			if (!tres_rec) {
				error("%s: no TRES known by type %s",
				      __func__, tres_name);
				xfree(tres_name);
				break;
			}
			id = tres_rec->id;
			xfree(tres_name);
		}
		/* 0 isn't a valid tres id */
		if (id <= 0) {
			error("slurmdb_tres_list_from_string: no id "
			      "found at %s instead", tmp_str);
			break;
		}

		if (!(tmp_str = strchr(tmp_str, '='))) {
			error("slurmdb_tres_list_from_string: "
			      "no value found %s", tres);
			break;
		}

		sign = *(++tmp_str);

		if ((sign != '+') && (sign != '-'))
			sign = '\0';
		else
			tmp_str++;

		count = slurm_atoull(tmp_str);

		if (!*tres_list)
			*tres_list = list_create(slurmdb_destroy_tres_rec);

		if (!(tres_rec = list_find_first(
			      *tres_list, slurmdb_find_tres_in_list, &id))) {
			tres_rec = xmalloc(sizeof(slurmdb_tres_rec_t));
			tres_rec->id = id;
			tres_rec->count = count;
			if (allow_amend && sign)
				tres_rec->modifier = sign;
			list_append(*tres_list, tres_rec);
			if (count == INFINITE64)
				remove_found++;
		} else if (flags & TRES_STR_FLAG_REPLACE) {
			debug2("TRES %u was already here with count %"PRIu64", "
			       "replacing with %"PRIu64,
			      tres_rec->id, tres_rec->count, count);
			tres_rec->count = count;
			if (allow_amend && sign)
				tres_rec->modifier = sign;
			else
				tres_rec->modifier = '\0';
		} else if (flags & TRES_STR_FLAG_SUM) {
			if (count != INFINITE64) {
				if (tres_rec->count == INFINITE64)
					tres_rec->count = count;
				else
					tres_rec->count += count;
			}
		} else if (flags & TRES_STR_FLAG_MAX) {
			if (count != INFINITE64) {
				if (tres_rec->count == INFINITE64)
					tres_rec->count = count;
				else
					tres_rec->count =
						MAX(tres_rec->count, count);
			}
		} else if (flags & TRES_STR_FLAG_MIN) {
			if (count != INFINITE64) {
				if (tres_rec->count == INFINITE64)
					tres_rec->count = count;
				else
					tres_rec->count =
						MIN(tres_rec->count, count);
			}
		} else if (allow_amend && (flags & TRES_STR_FLAG_COMB_AMEND) &&
			   tres_rec->modifier) {
			/*
			 * We will calculate the resulting value for tres_rec
			 * but we will avoid negative values and overflows.
			 * This operation REQUIRES that only up to two TRES of
			 * the same type can appear in the same string. This
			 * operation is only called from mod_tres_str(), which
			 * ensures amending TRES (if present) will appear in
			 * the string before the currently stored absolute
			 * values.
			 */

			if (tres_rec->modifier == '+') {
				tres_rec->count += count;
				if ((tres_rec->count < count) ||
				    (tres_rec->count > MAX_VAL64))
					tres_rec->count = MAX_VAL64;
			} else {
				tres_rec->count = count - tres_rec->count;
				if (tres_rec->count > count)
					tres_rec->count = 0;
			}
			tres_rec->modifier = '\0';
		}

		if (!(tmp_str = strchr(tmp_str, ',')))
			break;
		tmp_str++;
	}

	if (remove_found && (flags & TRES_STR_FLAG_REMOVE)) {
		/* here we will remove the tres we don't want in the
		   string */
		uint64_t inf64 = INFINITE64;
		int removed;

		if ((removed = list_delete_all(
			     *tres_list,
			     slurmdb_find_tres_in_list_by_count,
			     &inf64)) != remove_found)
			debug("slurmdb_tres_list_from_string: "
			      "was expecting to remove %d, but removed %d",
			      remove_found, removed);
	}

	/*
	 * If we are combining relative values, the resulting TRES list
	 * needs to be cleaned up and only hold absolute values
	 */
	if (*tres_list && (flags & TRES_STR_FLAG_COMB_AMEND))
		list_for_each(*tres_list, _unset_amending_tres, NULL);

	if (*tres_list && (flags & TRES_STR_FLAG_SORT_ID))
		list_sort(*tres_list, (ListCmpF)slurmdb_sort_tres_by_id_asc);

	return;
}

extern char *slurmdb_combine_tres_strings(
	char **tres_str_old, char *tres_str_new, uint32_t flags)
{
	list_t *tres_list = NULL;

	xassert(tres_str_old);

	/* If a new string is being added concat it onto the old
	 * string, then send it to slurmdb_tres_list_from_string which
	 * will make it a unique list if flags doesn't contain
	 * TRES_STR_FLAG_ONLY_CONCAT.
	 */
	if (tres_str_new && tres_str_new[0])
		xstrfmtcat(*tres_str_old, "%s%s%s",
			   (flags & (TRES_STR_FLAG_COMMA1 |
				     TRES_STR_FLAG_ONLY_CONCAT)) ? "," : "",
			   (*tres_str_old && tres_str_new[0] != ',') ? "," : "",
			   tres_str_new);

	if (flags & TRES_STR_FLAG_ONLY_CONCAT)
		goto endit;

	slurmdb_tres_list_from_string(&tres_list, *tres_str_old, flags, NULL);
	xfree(*tres_str_old);

	/* Always make it a simple string */
	flags |= TRES_STR_FLAG_SIMPLE;

	/* Make a new string from the combined */
	*tres_str_old = slurmdb_make_tres_string(tres_list, flags);

	FREE_NULL_LIST(tres_list);
endit:
	/* Send back a blank string instead of NULL. */
	if (!*tres_str_old && (flags & TRES_STR_FLAG_NO_NULL))
		*tres_str_old = xstrdup("");

	return *tres_str_old;
}

extern slurmdb_tres_rec_t *slurmdb_find_tres_in_string(
	char *tres_str_in, int id)
{
	slurmdb_tres_rec_t *tres_rec = NULL;
	char *tmp_str = tres_str_in;

	if (!tmp_str || !tmp_str[0])
		return tres_rec;

	while (tmp_str) {
		if (id == atoi(tmp_str)) {
			if (!(tmp_str = strchr(tmp_str, '='))) {
				error("%s: no value found", __func__);
				break;
			}
			tres_rec = xmalloc(sizeof(slurmdb_tres_rec_t));
			tres_rec->id = id;
			tres_rec->count = slurm_atoull(++tmp_str);
			return tres_rec;
		}

		if (!(tmp_str = strchr(tmp_str, ',')))
			break;
		tmp_str++;
	}

	return tres_rec;
}

extern uint64_t slurmdb_find_tres_count_in_string(char *tres_str_in, int id)
{
	char *tmp_str = tres_str_in;

	if (!tmp_str || !tmp_str[0])
		return INFINITE64;

	while (tmp_str) {
		if (id == atoi(tmp_str)) {
			if (!(tmp_str = strchr(tmp_str, '='))) {
				error("slurmdb_find_tres_count_in_string: "
				      "no value found");
				break;
			}
			return slurm_atoull(++tmp_str);
		}

		if (!(tmp_str = strchr(tmp_str, ',')))
			break;
		tmp_str++;
	}

	return INFINITE64;
}

extern int slurmdb_find_qos_in_list_by_name(void *x, void *key)
{
	slurmdb_qos_rec_t *qos_rec = (slurmdb_qos_rec_t *)x;
	char *name = (char *)key;

	if (!xstrcmp(qos_rec->name, name))
		return 1;

	return 0;
}

extern int slurmdb_find_qos_in_list(void *x, void *key)
{
	slurmdb_qos_rec_t *qos_rec = (slurmdb_qos_rec_t *)x;
	uint32_t qos_id = *(uint32_t *)key;

	if (qos_rec->id == qos_id)
		return 1;

	return 0;
}

extern int slurmdb_find_selected_step_in_list(void *x, void *key)
{
	slurm_selected_step_t *selected_step = (slurm_selected_step_t *)x;
	slurm_selected_step_t *query_step = (slurm_selected_step_t *)key;

	if (!memcmp(&query_step->step_id, &selected_step->step_id,
		    sizeof(query_step->step_id)) &&
	    (query_step->array_task_id == selected_step->array_task_id) &&
	    (query_step->het_job_offset == selected_step->het_job_offset))
		return 1;

	return 0;
}

extern int slurmdb_find_assoc_in_list(void *x, void *key)
{
	slurmdb_assoc_rec_t *assoc_rec = (slurmdb_assoc_rec_t *)x;
	uint32_t assoc_id = *(uint32_t *)key;

	if (assoc_rec->id == assoc_id)
		return 1;

	return 0;
}

extern int slurmdb_find_update_object_in_list(void *x, void *key)
{
	slurmdb_update_object_t *update_object = (slurmdb_update_object_t *)x;
	slurmdb_update_type_t type = *(slurmdb_update_type_t *)key;

	if (update_object->type == type)
		return 1;

	return 0;
}


extern int slurmdb_find_tres_in_list(void *x, void *key)
{
	slurmdb_tres_rec_t *tres_rec = (slurmdb_tres_rec_t *)x;
	uint32_t tres_id = *(uint32_t *)key;

	if (tres_rec->id == tres_id)
		return 1;

	return 0;
}

extern int slurmdb_find_tres_in_list_by_count(void *x, void *key)
{
	slurmdb_tres_rec_t *tres_rec = (slurmdb_tres_rec_t *)x;
	uint64_t count = *(uint64_t *)key;

	if (tres_rec->count == count)
		return 1;

	return 0;
}

extern int slurmdb_find_tres_in_list_by_type(void *x, void *key)
{
	slurmdb_tres_rec_t *tres_rec = (slurmdb_tres_rec_t *)x;
	char *type = (char *)key;
	int end = 0;
	bool found = false;

	while (type[end]) {
		if (type[end] == '/') {
			found = true;
			break;
		}
		end++;
	}

	if (!xstrncasecmp(tres_rec->type, type, end)) {
		if ((!found && !tres_rec->name) ||
		    (found && !xstrcasecmp(tres_rec->name, type + end + 1))) {
			return 1;
		}
	}

	return 0;
}

extern int slurmdb_find_cluster_in_list(void *x, void *key)
{
	slurmdb_cluster_rec_t *object = (slurmdb_cluster_rec_t *)x;
	char *name = (char *)key;

	if (!xstrcmp(object->name, name))
		return 1;

	return 0;
}

extern int slurmdb_find_cluster_accting_tres_in_list(void *x, void *key)
{
	slurmdb_cluster_accounting_rec_t *object =
		(slurmdb_cluster_accounting_rec_t *)x;
	uint32_t tres_id = *(uint32_t *)key;

	if (object->tres_rec.id == tres_id)
		return 1;

	return 0;
}

extern int slurmdb_add_cluster_accounting_to_tres_list(
	slurmdb_cluster_accounting_rec_t *accting,
	list_t **tres)
{
	slurmdb_tres_rec_t *tres_rec = NULL;

	if (!*tres)
		*tres = list_create(slurmdb_destroy_tres_rec);
	else
		tres_rec = list_find_first(*tres,
					   slurmdb_find_tres_in_list,
					   &accting->tres_rec.id);

	if (!tres_rec) {
		tres_rec = slurmdb_copy_tres_rec(&accting->tres_rec);
		if (!tres_rec) {
			error("slurmdb_copy_tres_rec returned NULL");
			return SLURM_ERROR;
		}
		list_push(*tres, tres_rec);
	}

	tres_rec->alloc_secs += accting->alloc_secs
		+ accting->down_secs + accting->idle_secs
		+ accting->plan_secs + accting->pdown_secs;
	tres_rec->count += accting->tres_rec.count;
	tres_rec->rec_count++;

	return SLURM_SUCCESS;
}

extern int slurmdb_add_accounting_to_tres_list(
	slurmdb_accounting_rec_t *accting,
	list_t **tres)
{
	slurmdb_tres_rec_t *tres_rec = NULL;

	if (!*tres)
		*tres = list_create(slurmdb_destroy_tres_rec);
	else
		tres_rec = list_find_first(*tres,
					   slurmdb_find_tres_in_list,
					   &accting->tres_rec.id);

	if (!tres_rec) {
		tres_rec = slurmdb_copy_tres_rec(&accting->tres_rec);
		if (!tres_rec) {
			error("slurmdb_copy_tres_rec returned NULL");
			return SLURM_ERROR;
		}
		list_push(*tres, tres_rec);
	}

	tres_rec->alloc_secs += accting->alloc_secs;

	return SLURM_SUCCESS;
}

extern int slurmdb_add_time_from_count_to_tres_list(
	slurmdb_tres_rec_t *tres_in, list_t **tres, time_t elapsed)
{
	slurmdb_tres_rec_t *tres_rec = NULL;

	if (!elapsed)
		return SLURM_SUCCESS;

	if (!*tres)
		*tres = list_create(slurmdb_destroy_tres_rec);
	else
		tres_rec = list_find_first(*tres,
					   slurmdb_find_tres_in_list,
					   &tres_in->id);

	if (!tres_rec) {
		tres_rec = slurmdb_copy_tres_rec(tres_in);
		if (!tres_rec) {
			error("slurmdb_copy_tres_rec returned NULL");
			return SLURM_ERROR;
		}
		list_push(*tres, tres_rec);
	}

	tres_rec->alloc_secs +=
		((uint64_t)tres_in->count * (uint64_t)elapsed);

	return SLURM_SUCCESS;
}

extern int slurmdb_sum_accounting_list(
	slurmdb_cluster_accounting_rec_t *accting,
	list_t **total_tres_acct)
{
	slurmdb_cluster_accounting_rec_t *total_acct = NULL;

	if (!*total_tres_acct)
		*total_tres_acct = list_create(
			slurmdb_destroy_cluster_accounting_rec);
	else
		total_acct = list_find_first(
			*total_tres_acct,
			slurmdb_find_cluster_accting_tres_in_list,
			&accting->tres_rec.id);

	if (!total_acct) {
		total_acct = xmalloc(sizeof(slurmdb_cluster_accounting_rec_t));
		total_acct->tres_rec.id = accting->tres_rec.id;
		list_push(*total_tres_acct, total_acct);
	}

	total_acct->alloc_secs += accting->alloc_secs;
	total_acct->down_secs  += accting->down_secs;
	total_acct->idle_secs  += accting->idle_secs;
	total_acct->plan_secs  += accting->plan_secs;
	total_acct->over_secs  += accting->over_secs;
	total_acct->pdown_secs += accting->pdown_secs;
	total_acct->tres_rec.count += accting->tres_rec.count;
	total_acct->tres_rec.rec_count++;

	return SLURM_SUCCESS;
}

extern void slurmdb_transfer_acct_list_2_tres(
	list_t *accounting_list, list_t **tres)
{
	list_itr_t *itr;
	slurmdb_accounting_rec_t *accting = NULL;

	xassert(accounting_list);
	xassert(tres);

	/* get the amount of time this assoc used
	   during the time we are looking at */
	itr = list_iterator_create(accounting_list);
	while ((accting = list_next(itr)))
		slurmdb_add_accounting_to_tres_list(accting, tres);
	list_iterator_destroy(itr);
}

extern void slurmdb_transfer_tres_time(
	list_t **tres_list_out, char *tres_str, int elapsed)
{
	list_itr_t *itr;
	slurmdb_tres_rec_t *tres_rec = NULL;
	list_t *job_tres_list = NULL;

	xassert(tres_list_out);

	slurmdb_tres_list_from_string(&job_tres_list, tres_str,
				      TRES_STR_FLAG_NONE, NULL);

	if (!job_tres_list)
		return;

	/* get the amount of time this assoc used
	   during the time we are looking at */
	itr = list_iterator_create(job_tres_list);
	while ((tres_rec = list_next(itr)))
		slurmdb_add_time_from_count_to_tres_list(
			tres_rec, tres_list_out, elapsed);
	list_iterator_destroy(itr);
	FREE_NULL_LIST(job_tres_list);
}

extern int slurmdb_get_tres_base_unit(char *tres_type)
{
	int ret_unit = UNIT_NONE;
	if ((!xstrcasecmp(tres_type, "mem")) ||
	    (!xstrcasecmp(tres_type, "bb"))) {
		ret_unit = UNIT_MEGA;
	}

	return ret_unit;
}

extern char *slurmdb_ave_tres_usage(char *tres_string, int tasks)
{
	list_t *tres_list = NULL;
	list_itr_t *itr;
	slurmdb_tres_rec_t *tres_rec = NULL;
	uint32_t flags = TRES_STR_FLAG_SIMPLE + TRES_STR_FLAG_REPLACE;
	char *ret_tres_str = NULL;

	if (!tres_string || (tres_string[0] == '\0'))
		return NULL;

	slurmdb_tres_list_from_string(&tres_list, tres_string, flags, NULL);
	if (!tres_list) {
		error("%s: couldn't make tres_list from \'%s\'", __func__,
		      tres_string);
		return ret_tres_str;
	}

	itr = list_iterator_create(tres_list);
	while ((tres_rec = list_next(itr)))
		tres_rec->count /= (uint64_t)tasks;
	list_iterator_destroy(itr);

	ret_tres_str = slurmdb_make_tres_string(tres_list, flags);
	FREE_NULL_LIST(tres_list);

	return ret_tres_str;
}

extern void slurmdb_destroy_rpc_obj(void *object)
{
	slurmdb_rpc_obj_t *rpc_obj = (slurmdb_rpc_obj_t *)object;

	if (!rpc_obj)
		return;

	xfree(rpc_obj);
}

extern void slurmdb_destroy_rollup_stats(void *object)
{
	slurmdb_rollup_stats_t *rollup_stats = (slurmdb_rollup_stats_t *)object;

	if (!rollup_stats)
		return;

	xfree(rollup_stats->cluster_name);
	xfree(rollup_stats);
}

extern void slurmdb_free_stats_rec_members(void *object)
{
	slurmdb_stats_rec_t *rpc_stats = (slurmdb_stats_rec_t *)object;

	if (!rpc_stats)
		return;

	slurmdb_destroy_rollup_stats(rpc_stats->dbd_rollup_stats);
	rpc_stats->dbd_rollup_stats = NULL;

	FREE_NULL_LIST(rpc_stats->rollup_stats);
	FREE_NULL_LIST(rpc_stats->rpc_list);
	FREE_NULL_LIST(rpc_stats->user_list);
}

extern void slurmdb_destroy_stats_rec(void *object)
{
	if (!object)
		return;

	slurmdb_free_stats_rec_members(object);
	xfree(object);
}

extern void slurmdb_free_slurmdb_stats_members(slurmdb_stats_t *stats)
{
	if (stats) {
		xfree(stats->tres_usage_in_ave);
		xfree(stats->tres_usage_in_max);
		xfree(stats->tres_usage_in_max_nodeid);
		xfree(stats->tres_usage_in_max_taskid);
		xfree(stats->tres_usage_in_min);
		xfree(stats->tres_usage_in_min_nodeid);
		xfree(stats->tres_usage_in_min_taskid);
		xfree(stats->tres_usage_in_tot);
		xfree(stats->tres_usage_out_ave);
		xfree(stats->tres_usage_out_max);
		xfree(stats->tres_usage_out_max_nodeid);
		xfree(stats->tres_usage_out_max_taskid);
		xfree(stats->tres_usage_out_min);
		xfree(stats->tres_usage_out_min_nodeid);
		xfree(stats->tres_usage_out_min_taskid);
		xfree(stats->tres_usage_out_tot);
	}
}

extern void slurmdb_destroy_slurmdb_stats(slurmdb_stats_t *stats)
{
	slurmdb_free_slurmdb_stats_members(stats);
	xfree(stats);
}

/*
 * Comparator for sorting jobs by submit time. 0 (unknown) submit time
 * is mapped to INFINITE
 */
extern int slurmdb_job_sort_by_submit_time(void *v1, void *v2)
{
	time_t time1 = (*(slurmdb_job_rec_t **)v1)->submit;
	time_t time2 = (*(slurmdb_job_rec_t **)v2)->submit;

	/*
	 * Sanity check submits should never be 0, but if somehow that does
	 * happen treat it as the highest number.
	 */
	time1 = time1 ? time1 : INFINITE;
	time2 = time2 ? time2 : INFINITE;

	if (time1 < time2)
		return -1;
	else if (time1 > time2)
		return 1;
	return 0;
}

extern void slurmdb_merge_grp_node_usage(bitstr_t **grp_node_bitmap1,
					 uint16_t **grp_node_job_cnt1,
					 bitstr_t *grp_node_bitmap2,
					 uint16_t *grp_node_job_cnt2)
{
	if (!grp_node_bitmap2)
		return;

	if (!grp_node_bitmap1) {
		error("%s: grp_node_bitmap1 is NULL", __func__);
		return;
	}

	if (!grp_node_job_cnt1) {
		error("%s: grp_node_job_cnt1 is NULL", __func__);
		return;
	}

	if (*grp_node_bitmap1)
		bit_or(*grp_node_bitmap1, grp_node_bitmap2);
	else
		*grp_node_bitmap1 = bit_copy(grp_node_bitmap2);

	if (!*grp_node_job_cnt1)
		*grp_node_job_cnt1 =
			xcalloc(bit_size(*grp_node_bitmap1), sizeof(uint16_t));

	for (int i = 0; next_node_bitmap(grp_node_bitmap2, &i); i++) {
		(*grp_node_job_cnt1)[i] +=
			grp_node_job_cnt2 ? grp_node_job_cnt2[i] : 1;
	}
}

extern char *slurmdb_get_job_id_str(slurmdb_job_rec_t *job)
{
	char *id = NULL;

	if (job->array_task_str) {
		xlate_array_task_str(
			&job->array_task_str,
			job->array_max_tasks, NULL);
		id = xstrdup_printf("%u_[%s]",
				    job->array_job_id,
				    job->array_task_str);
	} else if (job->array_task_id != NO_VAL)
		id = xstrdup_printf("%u_%u",
				    job->array_job_id,
				    job->array_task_id);
	else if (job->het_job_id)
		id = xstrdup_printf("%u+%u",
				    job->het_job_id,
				    job->het_job_offset);
	else
		id = xstrdup_printf("%u", job->jobid);

	return id;

}

/* This always refers to the batch step. */
extern char *slurmdb_expand_job_stdio_fields(char *path, slurmdb_job_rec_t *job)
{
	job_std_pattern_t job_stp = { 0 };
	hostlist_t *nodes = NULL;
	char *ret;

	if (job->nodes) {
		nodes = hostlist_create(job->nodes);
		job_stp.first_step_node = hostlist_shift(nodes);
	} else {
		/* Can be NULL if job was never allocated. */
		job_stp.first_step_node = NULL;
	}

	job_stp.array_job_id = job->array_job_id;
	job_stp.array_task_id = job->array_task_id;
	job_stp.jobname = job->jobname;
	job_stp.restart_cnt = job->restart_cnt;
	job_stp.step_id = SLURM_STEP_ID_INITIALIZER;
	job_stp.step_id.job_id = job->jobid;
	job_stp.step_id.sluid = job->db_index;
	job_stp.step_id.step_id = SLURM_BATCH_SCRIPT;
	job_stp.user = job->user;
	job_stp.work_dir = job->work_dir;

	ret = expand_stdio_fields(path, &job_stp);

	if (nodes) {
		hostlist_destroy(nodes);
		if (job_stp.first_step_node)
			free(job_stp.first_step_node);
	}

	return ret;
}

extern char *slurmdb_expand_step_stdio_fields(char *path,
					      slurmdb_step_rec_t *step)
{
	job_std_pattern_t job_stp = { 0 };
	slurmdb_job_rec_t *job = step->job_ptr;
	hostlist_t *nodes = hostlist_create(step->nodes);
	char *ret;

	job_stp.array_job_id = job->array_job_id;
	job_stp.array_task_id = job->array_task_id;
	job_stp.first_step_node = hostlist_shift(nodes);
	job_stp.jobname = job->jobname;
	job_stp.step_id = step->step_id;
	job_stp.user = job->user;
	job_stp.work_dir = step->cwd;

	ret = expand_stdio_fields(path, &job_stp);

	hostlist_destroy(nodes);
	if (job_stp.first_step_node)
		free(job_stp.first_step_node);

	return ret;
}

extern int slurmdb_add_coord_to_user(slurmdb_user_rec_t *user, char *acct_name,
				     uint16_t direct)
{
	slurmdb_coord_rec_t *coord = NULL;

	xassert(user);
	xassert(user->coord_accts);

	if (assoc_mgr_is_user_acct_coord_user_rec(user, acct_name))
		return 0;

	coord = xmalloc(sizeof(*coord));
	coord->name = xstrdup(acct_name);
	coord->direct = direct;
	list_append(user->coord_accts, coord);
	debug2("adding %s to coord_accts for user %s %s",
	       coord->name, user->name,
	       coord->direct ? "directly" : "indirectly");
	return 1;
}
