/*****************************************************************************\
 *  cluster_functions.c - functions dealing with clusters in the
 *                        accounting system.
 *****************************************************************************
 *  Copyright (C) 2002-2007 The Regents of the University of California.
 *  Copyright (C) 2008-2011 Lawrence Livermore National Security.
 *  Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
 *  Written by Danny Auble <da@llnl.gov>
 *  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 "src/common/sercli.h"
#include "src/common/uid.h"

#include "src/interfaces/data_parser.h"

#include "src/sacctmgr/sacctmgr.h"

static bool with_deleted = 0;
static bool with_fed = 0;
static bool without_limits = 0;

static int _set_cond(int *start, int argc, char **argv,
		     slurmdb_cluster_cond_t *cluster_cond,
		     list_t *format_list)
{
	int i = 0;
	int cond_set = 0;
	int end = 0;
	int command_len = 0;

	with_deleted = 0;
	without_limits = 0;

	for (i=(*start); i<argc; i++) {
		int op_type;
		end = parse_option_end(argv[i], &op_type, &command_len);
		if (!common_verify_option_syntax(argv[i], op_type, false))
			continue;

		if (!xstrncasecmp(argv[i], "Set", MAX(command_len, 3))) {
			i--;
			break;
		} else if (!end && !xstrncasecmp(argv[i], "where",
					       MAX(command_len, 5))) {
			continue;
		} else if (!end &&
			   !xstrncasecmp(argv[i], "WithDeleted",
					 MAX(command_len, 5))) {
			with_deleted = 1;
		} else if (!end &&
			   !xstrncasecmp(argv[i], "WithFed",
					 MAX(command_len, 5))) {
			with_fed = 1;
		} else if (!end && !xstrncasecmp(argv[i], "WOLimits",
						 MAX(command_len, 3))) {
			without_limits = 1;
		} else if (!end || !xstrncasecmp(argv[i], "Names",
						MAX(command_len, 1))
			  || !xstrncasecmp(argv[i], "Clusters",
					   MAX(command_len, 3))) {
			if (!cluster_cond->cluster_list)
				cluster_cond->cluster_list =
					list_create(xfree_ptr);
			if (slurm_addto_char_list(cluster_cond->cluster_list,
						 argv[i]+end))
				cond_set |= SA_SET_ASSOC;
		} else if (!end || !xstrncasecmp(argv[i], "Federations",
						MAX(command_len, 3))) {
			if (!cluster_cond->federation_list)
				cluster_cond->federation_list =
					list_create(xfree_ptr);
			if (slurm_addto_char_list(cluster_cond->federation_list,
						  argv[i]+end))
				cond_set |= SA_SET_ASSOC;
		} else if (!xstrncasecmp(argv[i], "Classification",
					 MAX(command_len, 3))) {
			cluster_cond->classification =
				str_2_classification(argv[i]+end);
			if (cluster_cond->classification)
				cond_set |= SA_SET_CLUST;
		} else if (!xstrncasecmp(argv[i], "flags",
					 MAX(command_len, 2))) {
			const char *flag_str = (argv[i] + end);
			int rc;

			if ((rc = slurmdb_str_2_cluster_flags(
				     flag_str, &cluster_cond->flags))) {
				fprintf(stderr, " Unknown Flag(s)=%s\n",
					flag_str);
				exit_code = 1;
			} else {
				cond_set |= SA_SET_CLUST;
			}
			break;
		} else if (!xstrncasecmp(argv[i], "Format",
					 MAX(command_len, 2))) {
			if (format_list)
				slurm_addto_char_list(format_list, argv[i]+end);
		} else if (!end || !xstrncasecmp(argv[i], "RPCVersions",
						MAX(command_len, 1))) {
			if (!cluster_cond->rpc_version_list)
				cluster_cond->rpc_version_list =
					list_create(xfree_ptr);
			if (slurm_addto_char_list(cluster_cond->rpc_version_list,
						 argv[i]+end))
				cond_set |= SA_SET_CLUST;
		} else {
			exit_code=1;
			fprintf(stderr, " Unknown condition: %s\n"
				" Use keyword 'set' to modify value\n",
				argv[i]);
			break;
		}
	}
	(*start) = i;

	return cond_set;
}

static int _set_rec(int *start, int argc, char **argv,
		    list_t *name_list,
		    slurmdb_assoc_rec_t *assoc,
		    slurmdb_cluster_rec_t *cluster)
{
	int i = 0;
	int rec_set = 0;
	int end = 0;
	int command_len = 0;
	int option = 0;
	bool allow_option = false;

	xassert(assoc);
	xassert(cluster);

	for (i=(*start); i<argc; i++) {
		end = parse_option_end(argv[i], &option, &command_len);

		if (!xstrncasecmp(argv[i], "Where", MAX(command_len, 5))) {
			i--;
			break;
		} else if (!end && !xstrncasecmp(argv[i], "set",
					       MAX(command_len, 3))) {
			continue;
		} else if (!end
			  || !xstrncasecmp(argv[i], "Names",
					   MAX(command_len, 1))
			  || !xstrncasecmp(argv[i], "Clusters",
					   MAX(command_len, 3))) {
			if (name_list)
				slurm_addto_char_list(name_list,
						      argv[i]+end);
		} else if (!xstrncasecmp(argv[i], "Classification",
					 MAX(command_len, 3))) {
			cluster->classification =
				str_2_classification(argv[i]+end);
			if (cluster->classification)
				rec_set |= SA_SET_CLUST;
		} else if (!xstrncasecmp(argv[i], "Features",
					MAX(command_len, 2))) {
			allow_option = true;
			if (*(argv[i]+end) == '\0' &&
			    (option == '+' || option == '-')) {
				fprintf(stderr,
					" You didn't specify any features to %s\n",
					(option == '-') ? "remove" : "add");
				exit_code = 1;
				break;
			}

			if (!cluster->fed.feature_list)
				cluster->fed.feature_list =
					list_create(xfree_ptr);
			if ((slurm_addto_mode_char_list(cluster->fed.feature_list,
						   argv[i]+end, option) < 0)) {
				FREE_NULL_LIST(cluster->fed.feature_list);
				exit_code = 1;
				break;
			}
			rec_set |= SA_SET_CLUST;

		} else if (!xstrncasecmp(argv[i], "Federation",
					 MAX(command_len, 3))) {
			cluster->fed.name = xstrdup(argv[i]+end);
			rec_set |= SA_SET_CLUST;
		} else if (!xstrncasecmp(argv[i], "FedState",
					 MAX(command_len, 2))) {
			if (cluster) {
				cluster->fed.state =
					str_2_cluster_fed_states(argv[i]+end);
				if (!cluster->fed.state) {
					exit_code=1;
					fprintf(stderr, "Invalid FedState "
						"%s.\n", argv[i]+end);
					break;
				}

				rec_set |= SA_SET_CLUST;
			}
		} else if (!xstrncasecmp(argv[i], "GrpCPURunMins",
					 MAX(command_len, 7)) ||
			   !xstrncasecmp(argv[i], "GrpTRESRunMins",
					MAX(command_len, 8))) {
			exit_code=1;
			fprintf(stderr, "GrpTRESRunMins is not a valid option "
				"for the root association of a cluster.\n");
			break;
		} else if (!xstrncasecmp(argv[i], "GrpCPUMins",
					 MAX(command_len, 7)) ||
			   !xstrncasecmp(argv[i], "GrpTRESMins",
					MAX(command_len, 8))) {
			exit_code=1;
			fprintf(stderr, "GrpTRESMins is not a valid option "
				"for the root association of a cluster.\n");
			break;
		} else if (!xstrncasecmp(argv[i], "GrpWall",
					 MAX(command_len, 4))) {
			exit_code=1;
			fprintf(stderr, "GrpWall is not a valid option "
				"for the root association of a cluster.\n");
		} else if (sacctmgr_set_assoc_rec(
				   assoc, argv[i], argv[i]+end,
				   command_len, option,
				   &allow_option)) {
			rec_set |= SA_SET_ASSOC;
		} else {
			allow_option = true;
			exit_code=1;
			fprintf(stderr, " Unknown option: %s\n"
				" Use keyword 'where' to modify condition\n",
				argv[i]);
		}

		common_verify_option_syntax(argv[i], option, allow_option);
	}
	(*start) = i;

	return rec_set;
}


extern int sacctmgr_add_cluster(int argc, char **argv)
{
	int rc = SLURM_SUCCESS;
	int i = 0;
	slurmdb_cluster_rec_t *cluster = NULL;
	slurmdb_cluster_rec_t *start_cluster =
		xmalloc(sizeof(slurmdb_cluster_rec_t));
	list_t *name_list = list_create(xfree_ptr);
	list_t *cluster_list = NULL;
	slurmdb_assoc_rec_t start_assoc;

	int rec_set = 0;
	list_itr_t *itr = NULL, *itr_c = NULL;
	char *name = NULL;

	slurmdb_init_assoc_rec(&start_assoc, 0);
	slurmdb_init_cluster_rec(start_cluster, 0);

	for (i=0; i<argc; i++) {
		int command_len = strlen(argv[i]);
		if (!xstrncasecmp(argv[i], "Where", MAX(command_len, 5))
		    || !xstrncasecmp(argv[i], "Set", MAX(command_len, 3)))
			i++;
		rec_set |= _set_rec(&i, argc, argv,
				    name_list, &start_assoc, start_cluster);
	}
	if (exit_code) {
		FREE_NULL_LIST(name_list);
		slurmdb_destroy_cluster_rec(start_cluster);
		return SLURM_ERROR;
	} else if (!list_count(name_list)) {
		FREE_NULL_LIST(name_list);
		slurmdb_destroy_cluster_rec(start_cluster);
		exit_code=1;
		fprintf(stderr, " Need name of cluster to add.\n");
		return SLURM_ERROR;
	} else {
		list_t *temp_list = NULL;
		slurmdb_cluster_cond_t cluster_cond;

		slurmdb_init_cluster_cond(&cluster_cond, 0);
		cluster_cond.cluster_list = name_list;
		cluster_cond.classification = start_cluster->classification;

		temp_list = slurmdb_clusters_get(db_conn, &cluster_cond);
		if (!temp_list) {
			exit_code=1;
			fprintf(stderr,
				" Problem getting clusters from database.  "
				"Contact your admin.\n");
			slurmdb_destroy_cluster_rec(start_cluster);
			return SLURM_ERROR;
		}

		itr_c = list_iterator_create(name_list);
		itr = list_iterator_create(temp_list);
		while((name = list_next(itr_c))) {
			slurmdb_cluster_rec_t *cluster_rec = NULL;

			list_iterator_reset(itr);
			while((cluster_rec = list_next(itr))) {
				if (!xstrcasecmp(cluster_rec->name, name))
					break;
			}
			if (cluster_rec) {
				printf(" This cluster %s already exists.  "
				       "Not adding.\n", name);
				list_delete_item(itr_c);
			}
		}
		list_iterator_destroy(itr);
		list_iterator_destroy(itr_c);
		FREE_NULL_LIST(temp_list);
		if (!list_count(name_list)) {
			FREE_NULL_LIST(name_list);
			slurmdb_destroy_cluster_rec(start_cluster);
			return SLURM_ERROR;
		}
	}

	if (start_cluster->fed.name) {
		int rc;
		list_t *fed_list = list_create(xfree_ptr);
		list_append(fed_list, xstrdup(start_cluster->fed.name));
		rc = verify_federations_exist(fed_list);
		FREE_NULL_LIST(fed_list);
		if (rc) {
			slurmdb_destroy_cluster_rec(start_cluster);
			FREE_NULL_LIST(name_list);
			return SLURM_ERROR;
		}
	}

	printf(" Adding Cluster(s)\n");
	cluster_list = list_create(slurmdb_destroy_cluster_rec);
	itr = list_iterator_create(name_list);
	while((name = list_next(itr))) {
		if (!name[0]) {
			exit_code=1;
			fprintf(stderr, " No blank names are "
				"allowed when adding.\n");
			rc = SLURM_ERROR;
			continue;
		}
		cluster = xmalloc(sizeof(slurmdb_cluster_rec_t));
		slurmdb_init_cluster_rec(cluster, 0);

		list_append(cluster_list, cluster);
		slurmdb_copy_cluster_rec(cluster, start_cluster);
		cluster->name = xstrdup(name);
		printf("  Name           = %s\n", cluster->name);

		cluster->root_assoc =
			xmalloc(sizeof(slurmdb_assoc_rec_t));
		slurmdb_init_assoc_rec(cluster->root_assoc, 0);
		cluster->root_assoc->def_qos_id = start_assoc.def_qos_id;
		cluster->root_assoc->shares_raw = start_assoc.shares_raw;

		slurmdb_copy_assoc_rec_limits(
			cluster->root_assoc, &start_assoc);
	}
	list_iterator_destroy(itr);
	FREE_NULL_LIST(name_list);

	if (rec_set)
		printf(" Setting\n");
	if (rec_set & SA_SET_CLUST)
		sacctmgr_print_cluster(start_cluster);
	if (rec_set & SA_SET_ASSOC) {
		printf("  Default Limits:\n");
		sacctmgr_print_assoc_limits(&start_assoc);
		FREE_NULL_LIST(start_assoc.qos_list);
	}
	slurmdb_destroy_cluster_rec(start_cluster);

	if (!list_count(cluster_list)) {
		printf(" Nothing new added.\n");
		rc = SLURM_ERROR;
		goto end_it;
	}

	/* Since we are creating tables with add cluster that can't be
	   rolled back.  So we ask before hand if they are serious
	   about it so we can rollback if needed.
	*/
	if (commit_check("Would you like to commit changes?")) {
		notice_thread_init();
		rc = slurmdb_clusters_add(db_conn, cluster_list);
		notice_thread_fini();
		if (rc == SLURM_SUCCESS) {
			rc = slurmdb_connection_commit(db_conn, 1);
			if (rc != SLURM_SUCCESS)
				fprintf(stderr, " Error committing changes: %s\n",
					slurm_strerror(rc));
		} else {
			exit_code = 1;
			fprintf(stderr, " Problem adding clusters: %s\n",
				slurm_strerror(rc));
			/* this isn't really needed, but just to be safe */
			rc = slurmdb_connection_commit(db_conn, 0);
			if (rc != SLURM_SUCCESS)
				fprintf(stderr, " Error rolling back changes: %s\n",
					slurm_strerror(rc));
		}
	} else {
		printf(" Changes Discarded\n");
		/* this isn't really needed, but just to be safe */
		slurmdb_connection_commit(db_conn, 0);
	}

end_it:
	FREE_NULL_LIST(cluster_list);

	return rc;
}

extern int sacctmgr_list_cluster(int argc, char **argv)
{
	int rc = SLURM_SUCCESS;
	slurmdb_cluster_cond_t *cluster_cond =
		xmalloc(sizeof(slurmdb_cluster_cond_t));
	list_t *cluster_list;
	int i=0;
	list_itr_t *itr = NULL;
	list_itr_t *itr2 = NULL;
	slurmdb_cluster_rec_t *cluster = NULL;
	char *tmp_char = NULL;

	int field_count = 0;

	print_field_t *field = NULL;

	list_t *format_list = list_create(xfree_ptr);
	list_t *print_fields_list; /* types are of print_field_t */

	slurmdb_init_cluster_cond(cluster_cond, 0);
	cluster_cond->cluster_list = list_create(xfree_ptr);
	for (i=0; i<argc; i++) {
		int command_len = strlen(argv[i]);
		if (!xstrncasecmp(argv[i], "Where", MAX(command_len, 5))
		    || !xstrncasecmp(argv[i], "Set", MAX(command_len, 3)))
			i++;
		_set_cond(&i, argc, argv, cluster_cond, format_list);
	}

	if (exit_code) {
		slurmdb_destroy_cluster_cond(cluster_cond);
		FREE_NULL_LIST(format_list);
		return SLURM_ERROR;
	}

	if (!list_count(format_list)) {
		slurm_addto_char_list(format_list,
				      "Cl,Controlh,Controlp,RPC");
		if (!without_limits)
			slurm_addto_char_list(format_list,
					      "Fa,GrpJ,GrpTRES,GrpS,MaxJ,"
					      "MaxTRES,MaxS,MaxW,QOS,"
					      "DefaultQOS");
		if (with_fed)
			slurm_addto_char_list(
				format_list,
				"Federation,ID,Features,FedState");
	}

	cluster_cond->with_deleted = with_deleted;

	print_fields_list = sacctmgr_process_format_list(format_list);
	FREE_NULL_LIST(format_list);

	if (exit_code) {
		slurmdb_destroy_cluster_cond(cluster_cond);
		FREE_NULL_LIST(print_fields_list);
		return SLURM_ERROR;
	}

	cluster_list = slurmdb_clusters_get(db_conn, cluster_cond);
	slurmdb_destroy_cluster_cond(cluster_cond);

	if (mime_type) {
		DATA_DUMP_CLI_SINGLE(OPENAPI_CLUSTERS_RESP, cluster_list,
				     orig_argc, orig_argv, db_conn, mime_type,
				     data_parser, rc);
		FREE_NULL_LIST(print_fields_list);
		FREE_NULL_LIST(cluster_list);
		return rc;
	}

	if (!cluster_list) {
		exit_code=1;
		fprintf(stderr, " Problem with query.\n");
		FREE_NULL_LIST(print_fields_list);
		return SLURM_ERROR;
	}

	itr = list_iterator_create(cluster_list);
	itr2 = list_iterator_create(print_fields_list);
	print_fields_header(print_fields_list);

	field_count = list_count(print_fields_list);

	while ((cluster = list_next(itr))) {
		int curr_inx = 1;

		/* set up the working cluster rec so nodecnt's and node names
		 * are handled correctly */
		working_cluster_rec = cluster;
		while((field = list_next(itr2))) {
			switch(field->type) {
			case PRINT_CLUSTER:
				field->print_routine(field, cluster->name,
						     (curr_inx == field_count));
				break;
			case PRINT_CHOST:
				field->print_routine(field,
						     cluster->control_host,
						     (curr_inx == field_count));
				break;
			case PRINT_CPORT:
				field->print_routine(field,
						     &cluster->control_port,
						     (curr_inx == field_count));
				break;
			case PRINT_CLASS:
				tmp_char = get_classification_str(
						cluster->classification);
				field->print_routine(field,
						     tmp_char,
						     (curr_inx == field_count));
				tmp_char = NULL;
				break;
			case PRINT_FEATURES:
				field->print_routine(field,
						     &cluster->fed.feature_list,
						     (curr_inx == field_count));
				break;
			case PRINT_FEDERATION:
				field->print_routine(field, cluster->fed.name,
						     (curr_inx == field_count));
				break;
			case PRINT_FEDSTATE:
			{
				char *tmp_str = slurmdb_cluster_fed_states_str(
							cluster->fed.state);
				field->print_routine(field, tmp_str,
						     (curr_inx == field_count));
				break;
			}
			case PRINT_FEDSTATERAW:
				field->print_routine(field, &cluster->fed.state,
						     (curr_inx == field_count));
				break;
			case PRINT_ID:
				field->print_routine(field, &cluster->fed.id,
						     (curr_inx == field_count));
				break;
			case PRINT_TRES:
				sacctmgr_initialize_g_tres_list();

				tmp_char = slurmdb_make_tres_string_from_simple(
					cluster->tres_str, g_tres_list, NO_VAL,
					CONVERT_NUM_UNIT_EXACT, 0, NULL);
				field->print_routine(field, tmp_char,
						     (curr_inx == field_count));
				xfree(tmp_char);
				break;
			case PRINT_FLAGS:
				tmp_char = slurmdb_cluster_flags_2_str(
							cluster->flags);
				field->print_routine(field, tmp_char,
						     (curr_inx == field_count));
				xfree(tmp_char);
				break;
			case PRINT_NODECNT:
			{
				hostlist_t *hl = hostlist_create(cluster->nodes);
				int cnt = 0;
				if (hl) {
					cnt = hostlist_count(hl);
					hostlist_destroy(hl);
				}
				field->print_routine(
					field,
					&cnt,
					(curr_inx == field_count));
				break;
			}
			case PRINT_CLUSTER_NODES:
				field->print_routine(
					field,
					cluster->nodes,
					(curr_inx == field_count));
				break;
			case PRINT_RPC_VERSION:
				field->print_routine(
					field,
					&cluster->rpc_version,
					(curr_inx == field_count));
				break;
			default:
				sacctmgr_print_assoc_rec(cluster->root_assoc,
							 field, NULL,
							 (curr_inx ==
							  field_count));
				break;
			}
			curr_inx++;
		}
		list_iterator_reset(itr2);
		printf("\n");
	}
	/* clear the working cluster rec */
	working_cluster_rec = NULL;

	list_iterator_destroy(itr2);
	list_iterator_destroy(itr);
	FREE_NULL_LIST(cluster_list);
	FREE_NULL_LIST(print_fields_list);

	return rc;
}

static int _find_cluster_rec_in_list(void *obj, void *key)
{
	slurmdb_cluster_rec_t *rec = (slurmdb_cluster_rec_t *)obj;
	char *char_key = (char *)key;

	if (!xstrcasecmp(rec->name, char_key))
		return 1;

	return 0;
}

/* Prepare cluster_list to be federation centric that will be passed to
 * verify_fed_clusters in federation_functions.c.
 */
static int _verify_fed_clusters(list_t *cluster_list, const char *fed_name,
				bool *existing_fed)
{
	int   rc         = SLURM_SUCCESS;
	char *tmp_name   = NULL;
	list_t *tmp_list = list_create(slurmdb_destroy_cluster_rec);
	list_itr_t *itr = list_iterator_create(cluster_list);

	while ((tmp_name = list_next(itr))) {
		slurmdb_cluster_rec_t *rec =
			xmalloc(sizeof(slurmdb_cluster_rec_t));
		slurmdb_init_cluster_rec(rec, 0);
		rec->name = xstrdup(tmp_name);
		list_append(tmp_list, rec);
	}

	if ((rc = verify_fed_clusters(tmp_list, fed_name, existing_fed)))
		goto end_it;

	/* have to reconcile lists now, clusters may have been removed from
	 * tmp_list */
	list_iterator_reset(itr);
	while ((tmp_name = list_next(itr))) {
		if (!list_find_first(tmp_list, _find_cluster_rec_in_list,
				     tmp_name))
			list_delete_item(itr);
	}

end_it:
	FREE_NULL_LIST(tmp_list);
	list_iterator_destroy(itr);

	return rc;
}

extern int sacctmgr_modify_cluster(int argc, char **argv)
{
	int rc = SLURM_SUCCESS;
	int i=0;
	slurmdb_cluster_rec_t *cluster =
		xmalloc(sizeof(slurmdb_cluster_rec_t));
	slurmdb_assoc_rec_t *assoc =
		xmalloc(sizeof(slurmdb_assoc_rec_t));
	slurmdb_assoc_cond_t *assoc_cond =
		xmalloc(sizeof(slurmdb_assoc_cond_t));
	int cond_set = 0, prev_set = 0, rec_set = 0, set = 0;
	list_t *ret_list = NULL;
	slurmdb_cluster_cond_t cluster_cond;
	bool existing_fed = false;

	slurmdb_init_assoc_rec(assoc, 0);

	assoc_cond->cluster_list = list_create(xfree_ptr);
	assoc_cond->acct_list = list_create(NULL);

	slurmdb_init_cluster_rec(cluster, 0);
	slurmdb_init_cluster_cond(&cluster_cond, 0);
	cluster_cond.cluster_list = assoc_cond->cluster_list;

	for (i=0; i<argc; i++) {
		int command_len = strlen(argv[i]);
		if (!xstrncasecmp(argv[i], "Where", MAX(command_len, 5))) {
			i++;
			prev_set = _set_cond(&i, argc, argv,
					     &cluster_cond, NULL);
			cond_set |= prev_set;
		} else if (!xstrncasecmp(argv[i], "Set", MAX(command_len, 3))) {
			i++;
			prev_set = _set_rec(&i, argc, argv,
					    NULL, assoc, cluster);
			rec_set |= prev_set;
		} else {
			prev_set = _set_cond(&i, argc, argv,
					     &cluster_cond, NULL);
			cond_set |= prev_set;
		}
	}

	if (exit_code) {
		rc = SLURM_ERROR;
		goto end_it;
	} else if (!rec_set) {
		exit_code=1;
		fprintf(stderr, " You didn't give me anything to set\n");
		rc = SLURM_ERROR;
		goto end_it;
	} else if (!cond_set) {
		if (!commit_check("You didn't set any conditions with 'WHERE'.\n"
				 "Are you sure you want to continue?")) {
			printf("Aborted\n");
			rc = SLURM_SUCCESS;
			goto end_it;
		}
	}

	if (rec_set & SA_SET_CLUST) {
		if (cluster->fed.name && cluster->fed.name[0]) {
			int rc;
			/* Make sure federation exists. */
			list_t *fed_list = list_create(xfree_ptr);
			list_append(fed_list, xstrdup(cluster->fed.name));
			rc = verify_federations_exist(fed_list);
			FREE_NULL_LIST(fed_list);
			if (rc)
				goto end_it;

			/*
			 * See if cluster is assigned to another federation
			 * already.
			 */
			if (list_count(cluster_cond.cluster_list)) {
				if (_verify_fed_clusters(
					    cluster_cond.cluster_list,
					    cluster->fed.name,
					    &existing_fed))
					goto end_it;

				/*
				 * _verify_fed_clusters() can remove from
				 * cluster_list.
				 */
				if (!list_count(cluster_cond.cluster_list)) {
					/*
					 * Irrelevant changes have been removed
					 * and nothing to change now.
					 */
					printf("  Nothing to change\n");
					rc = SLURM_ERROR;
					goto assoc;
				} else if (existing_fed &&
					   !commit_check("\nAre you sure you want to continue?")) {
					rc = SLURM_ERROR;
					goto end_it;
				}
			}
		}

		notice_thread_init();
		ret_list = slurmdb_clusters_modify(
			db_conn, &cluster_cond, cluster);

		if (ret_list && list_count(ret_list)) {
			char *object = NULL;
			printf(" Setting\n");
			sacctmgr_print_cluster(cluster);
			list_itr_t *itr = list_iterator_create(ret_list);
			printf(" Modified cluster...\n");
			while((object = list_next(itr))) {
				printf("  %s\n", object);
			}
			list_iterator_destroy(itr);
			set = 1;
		} else if (ret_list || errno == SLURM_NO_CHANGE_IN_DATA) {
			printf("  Nothing modified\n");
			rc = SLURM_ERROR;
		} else {
			exit_code=1;
			fprintf(stderr, " Error with request: %s\n",
				slurm_strerror(errno));
			rc = SLURM_ERROR;
		}

		FREE_NULL_LIST(ret_list);
		notice_thread_fini();
		if (set) {
			if (commit_check("Would you like to commit changes?")) {
				rc = slurmdb_connection_commit(db_conn, 1);
				if (rc != SLURM_SUCCESS)
					fprintf(stderr, " Error committing changes: %s\n",
						slurm_strerror(rc));
			} else {
				printf(" Changes Discarded\n");
				rc = slurmdb_connection_commit(db_conn, 0);
				if (rc != SLURM_SUCCESS)
					fprintf(stderr, " Error rolling back changes: %s\n",
						slurm_strerror(rc));
			}
			set = 0;
		}
	}
assoc:
	if (rec_set & SA_SET_ASSOC) {
		list_append(assoc_cond->acct_list, "root");
		notice_thread_init();
		ret_list = slurmdb_associations_modify(db_conn,
						       assoc_cond, assoc);

		if (ret_list && list_count(ret_list)) {
			char *object = NULL;
			list_itr_t *itr;

			printf(" Setting\n");
			printf("  Default Limits:\n");
			sacctmgr_print_assoc_limits(assoc);

			printf(" Modified cluster defaults for associations...\n");
			itr = list_iterator_create(ret_list);
			while((object = list_next(itr))) {
				printf("  %s\n", object);
			}
			list_iterator_destroy(itr);
			set = 1;
		} else if (ret_list || errno == SLURM_NO_CHANGE_IN_DATA) {
			printf("  Nothing modified\n");
			rc = SLURM_ERROR;
		} else {
			exit_code=1;
			fprintf(stderr, " Error with request: %s\n",
				slurm_strerror(errno));
			rc = SLURM_ERROR;
		}
		FREE_NULL_LIST(ret_list);
		notice_thread_fini();
	}

	if (set) {
		if (commit_check("Would you like to commit changes?")) {
			rc = slurmdb_connection_commit(db_conn, 1);
			if (rc != SLURM_SUCCESS)
				fprintf(stderr, " Error committing changes: %s\n",
					slurm_strerror(rc));
		} else {
			printf(" Changes Discarded\n");
			rc = slurmdb_connection_commit(db_conn, 0);
			if (rc != SLURM_SUCCESS)
				fprintf(stderr, " Error rolling back changes: %s\n",
					slurm_strerror(rc));
		}
	}
end_it:
	slurmdb_destroy_assoc_cond(assoc_cond);
	slurmdb_destroy_assoc_rec(assoc);
	slurmdb_destroy_cluster_rec(cluster);

	return rc;
}

extern int sacctmgr_delete_cluster(int argc, char **argv)
{
	int rc = SLURM_SUCCESS;
	slurmdb_cluster_cond_t *cluster_cond =
		xmalloc(sizeof(slurmdb_cluster_cond_t));
	int i=0;
	list_t *ret_list = NULL;
	int cond_set = 0, prev_set;

	slurmdb_init_cluster_cond(cluster_cond, 0);
	cluster_cond->cluster_list = list_create(xfree_ptr);

	for (i=0; i<argc; i++) {
		int command_len = strlen(argv[i]);
		if (!xstrncasecmp(argv[i], "Where", MAX(command_len, 5))
		    || !xstrncasecmp(argv[i], "Set", MAX(command_len, 3)))
			i++;
		prev_set = _set_cond(&i, argc, argv, cluster_cond, NULL);
		cond_set |= prev_set;
	}

	if (exit_code) {
		slurmdb_destroy_cluster_cond(cluster_cond);
		return SLURM_ERROR;
	} else if (!cond_set) {
		exit_code=1;
		fprintf(stderr,
			" No conditions given to remove, not executing.\n");
		slurmdb_destroy_cluster_cond(cluster_cond);
		return SLURM_ERROR;
	}

	if (!list_count(cluster_cond->cluster_list) &&
	    (!cluster_cond->rpc_version_list ||
	     !list_count(cluster_cond->rpc_version_list))
	   && !cluster_cond->classification
	   && (!cluster_cond->federation_list ||
	       !list_count(cluster_cond->federation_list))) {
		exit_code=1;
		fprintf(stderr,
			"problem with delete request.  "
			"Nothing given to delete.\n");
		slurmdb_destroy_cluster_cond(cluster_cond);
		return SLURM_SUCCESS;
	}
	notice_thread_init();
	ret_list = slurmdb_clusters_remove(db_conn, cluster_cond);
	rc = errno;
	notice_thread_fini();

	slurmdb_destroy_cluster_cond(cluster_cond);

	if (ret_list && list_count(ret_list)) {
		char *object = NULL;
		list_itr_t *itr = list_iterator_create(ret_list);
		/* If there were jobs running with an association to
		   be deleted, don't.
		*/
		if (rc == ESLURM_JOBS_RUNNING_ON_ASSOC) {
			fprintf(stderr, " Error with request: %s\n",
				slurm_strerror(rc));
			while((object = list_next(itr))) {
				fprintf(stderr,"  %s\n", object);
			}
			FREE_NULL_LIST(ret_list);
			slurmdb_connection_commit(db_conn, 0);
			return rc;
		}
		printf(" Deleting clusters...\n");
		while((object = list_next(itr))) {
			printf("  %s\n", object);
		}
		list_iterator_destroy(itr);
		if (commit_check("Would you like to commit changes?")) {
			rc = slurmdb_connection_commit(db_conn, 1);
			if (rc != SLURM_SUCCESS)
				fprintf(stderr, " Error committing changes: %s\n",
					slurm_strerror(rc));
		} else {
			printf(" Changes Discarded\n");
			rc = slurmdb_connection_commit(db_conn, 0);
			if (rc != SLURM_SUCCESS)
				fprintf(stderr, " Error rolling back changes: %s\n",
					slurm_strerror(rc));
		}
	} else if (ret_list) {
		printf(" Nothing deleted\n");
		rc = SLURM_ERROR;
	} else {
		exit_code=1;
		fprintf(stderr, " Error with request: %s\n",
			slurm_strerror(errno));
		rc = SLURM_ERROR;
	}

	FREE_NULL_LIST(ret_list);

	return rc;
}

extern int sacctmgr_dump_cluster (int argc, char **argv)
{
	slurmdb_user_cond_t user_cond;
	slurmdb_user_rec_t *user = NULL;
	slurmdb_hierarchical_rec_t *slurmdb_hierarchical_rec = NULL;
	slurmdb_assoc_rec_t *assoc = NULL;
	slurmdb_assoc_cond_t assoc_cond;
	list_t *assoc_list = NULL;
	list_t *acct_list = NULL;
	list_t *qos_list = NULL;
	list_t *user_list = NULL;
	list_t *slurmdb_hierarchical_rec_list = NULL;
	char *cluster_name = NULL;
	char *file_name = NULL;
	char *user_name = NULL;
	char *line = NULL;
	int i, command_len = 0, rc = SLURM_SUCCESS;
	FILE *fd = NULL;
	char *class_str = NULL;

	for (i = 0; i < argc; i++) {
		int op_type;
		int end = parse_option_end(argv[i], &op_type, &command_len);
		if (!common_verify_option_syntax(argv[i], op_type, false))
			continue;

		if (!end || !xstrncasecmp(argv[i], "Cluster",
					 MAX(command_len, 1))) {
			if (cluster_name) {
				exit_code = 1;
				fprintf(stderr,
					" Can only do one cluster at a time.  "
					"Already doing %s\n", cluster_name);
				continue;
			}
			cluster_name = xstrdup(argv[i]+end);
		} else if (!xstrncasecmp(argv[i], "File",
					MAX(command_len, 1))) {
			if (file_name) {
				exit_code = 1;
				fprintf(stderr,
					" File name already set to %s\n",
					file_name);
				continue;
			}
			file_name = xstrdup(argv[i]+end);
		} else {
			exit_code = 1;
			fprintf(stderr, " Unknown option: %s\n", argv[i]);
		}
	}

	if (!cluster_name) {
		rc = SLURM_ERROR;
		fprintf(stderr, " We need a cluster to dump.\n");
		goto end_it;
	} else {
		list_t *temp_list = NULL;
		slurmdb_cluster_cond_t cluster_cond;
		slurmdb_cluster_rec_t *cluster_rec = NULL;

		slurmdb_init_cluster_cond(&cluster_cond, 0);
		cluster_cond.cluster_list = list_create(NULL);
		list_push(cluster_cond.cluster_list, cluster_name);

		temp_list = slurmdb_clusters_get(db_conn, &cluster_cond);
		FREE_NULL_LIST(cluster_cond.cluster_list);
		if (!temp_list) {
			rc = SLURM_ERROR;
			fprintf(stderr,
				" Problem getting clusters from database.  "
				"Contact your admin.\n");
			goto end_it;
		}

		cluster_rec = list_peek(temp_list);
		if (!cluster_rec) {
			rc = SLURM_ERROR;
			fprintf(stderr, " Cluster %s doesn't exist.\n",
				cluster_name);
			FREE_NULL_LIST(temp_list);
			goto end_it;
		}
		class_str = get_classification_str(cluster_rec->classification);
		FREE_NULL_LIST(temp_list);
	}

	if (!file_name) {
		file_name = xstrdup_printf("./%s.cfg", cluster_name);
		printf(" No filename given, using %s.\n", file_name);
	}

	memset(&user_cond, 0, sizeof(slurmdb_user_cond_t));
	user_cond.with_coords = 1;
	user_cond.with_wckeys = 1;
	user_cond.with_assocs = 1;

	memset(&assoc_cond, 0, sizeof(slurmdb_assoc_cond_t));
	assoc_cond.flags = ASSOC_COND_FLAG_RAW_QOS | ASSOC_COND_FLAG_WOPL;
	assoc_cond.cluster_list = list_create(NULL);
	list_append(assoc_cond.cluster_list, cluster_name);
	/* this is needed for getting the correct wckeys */
	user_cond.assoc_cond = &assoc_cond;

	user_list = slurmdb_users_get(db_conn, &user_cond);
	/* If not running with the DBD assoc_cond.user_list can be set,
	 * which will mess other things up.
	 */
	if (assoc_cond.user_list) {
		FREE_NULL_LIST(assoc_cond.user_list);
		assoc_cond.user_list = NULL;
	}

	/* make sure this person running is an admin */
	user_name = uid_to_string_cached(my_uid);
	if (!(user = sacctmgr_find_user_from_list(user_list, user_name))) {
		rc = SLURM_ERROR;
		fprintf(stderr, " Your uid (%u) is not in the "
			"accounting system, can't dump cluster.\n", my_uid);
		FREE_NULL_LIST(assoc_cond.cluster_list);
		goto end_it;
	} else {
		if ((my_uid != slurm_conf.slurm_user_id) && (my_uid != 0)
		    && user->admin_level < SLURMDB_ADMIN_SUPER_USER) {
			rc = SLURM_ERROR;
			fprintf(stderr, " Your user does not have sufficient "
				"privileges to dump clusters.\n");
			FREE_NULL_LIST(assoc_cond.cluster_list);
			goto end_it;
		}
	}

	/* assoc_cond is set up above */
	assoc_list = slurmdb_associations_get(db_conn, &assoc_cond);
	FREE_NULL_LIST(assoc_cond.cluster_list);
	if (!assoc_list) {
		rc = SLURM_ERROR;
		fprintf(stderr, " Problem with query.\n");
		goto end_it;
	} else if (!list_count(assoc_list)) {
		rc = SLURM_ERROR;
		fprintf(stderr, " Cluster %s returned nothing.\n",
			cluster_name);
		goto end_it;
	}

	slurmdb_hierarchical_rec_list = slurmdb_get_acct_hierarchical_rec_list(
		assoc_list);

	acct_list = slurmdb_accounts_get(db_conn, NULL);

	if ((fd = fopen(file_name,"w")) == NULL) {
		fprintf(stderr, "Can't open file %s, %s\n", file_name,
			slurm_strerror(errno));
		rc = SLURM_ERROR;
		goto end_it;
	}

	/* Add header */
	if (fprintf(fd,
		    "# To edit this file start with a cluster line "
		    "for the new cluster\n"
		    "# Cluster - 'cluster_name':MaxTRESPerJob=node=50\n"
		    "# Followed by Accounts you want in this fashion "
		    "(root is created by default)...\n"
		    "# Parent - 'root'\n"
		    "# Account - 'cs':MaxTRESPerJob=node=5:MaxJobs=4:"
		    "MaxTRESMinsPerJob=cpu=20:FairShare=399:"
		    "MaxWallDurationPerJob=40:Description='Computer Science':"
		    "Organization='LC'\n"
		    "# Any of the options after a ':' can be left out and "
		    "they can be in any order.\n"
		    "# If you want to add any sub accounts just list the "
		    "Parent THAT HAS ALREADY\n"
		    "# BEEN CREATED before the account line in this "
		    "fashion...\n"
		    "# Parent - 'cs'\n"
		    "# Account - 'test':MaxTRESPerJob=node=1:MaxJobs=1:"
		    "MaxTRESMinsPerJob=cpu=1:FairShare=1:"
		    "MaxWallDurationPerJob=1:"
		    "Description='Test Account':Organization='Test'\n"
		    "# To add users to a account add a line like this after a "
		    "Parent - 'line'\n"
		    "# User - 'lipari':MaxTRESPerJob=node=2:MaxJobs=3:"
		    "MaxTRESMinsPerJob=cpu=4:FairShare=1:"
		    "MaxWallDurationPerJob=1\n") < 0) {
		rc = SLURM_ERROR;
		fprintf(stderr, "Can't write to file");
		goto end_it;
	}

	qos_list = slurmdb_qos_get(db_conn, NULL);

	if (qos_list) {
		(void) list_for_each(qos_list, file_print_qos, &line);
		FREE_NULL_LIST(qos_list);
	}

	xstrfmtcat(line, "Cluster - '%s'", cluster_name);

	if (class_str)
		xstrfmtcat(line, ":Classification='%s'", class_str);

	slurmdb_hierarchical_rec = list_peek(slurmdb_hierarchical_rec_list);
	assoc = slurmdb_hierarchical_rec->assoc;
	if (xstrcmp(assoc->acct, "root")) {
		fprintf(stderr, "Root association not on the top it was %s\n",
			assoc->acct);
	} else
		print_file_add_limits_to_line(&line, assoc);

	if (fprintf(fd, "%s\n", line) < 0) {
		rc = SLURM_ERROR;
		fprintf(stderr, " Can't write to file");
		xfree(line);
		goto end_it;
	}
	info("%s", line);
	xfree(line);

	print_file_slurmdb_hierarchical_rec_list(
		fd, slurmdb_hierarchical_rec_list, user_list, acct_list);

end_it:
	if (fd)
		fclose(fd);
	FREE_NULL_LIST(acct_list);
	FREE_NULL_LIST(assoc_list);
	FREE_NULL_LIST(user_list);
	xfree(cluster_name);
	xfree(file_name);
	FREE_NULL_LIST(slurmdb_hierarchical_rec_list);
	if (rc != SLURM_SUCCESS)
		exit_code = 1;
	return rc;
}
