1 /*****************************************************************************\
2  *  accounting_storage_mysql.c - accounting interface to as_mysql.
3  *****************************************************************************
4  *  Copyright (C) 2004-2007 The Regents of the University of California.
5  *  Copyright (C) 2008-2010 Lawrence Livermore National Security.
6  *  Copyright (C) 2011-2018 SchedMD LLC.
7  *  Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
8  *  Written by Danny Auble <da@schedmd.com, da@llnl.gov>
9  *
10  *  This file is part of Slurm, a resource management program.
11  *  For details, see <https://slurm.schedmd.com/>.
12  *  Please also read the included file: DISCLAIMER.
13  *
14  *  Slurm is free software; you can redistribute it and/or modify it under
15  *  the terms of the GNU General Public License as published by the Free
16  *  Software Foundation; either version 2 of the License, or (at your option)
17  *  any later version.
18  *
19  *  In addition, as a special exception, the copyright holders give permission
20  *  to link the code of portions of this program with the OpenSSL library under
21  *  certain conditions as described in each individual source file, and
22  *  distribute linked combinations including the two. You must obey the GNU
23  *  General Public License in all respects for all of the code used other than
24  *  OpenSSL. If you modify file(s) with this exception, you may extend this
25  *  exception to your version of the file(s), but you are not obligated to do
26  *  so. If you do not wish to do so, delete this exception statement from your
27  *  version.  If you delete this exception statement from all source files in
28  *  the program, then also delete it here.
29  *
30  *  Slurm is distributed in the hope that it will be useful, but WITHOUT ANY
31  *  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
32  *  FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
33  *  details.
34  *
35  *  You should have received a copy of the GNU General Public License along
36  *  with Slurm; if not, write to the Free Software Foundation, Inc.,
37  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA.
38  *****************************************************************************
39  * Notes on as_mysql configuration
40  *	Assumes mysql is installed as user root
41  *	Assumes SlurmUser is configured as user slurm
42  * # mysql --user=root -p
43  * mysql> GRANT ALL ON *.* TO 'slurm'@'localhost' IDENTIFIED BY PASSWORD 'pw';
44  * mysql> GRANT SELECT, INSERT ON *.* TO 'slurm'@'localhost';
45 \*****************************************************************************/
46 
47 #include "accounting_storage_mysql.h"
48 #include "as_mysql_acct.h"
49 #include "as_mysql_tres.h"
50 #include "as_mysql_archive.h"
51 #include "as_mysql_assoc.h"
52 #include "as_mysql_cluster.h"
53 #include "as_mysql_convert.h"
54 #include "as_mysql_federation.h"
55 #include "as_mysql_fix_runaway_jobs.h"
56 #include "as_mysql_job.h"
57 #include "as_mysql_jobacct_process.h"
58 #include "as_mysql_problems.h"
59 #include "as_mysql_qos.h"
60 #include "as_mysql_resource.h"
61 #include "as_mysql_resv.h"
62 #include "as_mysql_rollup.h"
63 #include "as_mysql_txn.h"
64 #include "as_mysql_usage.h"
65 #include "as_mysql_user.h"
66 #include "as_mysql_wckey.h"
67 
68 List as_mysql_cluster_list = NULL;
69 /* This total list is only used for converting things, so no
70    need to keep it upto date even though it lives until the
71    end of the life of the slurmdbd.
72 */
73 List as_mysql_total_cluster_list = NULL;
74 pthread_mutex_t as_mysql_cluster_list_lock = PTHREAD_MUTEX_INITIALIZER;
75 
76 /*
77  * These variables are required by the generic plugin interface.  If they
78  * are not found in the plugin, the plugin loader will ignore it.
79  *
80  * plugin_name - a string giving a human-readable description of the
81  * plugin.  There is no maximum length, but the symbol must refer to
82  * a valid string.
83  *
84  * plugin_type - a string suggesting the type of the plugin or its
85  * applicability to a particular form of data or method of data handling.
86  * If the low-level plugin API is used, the contents of this string are
87  * unimportant and may be anything.  Slurm uses the higher-level plugin
88  * interface which requires this string to be of the form
89  *
90  *	<application>/<method>
91  *
92  * where <application> is a description of the intended application of
93  * the plugin (e.g., "accounting_storage" for Slurm job completion
94  * logging) and <method>
95  * is a description of how this plugin satisfies that application.  Slurm will
96  * only load job completion logging plugins if the plugin_type string has a
97  * prefix of "accounting_storage/".
98  *
99  * plugin_version - an unsigned 32-bit integer containing the Slurm version
100  * (major.minor.micro combined into a single number).
101  */
102 const char plugin_name[] = "Accounting storage MYSQL plugin";
103 const char plugin_type[] = "accounting_storage/as_mysql";
104 const uint32_t plugin_version = SLURM_VERSION_NUMBER;
105 
106 static mysql_db_info_t *mysql_db_info = NULL;
107 static char *mysql_db_name = NULL;
108 
109 #define DELETE_SEC_BACK 86400
110 
111 char *acct_coord_table = "acct_coord_table";
112 char *acct_table = "acct_table";
113 char *tres_table = "tres_table";
114 char *assoc_day_table = "assoc_usage_day_table";
115 char *assoc_hour_table = "assoc_usage_hour_table";
116 char *assoc_month_table = "assoc_usage_month_table";
117 char *assoc_table = "assoc_table";
118 char *clus_res_table = "clus_res_table";
119 char *cluster_day_table = "usage_day_table";
120 char *cluster_hour_table = "usage_hour_table";
121 char *cluster_month_table = "usage_month_table";
122 char *cluster_table = "cluster_table";
123 char *convert_version_table = "convert_version_table";
124 char *federation_table = "federation_table";
125 char *event_table = "event_table";
126 char *job_table = "job_table";
127 char *last_ran_table = "last_ran_table";
128 char *qos_table = "qos_table";
129 char *resv_table = "resv_table";
130 char *res_table = "res_table";
131 char *step_table = "step_table";
132 char *txn_table = "txn_table";
133 char *user_table = "user_table";
134 char *suspend_table = "suspend_table";
135 char *wckey_day_table = "wckey_usage_day_table";
136 char *wckey_hour_table = "wckey_usage_hour_table";
137 char *wckey_month_table = "wckey_usage_month_table";
138 char *wckey_table = "wckey_table";
139 
140 char *event_view = "event_view";
141 char *event_ext_view = "event_ext_view";
142 char *job_view = "job_view";
143 char *job_ext_view = "job_ext_view";
144 char *resv_view = "resv_view";
145 char *resv_ext_view = "resv_ext_view";
146 char *step_view = "step_view";
147 char *step_ext_view = "step_ext_view";
148 
149 uint64_t debug_flags = 0;
150 bool backup_dbd = 0;
151 
152 static char *default_qos_str = NULL;
153 
154 enum {
155 	JASSOC_JOB,
156 	JASSOC_ACCT,
157 	JASSOC_USER,
158 	JASSOC_PART,
159 	JASSOC_COUNT
160 };
161 
162 extern int acct_storage_p_close_connection(mysql_conn_t **mysql_conn);
163 
_get_cluster_names(mysql_conn_t * mysql_conn,bool with_deleted)164 static List _get_cluster_names(mysql_conn_t *mysql_conn, bool with_deleted)
165 {
166 	MYSQL_RES *result = NULL;
167 	MYSQL_ROW row;
168 	List ret_list = NULL;
169 
170 	char *query = xstrdup_printf("select name from %s", cluster_table);
171 
172 	if (!with_deleted)
173 		xstrcat(query, " where deleted=0");
174 
175 	if (!(result = mysql_db_query_ret(mysql_conn, query, 0))) {
176 		xfree(query);
177 		return NULL;
178 	}
179 	xfree(query);
180 
181 	ret_list = list_create(xfree_ptr);
182 	while ((row = mysql_fetch_row(result))) {
183 		if (row[0] && row[0][0])
184 			list_append(ret_list, xstrdup(row[0]));
185 	}
186 	mysql_free_result(result);
187 
188 	return ret_list;
189 }
190 
_set_qos_cnt(mysql_conn_t * mysql_conn)191 static int _set_qos_cnt(mysql_conn_t *mysql_conn)
192 {
193 	MYSQL_RES *result = NULL;
194 	MYSQL_ROW row;
195 	char *query = xstrdup_printf("select MAX(id) from %s", qos_table);
196 	assoc_mgr_lock_t locks = { NO_LOCK, NO_LOCK, WRITE_LOCK, NO_LOCK,
197 				   NO_LOCK, NO_LOCK, NO_LOCK };
198 
199 	if (!(result = mysql_db_query_ret(mysql_conn, query, 0))) {
200 		xfree(query);
201 		return SLURM_ERROR;
202 	}
203 	xfree(query);
204 
205 	if (!(row = mysql_fetch_row(result))) {
206 		mysql_free_result(result);
207 		return SLURM_ERROR;
208 	}
209 
210 	/* Set the current qos_count on the system for
211 	   generating bitstr of that length.  Since 0 isn't
212 	   possible as an id we add 1 to the total to burn 0 and
213 	   start at the 1 bit.
214 	*/
215 	assoc_mgr_lock(&locks);
216 	g_qos_count = slurm_atoul(row[0]) + 1;
217 	assoc_mgr_unlock(&locks);
218 	mysql_free_result(result);
219 
220 	return SLURM_SUCCESS;
221 }
222 
_process_running_jobs_result(char * cluster_name,MYSQL_RES * result,List ret_list)223 static void _process_running_jobs_result(char *cluster_name,
224 					 MYSQL_RES *result, List ret_list)
225 {
226 	MYSQL_ROW row;
227 	char *object;
228 
229 	while ((row = mysql_fetch_row(result))) {
230 		if (!row[JASSOC_USER][0]) {
231 			/* This should never happen */
232 			error("How did we get a job running on an association "
233 			      "that isn't a user association job %s cluster "
234 			      "'%s' acct '%s'?", row[JASSOC_JOB],
235 			      cluster_name, row[JASSOC_ACCT]);
236 			continue;
237 		}
238 		object = xstrdup_printf(
239 			"JobID = %-10s C = %-10s A = %-10s U = %-9s",
240 			row[JASSOC_JOB], cluster_name, row[JASSOC_ACCT],
241 			row[JASSOC_USER]);
242 		if (row[JASSOC_PART][0])
243 			// see if there is a partition name
244 			xstrfmtcat(object, " P = %s", row[JASSOC_PART]);
245 		list_append(ret_list, object);
246 	}
247 }
248 
249 /* this function is here to see if any of what we are trying to remove
250  * has jobs that are not completed.  If we have jobs and the object is less
251  * than a day old we don't want to delete it, only set the deleted flag.
252  */
_check_jobs_before_remove(mysql_conn_t * mysql_conn,char * cluster_name,char * assoc_char,List ret_list,bool * already_flushed)253 static bool _check_jobs_before_remove(mysql_conn_t *mysql_conn,
254 				      char *cluster_name,
255 				      char *assoc_char,
256 				      List ret_list,
257 				      bool *already_flushed)
258 {
259 	char *query = NULL, *object = NULL;
260 	bool rc = 0;
261 	int i;
262 	MYSQL_RES *result = NULL;
263 
264 	/* if this changes you will need to edit the corresponding
265 	 * enum above in the global settings */
266 	static char *jassoc_req_inx[] = {
267 		"t0.id_job",
268 		"t1.acct",
269 		"t1.user",
270 		"t1.partition"
271 	};
272 	if (ret_list) {
273 		xstrcat(object, jassoc_req_inx[0]);
274 		for(i=1; i<JASSOC_COUNT; i++)
275 			xstrfmtcat(object, ", %s", jassoc_req_inx[i]);
276 
277 		query = xstrdup_printf(
278 			"select distinct %s "
279 			"from \"%s_%s\" as t0, "
280 			"\"%s_%s\" as t1, \"%s_%s\" as t2 "
281 			"where t1.lft between "
282 			"t2.lft and t2.rgt && (%s) "
283 			"and t0.id_assoc=t1.id_assoc "
284 			"and t0.time_end=0 && t0.state<%d;",
285 			object, cluster_name, job_table,
286 			cluster_name, assoc_table,
287 			cluster_name, assoc_table,
288 			assoc_char, JOB_COMPLETE);
289 		xfree(object);
290 	} else {
291 		query = xstrdup_printf(
292 			"select t0.id_assoc from \"%s_%s\" as t0, "
293 			"\"%s_%s\" as t1, \"%s_%s\" as t2 "
294 			"where t1.lft between "
295 			"t2.lft and t2.rgt && (%s) "
296 			"and t0.id_assoc=t1.id_assoc limit 1;",
297 			cluster_name, job_table,
298 			cluster_name, assoc_table,
299 			cluster_name, assoc_table,
300 			assoc_char);
301 	}
302 
303 	if (debug_flags & DEBUG_FLAG_DB_ASSOC)
304 		DB_DEBUG(mysql_conn->conn, "query\n%s", query);
305 	if (!(result = mysql_db_query_ret(mysql_conn, query, 0))) {
306 		xfree(query);
307 		return rc;
308 	}
309 	xfree(query);
310 
311 	if (mysql_num_rows(result)) {
312 		debug4("We have jobs for this combo");
313 		rc = true;
314 		if (ret_list && !(*already_flushed)) {
315 			list_flush(ret_list);
316 			(*already_flushed) = 1;
317 			reset_mysql_conn(mysql_conn);
318 		}
319 		if (ret_list)
320 			_process_running_jobs_result(cluster_name, result,
321 						     ret_list);
322 	}
323 
324 	mysql_free_result(result);
325 	return rc;
326 }
327 
328 /* Same as above but for associations instead of other tables */
_check_jobs_before_remove_assoc(mysql_conn_t * mysql_conn,char * cluster_name,char * assoc_char,List ret_list,bool * already_flushed)329 static bool _check_jobs_before_remove_assoc(mysql_conn_t *mysql_conn,
330 					    char *cluster_name,
331 					    char *assoc_char,
332 					    List ret_list,
333 					    bool *already_flushed)
334 {
335 	char *query = NULL, *object = NULL;
336 	bool rc = 0;
337 	int i;
338 	MYSQL_RES *result = NULL;
339 
340 	/* if this changes you will need to edit the corresponding
341 	 * enum above in the global settings */
342 	static char *jassoc_req_inx[] = {
343 		"t1.id_job",
344 		"t2.acct",
345 		"t2.user",
346 		"t2.partition"
347 	};
348 
349 	if (ret_list) {
350 		xstrcat(object, jassoc_req_inx[0]);
351 		for(i=1; i<JASSOC_COUNT; i++)
352 			xstrfmtcat(object, ", %s", jassoc_req_inx[i]);
353 
354 		query = xstrdup_printf("select %s "
355 				       "from \"%s_%s\" as t1, \"%s_%s\" as t2 "
356 				       "where (%s) and t1.id_assoc=t2.id_assoc "
357 				       "and t1.time_end=0 && t1.state<%d;",
358 				       object, cluster_name, job_table,
359 				       cluster_name, assoc_table,
360 				       assoc_char, JOB_COMPLETE);
361 		xfree(object);
362 	} else {
363 		query = xstrdup_printf(
364 			"select t1.id_assoc from \"%s_%s\" as t1, "
365 			"\"%s_%s\" as t2 where (%s) "
366 			"and t1.id_assoc=t2.id_assoc limit 1;",
367 			cluster_name, job_table,
368 			cluster_name, assoc_table,
369 			assoc_char);
370 	}
371 
372 	if (debug_flags & DEBUG_FLAG_DB_ASSOC)
373 		DB_DEBUG(mysql_conn->conn, "query\n%s", query);
374 
375 	if (!(result = mysql_db_query_ret(
376 		      mysql_conn, query, 0))) {
377 		xfree(query);
378 		return rc;
379 	}
380 	xfree(query);
381 
382 	if (mysql_num_rows(result)) {
383 		debug4("We have jobs for this combo");
384 		rc = true;
385 		if (ret_list && !(*already_flushed)) {
386 			list_flush(ret_list);
387 			(*already_flushed) = 1;
388 			reset_mysql_conn(mysql_conn);
389 		}
390 	}
391 
392 	if (ret_list)
393 		_process_running_jobs_result(cluster_name, result, ret_list);
394 
395 	mysql_free_result(result);
396 	return rc;
397 }
398 
399 /* Same as above but for things having nothing to do with associations
400  * like qos or wckey */
_check_jobs_before_remove_without_assoctable(mysql_conn_t * mysql_conn,char * cluster_name,char * where_char)401 static bool _check_jobs_before_remove_without_assoctable(
402 	mysql_conn_t *mysql_conn, char *cluster_name, char *where_char)
403 {
404 	char *query = NULL;
405 	bool rc = 0;
406 	MYSQL_RES *result = NULL;
407 
408 	query = xstrdup_printf("select id_assoc from \"%s_%s\" "
409 			       "where (%s) limit 1;",
410 			       cluster_name, job_table, where_char);
411 
412 	if (debug_flags & DEBUG_FLAG_DB_ASSOC)
413 		DB_DEBUG(mysql_conn->conn, "query\n%s", query);
414 
415 	if (!(result = mysql_db_query_ret(
416 		      mysql_conn, query, 0))) {
417 		xfree(query);
418 		return rc;
419 	}
420 	xfree(query);
421 
422 	if (mysql_num_rows(result)) {
423 		debug4("We have jobs for this combo");
424 		rc = true;
425 	}
426 
427 	mysql_free_result(result);
428 	return rc;
429 }
430 
431 /* static int _add_remove_tres_limit(char *tres_limit_str, char *name, */
432 /* 				  char **cols, char **vals, char **extra) */
433 /* { */
434 /* 	int rc = SLURM_SUCCESS; */
435 /* 	char *tmp_str = tres_limit_str; */
436 /* 	uint64_t value; */
437 /* 	bool first = true; */
438 
439 /* 	if (!tmp_str || !tmp_str[0]) */
440 /* 		return SLURM_SUCCESS; */
441 
442 /* 	while (tmp_str) { */
443 /* 		if (id == atoi(tmp_str)) { */
444 /* 			if (!(tmp_str = strchr(tmp_str, '='))) { */
445 /* 				error("_add_remove_tres_limit: no value found"); */
446 /* 				rc = SLURM_ERROR; */
447 /* 				break; */
448 /* 			} */
449 /* 			if (first) */
450 /* 				slurm_atoull(++tmp_str); */
451 /* 		} */
452 
453 /* 		if (!(tmp_str = strchr(tmp_str, ','))) */
454 /* 			break; */
455 /* 		tmp_str++; */
456 /* 	} */
457 
458 /* 	return SLURM_SUCCESS; */
459 /* } */
460 
461 /* Any time a new table is added set it up here */
_as_mysql_acct_check_tables(mysql_conn_t * mysql_conn)462 static int _as_mysql_acct_check_tables(mysql_conn_t *mysql_conn)
463 {
464 	storage_field_t acct_coord_table_fields[] = {
465 		{ "creation_time", "bigint unsigned not null" },
466 		{ "mod_time", "bigint unsigned default 0 not null" },
467 		{ "deleted", "tinyint default 0" },
468 		{ "acct", "tinytext not null" },
469 		{ "user", "tinytext not null" },
470 		{ NULL, NULL}
471 	};
472 
473 	storage_field_t acct_table_fields[] = {
474 		{ "creation_time", "bigint unsigned not null" },
475 		{ "mod_time", "bigint unsigned default 0 not null" },
476 		{ "deleted", "tinyint default 0" },
477 		{ "name", "tinytext not null" },
478 		{ "description", "text not null" },
479 		{ "organization", "text not null" },
480 		{ NULL, NULL}
481 	};
482 
483 	storage_field_t tres_table_fields[] = {
484 		{ "creation_time", "bigint unsigned not null" },
485 		{ "deleted", "tinyint default 0 not null" },
486 		{ "id", "int not null auto_increment" },
487 		{ "type", "tinytext not null" },
488 		{ "name", "tinytext not null default ''" },
489 		{ NULL, NULL}
490 	};
491 
492 	storage_field_t cluster_table_fields[] = {
493 		{ "creation_time", "bigint unsigned not null" },
494 		{ "mod_time", "bigint unsigned default 0 not null" },
495 		{ "deleted", "tinyint default 0" },
496 		{ "name", "tinytext not null" },
497 		{ "control_host", "tinytext not null default ''" },
498 		{ "control_port", "int unsigned not null default 0" },
499 		{ "last_port", "int unsigned not null default 0" },
500 		{ "rpc_version", "smallint unsigned not null default 0" },
501 		{ "classification", "smallint unsigned default 0" },
502 		{ "dimensions", "smallint unsigned default 1" },
503 		{ "plugin_id_select", "smallint unsigned default 0" },
504 		{ "flags", "int unsigned default 0" },
505 		{ "federation", "tinytext not null" },
506 		{ "features", "text not null default ''" },
507 		{ "fed_id", "int unsigned default 0 not null" },
508 		{ "fed_state", "smallint unsigned not null" },
509 		{ NULL, NULL}
510 	};
511 
512 	storage_field_t clus_res_table_fields[] = {
513 		{ "creation_time", "bigint unsigned not null" },
514 		{ "mod_time", "bigint unsigned default 0 not null" },
515 		{ "deleted", "tinyint default 0" },
516 		{ "cluster", "tinytext not null" },
517 		{ "res_id", "int not null" },
518 		{ "percent_allowed", "int unsigned default 0" },
519 		{ NULL, NULL}
520 	};
521 
522 	storage_field_t convert_version_table_fields[] = {
523 		{ "mod_time", "bigint unsigned default 0 not null" },
524 		{ "version", "int default 0" },
525 		{ NULL, NULL}
526 	};
527 
528 	storage_field_t federation_table_fields[] = {
529 		{ "creation_time", "int unsigned not null" },
530 		{ "mod_time", "int unsigned default 0 not null" },
531 		{ "deleted", "tinyint default 0" },
532 		{ "name", "tinytext not null" },
533 		{ "flags", "int unsigned default 0" },
534 		{ NULL, NULL}
535 	};
536 
537 	storage_field_t qos_table_fields[] = {
538 		{ "creation_time", "bigint unsigned not null" },
539 		{ "mod_time", "bigint unsigned default 0 not null" },
540 		{ "deleted", "tinyint default 0" },
541 		{ "id", "int not null auto_increment" },
542 		{ "name", "tinytext not null" },
543 		{ "description", "text" },
544 		{ "flags", "int unsigned default 0" },
545 		{ "grace_time", "int unsigned default NULL" },
546 		{ "max_jobs_pa", "int default NULL" },
547 		{ "max_jobs_per_user", "int default NULL" },
548 		{ "max_jobs_accrue_pa", "int default NULL" },
549 		{ "max_jobs_accrue_pu", "int default NULL" },
550 		{ "min_prio_thresh", "int default NULL" },
551 		{ "max_submit_jobs_pa", "int default NULL" },
552 		{ "max_submit_jobs_per_user", "int default NULL" },
553 		{ "max_tres_pa", "text not null default ''" },
554 		{ "max_tres_pj", "text not null default ''" },
555 		{ "max_tres_pn", "text not null default ''" },
556 		{ "max_tres_pu", "text not null default ''" },
557 		{ "max_tres_mins_pj", "text not null default ''" },
558 		{ "max_tres_run_mins_pa", "text not null default ''" },
559 		{ "max_tres_run_mins_pu", "text not null default ''" },
560 		{ "min_tres_pj", "text not null default ''" },
561 		{ "max_wall_duration_per_job", "int default NULL" },
562 		{ "grp_jobs", "int default NULL" },
563 		{ "grp_jobs_accrue", "int default NULL" },
564 		{ "grp_submit_jobs", "int default NULL" },
565 		{ "grp_tres", "text not null default ''" },
566 		{ "grp_tres_mins", "text not null default ''" },
567 		{ "grp_tres_run_mins", "text not null default ''" },
568 		{ "grp_wall", "int default NULL" },
569 		{ "preempt", "text not null default ''" },
570 		{ "preempt_mode", "int default 0" },
571 		{ "preempt_exempt_time", "int unsigned default NULL" },
572 		{ "priority", "int unsigned default 0" },
573 		{ "usage_factor", "double default 1.0 not null" },
574 		{ "usage_thres", "double default NULL" },
575 		{ NULL, NULL}
576 	};
577 
578 	storage_field_t res_table_fields[] = {
579 		{ "creation_time", "bigint unsigned not null" },
580 		{ "mod_time", "bigint unsigned default 0 not null" },
581 		{ "deleted", "tinyint default 0" },
582 		{ "id", "int not null auto_increment" },
583 		{ "name", "tinytext not null" },
584 		{ "description", "text default null" },
585 		{ "manager", "tinytext not null" },
586 		{ "server", "tinytext not null" },
587 		{ "count", "int unsigned default 0" },
588 		{ "type", "int unsigned default 0"},
589 		{ "flags", "int unsigned default 0"},
590 		{ NULL, NULL}
591 	};
592 
593 	storage_field_t txn_table_fields[] = {
594 		{ "id", "int not null auto_increment" },
595 		{ "timestamp", "bigint unsigned default 0 not null" },
596 		{ "action", "smallint not null" },
597 		{ "name", "text not null" },
598 		{ "actor", "tinytext not null" },
599 		{ "cluster", "tinytext not null default ''" },
600 		{ "info", "blob" },
601 		{ NULL, NULL}
602 	};
603 
604 	storage_field_t user_table_fields[] = {
605 		{ "creation_time", "bigint unsigned not null" },
606 		{ "mod_time", "bigint unsigned default 0 not null" },
607 		{ "deleted", "tinyint default 0" },
608 		{ "name", "tinytext not null" },
609 		{ "admin_level", "smallint default 1 not null" },
610 		{ NULL, NULL}
611 	};
612 
613 	/*
614 	 * If more limits are added here they need to be added to
615 	 * get_parent_limits_select in as_mysql_assoc.c
616 	 */
617 	char *get_parent_proc =
618 		"drop procedure if exists get_parent_limits; "
619 		"create procedure get_parent_limits("
620 		"my_table text, acct text, cluster text, without_limits int) "
621 		"begin "
622 		"set @par_id = NULL; "
623 		"set @mj = NULL; "
624 		"set @mja = NULL; "
625 		"set @mpt = NULL; "
626 		"set @msj = NULL; "
627 		"set @mwpj = NULL; "
628 		"set @mtpj = ''; "
629 		"set @mtpn = ''; "
630 		"set @mtmpj = ''; "
631 		"set @mtrm = ''; "
632 		"set @prio = NULL; "
633 		"set @def_qos_id = NULL; "
634 		"set @qos = ''; "
635 		"set @delta_qos = ''; "
636 		"set @my_acct = acct; "
637 		"if without_limits then "
638 		"set @mj = 0; "
639 		"set @msj = 0; "
640 		"set @mwpj = 0; "
641 		"set @prio = 0; "
642 		"set @def_qos_id = 0; "
643 		"set @qos = 1; "
644 		"end if; "
645 		"REPEAT "
646 		"set @s = 'select '; "
647 		"if @par_id is NULL then set @s = CONCAT("
648 		"@s, '@par_id := id_assoc, '); "
649 		"end if; "
650 		"if @mj is NULL then set @s = CONCAT("
651 		"@s, '@mj := max_jobs, '); "
652 		"end if; "
653 		"if @mja is NULL then set @s = CONCAT("
654 		"@s, '@mja := max_jobs_accrue, '); "
655 		"end if; "
656 		"if @mpt is NULL then set @s = CONCAT("
657 		"@s, '@mpt := min_prio_thresh, '); "
658 		"end if; "
659 		"if @msj is NULL then set @s = CONCAT("
660 		"@s, '@msj := max_submit_jobs, '); "
661 		"end if; "
662 		"if @mwpj is NULL then set @s = CONCAT("
663 		"@s, '@mwpj := max_wall_pj, '); "
664 		"end if; "
665 		"if @prio is NULL then set @s = CONCAT("
666 		"@s, '@prio := priority, '); "
667 		"end if; "
668 		"if @def_qos_id is NULL then set @s = CONCAT("
669 		"@s, '@def_qos_id := def_qos_id, '); "
670 		"end if; "
671 		"if @qos = '' then set @s = CONCAT("
672 		"@s, '@qos := qos, "
673 		"@delta_qos := REPLACE(CONCAT(delta_qos, @delta_qos), "
674 		"\\\',,\\\', \\\',\\\'), '); "
675 		"end if; "
676 		/* "set @s = CONCAT(@s, @mtpj := REPLACE(CONCAT(@mtpj, max_tres_pj), " */
677 		/* "\\\',,\\\', \\\',\\\'), '); " */
678 		/* "@mtmpj := REPLACE(CONCAT(@mtmpj, max_tres_mins_pj), " */
679 		/* "\\\',,\\\', \\\',\\\'), '); " */
680 		/* "@mtrm := REPLACE(CONCAT(@mtrm, max_tres_run_mins), " */
681 		/* "\\\',,\\\', \\\',\\\'), '); " */
682 		"set @s = concat(@s, "
683 		"'@mtpj := CONCAT(@mtpj, "
684 		"if (@mtpj != \\\'\\\' && max_tres_pj != \\\'\\\', "
685 		"\\\',\\\', \\\'\\\'), max_tres_pj), "
686 		"@mtpn := CONCAT(@mtpn, "
687 		"if (@mtpn != \\\'\\\' && max_tres_pn != \\\'\\\', "
688 		"\\\',\\\', \\\'\\\'), max_tres_pn), "
689 		"@mtmpj := CONCAT(@mtmpj, "
690 		"if (@mtmpj != \\\'\\\' && max_tres_mins_pj != \\\'\\\', "
691 		"\\\',\\\', \\\'\\\'), max_tres_mins_pj), "
692 		"@mtrm := CONCAT(@mtrm, "
693 		"if (@mtrm != \\\'\\\' && max_tres_run_mins != \\\'\\\', "
694 		"\\\',\\\', \\\'\\\'), max_tres_run_mins), "
695 		"@my_acct_new := parent_acct from \"', "
696 		"cluster, '_', my_table, '\" where "
697 		"acct = \\\'', @my_acct, '\\\' && user=\\\'\\\''); "
698 		"prepare query from @s; "
699 		"execute query; "
700 		"deallocate prepare query; "
701 		"set @my_acct = @my_acct_new; "
702 		"UNTIL without_limits || @my_acct = '' END REPEAT; "
703 		"END;";
704 	/* char *get_parent_proc = */
705 	/* 	"drop procedure if exists get_parent_limits; " */
706 	/* 	"create procedure get_parent_limits(" */
707 	/* 	"my_table text, acct text, cluster text, without_limits int) " */
708 	/* 	"begin " */
709 	/* 	"set @par_id = NULL; " */
710 	/* 	"set @mj = NULL; " */
711 	/* 	"set @msj = NULL; " */
712 	/* 	"set @mcpj = NULL; " */
713 	/* 	"set @mnpj = NULL; " */
714 	/* 	"set @mwpj = NULL; " */
715 	/* 	"set @mcmpj = NULL; " */
716 	/* 	"set @mcrm = NULL; " */
717 	/* 	"set @def_qos_id = NULL; " */
718 	/* 	"set @qos = ''; " */
719 	/* 	"set @delta_qos = ''; " */
720 	/* 	"set @my_acct = acct; " */
721 	/* 	"if without_limits then " */
722 	/* 	"set @mj = 0; " */
723 	/* 	"set @msj = 0; " */
724 	/* 	"set @mcpj = 0; " */
725 	/* 	"set @mnpj = 0; " */
726 	/* 	"set @mwpj = 0; " */
727 	/* 	"set @mcmpj = 0; " */
728 	/* 	"set @mcrm = 0; " */
729 	/* 	"set @def_qos_id = 0; " */
730 	/* 	"set @qos = 1; " */
731 	/* 	"end if; " */
732 	/* 	"REPEAT " */
733 	/* 	"set @s = 'select '; " */
734 	/* 	"if @par_id is NULL then set @s = CONCAT(" */
735 	/* 	"@s, '@par_id := id_assoc, '); " */
736 	/* 	"end if; " */
737 	/* 	"if @mj is NULL then set @s = CONCAT(" */
738 	/* 	"@s, '@mj := max_jobs, '); " */
739 	/* 	"end if; " */
740 	/* 	"if @msj is NULL then set @s = CONCAT(" */
741 	/* 	"@s, '@msj := max_submit_jobs, '); " */
742 	/* 	"end if; " */
743 	/* 	"if @mcpj is NULL then set @s = CONCAT(" */
744 	/* 	"@s, '@mcpj := max_cpus_pj, ') ;" */
745 	/* 	"end if; " */
746 	/* 	"if @mnpj is NULL then set @s = CONCAT(" */
747 	/* 	"@s, '@mnpj := max_nodes_pj, ') ;" */
748 	/* 	"end if; " */
749 	/* 	"if @mwpj is NULL then set @s = CONCAT(" */
750 	/* 	"@s, '@mwpj := max_wall_pj, '); " */
751 	/* 	"end if; " */
752 	/* 	"if @mcmpj is NULL then set @s = CONCAT(" */
753 	/* 	"@s, '@mcmpj := max_cpu_mins_pj, '); " */
754 	/* 	"end if; " */
755 	/* 	"if @mcrm is NULL then set @s = CONCAT(" */
756 	/* 	"@s, '@mcrm := max_cpu_run_mins, '); " */
757 	/* 	"end if; " */
758 	/* 	"if @def_qos_id is NULL then set @s = CONCAT(" */
759 	/* 	"@s, '@def_qos_id := def_qos_id, '); " */
760 	/* 	"end if; " */
761 	/* 	"if @qos = '' then set @s = CONCAT(" */
762 	/* 	"@s, '@qos := qos, " */
763 	/* 	"@delta_qos := REPLACE(CONCAT(delta_qos, @delta_qos), " */
764 	/* 	"\\\',,\\\', \\\',\\\'), '); " */
765 	/* 	"end if; " */
766 	/* 	"set @s = concat(@s, '@my_acct_new := parent_acct from \"', " */
767 	/* 	"cluster, '_', my_table, '\" where " */
768 	/* 	"acct = \\\'', @my_acct, '\\\' && user=\\\'\\\''); " */
769 	/* 	"prepare query from @s; " */
770 	/* 	"execute query; " */
771 	/* 	"deallocate prepare query; " */
772 	/* 	"set @my_acct = @my_acct_new; " */
773 	/* 	"UNTIL (@mj != -1 && @msj != -1 && @mcpj != -1 " */
774 	/* 	"&& @mnpj != -1 && @mwpj != -1 && @mcmpj != -1 " */
775 	/* 	"&& @mcrm != -1 && @def_qos_id != -1 && @qos != '') " */
776 	/* 	"|| @my_acct = '' END REPEAT; " */
777 	/* 	"END;"; */
778 	char *get_coord_qos =
779 		"drop procedure if exists get_coord_qos; "
780 		"create procedure get_coord_qos(my_table text, acct text, "
781 		"cluster text, coord text) "
782 		"begin "
783 		"set @qos = ''; "
784 		"set @delta_qos = ''; "
785 		"set @found_coord = NULL; "
786 		"set @my_acct = acct; "
787 		"REPEAT "
788 		"set @s = 'select @qos := t1.qos, "
789 		"@delta_qos := REPLACE(CONCAT(t1.delta_qos, @delta_qos), "
790 		"\\\',,\\\', \\\',\\\'), @my_acct_new := parent_acct, "
791 		"@found_coord_curr := t2.user '; "
792 		"set @s = concat(@s, 'from \"', cluster, '_', my_table, '\" "
793 		"as t1 left outer join acct_coord_table as t2 on "
794 		"t1.acct=t2.acct where t1.acct = @my_acct && t1.user=\\\'\\\' "
795 		"&& (t2.user=\\\'', coord, '\\\' || t2.user is null)'); "
796 		"prepare query from @s; "
797 		"execute query; "
798 		"deallocate prepare query; "
799 		"if @found_coord_curr is not NULL then "
800 		"set @found_coord = @found_coord_curr; "
801 		"end if; "
802 		"if @found_coord is NULL then "
803 		"set @qos = ''; "
804 		"set @delta_qos = ''; "
805 		"end if; "
806 		"set @my_acct = @my_acct_new; "
807 		"UNTIL @qos != '' || @my_acct = '' END REPEAT; "
808 		"select REPLACE(CONCAT(@qos, @delta_qos), ',,', ','); "
809 		"END;";
810 	char *query = NULL;
811 	time_t now = time(NULL);
812 	char *cluster_name = NULL;
813 	int rc = SLURM_SUCCESS, rc2;
814 	ListIterator itr = NULL;
815 
816 	/* Make the convert version table since we will check that going
817 	 * forward to see if we need to update or not.
818 	 */
819 
820 	if (mysql_db_create_table(mysql_conn, convert_version_table,
821 				  convert_version_table_fields,
822 				  ", primary key (version))") == SLURM_ERROR)
823 		return SLURM_ERROR;
824 
825 	/* Make the cluster table first since we build other tables
826 	   built off this one */
827 	if (mysql_db_create_table(mysql_conn, cluster_table,
828 				  cluster_table_fields,
829 				  ", primary key (name(42)))") == SLURM_ERROR)
830 		return SLURM_ERROR;
831 
832 	/* This table needs to be made before conversions also since
833 	   we add a cluster column.
834 	*/
835 	if (mysql_db_create_table(mysql_conn, txn_table, txn_table_fields,
836 				  ", primary key (id))") == SLURM_ERROR)
837 		return SLURM_ERROR;
838 
839 	if (mysql_db_create_table(mysql_conn, tres_table,
840 				  tres_table_fields,
841 				  ", primary key (id), "
842 				  "unique index udex (type(42), name(42))) "
843 				  "auto_increment=1001")
844 	    == SLURM_ERROR)
845 		return SLURM_ERROR;
846 
847 	if (!backup_dbd) {
848 		/* We always want CPU to be the first one, so create
849 		   it now.  We also add MEM here, the others tres
850 		   are site specific and could vary.  None but CPU
851 		   matter on order though.  CPU always has to be 1.
852 
853 		   TRES_OFFSET is needed since there's no way to force
854 		   the number of first automatic id in MySQL. auto_increment
855 		   value is lost on mysqld restart. Bug 4553.
856 		*/
857 		query = xstrdup_printf(
858 			"insert into %s (creation_time, id, deleted, type) values "
859 			"(%ld, %d, 0, 'cpu'), "
860 			"(%ld, %d, 0, 'mem'), "
861 			"(%ld, %d, 0, 'energy'), "
862 			"(%ld, %d, 0, 'node'), "
863 			"(%ld, %d, 0, 'billing'), "
864 			"(%ld, %d, 0, 'vmem'), "
865 			"(%ld, %d, 0, 'pages'), "
866 			"(%ld, %d, 1, 'dynamic_offset') "
867 			"on duplicate key update deleted=VALUES(deleted), type=VALUES(type), id=VALUES(id);",
868 			tres_table,
869 			now, TRES_CPU,
870 			now, TRES_MEM,
871 			now, TRES_ENERGY,
872 			now, TRES_NODE,
873 			now, TRES_BILLING,
874 			now, TRES_VMEM,
875 			now, TRES_PAGES,
876 			now, TRES_OFFSET);
877 		if (debug_flags & DEBUG_FLAG_DB_TRES)
878 			DB_DEBUG(mysql_conn->conn, "%s", query);
879 		rc = mysql_db_query(mysql_conn, query);
880 		xfree(query);
881 		if (rc != SLURM_SUCCESS)
882 			fatal("problem adding static tres");
883 
884 		/* Now insert TRES that have a name */
885 		query = xstrdup_printf(
886 			"insert into %s (creation_time, id, deleted, type, name) values "
887 			"(%ld, %d, 0, 'fs', 'disk') "
888 			"on duplicate key update deleted=VALUES(deleted), type=VALUES(type), name=VALUES(name), id=VALUES(id);",
889 			tres_table,
890 			now, TRES_FS_DISK);
891 		if (debug_flags & DEBUG_FLAG_DB_TRES)
892 			DB_DEBUG(mysql_conn->conn, "%s", query);
893 		rc = mysql_db_query(mysql_conn, query);
894 		xfree(query);
895 		if (rc != SLURM_SUCCESS)
896 			fatal("problem adding static tres");
897 	}
898 
899 	slurm_mutex_lock(&as_mysql_cluster_list_lock);
900 	if (!(as_mysql_cluster_list = _get_cluster_names(mysql_conn, 0))) {
901 		error("issue getting contents of %s", cluster_table);
902 		slurm_mutex_unlock(&as_mysql_cluster_list_lock);
903 		return SLURM_ERROR;
904 	}
905 
906 	/* This total list is only used for converting things, so no
907 	   need to keep it upto date even though it lives until the
908 	   end of the life of the slurmdbd.
909 	*/
910 	if (!(as_mysql_total_cluster_list =
911 	      _get_cluster_names(mysql_conn, 1))) {
912 		error("issue getting total contents of %s", cluster_table);
913 		slurm_mutex_unlock(&as_mysql_cluster_list_lock);
914 		return SLURM_ERROR;
915 	}
916 
917 	if ((rc = as_mysql_convert_tables_pre_create(mysql_conn)) !=
918 	    SLURM_SUCCESS) {
919 		slurm_mutex_unlock(&as_mysql_cluster_list_lock);
920 		error("issue converting tables before create");
921 		return rc;
922 	} else if (backup_dbd) {
923 		/*
924 		 * We do not want to create/check the database if we are the
925 		 * backup (see Bug 3827). This is only handled on the primary.
926 		 */
927 
928 		slurm_mutex_unlock(&as_mysql_cluster_list_lock);
929 
930 		/* We do want to set the QOS count though. */
931 		if (rc == SLURM_SUCCESS)
932 			rc = _set_qos_cnt(mysql_conn);
933 
934 		return rc;
935 	}
936 
937 	/* might as well do all the cluster centric tables inside this
938 	 * lock.  We need to do this on all the clusters deleted or
939 	 * other wise just to make sure everything is kept up to
940 	 * date. */
941 	itr = list_iterator_create(as_mysql_total_cluster_list);
942 	while ((cluster_name = list_next(itr))) {
943 		if ((rc = create_cluster_tables(mysql_conn, cluster_name))
944 		    != SLURM_SUCCESS)
945 			break;
946 	}
947 	list_iterator_destroy(itr);
948 	if (rc != SLURM_SUCCESS) {
949 		slurm_mutex_unlock(&as_mysql_cluster_list_lock);
950 		return rc;
951 	}
952 
953 	rc = as_mysql_convert_tables_post_create(mysql_conn);
954 
955 	slurm_mutex_unlock(&as_mysql_cluster_list_lock);
956 	if (rc != SLURM_SUCCESS) {
957 		error("issue converting tables after create");
958 		return rc;
959 	}
960 
961 	if (mysql_db_create_table(mysql_conn, acct_coord_table,
962 				  acct_coord_table_fields,
963 				  ", primary key (acct(42), user(42)), "
964 				  "key user (user(42)))")
965 	    == SLURM_ERROR)
966 		return SLURM_ERROR;
967 
968 	if (mysql_db_create_table(mysql_conn, acct_table, acct_table_fields,
969 				  ", primary key (name(42)))") == SLURM_ERROR)
970 		return SLURM_ERROR;
971 
972 	if (mysql_db_create_table(mysql_conn, res_table,
973 				  res_table_fields,
974 				  ", primary key (id), "
975 				  "unique index udex (name(42), server(42), type))")
976 	    == SLURM_ERROR)
977 		return SLURM_ERROR;
978 
979 	if (mysql_db_create_table(mysql_conn, clus_res_table,
980 				  clus_res_table_fields,
981 				  ", primary key (res_id, cluster(42)), "
982 				  "unique index udex (res_id, cluster(42)))")
983 	    == SLURM_ERROR)
984 		return SLURM_ERROR;
985 
986 
987 	if (mysql_db_create_table(mysql_conn, qos_table,
988 				  qos_table_fields,
989 				  ", primary key (id), "
990 				  "unique index udex (name(42)))")
991 	    == SLURM_ERROR)
992 		return SLURM_ERROR;
993 	else {
994 		int qos_id = 0;
995 		if (slurmdbd_conf && slurmdbd_conf->default_qos) {
996 			List char_list = list_create(xfree_ptr);
997 			char *qos = NULL;
998 			ListIterator itr = NULL;
999 			slurm_addto_char_list(char_list,
1000 					      slurmdbd_conf->default_qos);
1001 			/* NOTE: you can not use list_pop, or list_push
1002 			   anywhere either, since as_mysql is
1003 			   exporting something of the same type as a macro,
1004 			   which messes everything up
1005 			   (my_list.h is the bad boy).
1006 			*/
1007 			itr = list_iterator_create(char_list);
1008 			while ((qos = list_next(itr))) {
1009 				query = xstrdup_printf(
1010 					"insert into %s "
1011 					"(creation_time, mod_time, name, "
1012 					"description) "
1013 					"values (%ld, %ld, '%s', "
1014 					"'Added as default') "
1015 					"on duplicate key update "
1016 					"id=LAST_INSERT_ID(id), deleted=0;",
1017 					qos_table, now, now, qos);
1018 				if (debug_flags & DEBUG_FLAG_DB_QOS)
1019 					DB_DEBUG(mysql_conn->conn, "%s", query);
1020 				qos_id = (int)mysql_db_insert_ret_id(
1021 					mysql_conn, query);
1022 				if (!qos_id)
1023 					fatal("problem added qos '%s", qos);
1024 				xstrfmtcat(default_qos_str, ",%d", qos_id);
1025 				xfree(query);
1026 			}
1027 			list_iterator_destroy(itr);
1028 			FREE_NULL_LIST(char_list);
1029 		} else {
1030 			query = xstrdup_printf(
1031 				"insert into %s "
1032 				"(creation_time, mod_time, name, description) "
1033 				"values (%ld, %ld, 'normal', "
1034 				"'Normal QOS default') "
1035 				"on duplicate key update "
1036 				"id=LAST_INSERT_ID(id), deleted=0;",
1037 				qos_table, now, now);
1038 			if (debug_flags & DEBUG_FLAG_DB_QOS)
1039 				DB_DEBUG(mysql_conn->conn, "%s", query);
1040 			qos_id = (int)mysql_db_insert_ret_id(mysql_conn, query);
1041 			if (!qos_id)
1042 				fatal("problem added qos 'normal");
1043 
1044 			xstrfmtcat(default_qos_str, ",%d", qos_id);
1045 			xfree(query);
1046 		}
1047 
1048 		if (_set_qos_cnt(mysql_conn) != SLURM_SUCCESS)
1049 			return SLURM_ERROR;
1050 	}
1051 
1052 	/* This must be ran after create_cluster_tables() */
1053 	if (mysql_db_create_table(mysql_conn, user_table, user_table_fields,
1054 				  ", primary key (name(42)))") == SLURM_ERROR)
1055 		return SLURM_ERROR;
1056 
1057 	if (mysql_db_create_table(mysql_conn, federation_table,
1058 				  federation_table_fields,
1059 				  ", primary key (name(42)))") == SLURM_ERROR)
1060 		return SLURM_ERROR;
1061 
1062 	rc = as_mysql_convert_non_cluster_tables_post_create(mysql_conn);
1063 
1064 	if (rc != SLURM_SUCCESS) {
1065 		error("issue converting non-cluster tables after create");
1066 		return rc;
1067 	}
1068 
1069 	rc2 = mysql_db_query(mysql_conn, get_parent_proc);
1070 	if (rc2 != SLURM_SUCCESS)
1071 		rc = rc2;
1072 	rc2 = mysql_db_query(mysql_conn, get_coord_qos);
1073 	if (rc2 != SLURM_SUCCESS)
1074 		rc = rc2;
1075 
1076 	/* Add user root to be a user by default and have this default
1077 	 * account be root.  If already there just update
1078 	 * name='root'.  That way if the admins delete it it will
1079 	 * remain deleted. Creation time will be 0 so it will never
1080 	 * really be deleted.
1081 	 */
1082 	query = xstrdup_printf(
1083 		"insert into %s (creation_time, mod_time, name, "
1084 		"admin_level) values (%ld, %ld, 'root', %d) "
1085 		"on duplicate key update name='root';",
1086 		user_table, (long)now, (long)now, SLURMDB_ADMIN_SUPER_USER);
1087 	xstrfmtcat(query,
1088 		   "insert into %s (creation_time, mod_time, name, "
1089 		   "description, organization) values (%ld, %ld, 'root', "
1090 		   "'default root account', 'root') on duplicate key "
1091 		   "update name='root';",
1092 		   acct_table, (long)now, (long)now);
1093 
1094 	//DB_DEBUG(mysql_conn->conn, "%s", query);
1095 	mysql_db_query(mysql_conn, query);
1096 	xfree(query);
1097 
1098 	return rc;
1099 }
1100 
1101 /* This should be added to the beginning of each function to make sure
1102  * we have a connection to the database before we try to use it.
1103  */
check_connection(mysql_conn_t * mysql_conn)1104 extern int check_connection(mysql_conn_t *mysql_conn)
1105 {
1106 	if (!mysql_conn) {
1107 		error("We need a connection to run this");
1108 		errno = ESLURM_DB_CONNECTION;
1109 		return ESLURM_DB_CONNECTION;
1110 	} else if (mysql_db_ping(mysql_conn) != 0) {
1111 		/* avoid memory leak and end thread */
1112 		mysql_db_close_db_connection(mysql_conn);
1113 		if (mysql_db_get_db_connection(
1114 			    mysql_conn, mysql_db_name, mysql_db_info)
1115 		    != SLURM_SUCCESS) {
1116 			error("unable to re-connect to as_mysql database");
1117 			errno = ESLURM_DB_CONNECTION;
1118 			return ESLURM_DB_CONNECTION;
1119 		}
1120 	}
1121 
1122 	if (mysql_conn->cluster_deleted) {
1123 		errno = ESLURM_CLUSTER_DELETED;
1124 		return ESLURM_CLUSTER_DELETED;
1125 	}
1126 
1127 	return SLURM_SUCCESS;
1128 }
1129 
1130 /* Let me know if the last statement had rows that were affected.
1131  * This only gets called by a non-threaded connection, so there is no
1132  * need to worry about locks.
1133  */
last_affected_rows(mysql_conn_t * mysql_conn)1134 extern int last_affected_rows(mysql_conn_t *mysql_conn)
1135 {
1136 	int status=0, rows=0;
1137 	MYSQL_RES *result = NULL;
1138 
1139 	do {
1140 		result = mysql_store_result(mysql_conn->db_conn);
1141 		if (result)
1142 			mysql_free_result(result);
1143 		else
1144 			if (mysql_field_count(mysql_conn->db_conn) == 0) {
1145 				status = mysql_affected_rows(
1146 					mysql_conn->db_conn);
1147 				if (status > 0)
1148 					rows = status;
1149 			}
1150 		if ((status = mysql_next_result(mysql_conn->db_conn)) > 0)
1151 			if (debug_flags & DEBUG_FLAG_DB_ASSOC)
1152 				DB_DEBUG(mysql_conn->conn,
1153 					 "Could not execute statement\n");
1154 	} while (status == 0);
1155 
1156 	return rows;
1157 }
1158 
reset_mysql_conn(mysql_conn_t * mysql_conn)1159 extern void reset_mysql_conn(mysql_conn_t *mysql_conn)
1160 {
1161 	if (mysql_conn->rollback)
1162 		mysql_db_rollback(mysql_conn);
1163 	xfree(mysql_conn->pre_commit_query);
1164 	list_flush(mysql_conn->update_list);
1165 }
1166 
create_cluster_assoc_table(mysql_conn_t * mysql_conn,char * cluster_name)1167 extern int create_cluster_assoc_table(
1168 	mysql_conn_t *mysql_conn, char *cluster_name)
1169 {
1170 	storage_field_t assoc_table_fields[] = {
1171 		{ "creation_time", "bigint unsigned not null" },
1172 		{ "mod_time", "bigint unsigned default 0 not null" },
1173 		{ "deleted", "tinyint default 0 not null" },
1174 		{ "is_def", "tinyint default 0 not null" },
1175 		{ "id_assoc", "int unsigned not null auto_increment" },
1176 		{ "user", "tinytext not null default ''" },
1177 		{ "acct", "tinytext not null" },
1178 		{ "partition", "tinytext not null default ''" },
1179 		{ "parent_acct", "tinytext not null default ''" },
1180 		{ "lft", "int not null" },
1181 		{ "rgt", "int not null" },
1182 		{ "shares", "int default 1 not null" },
1183 		{ "max_jobs", "int default NULL" },
1184 		{ "max_jobs_accrue", "int default NULL" },
1185 		{ "min_prio_thresh", "int default NULL" },
1186 		{ "max_submit_jobs", "int default NULL" },
1187 		{ "max_tres_pj", "text not null default ''" },
1188 		{ "max_tres_pn", "text not null default ''" },
1189 		{ "max_tres_mins_pj", "text not null default ''" },
1190 		{ "max_tres_run_mins", "text not null default ''" },
1191 		{ "max_wall_pj", "int default NULL" },
1192 		{ "grp_jobs", "int default NULL" },
1193 		{ "grp_jobs_accrue", "int default NULL" },
1194 		{ "grp_submit_jobs", "int default NULL" },
1195 		{ "grp_tres", "text not null default ''" },
1196 		{ "grp_tres_mins", "text not null default ''" },
1197 		{ "grp_tres_run_mins", "text not null default ''" },
1198 		{ "grp_wall", "int default NULL" },
1199 		{ "priority", "int unsigned default NULL" },
1200 		{ "def_qos_id", "int default NULL" },
1201 		{ "qos", "blob not null default ''" },
1202 		{ "delta_qos", "blob not null default ''" },
1203 		{ NULL, NULL}
1204 	};
1205 
1206 	char table_name[200];
1207 
1208 	snprintf(table_name, sizeof(table_name), "\"%s_%s\"",
1209 		 cluster_name, assoc_table);
1210 	if (mysql_db_create_table(mysql_conn, table_name,
1211 				  assoc_table_fields,
1212 				  ", primary key (id_assoc), "
1213 				  "unique index udex (user(42), acct(42), "
1214 				  "`partition`(42)), "
1215 				  "key lft (lft), key account (acct(42)))")
1216 	    == SLURM_ERROR)
1217 		return SLURM_ERROR;
1218 
1219 	return SLURM_SUCCESS;
1220 }
1221 
create_cluster_tables(mysql_conn_t * mysql_conn,char * cluster_name)1222 extern int create_cluster_tables(mysql_conn_t *mysql_conn, char *cluster_name)
1223 {
1224 	storage_field_t cluster_usage_table_fields[] = {
1225 		{ "creation_time", "bigint unsigned not null" },
1226 		{ "mod_time", "bigint unsigned default 0 not null" },
1227 		{ "deleted", "tinyint default 0 not null" },
1228 		{ "id_tres", "int not null" },
1229 		{ "time_start", "bigint unsigned not null" },
1230 		{ "count", "bigint unsigned default 0 not null" },
1231 		{ "alloc_secs", "bigint unsigned default 0 not null" },
1232 		{ "down_secs", "bigint unsigned default 0 not null" },
1233 		{ "pdown_secs", "bigint unsigned default 0 not null" },
1234 		{ "idle_secs", "bigint unsigned default 0 not null" },
1235 		{ "resv_secs", "bigint unsigned default 0 not null" },
1236 		{ "over_secs", "bigint unsigned default 0 not null" },
1237 		{ NULL, NULL}
1238 	};
1239 
1240 	storage_field_t event_table_fields[] = {
1241 		{ "time_start", "bigint unsigned not null" },
1242 		{ "time_end", "bigint unsigned default 0 not null" },
1243 		{ "node_name", "tinytext default '' not null" },
1244 		{ "cluster_nodes", "text not null default ''" },
1245 		{ "reason", "tinytext not null" },
1246 		{ "reason_uid", "int unsigned default 0xfffffffe not null" },
1247 		{ "state", "int unsigned default 0 not null" },
1248 		{ "tres", "text not null default ''" },
1249 		{ NULL, NULL}
1250 	};
1251 
1252 	storage_field_t id_usage_table_fields[] = {
1253 		{ "creation_time", "bigint unsigned not null" },
1254 		{ "mod_time", "bigint unsigned default 0 not null" },
1255 		{ "deleted", "tinyint default 0 not null" },
1256 		{ "id", "int unsigned not null" },
1257 		{ "id_tres", "int default 1 not null" },
1258 		{ "time_start", "bigint unsigned not null" },
1259 		{ "alloc_secs", "bigint unsigned default 0 not null" },
1260 		{ NULL, NULL}
1261 	};
1262 
1263 	storage_field_t job_table_fields[] = {
1264 		{ "job_db_inx", "bigint unsigned not null auto_increment" },
1265 		{ "mod_time", "bigint unsigned default 0 not null" },
1266 		{ "deleted", "tinyint default 0 not null" },
1267 		{ "account", "tinytext" },
1268 		{ "admin_comment", "text" },
1269 		{ "array_task_str", "text" },
1270 		{ "array_max_tasks", "int unsigned default 0 not null" },
1271 		{ "array_task_pending", "int unsigned default 0 not null" },
1272 		{ "constraints", "text default ''" },
1273 		{ "cpus_req", "int unsigned not null" },
1274 		{ "derived_ec", "int unsigned default 0 not null" },
1275 		{ "derived_es", "text" },
1276 		{ "exit_code", "int unsigned default 0 not null" },
1277 		{ "flags", "int unsigned default 0 not null" },
1278 		{ "job_name", "tinytext not null" },
1279 		{ "id_assoc", "int unsigned not null" },
1280 		{ "id_array_job", "int unsigned default 0 not null" },
1281 		{ "id_array_task", "int unsigned default 0xfffffffe not null" },
1282 		{ "id_block", "tinytext" },
1283 		{ "id_job", "int unsigned not null" },
1284 		{ "id_qos", "int unsigned default 0 not null" },
1285 		{ "id_resv", "int unsigned not null" },
1286 		{ "id_wckey", "int unsigned not null" },
1287 		{ "id_user", "int unsigned not null" },
1288 		{ "id_group", "int unsigned not null" },
1289 		{ "het_job_id", "int unsigned not null" },
1290 		{ "het_job_offset", "int unsigned not null" },
1291 		{ "kill_requid", "int default -1 not null" },
1292 		{ "state_reason_prev", "int unsigned not null" },
1293 		{ "mcs_label", "tinytext default ''" },
1294 		{ "mem_req", "bigint unsigned default 0 not null" },
1295 		{ "nodelist", "text" },
1296 		{ "nodes_alloc", "int unsigned not null" },
1297 		{ "node_inx", "text" },
1298 		{ "partition", "tinytext not null" },
1299 		{ "priority", "int unsigned not null" },
1300 		{ "state", "int unsigned not null" },
1301 		{ "timelimit", "int unsigned default 0 not null" },
1302 		{ "time_submit", "bigint unsigned default 0 not null" },
1303 		{ "time_eligible", "bigint unsigned default 0 not null" },
1304 		{ "time_start", "bigint unsigned default 0 not null" },
1305 		{ "time_end", "bigint unsigned default 0 not null" },
1306 		{ "time_suspended", "bigint unsigned default 0 not null" },
1307 		{ "gres_req", "text not null default ''" },
1308 		{ "gres_alloc", "text not null default ''" },
1309 		{ "gres_used", "text not null default ''" },
1310 		{ "wckey", "tinytext not null default ''" },
1311 		{ "work_dir", "text not null default ''" },
1312 		{ "system_comment", "text" },
1313 		{ "track_steps", "tinyint not null" },
1314 		{ "tres_alloc", "text not null default ''" },
1315 		{ "tres_req", "text not null default ''" },
1316 		{ NULL, NULL}
1317 	};
1318 
1319 	storage_field_t last_ran_table_fields[] = {
1320 		{ "hourly_rollup", "bigint unsigned default 0 not null" },
1321 		{ "daily_rollup", "bigint unsigned default 0 not null" },
1322 		{ "monthly_rollup", "bigint unsigned default 0 not null" },
1323 		{ NULL, NULL}
1324 	};
1325 
1326 	storage_field_t resv_table_fields[] = {
1327 		{ "id_resv", "int unsigned default 0 not null" },
1328 		{ "deleted", "tinyint default 0 not null" },
1329 		{ "assoclist", "text not null default ''" },
1330 		{ "flags", "bigint unsigned default 0 not null" },
1331 		{ "nodelist", "text not null default ''" },
1332 		{ "node_inx", "text not null default ''" },
1333 		{ "resv_name", "text not null" },
1334 		{ "time_start", "bigint unsigned default 0 not null"},
1335 		{ "time_end", "bigint unsigned default 0 not null" },
1336 		{ "tres", "text not null default ''" },
1337 		{ "unused_wall", "double unsigned default 0.0 not null" },
1338 		{ NULL, NULL}
1339 	};
1340 
1341 	storage_field_t step_table_fields[] = {
1342 		{ "job_db_inx", "bigint unsigned not null" },
1343 		{ "deleted", "tinyint default 0 not null" },
1344 		{ "exit_code", "int default 0 not null" },
1345 		{ "id_step", "int not null" },
1346 		{ "kill_requid", "int default -1 not null" },
1347 		{ "nodelist", "text not null" },
1348 		{ "nodes_alloc", "int unsigned not null" },
1349 		{ "node_inx", "text" },
1350 		{ "state", "smallint unsigned not null" },
1351 		{ "step_name", "text not null" },
1352 		{ "task_cnt", "int unsigned not null" },
1353 		{ "task_dist", "smallint default 0 not null" },
1354 		{ "time_start", "bigint unsigned default 0 not null" },
1355 		{ "time_end", "bigint unsigned default 0 not null" },
1356 		{ "time_suspended", "bigint unsigned default 0 not null" },
1357 		{ "user_sec", "int unsigned default 0 not null" },
1358 		{ "user_usec", "int unsigned default 0 not null" },
1359 		{ "sys_sec", "int unsigned default 0 not null" },
1360 		{ "sys_usec", "int unsigned default 0 not null" },
1361 		{ "act_cpufreq", "double unsigned default 0.0 not null" },
1362 		{ "consumed_energy", "bigint unsigned default 0 not null" },
1363 		{ "req_cpufreq_min", "int unsigned default 0 not null" },
1364 		{ "req_cpufreq", "int unsigned default 0 not null" }, /* max */
1365 		{ "req_cpufreq_gov", "int unsigned default 0 not null" },
1366 		{ "tres_alloc", "text not null default ''" },
1367 		{ "tres_usage_in_ave", "text not null default ''" },
1368 		{ "tres_usage_in_max", "text not null default ''" },
1369 		{ "tres_usage_in_max_taskid", "text not null default ''" },
1370 		{ "tres_usage_in_max_nodeid", "text not null default ''" },
1371 		{ "tres_usage_in_min", "text not null default ''" },
1372 		{ "tres_usage_in_min_taskid", "text not null default ''" },
1373 		{ "tres_usage_in_min_nodeid", "text not null default ''" },
1374 		{ "tres_usage_in_tot", "text not null default ''" },
1375 		{ "tres_usage_out_ave", "text not null default ''" },
1376 		{ "tres_usage_out_max", "text not null default ''" },
1377 		{ "tres_usage_out_max_taskid", "text not null default ''" },
1378 		{ "tres_usage_out_max_nodeid", "text not null default ''" },
1379 		{ "tres_usage_out_min", "text not null default ''" },
1380 		{ "tres_usage_out_min_taskid", "text not null default ''" },
1381 		{ "tres_usage_out_min_nodeid", "text not null default ''" },
1382 		{ "tres_usage_out_tot", "text not null default ''" },
1383 		{ NULL, NULL}
1384 	};
1385 
1386 	storage_field_t suspend_table_fields[] = {
1387 		{ "job_db_inx", "bigint unsigned not null" },
1388 		{ "id_assoc", "int not null" },
1389 		{ "time_start", "bigint unsigned default 0 not null" },
1390 		{ "time_end", "bigint unsigned default 0 not null" },
1391 		{ NULL, NULL}
1392 	};
1393 
1394 	storage_field_t wckey_table_fields[] = {
1395 		{ "creation_time", "bigint unsigned not null" },
1396 		{ "mod_time", "bigint unsigned default 0 not null" },
1397 		{ "deleted", "tinyint default 0 not null" },
1398 		{ "is_def", "tinyint default 0 not null" },
1399 		{ "id_wckey", "int unsigned not null auto_increment" },
1400 		{ "wckey_name", "tinytext not null default ''" },
1401 		{ "user", "tinytext not null" },
1402 		{ NULL, NULL}
1403 	};
1404 
1405 	char table_name[200];
1406 
1407 	if (create_cluster_assoc_table(mysql_conn, cluster_name)
1408 	    == SLURM_ERROR)
1409 		return SLURM_ERROR;
1410 
1411 	snprintf(table_name, sizeof(table_name), "\"%s_%s\"",
1412 		 cluster_name, assoc_day_table);
1413 
1414 	if (mysql_db_create_table(mysql_conn, table_name,
1415 				  id_usage_table_fields,
1416 				  ", primary key (id, id_tres, time_start))")
1417 	    == SLURM_ERROR)
1418 		return SLURM_ERROR;
1419 
1420 	snprintf(table_name, sizeof(table_name), "\"%s_%s\"",
1421 		 cluster_name, assoc_hour_table);
1422 
1423 	if (mysql_db_create_table(mysql_conn, table_name,
1424 				  id_usage_table_fields,
1425 				  ", primary key (id, id_tres, time_start))")
1426 	    == SLURM_ERROR)
1427 		return SLURM_ERROR;
1428 
1429 	snprintf(table_name, sizeof(table_name), "\"%s_%s\"",
1430 		 cluster_name, assoc_month_table);
1431 
1432 	if (mysql_db_create_table(mysql_conn, table_name,
1433 				  id_usage_table_fields,
1434 				  ", primary key (id, id_tres, time_start))")
1435 	    == SLURM_ERROR)
1436 		return SLURM_ERROR;
1437 
1438 	snprintf(table_name, sizeof(table_name), "\"%s_%s\"",
1439 		 cluster_name, cluster_day_table);
1440 
1441 	if (mysql_db_create_table(mysql_conn, table_name,
1442 				  cluster_usage_table_fields,
1443 				  ", primary key (id_tres, time_start))")
1444 	    == SLURM_ERROR)
1445 		return SLURM_ERROR;
1446 
1447 	snprintf(table_name, sizeof(table_name), "\"%s_%s\"",
1448 		 cluster_name, cluster_hour_table);
1449 
1450 	if (mysql_db_create_table(mysql_conn, table_name,
1451 				  cluster_usage_table_fields,
1452 				  ", primary key (id_tres, time_start))")
1453 	    == SLURM_ERROR)
1454 		return SLURM_ERROR;
1455 
1456 	snprintf(table_name, sizeof(table_name), "\"%s_%s\"",
1457 		 cluster_name, cluster_month_table);
1458 
1459 	if (mysql_db_create_table(mysql_conn, table_name,
1460 				  cluster_usage_table_fields,
1461 				  ", primary key (id_tres, time_start))")
1462 	    == SLURM_ERROR)
1463 		return SLURM_ERROR;
1464 
1465 	snprintf(table_name, sizeof(table_name), "\"%s_%s\"",
1466 		 cluster_name, event_table);
1467 
1468 	if (mysql_db_create_table(mysql_conn, table_name,
1469 				  event_table_fields,
1470 				  ", primary key (node_name(42), time_start), "
1471 				  "key rollup (node_name(42), time_start, "
1472 				  "time_end, state))") == SLURM_ERROR)
1473 		return SLURM_ERROR;
1474 
1475 	snprintf(table_name, sizeof(table_name), "\"%s_%s\"",
1476 		 cluster_name, job_table);
1477 
1478 	/*
1479 	 * sacct_def is the index for query's with state as time_start is used
1480 	 * in these queries. sacct_def2 is for plain sacct queries.
1481 	 */
1482 	if (mysql_db_create_table(mysql_conn, table_name, job_table_fields,
1483 				  ", primary key (job_db_inx), "
1484 				  "unique index (id_job, time_submit), "
1485 				  "key old_tuple (id_job, "
1486 				  "id_assoc, time_submit), "
1487 				  "key rollup (time_eligible, time_end), "
1488 				  "key rollup2 (time_end, time_eligible), "
1489 				  "key nodes_alloc (nodes_alloc), "
1490 				  "key wckey (id_wckey), "
1491 				  "key qos (id_qos), "
1492 				  "key association (id_assoc), "
1493 				  "key array_job (id_array_job), "
1494 				  "key het_job (het_job_id), "
1495 				  "key reserv (id_resv), "
1496 				  "key sacct_def (id_user, time_start, "
1497 				  "time_end), "
1498 				  "key sacct_def2 (id_user, time_end, "
1499 				  "time_eligible))")
1500 	    == SLURM_ERROR)
1501 		return SLURM_ERROR;
1502 
1503 	snprintf(table_name, sizeof(table_name), "\"%s_%s\"",
1504 		 cluster_name, last_ran_table);
1505 	if (mysql_db_create_table(mysql_conn, table_name,
1506 				  last_ran_table_fields,
1507 				  ", primary key (hourly_rollup, "
1508 				  "daily_rollup, monthly_rollup))")
1509 	    == SLURM_ERROR)
1510 		return SLURM_ERROR;
1511 
1512 	snprintf(table_name, sizeof(table_name), "\"%s_%s\"",
1513 		 cluster_name, resv_table);
1514 	if (mysql_db_create_table(mysql_conn, table_name,
1515 				  resv_table_fields,
1516 				  ", primary key (id_resv, time_start))")
1517 	    == SLURM_ERROR)
1518 		return SLURM_ERROR;
1519 
1520 	snprintf(table_name, sizeof(table_name), "\"%s_%s\"",
1521 		 cluster_name, step_table);
1522 	if (mysql_db_create_table(mysql_conn, table_name,
1523 				  step_table_fields,
1524 				  ", primary key (job_db_inx, id_step))")
1525 	    == SLURM_ERROR)
1526 		return SLURM_ERROR;
1527 
1528 	snprintf(table_name, sizeof(table_name), "\"%s_%s\"",
1529 		 cluster_name, suspend_table);
1530 	if (mysql_db_create_table(mysql_conn, table_name,
1531 				  suspend_table_fields,
1532 				  ", primary key (job_db_inx, time_start), "
1533 				  "key job_db_inx_times (job_db_inx, "
1534 				  "time_start, time_end))") == SLURM_ERROR)
1535 		return SLURM_ERROR;
1536 
1537 	snprintf(table_name, sizeof(table_name), "\"%s_%s\"",
1538 		 cluster_name, wckey_table);
1539 	if (mysql_db_create_table(mysql_conn, table_name,
1540 				  wckey_table_fields,
1541 				  ", primary key (id_wckey), "
1542 				  " unique index udex (wckey_name(42), "
1543 				  "user(42)))")
1544 	    == SLURM_ERROR)
1545 		return SLURM_ERROR;
1546 
1547 	snprintf(table_name, sizeof(table_name), "\"%s_%s\"",
1548 		 cluster_name, wckey_day_table);
1549 
1550 	if (mysql_db_create_table(mysql_conn, table_name,
1551 				  id_usage_table_fields,
1552 				  ", primary key (id, id_tres, time_start))")
1553 	    == SLURM_ERROR)
1554 		return SLURM_ERROR;
1555 
1556 	snprintf(table_name, sizeof(table_name), "\"%s_%s\"",
1557 		 cluster_name, wckey_hour_table);
1558 
1559 	if (mysql_db_create_table(mysql_conn, table_name,
1560 				  id_usage_table_fields,
1561 				  ", primary key (id, id_tres, time_start))")
1562 	    == SLURM_ERROR)
1563 		return SLURM_ERROR;
1564 
1565 	snprintf(table_name, sizeof(table_name), "\"%s_%s\"",
1566 		 cluster_name, wckey_month_table);
1567 
1568 	if (mysql_db_create_table(mysql_conn, table_name,
1569 				  id_usage_table_fields,
1570 				  ", primary key (id, id_tres, time_start))")
1571 	    == SLURM_ERROR)
1572 		return SLURM_ERROR;
1573 
1574 	return SLURM_SUCCESS;
1575 }
1576 
remove_cluster_tables(mysql_conn_t * mysql_conn,char * cluster_name)1577 extern int remove_cluster_tables(mysql_conn_t *mysql_conn, char *cluster_name)
1578 {
1579 	char *query = NULL;
1580 	int rc = SLURM_SUCCESS;
1581 	MYSQL_RES *result = NULL;
1582 
1583 	query = xstrdup_printf("select id_assoc from \"%s_%s\" limit 1;",
1584 			       cluster_name, assoc_table);
1585 	debug4("%d(%s:%d) query\n%s",
1586 	       mysql_conn->conn, THIS_FILE, __LINE__, query);
1587 	if (!(result = mysql_db_query_ret(mysql_conn, query, 0))) {
1588 		xfree(query);
1589 		error("no result given when querying cluster %s", cluster_name);
1590 		return SLURM_ERROR;
1591 	}
1592 	xfree(query);
1593 
1594 	if (mysql_num_rows(result)) {
1595 		mysql_free_result(result);
1596 		debug4("we still have associations, can't remove tables");
1597 		return SLURM_SUCCESS;
1598 	}
1599 	mysql_free_result(result);
1600 	xstrfmtcat(mysql_conn->pre_commit_query,
1601 		   "drop table \"%s_%s\", \"%s_%s\", \"%s_%s\", "
1602 		   "\"%s_%s\", \"%s_%s\", \"%s_%s\", \"%s_%s\", "
1603 		   "\"%s_%s\", \"%s_%s\", \"%s_%s\", \"%s_%s\", "
1604 		   "\"%s_%s\", \"%s_%s\", \"%s_%s\", \"%s_%s\", "
1605 		   "\"%s_%s\", \"%s_%s\";",
1606 		   cluster_name, assoc_table,
1607 		   cluster_name, assoc_day_table,
1608 		   cluster_name, assoc_hour_table,
1609 		   cluster_name, assoc_month_table,
1610 		   cluster_name, cluster_day_table,
1611 		   cluster_name, cluster_hour_table,
1612 		   cluster_name, cluster_month_table,
1613 		   cluster_name, event_table,
1614 		   cluster_name, job_table,
1615 		   cluster_name, last_ran_table,
1616 		   cluster_name, resv_table,
1617 		   cluster_name, step_table,
1618 		   cluster_name, suspend_table,
1619 		   cluster_name, wckey_table,
1620 		   cluster_name, wckey_day_table,
1621 		   cluster_name, wckey_hour_table,
1622 		   cluster_name, wckey_month_table);
1623 	/* Since we could possibly add this exact cluster after this
1624 	   we will require a commit before doing anything else.  This
1625 	   flag will give us that.
1626 	*/
1627 	mysql_conn->cluster_deleted = 1;
1628 	return rc;
1629 }
1630 
setup_assoc_limits(slurmdb_assoc_rec_t * assoc,char ** cols,char ** vals,char ** extra,qos_level_t qos_level,bool for_add)1631 extern int setup_assoc_limits(slurmdb_assoc_rec_t *assoc,
1632 			      char **cols, char **vals,
1633 			      char **extra, qos_level_t qos_level,
1634 			      bool for_add)
1635 {
1636 	uint32_t tres_str_flags = TRES_STR_FLAG_REMOVE |
1637 		TRES_STR_FLAG_SORT_ID | TRES_STR_FLAG_SIMPLE |
1638 		TRES_STR_FLAG_NO_NULL;
1639 
1640 	assoc_mgr_lock_t locks = { NO_LOCK, NO_LOCK, READ_LOCK, NO_LOCK,
1641 				   NO_LOCK, NO_LOCK, NO_LOCK };
1642 	if (!assoc)
1643 		return SLURM_ERROR;
1644 
1645 	if (for_add) {
1646 		/* If we are adding we should make sure we don't get
1647 		   old reside sitting around from a former life.
1648 		*/
1649 		if (assoc->shares_raw == NO_VAL)
1650 			assoc->shares_raw = INFINITE;
1651 		if (assoc->grp_jobs == NO_VAL)
1652 			assoc->grp_jobs = INFINITE;
1653 		if (assoc->grp_jobs_accrue == NO_VAL)
1654 			assoc->grp_jobs_accrue = INFINITE;
1655 		if (assoc->grp_submit_jobs == NO_VAL)
1656 			assoc->grp_submit_jobs = INFINITE;
1657 		if (assoc->grp_wall == NO_VAL)
1658 			assoc->grp_wall = INFINITE;
1659 		if (assoc->max_jobs == NO_VAL)
1660 			assoc->max_jobs = INFINITE;
1661 		if (assoc->max_jobs_accrue == NO_VAL)
1662 			assoc->max_jobs_accrue = INFINITE;
1663 		if (assoc->min_prio_thresh == NO_VAL)
1664 			assoc->min_prio_thresh = INFINITE;
1665 		if (assoc->max_submit_jobs == NO_VAL)
1666 			assoc->max_submit_jobs = INFINITE;
1667 		if (assoc->max_wall_pj == NO_VAL)
1668 			assoc->max_wall_pj = INFINITE;
1669 		if (assoc->priority == NO_VAL)
1670 			assoc->priority = INFINITE;
1671 		if (assoc->def_qos_id == NO_VAL)
1672 			assoc->def_qos_id = INFINITE;
1673 	}
1674 
1675 	if (assoc->shares_raw == INFINITE) {
1676 		xstrcat(*cols, ", shares");
1677 		xstrcat(*vals, ", 1");
1678 		xstrcat(*extra, ", shares=1");
1679 		assoc->shares_raw = 1;
1680 	} else if ((assoc->shares_raw != NO_VAL)
1681 		   && (int32_t)assoc->shares_raw >= 0) {
1682 		xstrcat(*cols, ", shares");
1683 		xstrfmtcat(*vals, ", %u", assoc->shares_raw);
1684 		xstrfmtcat(*extra, ", shares=%u", assoc->shares_raw);
1685 	}
1686 
1687 	if (assoc->grp_jobs == INFINITE) {
1688 		xstrcat(*cols, ", grp_jobs");
1689 		xstrcat(*vals, ", NULL");
1690 		xstrcat(*extra, ", grp_jobs=NULL");
1691 	} else if ((assoc->grp_jobs != NO_VAL)
1692 		   && ((int32_t)assoc->grp_jobs >= 0)) {
1693 		xstrcat(*cols, ", grp_jobs");
1694 		xstrfmtcat(*vals, ", %u", assoc->grp_jobs);
1695 		xstrfmtcat(*extra, ", grp_jobs=%u", assoc->grp_jobs);
1696 	}
1697 
1698 	if (assoc->grp_jobs_accrue == INFINITE) {
1699 		xstrcat(*cols, ", grp_jobs_accrue");
1700 		xstrcat(*vals, ", NULL");
1701 		xstrcat(*extra, ", grp_jobs_accrue=NULL");
1702 	} else if ((assoc->grp_jobs_accrue != NO_VAL)
1703 		   && ((int32_t)assoc->grp_jobs_accrue >= 0)) {
1704 		xstrcat(*cols, ", grp_jobs_accrue");
1705 		xstrfmtcat(*vals, ", %u", assoc->grp_jobs_accrue);
1706 		xstrfmtcat(*extra, ", grp_jobs_accrue=%u",
1707 			   assoc->grp_jobs_accrue);
1708 	}
1709 
1710 	if (assoc->grp_submit_jobs == INFINITE) {
1711 		xstrcat(*cols, ", grp_submit_jobs");
1712 		xstrcat(*vals, ", NULL");
1713 		xstrcat(*extra, ", grp_submit_jobs=NULL");
1714 	} else if ((assoc->grp_submit_jobs != NO_VAL)
1715 		   && ((int32_t)assoc->grp_submit_jobs >= 0)) {
1716 		xstrcat(*cols, ", grp_submit_jobs");
1717 		xstrfmtcat(*vals, ", %u", assoc->grp_submit_jobs);
1718 		xstrfmtcat(*extra, ", grp_submit_jobs=%u",
1719 			   assoc->grp_submit_jobs);
1720 	}
1721 
1722 	if (assoc->grp_wall == INFINITE) {
1723 		xstrcat(*cols, ", grp_wall");
1724 		xstrcat(*vals, ", NULL");
1725 		xstrcat(*extra, ", grp_wall=NULL");
1726 	} else if ((assoc->grp_wall != NO_VAL)
1727 		   && ((int32_t)assoc->grp_wall >= 0)) {
1728 		xstrcat(*cols, ", grp_wall");
1729 		xstrfmtcat(*vals, ", %u", assoc->grp_wall);
1730 		xstrfmtcat(*extra, ", grp_wall=%u", assoc->grp_wall);
1731 	}
1732 
1733 	/* this only gets set on a user's association and is_def
1734 	 * could be NO_VAL only 1 is accepted */
1735 	if ((assoc->is_def == 1)
1736 	    && ((qos_level == QOS_LEVEL_MODIFY)
1737 		|| (assoc->user && assoc->cluster && assoc->acct))) {
1738 		xstrcat(*cols, ", is_def");
1739 		xstrcat(*vals, ", 1");
1740 		xstrcat(*extra, ", is_def=1");
1741 	}
1742 
1743 	if (assoc->max_jobs == INFINITE) {
1744 		xstrcat(*cols, ", max_jobs");
1745 		xstrcat(*vals, ", NULL");
1746 		xstrcat(*extra, ", max_jobs=NULL");
1747 	} else if ((assoc->max_jobs != NO_VAL)
1748 		   && ((int32_t)assoc->max_jobs >= 0)) {
1749 		xstrcat(*cols, ", max_jobs");
1750 		xstrfmtcat(*vals, ", %u", assoc->max_jobs);
1751 		xstrfmtcat(*extra, ", max_jobs=%u", assoc->max_jobs);
1752 	}
1753 
1754 	if (assoc->max_jobs_accrue == INFINITE) {
1755 		xstrcat(*cols, ", max_jobs_accrue");
1756 		xstrcat(*vals, ", NULL");
1757 		xstrcat(*extra, ", max_jobs_accrue=NULL");
1758 	} else if ((assoc->max_jobs_accrue != NO_VAL)
1759 		   && ((int32_t)assoc->max_jobs_accrue >= 0)) {
1760 		xstrcat(*cols, ", max_jobs_accrue");
1761 		xstrfmtcat(*vals, ", %u", assoc->max_jobs_accrue);
1762 		xstrfmtcat(*extra, ", max_jobs_accrue=%u",
1763 			   assoc->max_jobs_accrue);
1764 	}
1765 
1766 	if (assoc->min_prio_thresh == INFINITE) {
1767 		xstrcat(*cols, ", min_prio_thresh");
1768 		xstrcat(*vals, ", NULL");
1769 		xstrcat(*extra, ", min_prio_thresh=NULL");
1770 	} else if ((assoc->min_prio_thresh != NO_VAL)
1771 		   && ((int32_t)assoc->min_prio_thresh >= 0)) {
1772 		xstrcat(*cols, ", min_prio_thresh");
1773 		xstrfmtcat(*vals, ", %u", assoc->min_prio_thresh);
1774 		xstrfmtcat(*extra, ", min_prio_thresh=%u",
1775 			   assoc->min_prio_thresh);
1776 	}
1777 
1778 	if (assoc->max_submit_jobs == INFINITE) {
1779 		xstrcat(*cols, ", max_submit_jobs");
1780 		xstrcat(*vals, ", NULL");
1781 		xstrcat(*extra, ", max_submit_jobs=NULL");
1782 	} else if ((assoc->max_submit_jobs != NO_VAL)
1783 		   && ((int32_t)assoc->max_submit_jobs >= 0)) {
1784 		xstrcat(*cols, ", max_submit_jobs");
1785 		xstrfmtcat(*vals, ", %u", assoc->max_submit_jobs);
1786 		xstrfmtcat(*extra, ", max_submit_jobs=%u",
1787 			   assoc->max_submit_jobs);
1788 	}
1789 
1790 	if (assoc->max_wall_pj == INFINITE) {
1791 		xstrcat(*cols, ", max_wall_pj");
1792 		xstrcat(*vals, ", NULL");
1793 		xstrcat(*extra, ", max_wall_pj=NULL");
1794 	} else if ((assoc->max_wall_pj != NO_VAL)
1795 		   && ((int32_t)assoc->max_wall_pj >= 0)) {
1796 		xstrcat(*cols, ", max_wall_pj");
1797 		xstrfmtcat(*vals, ", %u", assoc->max_wall_pj);
1798 		xstrfmtcat(*extra, ", max_wall_pj=%u", assoc->max_wall_pj);
1799 	}
1800 
1801 	if (assoc->priority == INFINITE) {
1802 		xstrcat(*cols, ", priority");
1803 		xstrcat(*vals, ", NULL");
1804 		xstrcat(*extra, ", priority=NULL");
1805 	} else if ((assoc->priority != NO_VAL)
1806 		   && ((int32_t)assoc->priority >= 0)) {
1807 		xstrcat(*cols, ", priority");
1808 		xstrfmtcat(*vals, ", %u", assoc->priority);
1809 		xstrfmtcat(*extra, ", priority=%u", assoc->priority);
1810 	}
1811 
1812 	if (assoc->def_qos_id == INFINITE) {
1813 		xstrcat(*cols, ", def_qos_id");
1814 		xstrcat(*vals, ", NULL");
1815 		xstrcat(*extra, ", def_qos_id=NULL");
1816 	} else if ((assoc->def_qos_id != NO_VAL)
1817 		   && ((int32_t)assoc->def_qos_id > 0)) {
1818 		assoc_mgr_lock(&locks);
1819 		if (!list_find_first(assoc_mgr_qos_list,
1820 		    slurmdb_find_qos_in_list, &(assoc->def_qos_id))) {
1821 			assoc_mgr_unlock(&locks);
1822 			return ESLURM_INVALID_QOS;
1823 		}
1824 		assoc_mgr_unlock(&locks);
1825 		xstrcat(*cols, ", def_qos_id");
1826 		xstrfmtcat(*vals, ", %u", assoc->def_qos_id);
1827 		xstrfmtcat(*extra, ", def_qos_id=%u", assoc->def_qos_id);
1828 	}
1829 
1830 	/* When modifying anything below this comment it happens in
1831 	 * the actual function since we have to wait until we hear
1832 	 * about the parent first.
1833 	 * What we do to make it known something needs to be changed
1834 	 * is we cat "" onto extra which will inform the caller
1835 	 * something needs changing.
1836 	 */
1837 
1838 	if (assoc->grp_tres) {
1839 		if (qos_level == QOS_LEVEL_MODIFY) {
1840 			xstrcat(*extra, "");
1841 			goto end_modify;
1842 		}
1843 		xstrcat(*cols, ", grp_tres");
1844 		slurmdb_combine_tres_strings(
1845 			&assoc->grp_tres, NULL, tres_str_flags);
1846 		xstrfmtcat(*vals, ", '%s'", assoc->grp_tres);
1847 		xstrfmtcat(*extra, ", grp_tres='%s'", assoc->grp_tres);
1848 	}
1849 
1850 	if (assoc->grp_tres_mins) {
1851 		if (qos_level == QOS_LEVEL_MODIFY) {
1852 			xstrcat(*extra, "");
1853 			goto end_modify;
1854 		}
1855 		xstrcat(*cols, ", grp_tres_mins");
1856 		slurmdb_combine_tres_strings(
1857 			&assoc->grp_tres_mins, NULL, tres_str_flags);
1858 		xstrfmtcat(*vals, ", '%s'", assoc->grp_tres_mins);
1859 		xstrfmtcat(*extra, ", grp_tres_mins='%s'",
1860 			   assoc->grp_tres_mins);
1861 	}
1862 
1863 	if (assoc->grp_tres_run_mins) {
1864 		if (qos_level == QOS_LEVEL_MODIFY) {
1865 			xstrcat(*extra, "");
1866 			goto end_modify;
1867 		}
1868 		xstrcat(*cols, ", grp_tres_run_mins");
1869 		slurmdb_combine_tres_strings(
1870 			&assoc->grp_tres_run_mins, NULL, tres_str_flags);
1871 		xstrfmtcat(*vals, ", '%s'", assoc->grp_tres_run_mins);
1872 		xstrfmtcat(*extra, ", grp_tres_run_mins='%s'",
1873 			   assoc->grp_tres_run_mins);
1874 	}
1875 
1876 	if (assoc->max_tres_pj) {
1877 		if (qos_level == QOS_LEVEL_MODIFY) {
1878 			xstrcat(*extra, "");
1879 			goto end_modify;
1880 		}
1881 		xstrcat(*cols, ", max_tres_pj");
1882 		slurmdb_combine_tres_strings(
1883 			&assoc->max_tres_pj, NULL, tres_str_flags);
1884 		xstrfmtcat(*vals, ", '%s'", assoc->max_tres_pj);
1885 		xstrfmtcat(*extra, ", max_tres_pj='%s'", assoc->max_tres_pj);
1886 	}
1887 
1888 	if (assoc->max_tres_pn) {
1889 		if (qos_level == QOS_LEVEL_MODIFY) {
1890 			xstrcat(*extra, "");
1891 			goto end_modify;
1892 		}
1893 		xstrcat(*cols, ", max_tres_pn");
1894 		slurmdb_combine_tres_strings(
1895 			&assoc->max_tres_pn, NULL, tres_str_flags);
1896 		xstrfmtcat(*vals, ", '%s'", assoc->max_tres_pn);
1897 		xstrfmtcat(*extra, ", max_tres_pn='%s'", assoc->max_tres_pn);
1898 	}
1899 
1900 	if (assoc->max_tres_mins_pj) {
1901 		if (qos_level == QOS_LEVEL_MODIFY) {
1902 			xstrcat(*extra, "");
1903 			goto end_modify;
1904 		}
1905 		xstrcat(*cols, ", max_tres_mins_pj");
1906 		slurmdb_combine_tres_strings(
1907 			&assoc->max_tres_mins_pj, NULL, tres_str_flags);
1908 		xstrfmtcat(*vals, ", '%s'", assoc->max_tres_mins_pj);
1909 		xstrfmtcat(*extra, ", max_tres_mins_pj='%s'",
1910 			   assoc->max_tres_mins_pj);
1911 	}
1912 
1913 	if (assoc->max_tres_run_mins) {
1914 		if (qos_level == QOS_LEVEL_MODIFY) {
1915 			xstrcat(*extra, "");
1916 			goto end_modify;
1917 		}
1918 		xstrcat(*cols, ", max_tres_run_mins");
1919 		slurmdb_combine_tres_strings(
1920 			&assoc->max_tres_run_mins, NULL, tres_str_flags);
1921 		xstrfmtcat(*vals, ", '%s'", assoc->max_tres_run_mins);
1922 		xstrfmtcat(*extra, ", max_tres_run_mins='%s'",
1923 			   assoc->max_tres_run_mins);
1924 	}
1925 
1926 	if (assoc->qos_list && list_count(assoc->qos_list)) {
1927 		char *qos_type = "qos";
1928 		char *qos_val = NULL;
1929 		char *tmp_char = NULL;
1930 		int set = 0;
1931 		ListIterator qos_itr;
1932 
1933 		if (qos_level == QOS_LEVEL_MODIFY) {
1934 			xstrcat(*extra, "");
1935 			goto end_modify;
1936 		}
1937 
1938 		qos_itr = list_iterator_create(assoc->qos_list);
1939 		while ((tmp_char = list_next(qos_itr))) {
1940 			/* we don't want to include blank names */
1941 			if (!tmp_char[0])
1942 				continue;
1943 			if (!set) {
1944 				if (tmp_char[0] == '+' || tmp_char[0] == '-')
1945 					qos_type = "delta_qos";
1946 				set = 1;
1947 			}
1948 			xstrfmtcat(qos_val, ",%s", tmp_char);
1949 		}
1950 
1951 		list_iterator_destroy(qos_itr);
1952 		if (qos_val) {
1953 			xstrfmtcat(*cols, ", %s", qos_type);
1954 			xstrfmtcat(*vals, ", '%s,'", qos_val);
1955 			xstrfmtcat(*extra, ", %s='%s,'", qos_type, qos_val);
1956 			xfree(qos_val);
1957 		}
1958 	} else if ((qos_level == QOS_LEVEL_SET) && default_qos_str) {
1959 		/* Add default qos to the account */
1960 		xstrcat(*cols, ", qos");
1961 		xstrfmtcat(*vals, ", '%s,'", default_qos_str);
1962 		xstrfmtcat(*extra, ", qos='%s,'", default_qos_str);
1963 		if (!assoc->qos_list)
1964 			assoc->qos_list = list_create(xfree_ptr);
1965 		slurm_addto_char_list(assoc->qos_list, default_qos_str);
1966 	} else if (qos_level != QOS_LEVEL_MODIFY) {
1967 		/* clear the qos */
1968 		xstrcat(*cols, ", qos, delta_qos");
1969 		xstrcat(*vals, ", '', ''");
1970 		xstrcat(*extra, ", qos='', delta_qos=''");
1971 	}
1972 end_modify:
1973 
1974 	return SLURM_SUCCESS;
1975 
1976 }
1977 
1978 /* This is called by most modify functions to alter the table and
1979  * insert a new line in the transaction table.
1980  */
modify_common(mysql_conn_t * mysql_conn,uint16_t type,time_t now,char * user_name,char * table,char * cond_char,char * vals,char * cluster_name)1981 extern int modify_common(mysql_conn_t *mysql_conn,
1982 			 uint16_t type,
1983 			 time_t now,
1984 			 char *user_name,
1985 			 char *table,
1986 			 char *cond_char,
1987 			 char *vals,
1988 			 char *cluster_name)
1989 {
1990 	char *query = NULL;
1991 	int rc = SLURM_SUCCESS;
1992 	char *tmp_cond_char = slurm_add_slash_to_quotes(cond_char);
1993 	char *tmp_vals = NULL;
1994 	bool cluster_centric = true;
1995 
1996 	/* figure out which tables we need to append the cluster name to */
1997 	if ((table == cluster_table) || (table == acct_coord_table)
1998 	    || (table == acct_table) || (table == qos_table)
1999 	    || (table == txn_table) || (table == user_table)
2000 	    || (table == res_table) || (table == clus_res_table)
2001 	    || (table == federation_table))
2002 		cluster_centric = false;
2003 
2004 	if (vals && vals[1])
2005 		tmp_vals = slurm_add_slash_to_quotes(vals+2);
2006 
2007 	if (cluster_centric) {
2008 		xassert(cluster_name);
2009 		xstrfmtcat(query,
2010 			   "update \"%s_%s\" set mod_time=%ld%s "
2011 			   "where deleted=0 && %s;",
2012 			   cluster_name, table, now, vals, cond_char);
2013 		xstrfmtcat(query,
2014 			   "insert into %s "
2015 			   "(timestamp, action, name, cluster, actor, info) "
2016 			   "values (%ld, %d, '%s', '%s', '%s', '%s');",
2017 			   txn_table,
2018 			   now, type, tmp_cond_char, cluster_name,
2019 			   user_name, tmp_vals);
2020 	} else {
2021 		xstrfmtcat(query,
2022 			   "update %s set mod_time=%ld%s "
2023 			   "where deleted=0 && %s;",
2024 			   table, now, vals, cond_char);
2025 		xstrfmtcat(query,
2026 			   "insert into %s "
2027 			   "(timestamp, action, name, actor, info) "
2028 			   "values (%ld, %d, '%s', '%s', '%s');",
2029 			   txn_table,
2030 			   now, type, tmp_cond_char, user_name, tmp_vals);
2031 	}
2032 	xfree(tmp_cond_char);
2033 	xfree(tmp_vals);
2034 	if (debug_flags & DEBUG_FLAG_DB_ASSOC)
2035 		DB_DEBUG(mysql_conn->conn, "query\n%s", query);
2036 	rc = mysql_db_query(mysql_conn, query);
2037 	xfree(query);
2038 
2039 	if (rc != SLURM_SUCCESS) {
2040 		reset_mysql_conn(mysql_conn);
2041 		return SLURM_ERROR;
2042 	}
2043 
2044 	return SLURM_SUCCESS;
2045 }
2046 
2047 /* Every option in assoc_char should have a 't1.' infront of it. */
remove_common(mysql_conn_t * mysql_conn,uint16_t type,time_t now,char * user_name,char * table,char * name_char,char * assoc_char,char * cluster_name,List ret_list,bool * jobs_running)2048 extern int remove_common(mysql_conn_t *mysql_conn,
2049 			 uint16_t type,
2050 			 time_t now,
2051 			 char *user_name,
2052 			 char *table,
2053 			 char *name_char,
2054 			 char *assoc_char,
2055 			 char *cluster_name,
2056 			 List ret_list,
2057 			 bool *jobs_running)
2058 {
2059 	int rc = SLURM_SUCCESS;
2060 	char *query = NULL;
2061 	char *loc_assoc_char = NULL, *loc_usage_id_char = NULL;
2062 	MYSQL_RES *result = NULL;
2063 	MYSQL_ROW row;
2064 	time_t day_old = now - DELETE_SEC_BACK;
2065 	bool has_jobs = false;
2066 	char *tmp_name_char = NULL;
2067 	bool cluster_centric = true;
2068 	uint32_t smallest_lft = 0xFFFFFFFF;
2069 
2070 	/* figure out which tables we need to append the cluster name to */
2071 	if ((table == cluster_table) || (table == acct_coord_table)
2072 	    || (table == acct_table) || (table == qos_table)
2073 	    || (table == txn_table) || (table == user_table)
2074 	    || (table == res_table) || (table == clus_res_table)
2075 	    || (table == federation_table))
2076 		cluster_centric = false;
2077 
2078 	/* If we have jobs associated with this we do not want to
2079 	 * really delete it for accounting purposes.  This is for
2080 	 * corner cases most of the time this won't matter.
2081 	 */
2082 	if ((table == acct_coord_table) || (table == res_table)
2083 	    || (table == clus_res_table) || (table == federation_table)) {
2084 		/* This doesn't apply for these tables since we are
2085 		 * only looking for association type tables.
2086 		 */
2087 	} else if ((table == qos_table) || (table == wckey_table)) {
2088 		if (cluster_name)
2089 			has_jobs = _check_jobs_before_remove_without_assoctable(
2090 				mysql_conn, cluster_name, assoc_char);
2091 	} else if (table != assoc_table) {
2092 		/* first check to see if we are running jobs now */
2093 		if (_check_jobs_before_remove(
2094 			    mysql_conn, cluster_name, assoc_char,
2095 			    ret_list, jobs_running) || (*jobs_running))
2096 			return SLURM_SUCCESS;
2097 
2098 		has_jobs = _check_jobs_before_remove(
2099 			mysql_conn, cluster_name, assoc_char, NULL, NULL);
2100 	} else {
2101 		/* first check to see if we are running jobs now */
2102 		if (_check_jobs_before_remove_assoc(
2103 			    mysql_conn, cluster_name, name_char,
2104 			    ret_list, jobs_running) || (*jobs_running))
2105 			return SLURM_SUCCESS;
2106 
2107 		/* now check to see if any jobs were ever run. */
2108 		has_jobs = _check_jobs_before_remove_assoc(
2109 			mysql_conn, cluster_name, name_char,
2110 			NULL, NULL);
2111 	}
2112 	/* we want to remove completely all that is less than a day old */
2113 	if (!has_jobs && table != assoc_table) {
2114 		if (cluster_centric) {
2115 			query = xstrdup_printf("delete from \"%s_%s\" where "
2116 					       "creation_time>%ld && (%s);",
2117 					       cluster_name, table, day_old,
2118 					       name_char);
2119 		} else {
2120 			query = xstrdup_printf("delete from %s where "
2121 					       "creation_time>%ld && (%s);",
2122 					       table, day_old, name_char);
2123 		}
2124 	}
2125 
2126 	if (table != assoc_table) {
2127 		if (cluster_centric) {
2128 			xstrfmtcat(query,
2129 				   "update \"%s_%s\" set mod_time=%ld, "
2130 				   "deleted=1 where deleted=0 && (%s);",
2131 				   cluster_name, table, now, name_char);
2132 		} else if (table == federation_table) {
2133 			xstrfmtcat(query,
2134 				   "update %s set "
2135 				   "mod_time=%ld, deleted=1, "
2136 				   "flags=DEFAULT "
2137 				   "where deleted=0 && (%s);",
2138 				   federation_table, now,
2139 				   name_char);
2140 		} else if (table == qos_table) {
2141 			xstrfmtcat(query,
2142 				   "update %s set "
2143 				   "mod_time=%ld, deleted=1, "
2144 				   "grace_time=DEFAULT, "
2145 				   "max_jobs_pa=DEFAULT, "
2146 				   "max_jobs_per_user=DEFAULT, "
2147 				   "max_jobs_accrue_pa=DEFAULT, "
2148 				   "max_jobs_accrue_pu=DEFAULT, "
2149 				   "min_prio_thresh=DEFAULT, "
2150 				   "max_submit_jobs_pa=DEFAULT, "
2151 				   "max_submit_jobs_per_user=DEFAULT, "
2152 				   "max_tres_pa=DEFAULT, "
2153 				   "max_tres_pj=DEFAULT, "
2154 				   "max_tres_pn=DEFAULT, "
2155 				   "max_tres_pu=DEFAULT, "
2156 				   "max_tres_mins_pj=DEFAULT, "
2157 				   "max_tres_run_mins_pa=DEFAULT, "
2158 				   "max_tres_run_mins_pu=DEFAULT, "
2159 				   "min_tres_pj=DEFAULT, "
2160 				   "max_wall_duration_per_job=DEFAULT, "
2161 				   "grp_jobs=DEFAULT, grp_submit_jobs=DEFAULT, "
2162 				   "grp_jobs_accrue=DEFAULT, grp_tres=DEFAULT, "
2163 				   "grp_tres_mins=DEFAULT, "
2164 				   "grp_tres_run_mins=DEFAULT, "
2165 				   "grp_wall=DEFAULT, "
2166 				   "preempt=DEFAULT, "
2167 				   "preempt_exempt_time=DEFAULT, "
2168 				   "priority=DEFAULT, "
2169 				   "usage_factor=DEFAULT, "
2170 				   "usage_thres=DEFAULT "
2171 				   "where deleted=0 && (%s);",
2172 				   qos_table, now, name_char);
2173 		} else {
2174 			xstrfmtcat(query,
2175 				   "update %s set mod_time=%ld, deleted=1 "
2176 				   "where deleted=0 && (%s);",
2177 				   table, now, name_char);
2178 		}
2179 	}
2180 
2181 	/* If we are removing assocs use the assoc_char since the
2182 	   name_char has lft between statements that can change over
2183 	   time.  The assoc_char has the actual ids of the assocs
2184 	   which never change.
2185 	*/
2186 	if (type == DBD_REMOVE_ASSOCS && assoc_char)
2187 		tmp_name_char = slurm_add_slash_to_quotes(assoc_char);
2188 	else
2189 		tmp_name_char = slurm_add_slash_to_quotes(name_char);
2190 
2191 	if (cluster_centric)
2192 		xstrfmtcat(query,
2193 			   "insert into %s (timestamp, action, name, "
2194 			   "actor, cluster) values "
2195 			   "(%ld, %d, '%s', '%s', '%s');",
2196 			   txn_table,
2197 			   now, type, tmp_name_char, user_name, cluster_name);
2198 	else
2199 		xstrfmtcat(query,
2200 			   "insert into %s (timestamp, action, name, actor) "
2201 			   "values (%ld, %d, '%s', '%s');",
2202 			   txn_table,
2203 			   now, type, tmp_name_char, user_name);
2204 
2205 	xfree(tmp_name_char);
2206 
2207 	if (debug_flags & DEBUG_FLAG_DB_ASSOC)
2208 		DB_DEBUG(mysql_conn->conn, "query\n%s", query);
2209 	rc = mysql_db_query(mysql_conn, query);
2210 	xfree(query);
2211 	if (rc != SLURM_SUCCESS) {
2212 		reset_mysql_conn(mysql_conn);
2213 		return SLURM_ERROR;
2214 	} else if ((table == acct_coord_table)
2215 		   || (table == wckey_table)
2216 		   || (table == clus_res_table)
2217 		   || (table == res_table)
2218 		   || (table == federation_table)
2219 		   || (table == qos_table))
2220 		return SLURM_SUCCESS;
2221 
2222 	/* mark deleted=1 or remove completely the accounting tables
2223 	 */
2224 	if (table != assoc_table) {
2225 		if (!assoc_char) {
2226 			error("no assoc_char");
2227 			if (mysql_conn->rollback) {
2228 				mysql_db_rollback(mysql_conn);
2229 			}
2230 			list_flush(mysql_conn->update_list);
2231 			return SLURM_ERROR;
2232 		}
2233 
2234 		/* If we are doing this on an assoc_table we have
2235 		   already done this, so don't */
2236 		query = xstrdup_printf("select distinct t1.id_assoc "
2237 				       "from \"%s_%s\" as t1, \"%s_%s\" as t2 "
2238 				       "where (%s) && t1.lft between "
2239 				       "t2.lft and t2.rgt && t1.deleted=0 "
2240 				       "&& t2.deleted=0;",
2241 				       cluster_name, assoc_table,
2242 				       cluster_name, assoc_table, assoc_char);
2243 
2244 		if (debug_flags & DEBUG_FLAG_DB_ASSOC)
2245 			DB_DEBUG(mysql_conn->conn, "query\n%s", query);
2246 		if (!(result = mysql_db_query_ret(
2247 			      mysql_conn, query, 0))) {
2248 			xfree(query);
2249 			if (mysql_conn->rollback) {
2250 				mysql_db_rollback(mysql_conn);
2251 			}
2252 			list_flush(mysql_conn->update_list);
2253 			return SLURM_ERROR;
2254 		}
2255 		xfree(query);
2256 
2257 		rc = 0;
2258 		xfree(loc_assoc_char);
2259 		while ((row = mysql_fetch_row(result))) {
2260 			slurmdb_assoc_rec_t *rem_assoc = NULL;
2261 			if (loc_assoc_char)
2262 				xstrcat(loc_assoc_char, " || ");
2263 			xstrfmtcat(loc_assoc_char, "id_assoc=%s", row[0]);
2264 
2265 			rem_assoc = xmalloc(sizeof(slurmdb_assoc_rec_t));
2266 			rem_assoc->id = slurm_atoul(row[0]);
2267 			rem_assoc->cluster = xstrdup(cluster_name);
2268 			if (addto_update_list(mysql_conn->update_list,
2269 					      SLURMDB_REMOVE_ASSOC,
2270 					      rem_assoc) != SLURM_SUCCESS)
2271 				error("couldn't add to the update list");
2272 		}
2273 		mysql_free_result(result);
2274 	} else
2275 		loc_assoc_char = assoc_char;
2276 
2277 	if (!loc_assoc_char) {
2278 		debug2("No associations with object being deleted");
2279 		return rc;
2280 	}
2281 
2282 	loc_usage_id_char = xstrdup(loc_assoc_char);
2283 	xstrsubstituteall(loc_usage_id_char, "id_assoc", "id");
2284 
2285 	/* We should not have to delete from usage table, only flag since we
2286 	 * only delete things that are typos.
2287 	 */
2288 	xstrfmtcat(query,
2289 		   "update \"%s_%s\" set mod_time=%ld, deleted=1 where (%s);"
2290 		   "update \"%s_%s\" set mod_time=%ld, deleted=1 where (%s);"
2291 		   "update \"%s_%s\" set mod_time=%ld, deleted=1 where (%s);",
2292 		   cluster_name, assoc_day_table, now, loc_usage_id_char,
2293 		   cluster_name, assoc_hour_table, now, loc_usage_id_char,
2294 		   cluster_name, assoc_month_table, now, loc_usage_id_char);
2295 	xfree(loc_usage_id_char);
2296 
2297 	if (debug_flags & DEBUG_FLAG_DB_ASSOC)
2298 		DB_DEBUG(mysql_conn->conn, "query\n%s %zu",
2299 			 query, strlen(query));
2300 	rc = mysql_db_query(mysql_conn, query);
2301 	xfree(query);
2302 	if (rc != SLURM_SUCCESS) {
2303 		reset_mysql_conn(mysql_conn);
2304 		return SLURM_ERROR;
2305 	}
2306 
2307 	/* If we have jobs that have ran don't go through the logic of
2308 	 * removing the associations. Since we may want them for
2309 	 * reports in the future since jobs had ran.
2310 	 */
2311 	if (has_jobs)
2312 		goto just_update;
2313 
2314 	/* remove completely all the associations for this added in the last
2315 	 * day, since they are most likely nothing we really wanted in
2316 	 * the first place.
2317 	 */
2318 	query = xstrdup_printf("select id_assoc from \"%s_%s\" as t1 where "
2319 			       "creation_time>%ld && (%s);",
2320 			       cluster_name, assoc_table,
2321 			       day_old, loc_assoc_char);
2322 
2323 	if (debug_flags & DEBUG_FLAG_DB_ASSOC)
2324 		DB_DEBUG(mysql_conn->conn, "query\n%s", query);
2325 	if (!(result = mysql_db_query_ret(
2326 		      mysql_conn, query, 0))) {
2327 		xfree(query);
2328 		reset_mysql_conn(mysql_conn);
2329 		return SLURM_ERROR;
2330 	}
2331 	xfree(query);
2332 
2333 	while ((row = mysql_fetch_row(result))) {
2334 		MYSQL_RES *result2 = NULL;
2335 		MYSQL_ROW row2;
2336 		uint32_t lft;
2337 
2338 		/* we have to do this one at a time since the lft's and rgt's
2339 		   change. If you think you need to remove this make
2340 		   sure your new way can handle changing lft and rgt's
2341 		   in the association. */
2342 		xstrfmtcat(query,
2343 			   "SELECT lft, rgt, (rgt - lft + 1) "
2344 			   "FROM \"%s_%s\" WHERE id_assoc = %s;",
2345 			   cluster_name, assoc_table, row[0]);
2346 		if (debug_flags & DEBUG_FLAG_DB_ASSOC)
2347 			DB_DEBUG(mysql_conn->conn, "query\n%s", query);
2348 		if (!(result2 = mysql_db_query_ret(
2349 			      mysql_conn, query, 0))) {
2350 			xfree(query);
2351 			rc = SLURM_ERROR;
2352 			break;
2353 		}
2354 		xfree(query);
2355 		if (!(row2 = mysql_fetch_row(result2))) {
2356 			mysql_free_result(result2);
2357 			continue;
2358 		}
2359 
2360 		xstrfmtcat(query,
2361 			   "delete quick from \"%s_%s\" where "
2362 			   "lft between %s AND %s;",
2363 			   cluster_name, assoc_table, row2[0], row2[1]);
2364 
2365 		xstrfmtcat(query,
2366 			   "UPDATE \"%s_%s\" SET rgt = rgt - %s WHERE rgt > %s;"
2367 			   "UPDATE \"%s_%s\" SET "
2368 			   "lft = lft - %s WHERE lft > %s;",
2369 			   cluster_name, assoc_table, row2[2], row2[1],
2370 			   cluster_name, assoc_table, row2[2], row2[1]);
2371 
2372 		lft = slurm_atoul(row2[0]);
2373 		if (lft < smallest_lft)
2374 			smallest_lft = lft;
2375 
2376 		mysql_free_result(result2);
2377 
2378 		if (debug_flags & DEBUG_FLAG_DB_ASSOC)
2379 			DB_DEBUG(mysql_conn->conn, "query\n%s", query);
2380 		rc = mysql_db_query(mysql_conn, query);
2381 		xfree(query);
2382 		if (rc != SLURM_SUCCESS) {
2383 			error("couldn't remove assoc");
2384 			break;
2385 		}
2386 	}
2387 	mysql_free_result(result);
2388 	/* This already happened before, but we need to run it again
2389 	   since the first time we ran it we didn't know if we were
2390 	   going to remove the above associations.
2391 	*/
2392 	if (rc == SLURM_SUCCESS)
2393 		rc = as_mysql_get_modified_lfts(mysql_conn,
2394 						cluster_name, smallest_lft);
2395 
2396 	if (rc == SLURM_ERROR) {
2397 		reset_mysql_conn(mysql_conn);
2398 		return rc;
2399 	}
2400 
2401 just_update:
2402 	/* now update the associations themselves that are still
2403 	 * around clearing all the limits since if we add them back
2404 	 * we don't want any residue from past associations lingering
2405 	 * around.
2406 	 */
2407 	query = xstrdup_printf("update \"%s_%s\" as t1 set "
2408 			       "mod_time=%ld, deleted=1, def_qos_id=DEFAULT, "
2409 			       "shares=DEFAULT, max_jobs=DEFAULT, "
2410 			       "max_jobs_accrue=DEFAULT, "
2411 			       "min_prio_thresh=DEFAULT, "
2412 			       "max_submit_jobs=DEFAULT, "
2413 			       "max_wall_pj=DEFAULT, "
2414 			       "max_tres_pj=DEFAULT, "
2415 			       "max_tres_pn=DEFAULT, "
2416 			       "max_tres_mins_pj=DEFAULT, "
2417 			       "max_tres_run_mins=DEFAULT, "
2418 			       "grp_jobs=DEFAULT, grp_submit_jobs=DEFAULT, "
2419 			       "grp_jobs_accrue=DEFAULT, grp_wall=DEFAULT, "
2420 			       "grp_tres=DEFAULT, "
2421 			       "grp_tres_mins=DEFAULT, "
2422 			       "grp_tres_run_mins=DEFAULT, "
2423 			       "qos=DEFAULT, delta_qos=DEFAULT, "
2424 			       "priority=DEFAULT "
2425 			       "where (%s);",
2426 			       cluster_name, assoc_table, now,
2427 			       loc_assoc_char);
2428 
2429 	if (table != assoc_table)
2430 		xfree(loc_assoc_char);
2431 
2432 	if (debug_flags & DEBUG_FLAG_DB_ASSOC)
2433 		DB_DEBUG(mysql_conn->conn, "query\n%s", query);
2434 	rc = mysql_db_query(mysql_conn, query);
2435 	xfree(query);
2436 	if (rc != SLURM_SUCCESS) {
2437 		reset_mysql_conn(mysql_conn);
2438 	}
2439 
2440 	return rc;
2441 }
2442 
mod_tres_str(char ** out,char * mod,char * cur,char * cur_par,char * name,char ** vals,uint32_t id,bool assoc)2443 extern void mod_tres_str(char **out, char *mod, char *cur,
2444 			 char *cur_par, char *name, char **vals,
2445 			 uint32_t id, bool assoc)
2446 {
2447 	uint32_t tres_str_flags = TRES_STR_FLAG_REMOVE |
2448 		TRES_STR_FLAG_SORT_ID | TRES_STR_FLAG_SIMPLE |
2449 		TRES_STR_FLAG_NO_NULL;
2450 
2451 	xassert(out);
2452 	xassert(name);
2453 
2454 	if (!mod)
2455 		return;
2456 
2457 	/* We have to add strings in waves or we will not be able to
2458 	 * get removes to work correctly.  We want the string returned
2459 	 * after the first slurmdb_combine_tres_strings to be put in
2460 	 * the database.
2461 	 */
2462 	xfree(*out); /* just to make sure */
2463 	*out = xstrdup(mod);
2464 	slurmdb_combine_tres_strings(out, cur, tres_str_flags);
2465 
2466 	if (xstrcmp(*out, cur)) {
2467 		if (vals) {
2468 			/* This logic is here because while the change
2469 			 * we are doing on the limit is the same for
2470 			 * each limit the other limits on the
2471 			 * associations might not be.  What this does
2472 			 * is only change the limit on the association
2473 			 * given the id.  I'm hoping someone in the
2474 			 * future comes up with a better way to do
2475 			 * this since this seems like a hack, but it
2476 			 * does do the job.
2477 			 */
2478 			xstrfmtcat(*vals, ", %s = "
2479 				   "if (%s=%u, '%s', %s)",
2480 				   name, assoc ? "id_assoc" : "id", id,
2481 				   *out, name);
2482 			/* xstrfmtcat(*vals, ", %s='%s%s')", */
2483 			/* 	   name, */
2484 			/* 	   *out[0] ? "," : "", */
2485 			/* 	   *out); */
2486 		}
2487 		if (cur_par)
2488 			slurmdb_combine_tres_strings(
2489 				out, cur_par, tres_str_flags);
2490 	} else
2491 		xfree(*out);
2492 }
2493 
_get_database_variable(mysql_conn_t * mysql_conn,const char * variable_name,uint64_t * value)2494 static int _get_database_variable(mysql_conn_t *mysql_conn,
2495 				  const char *variable_name, uint64_t *value)
2496 {
2497 	MYSQL_ROW row = NULL;
2498 	MYSQL_RES *result = NULL;
2499 	char *err_check = NULL;
2500 	char *query;
2501 
2502 	query = xstrdup_printf("show variables like \'%s\';",
2503 			       variable_name);
2504 	result = mysql_db_query_ret(mysql_conn, query, 0);
2505 	if (!result) {
2506 		error("%s: null result from query `%s`", __func__, query);
2507 		xfree(query);
2508 		return SLURM_ERROR;
2509 	}
2510 
2511 	if (mysql_num_rows(result) != 1) {
2512 		error("%s: invalid results from query `%s`", __func__, query);
2513 		xfree(query);
2514 		mysql_free_result(result);
2515 		return SLURM_ERROR;
2516 	}
2517 
2518 	xfree(query);
2519 
2520 	row = mysql_fetch_row(result);
2521 	*value = (uint64_t) strtoll(row[1], &err_check, 10);
2522 
2523 	if (*err_check) {
2524 		error("%s: error parsing string to int `%s`", __func__, row[1]);
2525 		mysql_free_result(result);
2526 		return SLURM_ERROR;
2527 	}
2528 	mysql_free_result(result);
2529 
2530 	return SLURM_SUCCESS;
2531 }
2532 
2533 /*
2534  * MySQL version 5.6.48 and 5.7.30 introduced a regression in the
2535  * implementation of CONCAT() that will lead to incorrect NULL values.
2536  *
2537  * We cannot safely work around this mistake without restructing our stored
2538  * procedures, and thus fatal() here to avoid a segfault.
2539  *
2540  * Test that concat() is working as expected, rather than trying to blacklist
2541  * specific versions.
2542  */
_check_mysql_concat_is_sane(mysql_conn_t * mysql_conn)2543 static void _check_mysql_concat_is_sane(mysql_conn_t *mysql_conn)
2544 {
2545 	MYSQL_ROW row = NULL;
2546 	MYSQL_RES *result = NULL;
2547 	char *query = "select @var := concat('');";
2548 	const char *version = mysql_get_server_info(mysql_conn->db_conn);
2549 
2550 	info("MySQL server version is: %s", version);
2551 
2552 	result = mysql_db_query_ret(mysql_conn, query, 0);
2553 	if (!result)
2554 		fatal("%s: null result from query `%s`", __func__, query);
2555 
2556 	if (mysql_num_rows(result) != 1)
2557 		fatal("%s: invalid results from query `%s`", __func__, query);
2558 
2559 	if (!(row = mysql_fetch_row(result)) || !row[0])
2560 		fatal("MySQL concat() function is defective. Please upgrade to a fixed version. See https://bugs.mysql.com/bug.php?id=99485.");
2561 
2562 	mysql_free_result(result);
2563 }
2564 
2565 /*
2566  * Check the values of innodb global database variables, and print
2567  * an error if the values are not at least half the recommendation.
2568  */
_check_database_variables(mysql_conn_t * mysql_conn)2569 static int _check_database_variables(mysql_conn_t *mysql_conn)
2570 {
2571 	const char buffer_var[] = "innodb_buffer_pool_size";
2572 	const uint64_t buffer_size = 1073741824;
2573 	const char logfile_var[] = "innodb_log_file_size";
2574 	const uint64_t logfile_size = 67108864;
2575 	const char lockwait_var[] = "innodb_lock_wait_timeout";
2576 	const uint64_t lockwait_timeout = 900;
2577 
2578 	uint64_t value;
2579 	bool recommended_values = true;
2580 	char *error_msg = xstrdup("Database settings not recommended values:");
2581 
2582 	if (_get_database_variable(mysql_conn, buffer_var, &value))
2583 		goto error;
2584 	debug2("%s: %"PRIu64, buffer_var, value);
2585 	if (value < (buffer_size / 2)) {
2586 		recommended_values = false;
2587 		xstrfmtcat(error_msg, " %s", buffer_var);
2588 	}
2589 
2590 	if (_get_database_variable(mysql_conn, logfile_var, &value))
2591 		goto error;
2592 	debug2("%s: %"PRIu64, logfile_var, value);
2593 	if (value < (logfile_size / 2)) {
2594 		recommended_values = false;
2595 		xstrfmtcat(error_msg, " %s", logfile_var);
2596 	}
2597 
2598 	if (_get_database_variable(mysql_conn, lockwait_var, &value))
2599 		goto error;
2600 	debug2("%s: %"PRIu64, lockwait_var, value);
2601 	if (value < (lockwait_timeout / 2)) {
2602 		recommended_values = false;
2603 		xstrfmtcat(error_msg, " %s", lockwait_var);
2604 	}
2605 
2606 	if (!recommended_values) {
2607 		error("%s", error_msg);
2608 	}
2609 
2610 	xfree(error_msg);
2611 	return SLURM_SUCCESS;
2612 
2613 error:
2614 	xfree(error_msg);
2615 	return SLURM_ERROR;
2616 }
2617 
2618 /*
2619  * init() is called when the plugin is loaded, before any other functions
2620  * are called.  Put global initialization here.
2621  */
init(void)2622 extern int init(void)
2623 {
2624 	int rc = SLURM_SUCCESS;
2625 	mysql_conn_t *mysql_conn = NULL;
2626 
2627 	debug_flags = slurm_get_debug_flags();
2628 
2629 	if (slurmdbd_conf->dbd_backup) {
2630 		char node_name_short[128];
2631 		char node_name_long[128];
2632 		if (gethostname(node_name_long, sizeof(node_name_long)))
2633 			fatal("getnodename: %m");
2634 		if (gethostname_short(node_name_short, sizeof(node_name_short)))
2635 			fatal("getnodename_short: %m");
2636 		if (!xstrcmp(node_name_short, slurmdbd_conf->dbd_backup) ||
2637 		    !xstrcmp(node_name_long, slurmdbd_conf->dbd_backup) ||
2638 		    !xstrcmp(slurmdbd_conf->dbd_backup, "localhost"))
2639 			backup_dbd = true;
2640 	}
2641 
2642 	mysql_db_info = create_mysql_db_info(SLURM_MYSQL_PLUGIN_AS);
2643 	mysql_db_name = acct_get_db_name();
2644 
2645 	debug2("mysql_connect() called for db %s", mysql_db_name);
2646 	mysql_conn = create_mysql_conn(0, 1, NULL);
2647 	while (mysql_db_get_db_connection(
2648 		       mysql_conn, mysql_db_name, mysql_db_info)
2649 	       != SLURM_SUCCESS) {
2650 		error("The database must be up when starting "
2651 		      "the MYSQL plugin.  Trying again in 5 seconds.");
2652 		sleep(5);
2653 	}
2654 
2655 	_check_mysql_concat_is_sane(mysql_conn);
2656 	_check_database_variables(mysql_conn);
2657 
2658 	rc = _as_mysql_acct_check_tables(mysql_conn);
2659 
2660 	if (rc == SLURM_SUCCESS) {
2661 		if (mysql_db_commit(mysql_conn)) {
2662 			error("commit failed, meaning %s failed", plugin_name);
2663 			rc = SLURM_ERROR;
2664 		} else
2665 			verbose("%s loaded", plugin_name);
2666 	} else {
2667 		verbose("%s failed", plugin_name);
2668 		if (mysql_db_rollback(mysql_conn))
2669 			error("rollback failed");
2670 	}
2671 
2672 	destroy_mysql_conn(mysql_conn);
2673 
2674 	return rc;
2675 }
2676 
fini(void)2677 extern int fini ( void )
2678 {
2679 	slurm_mutex_lock(&as_mysql_cluster_list_lock);
2680 	FREE_NULL_LIST(as_mysql_cluster_list);
2681 	FREE_NULL_LIST(as_mysql_total_cluster_list);
2682 	slurm_mutex_unlock(&as_mysql_cluster_list_lock);
2683 	slurm_mutex_destroy(&as_mysql_cluster_list_lock);
2684 	destroy_mysql_db_info(mysql_db_info);
2685 	xfree(mysql_db_name);
2686 	xfree(default_qos_str);
2687 
2688 	mysql_db_cleanup();
2689 	return SLURM_SUCCESS;
2690 }
2691 
2692 /*
2693  * Get the dimensions of this cluster so we know how to deal with the hostlists.
2694  *
2695  * IN mysql_conn - mysql connection
2696  * IN cluster_name - name of cluster to get dimensions for
2697  * OUT dims - dimenions of cluster
2698  *
2699  * RET return SLURM_SUCCESS on success, SLURM_FAILURE otherwise.
2700  */
get_cluster_dims(mysql_conn_t * mysql_conn,char * cluster_name,int * dims)2701 extern int get_cluster_dims(mysql_conn_t *mysql_conn, char *cluster_name,
2702 			    int *dims)
2703 {
2704 	char *query;
2705 	MYSQL_ROW row;
2706 	MYSQL_RES *result = NULL;
2707 
2708 	query = xstrdup_printf("select dimensions, flags from %s where "
2709 			       "name='%s'",
2710 			       cluster_table, cluster_name);
2711 
2712 	debug4("%d(%s:%d) query\n%s",
2713 	       mysql_conn->conn, THIS_FILE, __LINE__, query);
2714 	if (!(result = mysql_db_query_ret(mysql_conn, query, 0))) {
2715 		xfree(query);
2716 		return SLURM_ERROR;
2717 	}
2718 	xfree(query);
2719 
2720 	if (!(row = mysql_fetch_row(result))) {
2721 		error("Couldn't get the dimensions of cluster '%s'.",
2722 		      cluster_name);
2723 		mysql_free_result(result);
2724 		return SLURM_ERROR;
2725 	}
2726 
2727 	/*
2728 	 * On a Cray System when dealing with hostlists as we are here this
2729 	 * always needs to be 1.
2730 	 */
2731 	if (slurm_atoul(row[1]) & CLUSTER_FLAG_CRAY_A)
2732 		*dims = 1;
2733 	else
2734 		*dims = atoi(row[0]);
2735 
2736 	mysql_free_result(result);
2737 
2738 	return SLURM_SUCCESS;
2739 }
2740 
acct_storage_p_get_connection(const slurm_trigger_callbacks_t * cb,int conn_num,uint16_t * persist_conn_flags,bool rollback,char * cluster_name)2741 extern void *acct_storage_p_get_connection(
2742 	const slurm_trigger_callbacks_t *cb,
2743 	int conn_num, uint16_t *persist_conn_flags,
2744 	bool rollback, char *cluster_name)
2745 {
2746 	mysql_conn_t *mysql_conn = NULL;
2747 
2748 	debug2("acct_storage_p_get_connection: request new connection %d",
2749 	       rollback);
2750 
2751 	if (!(mysql_conn = create_mysql_conn(
2752 		      conn_num, rollback, cluster_name))) {
2753 		fatal("couldn't get a mysql_conn");
2754 		return NULL;	/* Fix CLANG false positive error */
2755 	}
2756 
2757 	errno = SLURM_SUCCESS;
2758 	mysql_db_get_db_connection(mysql_conn, mysql_db_name, mysql_db_info);
2759 
2760 	if (mysql_conn->db_conn)
2761 		errno = SLURM_SUCCESS;
2762 
2763 	return (void *)mysql_conn;
2764 }
2765 
acct_storage_p_close_connection(mysql_conn_t ** mysql_conn)2766 extern int acct_storage_p_close_connection(mysql_conn_t **mysql_conn)
2767 {
2768 	int rc;
2769 
2770 	if (!mysql_conn || !(*mysql_conn))
2771 		return SLURM_SUCCESS;
2772 
2773 	acct_storage_p_commit((*mysql_conn), 0);
2774 	rc = destroy_mysql_conn(*mysql_conn);
2775 	*mysql_conn = NULL;
2776 
2777 	return rc;
2778 }
2779 
acct_storage_p_commit(mysql_conn_t * mysql_conn,bool commit)2780 extern int acct_storage_p_commit(mysql_conn_t *mysql_conn, bool commit)
2781 {
2782 	int rc = check_connection(mysql_conn);
2783 	List update_list = NULL;
2784 
2785 	/* always reset this here */
2786 	if (mysql_conn)
2787 		mysql_conn->cluster_deleted = 0;
2788 
2789 	if ((rc != SLURM_SUCCESS) && (rc != ESLURM_CLUSTER_DELETED))
2790 		return rc;
2791 	/*
2792 	 * We should never get here since check_connection will return
2793 	 * ESLURM_DB_CONNECTION when !mysql_conn, but Coverity doesn't
2794 	 * understand that. CID 44841.
2795 	 */
2796 	xassert(mysql_conn);
2797 
2798 	update_list = list_create(slurmdb_destroy_update_object);
2799 	list_transfer(update_list, mysql_conn->update_list);
2800 	debug4("got %d commits", list_count(update_list));
2801 
2802 	if (mysql_conn->rollback) {
2803 		if (!commit) {
2804 			if (mysql_db_rollback(mysql_conn))
2805 				error("rollback failed");
2806 		} else {
2807 			int rc = SLURM_SUCCESS;
2808 			/*
2809 			 * Handle anything here we were unable to do
2810 			 * because of rollback issues.
2811 			 */
2812 			if (mysql_conn->pre_commit_query) {
2813 				if (debug_flags & DEBUG_FLAG_DB_ASSOC)
2814 					DB_DEBUG(mysql_conn->conn, "query\n%s",
2815 						 mysql_conn->pre_commit_query);
2816 				rc = mysql_db_query(
2817 					mysql_conn,
2818 					mysql_conn->pre_commit_query);
2819 			}
2820 
2821 			if (rc != SLURM_SUCCESS) {
2822 				if (mysql_db_rollback(mysql_conn))
2823 					error("rollback failed");
2824 			} else {
2825 				if (mysql_db_commit(mysql_conn))
2826 					error("commit failed");
2827 			}
2828 		}
2829 	}
2830 
2831 	if (commit && list_count(update_list)) {
2832 		char *query = NULL;
2833 		MYSQL_RES *result = NULL;
2834 		MYSQL_ROW row;
2835 		ListIterator itr = NULL;
2836 		slurmdb_update_object_t *object = NULL;
2837 
2838 		xstrfmtcat(query, "select control_host, control_port, "
2839 			   "name, rpc_version, flags "
2840 			   "from %s where deleted=0 && control_port != 0",
2841 			   cluster_table);
2842 		if (!(result = mysql_db_query_ret(
2843 			      mysql_conn, query, 0))) {
2844 			xfree(query);
2845 			goto skip;
2846 		}
2847 		xfree(query);
2848 		while ((row = mysql_fetch_row(result))) {
2849 			if (slurm_atoul(row[4]) & CLUSTER_FLAG_EXT)
2850 				continue;
2851 			(void) slurmdb_send_accounting_update(
2852 				update_list,
2853 				row[2], row[0],
2854 				slurm_atoul(row[1]),
2855 				slurm_atoul(row[3]));
2856 		}
2857 		mysql_free_result(result);
2858 	skip:
2859 		(void) assoc_mgr_update(update_list, 0);
2860 
2861 		slurm_mutex_lock(&as_mysql_cluster_list_lock);
2862 		itr = list_iterator_create(update_list);
2863 		while ((object = list_next(itr))) {
2864 			if (!object->objects || !list_count(object->objects))
2865 				continue;
2866 			/* We only care about clusters removed here. */
2867 			switch (object->type) {
2868 			case SLURMDB_REMOVE_CLUSTER:
2869 			{
2870 				ListIterator rem_itr = NULL;
2871 				char *rem_cluster = NULL;
2872 				rem_itr = list_iterator_create(object->objects);
2873 				while ((rem_cluster = list_next(rem_itr))) {
2874 					list_delete_all(as_mysql_cluster_list,
2875 							slurm_find_char_in_list,
2876 							rem_cluster);
2877 				}
2878 				list_iterator_destroy(rem_itr);
2879 				break;
2880 			}
2881 			default:
2882 				break;
2883 			}
2884 		}
2885 		list_iterator_destroy(itr);
2886 		slurm_mutex_unlock(&as_mysql_cluster_list_lock);
2887 	}
2888 	xfree(mysql_conn->pre_commit_query);
2889 	FREE_NULL_LIST(update_list);
2890 
2891 	return SLURM_SUCCESS;
2892 }
2893 
acct_storage_p_add_users(mysql_conn_t * mysql_conn,uint32_t uid,List user_list)2894 extern int acct_storage_p_add_users(mysql_conn_t *mysql_conn, uint32_t uid,
2895 				    List user_list)
2896 {
2897 	return as_mysql_add_users(mysql_conn, uid, user_list);
2898 }
2899 
acct_storage_p_add_coord(mysql_conn_t * mysql_conn,uint32_t uid,List acct_list,slurmdb_user_cond_t * user_cond)2900 extern int acct_storage_p_add_coord(mysql_conn_t *mysql_conn, uint32_t uid,
2901 				    List acct_list,
2902 				    slurmdb_user_cond_t *user_cond)
2903 {
2904 	return as_mysql_add_coord(mysql_conn, uid, acct_list, user_cond);
2905 }
2906 
acct_storage_p_add_accts(mysql_conn_t * mysql_conn,uint32_t uid,List acct_list)2907 extern int acct_storage_p_add_accts(mysql_conn_t *mysql_conn, uint32_t uid,
2908 				    List acct_list)
2909 {
2910 	return as_mysql_add_accts(mysql_conn, uid, acct_list);
2911 }
2912 
acct_storage_p_add_clusters(mysql_conn_t * mysql_conn,uint32_t uid,List cluster_list)2913 extern int acct_storage_p_add_clusters(mysql_conn_t *mysql_conn, uint32_t uid,
2914 				       List cluster_list)
2915 {
2916 	return as_mysql_add_clusters(mysql_conn, uid, cluster_list);
2917 }
2918 
acct_storage_p_add_federations(mysql_conn_t * mysql_conn,uint32_t uid,List federation_list)2919 extern int acct_storage_p_add_federations(mysql_conn_t *mysql_conn,
2920 					  uint32_t uid, List federation_list)
2921 {
2922 	return as_mysql_add_federations(mysql_conn, uid, federation_list);
2923 }
2924 
acct_storage_p_add_tres(mysql_conn_t * mysql_conn,uint32_t uid,List tres_list_in)2925 extern int acct_storage_p_add_tres(mysql_conn_t *mysql_conn,
2926 				   uint32_t uid, List tres_list_in)
2927 {
2928 	return as_mysql_add_tres(mysql_conn, uid, tres_list_in);
2929 }
2930 
acct_storage_p_add_assocs(mysql_conn_t * mysql_conn,uint32_t uid,List assoc_list)2931 extern int acct_storage_p_add_assocs(mysql_conn_t *mysql_conn,
2932 				     uint32_t uid,
2933 				     List assoc_list)
2934 {
2935 	return as_mysql_add_assocs(mysql_conn, uid, assoc_list);
2936 }
2937 
acct_storage_p_add_qos(mysql_conn_t * mysql_conn,uint32_t uid,List qos_list)2938 extern int acct_storage_p_add_qos(mysql_conn_t *mysql_conn, uint32_t uid,
2939 				  List qos_list)
2940 {
2941 	return as_mysql_add_qos(mysql_conn, uid, qos_list);
2942 }
2943 
acct_storage_p_add_res(mysql_conn_t * mysql_conn,uint32_t uid,List res_list)2944 extern int acct_storage_p_add_res(mysql_conn_t *mysql_conn, uint32_t uid,
2945 				  List res_list)
2946 {
2947 	return as_mysql_add_res(mysql_conn, uid, res_list);
2948 }
2949 
acct_storage_p_add_wckeys(mysql_conn_t * mysql_conn,uint32_t uid,List wckey_list)2950 extern int acct_storage_p_add_wckeys(mysql_conn_t *mysql_conn, uint32_t uid,
2951 				     List wckey_list)
2952 {
2953 	return as_mysql_add_wckeys(mysql_conn, uid, wckey_list);
2954 }
2955 
acct_storage_p_add_reservation(mysql_conn_t * mysql_conn,slurmdb_reservation_rec_t * resv)2956 extern int acct_storage_p_add_reservation(mysql_conn_t *mysql_conn,
2957 					  slurmdb_reservation_rec_t *resv)
2958 {
2959 	return as_mysql_add_resv(mysql_conn, resv);
2960 }
2961 
acct_storage_p_modify_users(mysql_conn_t * mysql_conn,uint32_t uid,slurmdb_user_cond_t * user_cond,slurmdb_user_rec_t * user)2962 extern List acct_storage_p_modify_users(mysql_conn_t *mysql_conn, uint32_t uid,
2963 					slurmdb_user_cond_t *user_cond,
2964 					slurmdb_user_rec_t *user)
2965 {
2966 	return as_mysql_modify_users(mysql_conn, uid, user_cond, user);
2967 }
2968 
acct_storage_p_modify_accts(mysql_conn_t * mysql_conn,uint32_t uid,slurmdb_account_cond_t * acct_cond,slurmdb_account_rec_t * acct)2969 extern List acct_storage_p_modify_accts(mysql_conn_t *mysql_conn, uint32_t uid,
2970 					slurmdb_account_cond_t *acct_cond,
2971 					slurmdb_account_rec_t *acct)
2972 {
2973 	return as_mysql_modify_accts(mysql_conn, uid, acct_cond, acct);
2974 }
2975 
acct_storage_p_modify_clusters(mysql_conn_t * mysql_conn,uint32_t uid,slurmdb_cluster_cond_t * cluster_cond,slurmdb_cluster_rec_t * cluster)2976 extern List acct_storage_p_modify_clusters(mysql_conn_t *mysql_conn,
2977 					   uint32_t uid,
2978 					   slurmdb_cluster_cond_t *cluster_cond,
2979 					   slurmdb_cluster_rec_t *cluster)
2980 {
2981 	return as_mysql_modify_clusters(mysql_conn, uid, cluster_cond, cluster);
2982 }
2983 
acct_storage_p_modify_assocs(mysql_conn_t * mysql_conn,uint32_t uid,slurmdb_assoc_cond_t * assoc_cond,slurmdb_assoc_rec_t * assoc)2984 extern List acct_storage_p_modify_assocs(
2985 	mysql_conn_t *mysql_conn, uint32_t uid,
2986 	slurmdb_assoc_cond_t *assoc_cond,
2987 	slurmdb_assoc_rec_t *assoc)
2988 {
2989 	return as_mysql_modify_assocs(mysql_conn, uid, assoc_cond, assoc);
2990 }
2991 
acct_storage_p_modify_federations(mysql_conn_t * mysql_conn,uint32_t uid,slurmdb_federation_cond_t * fed_cond,slurmdb_federation_rec_t * fed)2992 extern List acct_storage_p_modify_federations(
2993 				mysql_conn_t *mysql_conn, uint32_t uid,
2994 				slurmdb_federation_cond_t *fed_cond,
2995 				slurmdb_federation_rec_t *fed)
2996 {
2997 	return as_mysql_modify_federations(mysql_conn, uid, fed_cond, fed);
2998 }
2999 
acct_storage_p_modify_job(mysql_conn_t * mysql_conn,uint32_t uid,slurmdb_job_cond_t * job_cond,slurmdb_job_rec_t * job)3000 extern List acct_storage_p_modify_job(mysql_conn_t *mysql_conn, uint32_t uid,
3001 				      slurmdb_job_cond_t *job_cond,
3002 				      slurmdb_job_rec_t *job)
3003 {
3004 	return as_mysql_modify_job(mysql_conn, uid, job_cond, job);
3005 }
3006 
acct_storage_p_modify_qos(mysql_conn_t * mysql_conn,uint32_t uid,slurmdb_qos_cond_t * qos_cond,slurmdb_qos_rec_t * qos)3007 extern List acct_storage_p_modify_qos(mysql_conn_t *mysql_conn, uint32_t uid,
3008 				      slurmdb_qos_cond_t *qos_cond,
3009 				      slurmdb_qos_rec_t *qos)
3010 {
3011 	return as_mysql_modify_qos(mysql_conn, uid, qos_cond, qos);
3012 }
3013 
acct_storage_p_modify_res(mysql_conn_t * mysql_conn,uint32_t uid,slurmdb_res_cond_t * res_cond,slurmdb_res_rec_t * res)3014 extern List acct_storage_p_modify_res(mysql_conn_t *mysql_conn,
3015 				      uint32_t uid,
3016 				      slurmdb_res_cond_t *res_cond,
3017 				      slurmdb_res_rec_t *res)
3018 {
3019 	return as_mysql_modify_res(mysql_conn, uid, res_cond, res);
3020 }
3021 
acct_storage_p_modify_wckeys(mysql_conn_t * mysql_conn,uint32_t uid,slurmdb_wckey_cond_t * wckey_cond,slurmdb_wckey_rec_t * wckey)3022 extern List acct_storage_p_modify_wckeys(mysql_conn_t *mysql_conn,
3023 					 uint32_t uid,
3024 					 slurmdb_wckey_cond_t *wckey_cond,
3025 					 slurmdb_wckey_rec_t *wckey)
3026 {
3027 	return as_mysql_modify_wckeys(mysql_conn, uid, wckey_cond, wckey);
3028 }
3029 
acct_storage_p_modify_reservation(mysql_conn_t * mysql_conn,slurmdb_reservation_rec_t * resv)3030 extern int acct_storage_p_modify_reservation(mysql_conn_t *mysql_conn,
3031 					     slurmdb_reservation_rec_t *resv)
3032 {
3033 	return as_mysql_modify_resv(mysql_conn, resv);
3034 }
3035 
acct_storage_p_remove_users(mysql_conn_t * mysql_conn,uint32_t uid,slurmdb_user_cond_t * user_cond)3036 extern List acct_storage_p_remove_users(mysql_conn_t *mysql_conn, uint32_t uid,
3037 					slurmdb_user_cond_t *user_cond)
3038 {
3039 	return as_mysql_remove_users(mysql_conn, uid, user_cond);
3040 }
3041 
acct_storage_p_remove_coord(mysql_conn_t * mysql_conn,uint32_t uid,List acct_list,slurmdb_user_cond_t * user_cond)3042 extern List acct_storage_p_remove_coord(mysql_conn_t *mysql_conn, uint32_t uid,
3043 					List acct_list,
3044 					slurmdb_user_cond_t *user_cond)
3045 {
3046 	return as_mysql_remove_coord(mysql_conn, uid, acct_list, user_cond);
3047 }
3048 
acct_storage_p_remove_accts(mysql_conn_t * mysql_conn,uint32_t uid,slurmdb_account_cond_t * acct_cond)3049 extern List acct_storage_p_remove_accts(mysql_conn_t *mysql_conn, uint32_t uid,
3050 					slurmdb_account_cond_t *acct_cond)
3051 {
3052 	return as_mysql_remove_accts(mysql_conn, uid, acct_cond);
3053 }
3054 
acct_storage_p_remove_clusters(mysql_conn_t * mysql_conn,uint32_t uid,slurmdb_cluster_cond_t * cluster_cond)3055 extern List acct_storage_p_remove_clusters(mysql_conn_t *mysql_conn,
3056 					   uint32_t uid,
3057 					   slurmdb_cluster_cond_t *cluster_cond)
3058 {
3059 	return as_mysql_remove_clusters(mysql_conn, uid, cluster_cond);
3060 }
3061 
acct_storage_p_remove_assocs(mysql_conn_t * mysql_conn,uint32_t uid,slurmdb_assoc_cond_t * assoc_cond)3062 extern List acct_storage_p_remove_assocs(
3063 	mysql_conn_t *mysql_conn, uint32_t uid,
3064 	slurmdb_assoc_cond_t *assoc_cond)
3065 {
3066 	return as_mysql_remove_assocs(mysql_conn, uid, assoc_cond);
3067 }
3068 
acct_storage_p_remove_federations(mysql_conn_t * mysql_conn,uint32_t uid,slurmdb_federation_cond_t * fed_cond)3069 extern List acct_storage_p_remove_federations(
3070 					mysql_conn_t *mysql_conn, uint32_t uid,
3071 					slurmdb_federation_cond_t *fed_cond)
3072 {
3073 	return as_mysql_remove_federations(mysql_conn, uid, fed_cond);
3074 }
3075 
acct_storage_p_remove_qos(mysql_conn_t * mysql_conn,uint32_t uid,slurmdb_qos_cond_t * qos_cond)3076 extern List acct_storage_p_remove_qos(mysql_conn_t *mysql_conn, uint32_t uid,
3077 				      slurmdb_qos_cond_t *qos_cond)
3078 {
3079 	return as_mysql_remove_qos(mysql_conn, uid, qos_cond);
3080 }
3081 
acct_storage_p_remove_res(mysql_conn_t * mysql_conn,uint32_t uid,slurmdb_res_cond_t * res_cond)3082 extern List acct_storage_p_remove_res(mysql_conn_t *mysql_conn,
3083 				      uint32_t uid,
3084 				      slurmdb_res_cond_t *res_cond)
3085 {
3086 	return as_mysql_remove_res(mysql_conn, uid, res_cond);
3087 }
3088 
acct_storage_p_remove_wckeys(mysql_conn_t * mysql_conn,uint32_t uid,slurmdb_wckey_cond_t * wckey_cond)3089 extern List acct_storage_p_remove_wckeys(mysql_conn_t *mysql_conn,
3090 					 uint32_t uid,
3091 					 slurmdb_wckey_cond_t *wckey_cond)
3092 {
3093 	return as_mysql_remove_wckeys(mysql_conn, uid, wckey_cond);
3094 }
3095 
acct_storage_p_remove_reservation(mysql_conn_t * mysql_conn,slurmdb_reservation_rec_t * resv)3096 extern int acct_storage_p_remove_reservation(mysql_conn_t *mysql_conn,
3097 					     slurmdb_reservation_rec_t *resv)
3098 {
3099 	return as_mysql_remove_resv(mysql_conn, resv);
3100 }
3101 
acct_storage_p_get_users(mysql_conn_t * mysql_conn,uid_t uid,slurmdb_user_cond_t * user_cond)3102 extern List acct_storage_p_get_users(mysql_conn_t *mysql_conn, uid_t uid,
3103 				     slurmdb_user_cond_t *user_cond)
3104 {
3105 	return as_mysql_get_users(mysql_conn, uid, user_cond);
3106 }
3107 
acct_storage_p_get_accts(mysql_conn_t * mysql_conn,uid_t uid,slurmdb_account_cond_t * acct_cond)3108 extern List acct_storage_p_get_accts(mysql_conn_t *mysql_conn, uid_t uid,
3109 				     slurmdb_account_cond_t *acct_cond)
3110 {
3111 	return as_mysql_get_accts(mysql_conn, uid, acct_cond);
3112 }
3113 
acct_storage_p_get_clusters(mysql_conn_t * mysql_conn,uid_t uid,slurmdb_cluster_cond_t * cluster_cond)3114 extern List acct_storage_p_get_clusters(mysql_conn_t *mysql_conn, uid_t uid,
3115 					slurmdb_cluster_cond_t *cluster_cond)
3116 {
3117 	return as_mysql_get_clusters(mysql_conn, uid, cluster_cond);
3118 }
3119 
acct_storage_p_get_federations(mysql_conn_t * mysql_conn,uid_t uid,slurmdb_federation_cond_t * fed_cond)3120 extern List acct_storage_p_get_federations(mysql_conn_t *mysql_conn, uid_t uid,
3121 					   slurmdb_federation_cond_t *fed_cond)
3122 {
3123 	return as_mysql_get_federations(mysql_conn, uid, fed_cond);
3124 }
3125 
acct_storage_p_get_tres(mysql_conn_t * mysql_conn,uid_t uid,slurmdb_tres_cond_t * tres_cond)3126 extern List acct_storage_p_get_tres(
3127 	mysql_conn_t *mysql_conn, uid_t uid,
3128 	slurmdb_tres_cond_t *tres_cond)
3129 {
3130 	return as_mysql_get_tres(mysql_conn, uid, tres_cond);
3131 }
3132 
acct_storage_p_get_assocs(mysql_conn_t * mysql_conn,uid_t uid,slurmdb_assoc_cond_t * assoc_cond)3133 extern List acct_storage_p_get_assocs(
3134 	mysql_conn_t *mysql_conn, uid_t uid,
3135 	slurmdb_assoc_cond_t *assoc_cond)
3136 {
3137 	return as_mysql_get_assocs(mysql_conn, uid, assoc_cond);
3138 }
3139 
acct_storage_p_get_events(mysql_conn_t * mysql_conn,uint32_t uid,slurmdb_event_cond_t * event_cond)3140 extern List acct_storage_p_get_events(mysql_conn_t *mysql_conn, uint32_t uid,
3141 				      slurmdb_event_cond_t *event_cond)
3142 {
3143 	return as_mysql_get_cluster_events(mysql_conn, uid, event_cond);
3144 }
3145 
acct_storage_p_get_problems(mysql_conn_t * mysql_conn,uint32_t uid,slurmdb_assoc_cond_t * assoc_cond)3146 extern List acct_storage_p_get_problems(mysql_conn_t *mysql_conn, uint32_t uid,
3147 					slurmdb_assoc_cond_t *assoc_cond)
3148 {
3149 	List ret_list = NULL;
3150 
3151 	if (check_connection(mysql_conn) != SLURM_SUCCESS)
3152 		return NULL;
3153 
3154 	if (!is_user_min_admin_level(mysql_conn, uid, SLURMDB_ADMIN_OPERATOR)) {
3155 		errno = ESLURM_ACCESS_DENIED;
3156 		return NULL;
3157 	}
3158 
3159 	ret_list = list_create(slurmdb_destroy_assoc_rec);
3160 
3161 	if (as_mysql_acct_no_assocs(mysql_conn, assoc_cond, ret_list)
3162 	    != SLURM_SUCCESS)
3163 		goto end_it;
3164 
3165 	if (as_mysql_acct_no_users(mysql_conn, assoc_cond, ret_list)
3166 	    != SLURM_SUCCESS)
3167 		goto end_it;
3168 
3169 	if (as_mysql_user_no_assocs_or_no_uid(mysql_conn, assoc_cond, ret_list)
3170 	    != SLURM_SUCCESS)
3171 		goto end_it;
3172 
3173 end_it:
3174 
3175 	return ret_list;
3176 }
3177 
acct_storage_p_get_config(void * db_conn,char * config_name)3178 extern List acct_storage_p_get_config(void *db_conn, char *config_name)
3179 {
3180 	return NULL;
3181 }
3182 
acct_storage_p_get_qos(mysql_conn_t * mysql_conn,uid_t uid,slurmdb_qos_cond_t * qos_cond)3183 extern List acct_storage_p_get_qos(mysql_conn_t *mysql_conn, uid_t uid,
3184 				   slurmdb_qos_cond_t *qos_cond)
3185 {
3186 	return as_mysql_get_qos(mysql_conn, uid, qos_cond);
3187 }
3188 
acct_storage_p_get_res(mysql_conn_t * mysql_conn,uid_t uid,slurmdb_res_cond_t * res_cond)3189 extern List acct_storage_p_get_res(mysql_conn_t *mysql_conn, uid_t uid,
3190 				   slurmdb_res_cond_t *res_cond)
3191 {
3192 	return as_mysql_get_res(mysql_conn, uid, res_cond);
3193 }
3194 
acct_storage_p_get_wckeys(mysql_conn_t * mysql_conn,uid_t uid,slurmdb_wckey_cond_t * wckey_cond)3195 extern List acct_storage_p_get_wckeys(mysql_conn_t *mysql_conn, uid_t uid,
3196 				      slurmdb_wckey_cond_t *wckey_cond)
3197 {
3198 	return as_mysql_get_wckeys(mysql_conn, uid, wckey_cond);
3199 }
3200 
acct_storage_p_get_reservations(mysql_conn_t * mysql_conn,uid_t uid,slurmdb_reservation_cond_t * resv_cond)3201 extern List acct_storage_p_get_reservations(
3202 	mysql_conn_t *mysql_conn, uid_t uid,
3203 	slurmdb_reservation_cond_t *resv_cond)
3204 {
3205 	return as_mysql_get_resvs(mysql_conn, uid, resv_cond);
3206 }
3207 
acct_storage_p_get_txn(mysql_conn_t * mysql_conn,uid_t uid,slurmdb_txn_cond_t * txn_cond)3208 extern List acct_storage_p_get_txn(mysql_conn_t *mysql_conn, uid_t uid,
3209 				   slurmdb_txn_cond_t *txn_cond)
3210 {
3211 	return as_mysql_get_txn(mysql_conn, uid, txn_cond);
3212 }
3213 
acct_storage_p_get_usage(mysql_conn_t * mysql_conn,uid_t uid,void * in,slurmdbd_msg_type_t type,time_t start,time_t end)3214 extern int acct_storage_p_get_usage(mysql_conn_t *mysql_conn, uid_t uid,
3215 				    void *in, slurmdbd_msg_type_t type,
3216 				    time_t start, time_t end)
3217 {
3218 	return as_mysql_get_usage(mysql_conn, uid, in, type, start, end);
3219 }
3220 
acct_storage_p_roll_usage(mysql_conn_t * mysql_conn,time_t sent_start,time_t sent_end,uint16_t archive_data,List * rollup_stats_list_in)3221 extern int acct_storage_p_roll_usage(mysql_conn_t *mysql_conn,
3222 				     time_t sent_start, time_t sent_end,
3223 				     uint16_t archive_data,
3224 				     List *rollup_stats_list_in)
3225 {
3226 	return as_mysql_roll_usage(mysql_conn, sent_start, sent_end,
3227 				   archive_data, rollup_stats_list_in);
3228 }
3229 
acct_storage_p_fix_runaway_jobs(void * db_conn,uint32_t uid,List jobs)3230 extern int acct_storage_p_fix_runaway_jobs(void *db_conn, uint32_t uid,
3231 					List jobs)
3232 {
3233 	return as_mysql_fix_runaway_jobs(db_conn, uid, jobs);
3234 }
3235 
clusteracct_storage_p_node_down(mysql_conn_t * mysql_conn,node_record_t * node_ptr,time_t event_time,char * reason,uint32_t reason_uid)3236 extern int clusteracct_storage_p_node_down(mysql_conn_t *mysql_conn,
3237 					   node_record_t *node_ptr,
3238 					   time_t event_time, char *reason,
3239 					   uint32_t reason_uid)
3240 {
3241 	return as_mysql_node_down(mysql_conn, node_ptr,
3242 				  event_time, reason, reason_uid);
3243 }
3244 
clusteracct_storage_p_node_up(mysql_conn_t * mysql_conn,node_record_t * node_ptr,time_t event_time)3245 extern int clusteracct_storage_p_node_up(mysql_conn_t *mysql_conn,
3246 					 node_record_t *node_ptr,
3247 					 time_t event_time)
3248 {
3249 	return as_mysql_node_up(mysql_conn, node_ptr, event_time);
3250 }
3251 
3252 /* This is only called when not running from the slurmdbd so we can
3253  * assumes some things like rpc_version.
3254  */
clusteracct_storage_p_register_ctld(mysql_conn_t * mysql_conn,uint16_t port)3255 extern int clusteracct_storage_p_register_ctld(mysql_conn_t *mysql_conn,
3256 					       uint16_t port)
3257 {
3258 	return as_mysql_register_ctld(
3259 		mysql_conn, mysql_conn->cluster_name, port);
3260 }
3261 
clusteracct_storage_p_register_disconn_ctld(mysql_conn_t * mysql_conn,char * control_host)3262 extern uint16_t clusteracct_storage_p_register_disconn_ctld(
3263 	mysql_conn_t *mysql_conn, char *control_host)
3264 {
3265 	uint16_t control_port = 0;
3266 	char *query = NULL;
3267 	MYSQL_RES *result = NULL;
3268 	MYSQL_ROW row;
3269 
3270 	if (check_connection(mysql_conn) != SLURM_SUCCESS)
3271 		return ESLURM_DB_CONNECTION;
3272 
3273 	if (!mysql_conn->cluster_name) {
3274 		error("%s:%d no cluster name", THIS_FILE, __LINE__);
3275 		return control_port;
3276 	} else if (!control_host) {
3277 		error("%s:%d no control host for cluster %s",
3278 		      THIS_FILE, __LINE__, mysql_conn->cluster_name);
3279 		return control_port;
3280 	}
3281 
3282 	query = xstrdup_printf("select last_port from %s where name='%s';",
3283 			       cluster_table, mysql_conn->cluster_name);
3284 	if (!(result = mysql_db_query_ret(mysql_conn, query, 0))) {
3285 		xfree(query);
3286 		error("register_disconn_ctld: no result given for cluster %s",
3287 		      mysql_conn->cluster_name);
3288 		return control_port;
3289 	}
3290 	xfree(query);
3291 
3292 	if ((row = mysql_fetch_row(result))) {
3293 		control_port = slurm_atoul(row[0]);
3294 		/* If there is ever a network issue talking to the DBD, and
3295 		   both the DBD and the ctrl stay up when the ctld goes to
3296 		   talk to the DBD again it may not re-register (<=2.2).
3297 		   Since the slurmctld didn't go down we can presume the port
3298 		   is still the same and just use the last information as the
3299 		   information we should use and go along our merry way.
3300 		*/
3301 		query = xstrdup_printf(
3302 			"update %s set control_host='%s', "
3303 			"control_port=%u where name='%s';",
3304 			cluster_table, control_host, control_port,
3305 			mysql_conn->cluster_name);
3306 		if (debug_flags & DEBUG_FLAG_DB_EVENT)
3307 			DB_DEBUG(mysql_conn->conn, "query\n%s", query);
3308 		if (mysql_db_query(mysql_conn, query) != SLURM_SUCCESS)
3309 			control_port = 0;
3310 		xfree(query);
3311 	}
3312 	mysql_free_result(result);
3313 
3314 	return control_port;
3315 }
3316 
clusteracct_storage_p_fini_ctld(mysql_conn_t * mysql_conn,slurmdb_cluster_rec_t * cluster_rec)3317 extern int clusteracct_storage_p_fini_ctld(mysql_conn_t *mysql_conn,
3318 					   slurmdb_cluster_rec_t *cluster_rec)
3319 {
3320 	if (check_connection(mysql_conn) != SLURM_SUCCESS)
3321 		return ESLURM_DB_CONNECTION;
3322 
3323 	if (!cluster_rec || (!mysql_conn->cluster_name && !cluster_rec->name)) {
3324 		error("%s:%d no cluster name", THIS_FILE, __LINE__);
3325 		return SLURM_ERROR;
3326 	}
3327 
3328 	if (!cluster_rec->name)
3329 		cluster_rec->name = mysql_conn->cluster_name;
3330 
3331 	return as_mysql_fini_ctld(mysql_conn, cluster_rec);
3332 }
3333 
clusteracct_storage_p_cluster_tres(mysql_conn_t * mysql_conn,char * cluster_nodes,char * tres_str_in,time_t event_time,uint16_t rpc_version)3334 extern int clusteracct_storage_p_cluster_tres(mysql_conn_t *mysql_conn,
3335 					      char *cluster_nodes,
3336 					      char *tres_str_in,
3337 					      time_t event_time,
3338 					      uint16_t rpc_version)
3339 {
3340 	return as_mysql_cluster_tres(mysql_conn,
3341 				     cluster_nodes, &tres_str_in,
3342 				     event_time, rpc_version);
3343 }
3344 
3345 /*
3346  * load into the storage the start of a job
3347  */
jobacct_storage_p_job_start(mysql_conn_t * mysql_conn,job_record_t * job_ptr)3348 extern int jobacct_storage_p_job_start(mysql_conn_t *mysql_conn,
3349 				       job_record_t *job_ptr)
3350 {
3351 	return as_mysql_job_start(mysql_conn, job_ptr);
3352 }
3353 
3354 /*
3355  * load into the storage the end of a job
3356  */
jobacct_storage_p_job_complete(mysql_conn_t * mysql_conn,job_record_t * job_ptr)3357 extern int jobacct_storage_p_job_complete(mysql_conn_t *mysql_conn,
3358 					  job_record_t *job_ptr)
3359 {
3360 	return as_mysql_job_complete(mysql_conn, job_ptr);
3361 }
3362 
3363 /*
3364  * load into the storage the start of a job step
3365  */
jobacct_storage_p_step_start(mysql_conn_t * mysql_conn,step_record_t * step_ptr)3366 extern int jobacct_storage_p_step_start(mysql_conn_t *mysql_conn,
3367 					step_record_t *step_ptr)
3368 {
3369 	return as_mysql_step_start(mysql_conn, step_ptr);
3370 }
3371 
3372 /*
3373  * load into the storage the end of a job step
3374  */
jobacct_storage_p_step_complete(mysql_conn_t * mysql_conn,step_record_t * step_ptr)3375 extern int jobacct_storage_p_step_complete(mysql_conn_t *mysql_conn,
3376 					   step_record_t *step_ptr)
3377 {
3378 	return as_mysql_step_complete(mysql_conn, step_ptr);
3379 }
3380 
3381 /*
3382  * load into the storage a suspension of a job
3383  */
jobacct_storage_p_suspend(mysql_conn_t * mysql_conn,job_record_t * job_ptr)3384 extern int jobacct_storage_p_suspend(mysql_conn_t *mysql_conn,
3385 				     job_record_t *job_ptr)
3386 {
3387 	return as_mysql_suspend(mysql_conn, 0, job_ptr);
3388 }
3389 
3390 /*
3391  * get info from the storage
3392  * returns List of job_rec_t *
3393  * note List needs to be freed when called
3394  */
jobacct_storage_p_get_jobs_cond(mysql_conn_t * mysql_conn,uid_t uid,slurmdb_job_cond_t * job_cond)3395 extern List jobacct_storage_p_get_jobs_cond(mysql_conn_t *mysql_conn,
3396 					    uid_t uid,
3397 					    slurmdb_job_cond_t *job_cond)
3398 {
3399 	List job_list = NULL;
3400 
3401 	if (check_connection(mysql_conn) != SLURM_SUCCESS) {
3402 		return NULL;
3403 	}
3404 	job_list = as_mysql_jobacct_process_get_jobs(mysql_conn, uid, job_cond);
3405 
3406 	return job_list;
3407 }
3408 
3409 /*
3410  * expire old info from the storage
3411  */
jobacct_storage_p_archive(mysql_conn_t * mysql_conn,slurmdb_archive_cond_t * arch_cond)3412 extern int jobacct_storage_p_archive(mysql_conn_t *mysql_conn,
3413 				     slurmdb_archive_cond_t *arch_cond)
3414 {
3415 	int rc;
3416 
3417 	if (check_connection(mysql_conn) != SLURM_SUCCESS)
3418 		return ESLURM_DB_CONNECTION;
3419 
3420 	/* Make sure only 1 archive is happening at a time. */
3421 	slurm_mutex_lock(&usage_rollup_lock);
3422 	rc = as_mysql_jobacct_process_archive(mysql_conn, arch_cond);
3423 	slurm_mutex_unlock(&usage_rollup_lock);
3424 
3425 	return rc;
3426 }
3427 
3428 /*
3429  * load old info into the storage
3430  */
jobacct_storage_p_archive_load(mysql_conn_t * mysql_conn,slurmdb_archive_rec_t * arch_rec)3431 extern int jobacct_storage_p_archive_load(mysql_conn_t *mysql_conn,
3432 					  slurmdb_archive_rec_t *arch_rec)
3433 {
3434 	if (check_connection(mysql_conn) != SLURM_SUCCESS)
3435 		return ESLURM_DB_CONNECTION;
3436 
3437 	return as_mysql_jobacct_process_archive_load(mysql_conn, arch_rec);
3438 }
3439 
acct_storage_p_update_shares_used(mysql_conn_t * mysql_conn,List shares_used)3440 extern int acct_storage_p_update_shares_used(mysql_conn_t *mysql_conn,
3441 					     List shares_used)
3442 {
3443 	/* No plans to have the database hold the used shares */
3444 	return SLURM_SUCCESS;
3445 }
3446 
acct_storage_p_flush_jobs_on_cluster(mysql_conn_t * mysql_conn,time_t event_time)3447 extern int acct_storage_p_flush_jobs_on_cluster(
3448 	mysql_conn_t *mysql_conn, time_t event_time)
3449 {
3450 	return as_mysql_flush_jobs_on_cluster(mysql_conn, event_time);
3451 }
3452 
acct_storage_p_reconfig(mysql_conn_t * mysql_conn,bool dbd)3453 extern int acct_storage_p_reconfig(mysql_conn_t *mysql_conn, bool dbd)
3454 {
3455 	debug_flags = slurm_get_debug_flags();
3456 	return SLURM_SUCCESS;
3457 }
3458 
acct_storage_p_reset_lft_rgt(mysql_conn_t * mysql_conn,uid_t uid,List cluster_list)3459 extern int acct_storage_p_reset_lft_rgt(mysql_conn_t *mysql_conn, uid_t uid,
3460 					List cluster_list)
3461 {
3462 	if (check_connection(mysql_conn) != SLURM_SUCCESS)
3463 		return ESLURM_DB_CONNECTION;
3464 
3465 	return as_mysql_reset_lft_rgt(mysql_conn, uid, cluster_list);
3466 }
3467 
acct_storage_p_get_stats(void * db_conn,bool dbd)3468 extern int acct_storage_p_get_stats(void *db_conn, bool dbd)
3469 {
3470 	return SLURM_SUCCESS;
3471 }
3472 
acct_storage_p_clear_stats(void * db_conn,bool dbd)3473 extern int acct_storage_p_clear_stats(void *db_conn, bool dbd)
3474 {
3475 	return SLURM_SUCCESS;
3476 }
3477 
acct_storage_p_get_data(void * db_conn,acct_storage_info_t dinfo,void * data)3478 extern int acct_storage_p_get_data(void *db_conn, acct_storage_info_t dinfo,
3479 				   void *data)
3480 {
3481 	return SLURM_SUCCESS;
3482 }
3483 
acct_storage_p_shutdown(void * db_conn,bool dbd)3484 extern int acct_storage_p_shutdown(void *db_conn, bool dbd)
3485 {
3486 	return SLURM_SUCCESS;
3487 }
3488