1 /*****************************************************************************\
2  *  as_mysql_cluster.c - functions dealing with clusters.
3  *****************************************************************************
4  *
5  *  Copyright (C) 2004-2007 The Regents of the University of California.
6  *  Copyright (C) 2008-2011 Lawrence Livermore National Security.
7  *  Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
8  *  Written by Danny Auble <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 
40 #include "as_mysql_tres.h"
41 #include "as_mysql_assoc.h"
42 #include "as_mysql_cluster.h"
43 #include "as_mysql_federation.h"
44 #include "as_mysql_usage.h"
45 #include "as_mysql_wckey.h"
46 #include "src/common/node_select.h"
47 
as_mysql_get_fed_cluster_id(mysql_conn_t * mysql_conn,const char * cluster,const char * federation,int last_id,int * ret_id)48 extern int as_mysql_get_fed_cluster_id(mysql_conn_t *mysql_conn,
49 				       const char *cluster,
50 				       const char *federation,
51 				       int last_id, int *ret_id)
52 {
53 	/* find id for cluster in federation.
54 	 * don't do anything if cluster is already part of federation
55 	 * get list of clusters that are part of the federration.
56 	 * loop through each cluster and find the first id available.
57 	 * report error if all are full in 63 slots. */
58 
59 	int        id     = 1;
60 	char      *query  = NULL;
61 	MYSQL_ROW  row;
62 	MYSQL_RES *result = NULL;
63 
64 	xassert(cluster);
65 	xassert(federation);
66 	xassert(ret_id);
67 
68 	/* See if cluster is already part of federation */
69 	xstrfmtcat(query, "SELECT name, fed_id "
70 			  "FROM %s "
71 			  "WHERE deleted=0 AND name='%s' AND federation='%s';",
72 		   cluster_table, cluster, federation);
73 	if (debug_flags & DEBUG_FLAG_FEDR)
74 		DB_DEBUG(mysql_conn->conn, "query\n%s", query);
75 	if (!(result = mysql_db_query_ret(mysql_conn, query, 0))) {
76 		xfree(query);
77 		error("no result given for %s", query);
78 		return SLURM_ERROR;
79 	}
80 	xfree(query);
81 	while ((row = mysql_fetch_row(result))) {
82 		int tmp_id = slurm_atoul(row[1]);
83 		if (debug_flags & DEBUG_FLAG_FEDR)
84 			info("cluster '%s' already part of federation '%s', "
85 			     "using existing id %d", cluster, federation,
86 			     tmp_id);
87 		mysql_free_result(result);
88 		*ret_id = tmp_id;
89 		return SLURM_SUCCESS;
90 	}
91 	mysql_free_result(result);
92 
93 	/* Get all other clusters in the federation and find an open id. */
94 	xstrfmtcat(query, "SELECT name, federation, fed_id "
95 		   	  "FROM %s "
96 		   	  "WHERE name!='%s' AND federation='%s' "
97 			  "AND fed_id > %d AND deleted=0 ORDER BY fed_id;",
98 			  cluster_table, cluster, federation, last_id);
99 	if (debug_flags & DEBUG_FLAG_FEDR)
100 		DB_DEBUG(mysql_conn->conn, "query\n%s", query);
101 	if (!(result = mysql_db_query_ret(mysql_conn, query, 0))) {
102 		xfree(query);
103 		error("no result given for %s", query);
104 		return SLURM_ERROR;
105 	}
106 	xfree(query);
107 
108 	if (last_id >= id)
109 		id = last_id + 1;
110 	while ((row = mysql_fetch_row(result))) {
111 		if (id != slurm_atoul(row[2]))
112 			break;
113 		id++;
114 	}
115 	mysql_free_result(result);
116 
117 	if (id > MAX_FED_CLUSTERS) {
118 		error("Too many clusters in this federation.");
119 		errno = ESLURM_FED_CLUSTER_MAX_CNT;
120 		return  ESLURM_FED_CLUSTER_MAX_CNT;
121 	}
122 
123 	*ret_id = id;
124 	return SLURM_SUCCESS;
125 }
126 
_setup_cluster_cond_limits(slurmdb_cluster_cond_t * cluster_cond,char ** extra)127 static int _setup_cluster_cond_limits(slurmdb_cluster_cond_t *cluster_cond,
128 				      char **extra)
129 {
130 	int set = 0;
131 	ListIterator itr = NULL;
132 	char *object = NULL;
133 
134 	if (!cluster_cond)
135 		return 0;
136 
137 	if (cluster_cond->with_deleted)
138 		xstrcat(*extra, " where (deleted=0 || deleted=1)");
139 	else
140 		xstrcat(*extra, " where deleted=0");
141 
142 	if (cluster_cond->cluster_list
143 	    && list_count(cluster_cond->cluster_list)) {
144 		set = 0;
145 		xstrcat(*extra, " && (");
146 		itr = list_iterator_create(cluster_cond->cluster_list);
147 		while ((object = list_next(itr))) {
148 			if (set)
149 				xstrcat(*extra, " || ");
150 			xstrfmtcat(*extra, "name='%s'", object);
151 			set = 1;
152 		}
153 		list_iterator_destroy(itr);
154 		xstrcat(*extra, ")");
155 	}
156 
157 	if (cluster_cond->federation_list
158 	    && list_count(cluster_cond->federation_list)) {
159 		set = 0;
160 		xstrcat(*extra, " && (");
161 		itr = list_iterator_create(cluster_cond->federation_list);
162 		while ((object = list_next(itr))) {
163 			if (set)
164 				xstrcat(*extra, " || ");
165 			xstrfmtcat(*extra, "federation='%s'", object);
166 			set = 1;
167 		}
168 		list_iterator_destroy(itr);
169 		xstrcat(*extra, ")");
170 	}
171 
172 	if (cluster_cond->plugin_id_select_list
173 	    && list_count(cluster_cond->plugin_id_select_list)) {
174 		set = 0;
175 		xstrcat(*extra, " && (");
176 		itr = list_iterator_create(cluster_cond->plugin_id_select_list);
177 		while ((object = list_next(itr))) {
178 			if (set)
179 				xstrcat(*extra, " || ");
180 			xstrfmtcat(*extra, "plugin_id_select='%s'", object);
181 			set = 1;
182 		}
183 		list_iterator_destroy(itr);
184 		xstrcat(*extra, ")");
185 	}
186 
187 	if (cluster_cond->rpc_version_list
188 	    && list_count(cluster_cond->rpc_version_list)) {
189 		set = 0;
190 		xstrcat(*extra, " && (");
191 		itr = list_iterator_create(cluster_cond->rpc_version_list);
192 		while ((object = list_next(itr))) {
193 			if (set)
194 				xstrcat(*extra, " || ");
195 			xstrfmtcat(*extra, "rpc_version='%s'", object);
196 			set = 1;
197 		}
198 		list_iterator_destroy(itr);
199 		xstrcat(*extra, ")");
200 	}
201 
202 	if (cluster_cond->classification) {
203 		xstrfmtcat(*extra, " && (classification & %u)",
204 			   cluster_cond->classification);
205 	}
206 
207 	if (cluster_cond->flags != NO_VAL) {
208 		xstrfmtcat(*extra, " && (flags & %u)",
209 			   cluster_cond->flags);
210 	}
211 
212 	return set;
213 }
214 
as_mysql_add_clusters(mysql_conn_t * mysql_conn,uint32_t uid,List cluster_list)215 extern int as_mysql_add_clusters(mysql_conn_t *mysql_conn, uint32_t uid,
216 				 List cluster_list)
217 {
218 	ListIterator itr = NULL;
219 	int rc = SLURM_SUCCESS;
220 	slurmdb_cluster_rec_t *object = NULL;
221 	char *cols = NULL, *vals = NULL, *extra = NULL,
222 		*query = NULL, *tmp_extra = NULL;
223 	time_t now = time(NULL);
224 	char *user_name = NULL;
225 	int affect_rows = 0;
226 	int added = 0;
227 	bool has_feds = false;
228 	List assoc_list = NULL;
229 	slurmdb_assoc_rec_t *assoc = NULL;
230 	bool external_cluster = false;
231 
232 	if (check_connection(mysql_conn) != SLURM_SUCCESS)
233 		return ESLURM_DB_CONNECTION;
234 
235 	if (!is_user_min_admin_level(mysql_conn, uid, SLURMDB_ADMIN_SUPER_USER))
236 		return ESLURM_ACCESS_DENIED;
237 
238 	assoc_list = list_create(slurmdb_destroy_assoc_rec);
239 
240 	user_name = uid_to_string((uid_t) uid);
241 	/* Since adding tables make it so you can't roll back, if
242 	   there is an error there is no way to easily remove entries
243 	   in the database, so we will create the tables first and
244 	   then after that works out then add them to the mix.
245 	*/
246 	itr = list_iterator_create(cluster_list);
247 	while ((object = list_next(itr))) {
248 		if (!object->name || !object->name[0]) {
249 			error("We need a cluster name to add.");
250 			rc = SLURM_ERROR;
251 			list_remove(itr);
252 			continue;
253 		}
254 		if ((object->flags != NO_VAL) &&
255 		    (object->flags & CLUSTER_FLAG_EXT))
256 			external_cluster = true;
257 		if ((rc = create_cluster_tables(mysql_conn,
258 						object->name))
259 		    != SLURM_SUCCESS) {
260 			added = 0;
261 			if (mysql_errno(mysql_conn->db_conn)
262 			    == ER_WRONG_TABLE_NAME)
263 				rc = ESLURM_BAD_NAME;
264 			goto end_it;
265 		}
266 	}
267 
268 	/* Now that all the tables were created successfully lets go
269 	   ahead and add it to the system.
270 	*/
271 	list_iterator_reset(itr);
272 	while ((object = list_next(itr))) {
273 		int fed_id = 0;
274 		uint16_t fed_state = CLUSTER_FED_STATE_NA;
275 		char *features = NULL;
276 		xstrcat(cols, "creation_time, mod_time, acct");
277 		xstrfmtcat(vals, "%ld, %ld, 'root'", now, now);
278 		xstrfmtcat(extra, ", mod_time=%ld", now);
279 		if (object->root_assoc) {
280 			rc = setup_assoc_limits(object->root_assoc, &cols,
281 						&vals, &extra,
282 						QOS_LEVEL_SET, 1);
283 			if (rc) {
284 				xfree(extra);
285 				xfree(cols);
286 				xfree(vals);
287 				added=0;
288 				error("%s: Failed, setup_assoc_limits functions returned error",
289 				      __func__);
290 				goto end_it;
291 
292 			}
293 		}
294 
295 		if (object->fed.name) {
296 			has_feds = 1;
297 			rc = as_mysql_get_fed_cluster_id(mysql_conn,
298 							 object->name,
299 							 object->fed.name, -1,
300 							 &fed_id);
301 			if (rc) {
302 				error("failed to get cluster id for "
303 				      "federation");
304 				xfree(extra);
305 				xfree(cols);
306 				xfree(vals);
307 				added=0;
308 				goto end_it;
309 			}
310 
311 			if (object->fed.state != NO_VAL)
312 				fed_state = object->fed.state;
313 			else
314 				fed_state = CLUSTER_FED_STATE_ACTIVE;
315 		}
316 
317 		if (object->fed.feature_list) {
318 			features =
319 				slurm_char_list_to_xstr(
320 						object->fed.feature_list);
321 			has_feds = 1;
322 		}
323 
324 		xstrfmtcat(query,
325 			   "insert into %s (creation_time, mod_time, "
326 			   "name, classification, federation, fed_id, "
327 			   "fed_state, features) "
328 			   "values (%ld, %ld, '%s', %u, '%s', %d, %u, '%s') "
329 			   "on duplicate key update deleted=0, mod_time=%ld, "
330 			   "control_host='', control_port=0, "
331 			   "classification=%u, flags=0, federation='%s', "
332 			   "fed_id=%d, fed_state=%u, features='%s'",
333 			   cluster_table,
334 			   now, now, object->name, object->classification,
335 			   (object->fed.name) ? object->fed.name : "",
336 			   fed_id, fed_state, (features) ? features : "",
337 			   now, object->classification,
338 			   (object->fed.name) ? object->fed.name : "",
339 			   fed_id, fed_state, (features) ? features : "");
340 		if (debug_flags & DEBUG_FLAG_DB_ASSOC)
341 			DB_DEBUG(mysql_conn->conn, "query\n%s", query);
342 		rc = mysql_db_query(mysql_conn, query);
343 		xfree(query);
344 		if (rc != SLURM_SUCCESS) {
345 			error("Couldn't add cluster %s", object->name);
346 			xfree(extra);
347 			xfree(cols);
348 			xfree(vals);
349 			xfree(features);
350 			added=0;
351 			break;
352 		}
353 
354 		affect_rows = last_affected_rows(mysql_conn);
355 
356 		if (!affect_rows) {
357 			debug2("nothing changed %d", affect_rows);
358 			xfree(extra);
359 			xfree(cols);
360 			xfree(vals);
361 			xfree(features);
362 			continue;
363 		}
364 
365 		if (!external_cluster) {
366 			/* Add root account */
367 			xstrfmtcat(query,
368 				   "insert into \"%s_%s\" (%s, lft, rgt) "
369 				   "values (%s, 1, 2) "
370 				   "on duplicate key update deleted=0, "
371 				   "id_assoc=LAST_INSERT_ID(id_assoc)%s;",
372 				   object->name, assoc_table, cols,
373 				   vals,
374 				   extra);
375 			xfree(cols);
376 			xfree(vals);
377 			if (debug_flags & DEBUG_FLAG_DB_ASSOC)
378 				DB_DEBUG(mysql_conn->conn, "query\n%s", query);
379 
380 			rc = mysql_db_query(mysql_conn, query);
381 			xfree(query);
382 
383 			if (rc != SLURM_SUCCESS) {
384 				error("Couldn't add cluster root assoc");
385 				xfree(extra);
386 				xfree(features);
387 				added=0;
388 				break;
389 			}
390 		} else {
391 			xfree(cols);
392 			xfree(vals);
393 		}
394 
395 		/* Build up extra with cluster specfic values for txn table */
396 		xstrfmtcat(extra, ", federation='%s', fed_id=%d, fed_state=%u, "
397 				  "features='%s'",
398 			   (object->fed.name) ? object->fed.name : "",
399 			   fed_id, fed_state, (features) ? features : "");
400 		xfree(features);
401 
402 		/* we always have a ', ' as the first 2 chars */
403 		tmp_extra = slurm_add_slash_to_quotes(extra+2);
404 
405 		xstrfmtcat(query,
406 			   "insert into %s "
407 			   "(timestamp, action, name, actor, info) "
408 			   "values (%ld, %u, '%s', '%s', '%s');",
409 			   txn_table, now, DBD_ADD_CLUSTERS,
410 			   object->name, user_name, tmp_extra);
411 		xfree(tmp_extra);
412 		xfree(extra);
413 		debug4("%d(%s:%d) query\n%s",
414 		       mysql_conn->conn, THIS_FILE, __LINE__, query);
415 
416 		rc = mysql_db_query(mysql_conn, query);
417 		xfree(query);
418 		if (rc != SLURM_SUCCESS) {
419 			error("Couldn't add txn");
420 		} else {
421 			ListIterator check_itr;
422 			char *tmp_name;
423 
424 			added++;
425 			/* add it to the list and sort */
426 			slurm_mutex_lock(&as_mysql_cluster_list_lock);
427 			check_itr = list_iterator_create(as_mysql_cluster_list);
428 			while ((tmp_name = list_next(check_itr))) {
429 				if (!xstrcmp(tmp_name, object->name))
430 					break;
431 			}
432 			list_iterator_destroy(check_itr);
433 			if (!tmp_name) {
434 				list_append(as_mysql_cluster_list,
435 					    xstrdup(object->name));
436 				list_sort(as_mysql_cluster_list,
437 					  (ListCmpF)slurm_sort_char_list_asc);
438 			} else
439 				error("Cluster %s(%s) appears to already be in "
440 				      "our cache list, not adding.", tmp_name,
441 				      object->name);
442 			slurm_mutex_unlock(&as_mysql_cluster_list_lock);
443 		}
444 
445 		if (!external_cluster) {
446 			/* Add user root by default to run from the root
447 			 * association.  This gets popped off so we need to
448 			 * read it every time here.
449 			 */
450 			assoc = xmalloc(sizeof(slurmdb_assoc_rec_t));
451 			slurmdb_init_assoc_rec(assoc, 0);
452 			list_append(assoc_list, assoc);
453 
454 			assoc->cluster = xstrdup(object->name);
455 			assoc->user = xstrdup("root");
456 			assoc->acct = xstrdup("root");
457 			assoc->is_def = 1;
458 
459 			if (as_mysql_add_assocs(mysql_conn, uid, assoc_list)
460 			    == SLURM_ERROR) {
461 				error("Problem adding root user association");
462 				rc = SLURM_ERROR;
463 			}
464 		}
465 	}
466 end_it:
467 	list_iterator_destroy(itr);
468 	xfree(user_name);
469 
470 	FREE_NULL_LIST(assoc_list);
471 
472 	if (!added)
473 		reset_mysql_conn(mysql_conn);
474 	else if (has_feds)
475 		as_mysql_add_feds_to_update_list(mysql_conn);
476 
477 	return rc;
478 }
479 
_reconcile_existing_features(void * object,void * arg)480 static int _reconcile_existing_features(void *object, void *arg)
481 {
482 	char *new_feature = (char *)object;
483 	List existing_features = (List)arg;
484 
485 	if (new_feature[0] == '-')
486 		list_delete_all(existing_features, slurm_find_char_in_list,
487 				new_feature + 1);
488 	else if (new_feature[0] == '+')
489 		list_append(existing_features, xstrdup(new_feature + 1));
490 	else
491 		list_append(existing_features, xstrdup(new_feature));
492 
493 	return SLURM_SUCCESS;
494 }
495 
as_mysql_modify_clusters(mysql_conn_t * mysql_conn,uint32_t uid,slurmdb_cluster_cond_t * cluster_cond,slurmdb_cluster_rec_t * cluster)496 extern List as_mysql_modify_clusters(mysql_conn_t *mysql_conn, uint32_t uid,
497 				     slurmdb_cluster_cond_t *cluster_cond,
498 				     slurmdb_cluster_rec_t *cluster)
499 {
500 	List ret_list = NULL;
501 	int rc = SLURM_SUCCESS;
502 	char *object = NULL;
503 	char *vals = NULL, *extra = NULL, *query = NULL, *name_char = NULL;
504 	time_t now = time(NULL);
505 	char *user_name = NULL;
506 	int set = 0;
507 	MYSQL_RES *result = NULL;
508 	MYSQL_ROW row;
509 	bool clust_reg = false, fed_update = false;
510 
511 	/* If you need to alter the default values of the cluster use
512 	 * modify_assocs since this is used only for registering
513 	 * the controller when it loads
514 	 */
515 
516 	if (!cluster_cond || !cluster) {
517 		error("we need something to change");
518 		return NULL;
519 	}
520 
521 	if (check_connection(mysql_conn) != SLURM_SUCCESS)
522 		return NULL;
523 
524 	if (!is_user_min_admin_level(mysql_conn, uid,
525 				     SLURMDB_ADMIN_SUPER_USER)) {
526 		errno = ESLURM_ACCESS_DENIED;
527 		return NULL;
528 	}
529 
530 	/* force to only do non-deleted clusters */
531 	cluster_cond->with_deleted = 0;
532 	_setup_cluster_cond_limits(cluster_cond, &extra);
533 
534 	/* Needed if talking to older Slurm versions < 2.2 */
535 	if (!mysql_conn->cluster_name && cluster_cond->cluster_list
536 	    && list_count(cluster_cond->cluster_list))
537 		mysql_conn->cluster_name =
538 			xstrdup(list_peek(cluster_cond->cluster_list));
539 
540 	set = 0;
541 	if (cluster->control_host) {
542 		xstrfmtcat(vals, ", control_host='%s'", cluster->control_host);
543 		set++;
544 		clust_reg = true;
545 	}
546 
547 	if (cluster->control_port) {
548 		xstrfmtcat(vals, ", control_port=%u, last_port=%u",
549 			   cluster->control_port, cluster->control_port);
550 		set++;
551 		clust_reg = true;
552 	}
553 
554 	if (cluster->rpc_version) {
555 		xstrfmtcat(vals, ", rpc_version=%u", cluster->rpc_version);
556 		set++;
557 		clust_reg = true;
558 	}
559 
560 	if (cluster->dimensions) {
561 		xstrfmtcat(vals, ", dimensions=%u", cluster->dimensions);
562 		clust_reg = true;
563 	}
564 
565 	if (cluster->plugin_id_select) {
566 		xstrfmtcat(vals, ", plugin_id_select=%u",
567 			   cluster->plugin_id_select);
568 		clust_reg = true;
569 	}
570 	if (cluster->flags != NO_VAL) {
571 		xstrfmtcat(vals, ", flags=%u", cluster->flags);
572 		clust_reg = true;
573 	}
574 
575 	if (cluster->classification) {
576 		xstrfmtcat(vals, ", classification=%u",
577 			   cluster->classification);
578 	}
579 
580 	if (cluster->fed.name) {
581 		xstrfmtcat(vals, ", federation='%s'", cluster->fed.name);
582 		fed_update = true;
583 	}
584 
585 	if (cluster->fed.state != NO_VAL) {
586 		xstrfmtcat(vals, ", fed_state=%u", cluster->fed.state);
587 		fed_update = true;
588 	}
589 
590 	if (!vals && !cluster->fed.feature_list) {
591 		xfree(extra);
592 		errno = SLURM_NO_CHANGE_IN_DATA;
593 		error("Nothing to change");
594 		return NULL;
595 	} else if (clust_reg && (set != 3)) {
596 		xfree(vals);
597 		xfree(extra);
598 		errno = EFAULT;
599 		error("Need control host, port and rpc version "
600 		      "to register a cluster");
601 		return NULL;
602 	}
603 
604 	xstrfmtcat(query, "select name, control_port, federation, features from %s%s;",
605 		   cluster_table, extra);
606 
607 	if (debug_flags & DEBUG_FLAG_DB_ASSOC)
608 		DB_DEBUG(mysql_conn->conn, "query\n%s", query);
609 	if (!(result = mysql_db_query_ret(
610 		      mysql_conn, query, 0))) {
611 		xfree(query);
612 		xfree(vals);
613 		error("no result given for %s", extra);
614 		xfree(extra);
615 		return NULL;
616 	}
617 	xfree(extra);
618 
619 	ret_list = list_create(xfree_ptr);
620 	user_name = uid_to_string((uid_t) uid);
621 	while ((row = mysql_fetch_row(result))) {
622 		char *tmp_vals = xstrdup(vals);
623 
624 		object = xstrdup(row[0]);
625 
626 		if (cluster->fed.name) {
627 			int id = 0;
628 			char *curr_fed = NULL;
629 			uint32_t set_state = NO_VAL;
630 
631 			if (cluster->fed.name[0] != '\0') {
632 				rc = as_mysql_get_fed_cluster_id(
633 							mysql_conn, object,
634 							cluster->fed.name, -1,
635 							&id);
636 				if (rc) {
637 					error("failed to get cluster id for "
638 					      "federation");
639 					xfree(tmp_vals);
640 					xfree(object);
641 					FREE_NULL_LIST(ret_list);
642 					mysql_free_result(result);
643 					goto end_it;
644 				}
645 			}
646 			/* will set fed_id=0 if being removed from fed. */
647 			xstrfmtcat(tmp_vals, ", fed_id=%d", id);
648 
649 			curr_fed = xstrdup(row[2]);
650 			if (cluster->fed.name[0] == '\0')
651 				/* clear fed_state when leaving federation */
652 				set_state = CLUSTER_FED_STATE_NA;
653 			else if (cluster->fed.state != NO_VAL) {
654 				/* NOOP: fed_state already set in vals */
655 			} else if (xstrcmp(curr_fed, cluster->fed.name))
656 				/* set state to active when joining fed * */
657 				set_state = CLUSTER_FED_STATE_ACTIVE;
658 			/* else use existing state */
659 
660 			if (set_state != NO_VAL)
661 				xstrfmtcat(tmp_vals, ", fed_state=%u",
662 					   set_state);
663 
664 			xfree(curr_fed);
665 		}
666 
667 		if (cluster->fed.feature_list) {
668 			if (!list_count(cluster->fed.feature_list)) {
669 				/* clear all existing features */
670 				xstrfmtcat(tmp_vals, ", features=''");
671 			} else {
672 				char *features = NULL, *feature = NULL;
673 				List existing_features = list_create(xfree_ptr);
674 
675 				if ((feature =
676 				     list_peek(cluster->fed.feature_list)) &&
677 				    (feature[0] == '+' || feature[0] == '-'))
678 					slurm_addto_char_list(existing_features,
679 							      row[3]);
680 
681 				list_for_each(cluster->fed.feature_list,
682 					      _reconcile_existing_features,
683 					      existing_features);
684 
685 				features =
686 					slurm_char_list_to_xstr(
687 							existing_features);
688 				xstrfmtcat(tmp_vals, ", features='%s'",
689 					   features ? features : "");
690 
691 				xfree(features);
692 				FREE_NULL_LIST(existing_features);
693 			}
694 
695 			fed_update = true;
696 		}
697 
698 		list_append(ret_list, object);
699 		xstrfmtcat(name_char, "name='%s'", object);
700 
701 		rc = modify_common(mysql_conn, DBD_MODIFY_CLUSTERS, now,
702 				   user_name, cluster_table,
703 				   name_char, tmp_vals, NULL);
704 		xfree(name_char);
705 		xfree(tmp_vals);
706 		if (rc == SLURM_ERROR) {
707 			error("Couldn't modify cluster 1");
708 			FREE_NULL_LIST(ret_list);
709 			mysql_free_result(result);
710 			goto end_it;
711 		}
712 	}
713 	mysql_free_result(result);
714 	xfree(user_name);
715 
716 	if (!list_count(ret_list)) {
717 		errno = SLURM_NO_CHANGE_IN_DATA;
718 		if (debug_flags & DEBUG_FLAG_DB_ASSOC)
719 			DB_DEBUG(mysql_conn->conn,
720 				 "didn't effect anything\n%s", query);
721 		xfree(name_char);
722 		xfree(vals);
723 		xfree(query);
724 		return ret_list;
725 	}
726 
727 	if (fed_update)
728 		as_mysql_add_feds_to_update_list(mysql_conn);
729 
730 end_it:
731 	xfree(query);
732 	xfree(vals);
733 	xfree(user_name);
734 
735 	return ret_list;
736 }
737 
as_mysql_remove_clusters(mysql_conn_t * mysql_conn,uint32_t uid,slurmdb_cluster_cond_t * cluster_cond)738 extern List as_mysql_remove_clusters(mysql_conn_t *mysql_conn, uint32_t uid,
739 				     slurmdb_cluster_cond_t *cluster_cond)
740 {
741 	ListIterator itr = NULL;
742 	List ret_list = NULL;
743 	List tmp_list = NULL;
744 	int rc = SLURM_SUCCESS;
745 	char *object = NULL;
746 	char *extra = NULL, *query = NULL, *cluster_name = NULL,
747 		*name_char = NULL, *assoc_char = NULL;
748 	time_t now = time(NULL);
749 	char *user_name = NULL;
750 	slurmdb_wckey_cond_t wckey_cond;
751 	MYSQL_RES *result = NULL;
752 	MYSQL_ROW row;
753 	bool jobs_running = 0, fed_update = false;
754 
755 	if (!cluster_cond) {
756 		error("we need something to change");
757 		return NULL;
758 	}
759 
760 	if (check_connection(mysql_conn) != SLURM_SUCCESS)
761 		return NULL;
762 
763 	if (!is_user_min_admin_level(
764 		    mysql_conn, uid, SLURMDB_ADMIN_SUPER_USER)) {
765 		errno = ESLURM_ACCESS_DENIED;
766 		return NULL;
767 	}
768 
769 	/* force to only do non-deleted clusters */
770 	cluster_cond->with_deleted = 0;
771 	_setup_cluster_cond_limits(cluster_cond, &extra);
772 
773 	if (!extra) {
774 		error("Nothing to remove");
775 		return NULL;
776 	}
777 
778 	query = xstrdup_printf("select name,federation from %s%s;",
779 			       cluster_table, extra);
780 	xfree(extra);
781 	if (!(result = mysql_db_query_ret(
782 		      mysql_conn, query, 0))) {
783 		xfree(query);
784 		return NULL;
785 	}
786 	rc = 0;
787 	ret_list = list_create(xfree_ptr);
788 
789 	if (!mysql_num_rows(result)) {
790 		mysql_free_result(result);
791 		errno = SLURM_NO_CHANGE_IN_DATA;
792 		if (debug_flags & DEBUG_FLAG_DB_ASSOC)
793 			DB_DEBUG(mysql_conn->conn,
794 				 "didn't effect anything\n%s", query);
795 		xfree(query);
796 		return ret_list;
797 	}
798 	xfree(query);
799 
800 	assoc_char = xstrdup_printf("t2.acct='root'");
801 
802 	user_name = uid_to_string((uid_t) uid);
803 	while ((row = mysql_fetch_row(result))) {
804 		char *object = xstrdup(row[0]);
805 		if (!jobs_running) {
806 			/* strdup the cluster name because ret_list will be
807 			 * flushed if there are running jobs. This will cause an
808 			 * invalid read because _check_jobs_before_remove() will
809 			 * still try to access "cluster_name" which was
810 			 * "object". */
811 			list_append(ret_list, xstrdup(object));
812 		}
813 
814 		if (row[1] && (*row[1] != '\0'))
815 			fed_update = true;
816 
817 		xfree(name_char);
818 		xstrfmtcat(name_char, "name='%s'", object);
819 		/* We should not need to delete any cluster usage just set it
820 		 * to deleted */
821 		xstrfmtcat(query,
822 			   "update \"%s_%s\" set time_end=%ld where time_end=0;"
823 			   "update \"%s_%s\" set mod_time=%ld, deleted=1;"
824 			   "update \"%s_%s\" set mod_time=%ld, deleted=1;"
825 			   "update \"%s_%s\" set mod_time=%ld, deleted=1;",
826 			   object, event_table, now,
827 			   object, cluster_day_table, now,
828 			   object, cluster_hour_table, now,
829 			   object, cluster_month_table, now);
830 		rc = remove_common(mysql_conn, DBD_REMOVE_CLUSTERS, now,
831 				   user_name, cluster_table, name_char,
832 				   assoc_char, object, ret_list, &jobs_running);
833 		xfree(object);
834 		if (rc != SLURM_SUCCESS)
835 			break;
836 	}
837 	mysql_free_result(result);
838 	xfree(user_name);
839 	xfree(name_char);
840 	xfree(assoc_char);
841 
842 	if (rc != SLURM_SUCCESS) {
843 		FREE_NULL_LIST(ret_list);
844 		xfree(query);
845 		return NULL;
846 	}
847 	if (!jobs_running) {
848 		if (debug_flags & DEBUG_FLAG_DB_ASSOC)
849 			DB_DEBUG(mysql_conn->conn, "query\n%s", query);
850 		rc = mysql_db_query(mysql_conn, query);
851 		xfree(query);
852 		if (rc != SLURM_SUCCESS) {
853 			reset_mysql_conn(mysql_conn);
854 			FREE_NULL_LIST(ret_list);
855 			return NULL;
856 		}
857 
858 		/* We need to remove these clusters from the wckey table */
859 		memset(&wckey_cond, 0, sizeof(slurmdb_wckey_cond_t));
860 		wckey_cond.cluster_list = ret_list;
861 		tmp_list = as_mysql_remove_wckeys(mysql_conn, uid, &wckey_cond);
862 		FREE_NULL_LIST(tmp_list);
863 
864 		itr = list_iterator_create(ret_list);
865 		while ((object = list_next(itr))) {
866 			if ((rc = remove_cluster_tables(mysql_conn, object))
867 			    != SLURM_SUCCESS)
868 				break;
869 			cluster_name = xstrdup(object);
870 			if (addto_update_list(mysql_conn->update_list,
871 					      SLURMDB_REMOVE_CLUSTER,
872 					      cluster_name) != SLURM_SUCCESS)
873 				xfree(cluster_name);
874 		}
875 		list_iterator_destroy(itr);
876 
877 		if (rc != SLURM_SUCCESS) {
878 			reset_mysql_conn(mysql_conn);
879 			FREE_NULL_LIST(ret_list);
880 			errno = rc;
881 			return NULL;
882 		}
883 
884 		if (fed_update)
885 			as_mysql_add_feds_to_update_list(mysql_conn);
886 
887 		errno = SLURM_SUCCESS;
888 	} else
889 		errno = ESLURM_JOBS_RUNNING_ON_ASSOC;
890 
891 	xfree(query);
892 
893 	return ret_list;
894 }
895 
as_mysql_get_clusters(mysql_conn_t * mysql_conn,uid_t uid,slurmdb_cluster_cond_t * cluster_cond)896 extern List as_mysql_get_clusters(mysql_conn_t *mysql_conn, uid_t uid,
897 				  slurmdb_cluster_cond_t *cluster_cond)
898 {
899 	char *query = NULL;
900 	char *extra = NULL;
901 	char *tmp = NULL;
902 	List cluster_list = NULL;
903 	ListIterator itr = NULL;
904 	int i=0;
905 	MYSQL_RES *result = NULL;
906 	MYSQL_ROW row;
907 	slurmdb_assoc_cond_t assoc_cond;
908 	ListIterator assoc_itr = NULL;
909 	slurmdb_cluster_rec_t *cluster = NULL;
910 	slurmdb_assoc_rec_t *assoc = NULL;
911 	List assoc_list = NULL;
912 
913 	/* if this changes you will need to edit the corresponding enum */
914 	char *cluster_req_inx[] = {
915 		"name",
916 		"classification",
917 		"control_host",
918 		"control_port",
919 		"features",
920 		"federation",
921 		"fed_id",
922 		"fed_state",
923 		"rpc_version",
924 		"dimensions",
925 		"flags",
926 		"plugin_id_select"
927 	};
928 	enum {
929 		CLUSTER_REQ_NAME,
930 		CLUSTER_REQ_CLASS,
931 		CLUSTER_REQ_CH,
932 		CLUSTER_REQ_CP,
933 		CLUSTER_REQ_FEATURES,
934 		CLUSTER_REQ_FEDR,
935 		CLUSTER_REQ_FEDID,
936 		CLUSTER_REQ_FEDSTATE,
937 		CLUSTER_REQ_VERSION,
938 		CLUSTER_REQ_DIMS,
939 		CLUSTER_REQ_FLAGS,
940 		CLUSTER_REQ_PI_SELECT,
941 		CLUSTER_REQ_COUNT
942 	};
943 
944 	if (check_connection(mysql_conn) != SLURM_SUCCESS)
945 		return NULL;
946 
947 
948 	if (!cluster_cond) {
949 		xstrcat(extra, " where deleted=0");
950 		goto empty;
951 	}
952 
953 	_setup_cluster_cond_limits(cluster_cond, &extra);
954 
955 empty:
956 
957 	xfree(tmp);
958 	i=0;
959 	xstrfmtcat(tmp, "%s", cluster_req_inx[i]);
960 	for(i=1; i<CLUSTER_REQ_COUNT; i++) {
961 		xstrfmtcat(tmp, ", %s", cluster_req_inx[i]);
962 	}
963 
964 	query = xstrdup_printf("select %s from %s%s",
965 			       tmp, cluster_table, extra);
966 	xfree(tmp);
967 	xfree(extra);
968 
969 	if (debug_flags & DEBUG_FLAG_DB_ASSOC)
970 		DB_DEBUG(mysql_conn->conn, "query\n%s", query);
971 	if (!(result = mysql_db_query_ret(
972 		      mysql_conn, query, 0))) {
973 		xfree(query);
974 		return NULL;
975 	}
976 	xfree(query);
977 
978 	cluster_list = list_create(slurmdb_destroy_cluster_rec);
979 
980 	memset(&assoc_cond, 0, sizeof(slurmdb_assoc_cond_t));
981 
982 	if (cluster_cond) {
983 		/* I don't think we want the with_usage flag here.
984 		 * We do need the with_deleted though. */
985 		//assoc_cond.with_usage = cluster_cond->with_usage;
986 		assoc_cond.with_deleted = cluster_cond->with_deleted;
987 	}
988 	assoc_cond.cluster_list = list_create(NULL);
989 
990 	while ((row = mysql_fetch_row(result))) {
991 		MYSQL_RES *result2 = NULL;
992 		MYSQL_ROW row2;
993 		char *features = NULL;
994 		cluster = xmalloc(sizeof(slurmdb_cluster_rec_t));
995 		slurmdb_init_cluster_rec(cluster, 0);
996 		list_append(cluster_list, cluster);
997 
998 		cluster->name = xstrdup(row[CLUSTER_REQ_NAME]);
999 
1000 		list_append(assoc_cond.cluster_list, cluster->name);
1001 
1002 		cluster->classification = slurm_atoul(row[CLUSTER_REQ_CLASS]);
1003 		cluster->control_host = xstrdup(row[CLUSTER_REQ_CH]);
1004 		cluster->control_port = slurm_atoul(row[CLUSTER_REQ_CP]);
1005 		cluster->fed.name     = xstrdup(row[CLUSTER_REQ_FEDR]);
1006 		features              = row[CLUSTER_REQ_FEATURES];
1007 		if (features && *features) {
1008 			cluster->fed.feature_list = list_create(xfree_ptr);
1009 			slurm_addto_char_list(cluster->fed.feature_list,
1010 					      features);
1011 		}
1012 		cluster->fed.id       = slurm_atoul(row[CLUSTER_REQ_FEDID]);
1013 		cluster->fed.state    = slurm_atoul(row[CLUSTER_REQ_FEDSTATE]);
1014 		cluster->rpc_version = slurm_atoul(row[CLUSTER_REQ_VERSION]);
1015 		cluster->dimensions = slurm_atoul(row[CLUSTER_REQ_DIMS]);
1016 		cluster->flags = slurm_atoul(row[CLUSTER_REQ_FLAGS]);
1017 		cluster->plugin_id_select =
1018 			slurm_atoul(row[CLUSTER_REQ_PI_SELECT]);
1019 
1020 		query = xstrdup_printf(
1021 			"select tres, cluster_nodes from "
1022 			"\"%s_%s\" where time_end=0 and node_name='' limit 1",
1023 			cluster->name, event_table);
1024 		if (debug_flags & DEBUG_FLAG_DB_TRES)
1025 			DB_DEBUG(mysql_conn->conn, "query\n%s", query);
1026 		if (!(result2 = mysql_db_query_ret(mysql_conn, query, 0))) {
1027 			xfree(query);
1028 			continue;
1029 		}
1030 		xfree(query);
1031 		if ((row2 = mysql_fetch_row(result2))) {
1032 			cluster->tres_str = xstrdup(row2[0]);
1033 			if (row2[1] && row2[1][0])
1034 				cluster->nodes = xstrdup(row2[1]);
1035 		}
1036 		mysql_free_result(result2);
1037 
1038 		/* get the usage if requested */
1039 		if (cluster_cond && cluster_cond->with_usage) {
1040 			as_mysql_get_usage(
1041 				mysql_conn, uid, cluster,
1042 				DBD_GET_CLUSTER_USAGE,
1043 				cluster_cond->usage_start,
1044 				cluster_cond->usage_end);
1045 		}
1046 
1047 	}
1048 	mysql_free_result(result);
1049 
1050 	if (!list_count(assoc_cond.cluster_list)) {
1051 		FREE_NULL_LIST(assoc_cond.cluster_list);
1052 		return cluster_list;
1053 	}
1054 
1055 	assoc_cond.acct_list = list_create(NULL);
1056 	list_append(assoc_cond.acct_list, "root");
1057 
1058 	assoc_cond.user_list = list_create(NULL);
1059 	list_append(assoc_cond.user_list, "");
1060 
1061 	assoc_list = as_mysql_get_assocs(mysql_conn, uid, &assoc_cond);
1062 	FREE_NULL_LIST(assoc_cond.cluster_list);
1063 	FREE_NULL_LIST(assoc_cond.acct_list);
1064 	FREE_NULL_LIST(assoc_cond.user_list);
1065 
1066 	if (!assoc_list)
1067 		return cluster_list;
1068 
1069 	itr = list_iterator_create(cluster_list);
1070 	assoc_itr = list_iterator_create(assoc_list);
1071 	while ((cluster = list_next(itr))) {
1072 		while ((assoc = list_next(assoc_itr))) {
1073 			if (xstrcmp(assoc->cluster, cluster->name))
1074 				continue;
1075 
1076 			if (cluster->root_assoc) {
1077 				debug("This cluster %s already has "
1078 				      "an association.", cluster->name);
1079 				continue;
1080 			}
1081 			cluster->root_assoc = assoc;
1082 			list_remove(assoc_itr);
1083 		}
1084 		list_iterator_reset(assoc_itr);
1085 	}
1086 	list_iterator_destroy(itr);
1087 	list_iterator_destroy(assoc_itr);
1088 	if (list_count(assoc_list))
1089 		error("I have %d left over associations",
1090 		      list_count(assoc_list));
1091 	FREE_NULL_LIST(assoc_list);
1092 
1093 	return cluster_list;
1094 }
1095 
as_mysql_get_cluster_events(mysql_conn_t * mysql_conn,uint32_t uid,slurmdb_event_cond_t * event_cond)1096 extern List as_mysql_get_cluster_events(mysql_conn_t *mysql_conn, uint32_t uid,
1097 					slurmdb_event_cond_t *event_cond)
1098 {
1099 	char *query = NULL;
1100 	char *extra = NULL;
1101 	char *tmp = NULL;
1102 	List ret_list = NULL;
1103 	ListIterator itr = NULL;
1104 	char *object = NULL;
1105 	int set = 0;
1106 	int i=0;
1107 	MYSQL_RES *result = NULL;
1108 	MYSQL_ROW row;
1109 	uint16_t private_data = 0;
1110 	time_t now = time(NULL);
1111 	List use_cluster_list = as_mysql_cluster_list;
1112 	slurmdb_user_rec_t user;
1113 
1114 	/* if this changes you will need to edit the corresponding enum */
1115 	char *event_req_inx[] = {
1116 		"cluster_nodes",
1117 		"node_name",
1118 		"state",
1119 		"time_start",
1120 		"time_end",
1121 		"reason",
1122 		"reason_uid",
1123 		"tres",
1124 	};
1125 
1126 	enum {
1127 		EVENT_REQ_CNODES,
1128 		EVENT_REQ_NODE,
1129 		EVENT_REQ_STATE,
1130 		EVENT_REQ_START,
1131 		EVENT_REQ_END,
1132 		EVENT_REQ_REASON,
1133 		EVENT_REQ_REASON_UID,
1134 		EVENT_REQ_TRES,
1135 		EVENT_REQ_COUNT
1136 	};
1137 
1138 	if (check_connection(mysql_conn) != SLURM_SUCCESS)
1139 		return NULL;
1140 
1141 	memset(&user, 0, sizeof(slurmdb_user_rec_t));
1142 	user.uid = uid;
1143 
1144 	private_data = slurm_get_private_data();
1145 
1146 	if (private_data & PRIVATE_DATA_EVENTS) {
1147 		if (!is_user_min_admin_level(
1148 			      mysql_conn, uid, SLURMDB_ADMIN_OPERATOR)) {
1149 			error("UID %u tried to access events, only administrators can look at events",
1150 			      uid);
1151 			errno = ESLURM_ACCESS_DENIED;
1152 			return NULL;
1153 		}
1154 	}
1155 
1156 	if (!event_cond)
1157 		goto empty;
1158 
1159 	if (event_cond->cpus_min) {
1160 		if (extra)
1161 			xstrcat(extra, " && (");
1162 		else
1163 			xstrcat(extra, " where (");
1164 
1165 		if (event_cond->cpus_max) {
1166 			xstrfmtcat(extra, "count between %u and %u)",
1167 				   event_cond->cpus_min, event_cond->cpus_max);
1168 
1169 		} else {
1170 			xstrfmtcat(extra, "count='%u')",
1171 				   event_cond->cpus_min);
1172 
1173 		}
1174 	}
1175 
1176 	switch(event_cond->event_type) {
1177 	case SLURMDB_EVENT_ALL:
1178 		break;
1179 	case SLURMDB_EVENT_CLUSTER:
1180 		if (extra)
1181 			xstrcat(extra, " && (");
1182 		else
1183 			xstrcat(extra, " where (");
1184 		xstrcat(extra, "node_name = '')");
1185 
1186 		break;
1187 	case SLURMDB_EVENT_NODE:
1188 		if (extra)
1189 			xstrcat(extra, " && (");
1190 		else
1191 			xstrcat(extra, " where (");
1192 		xstrcat(extra, "node_name != '')");
1193 
1194 		break;
1195 	default:
1196 		error("Unknown event %u doing all", event_cond->event_type);
1197 		break;
1198 	}
1199 
1200 	if (event_cond->node_list) {
1201 		int dims = 0;
1202 		hostlist_t temp_hl = NULL;
1203 
1204 		if (get_cluster_dims(mysql_conn,
1205 				     (char *)list_peek(event_cond->cluster_list),
1206 				     &dims))
1207 			return NULL;
1208 
1209 		temp_hl = hostlist_create_dims(event_cond->node_list, dims);
1210 		if (hostlist_count(temp_hl) <= 0) {
1211 			error("we didn't get any real hosts to look for.");
1212 			return NULL;
1213 		}
1214 
1215 		set = 0;
1216 		if (extra)
1217 			xstrcat(extra, " && (");
1218 		else
1219 			xstrcat(extra, " where (");
1220 
1221 		while ((object = hostlist_shift(temp_hl))) {
1222 			if (set)
1223 				xstrcat(extra, " || ");
1224 			xstrfmtcat(extra, "node_name='%s'", object);
1225 			set = 1;
1226 			free(object);
1227 		}
1228 		xstrcat(extra, ")");
1229 		hostlist_destroy(temp_hl);
1230 	}
1231 
1232 	if (event_cond->period_start) {
1233 		if (!event_cond->period_end)
1234 			event_cond->period_end = now;
1235 
1236 		if (extra)
1237 			xstrcat(extra, " && (");
1238 		else
1239 			xstrcat(extra, " where (");
1240 
1241 		xstrfmtcat(extra,
1242 			   "(time_start < %ld) "
1243 			   "&& (time_end >= %ld || time_end = 0))",
1244 			   event_cond->period_end, event_cond->period_start);
1245 	}
1246 
1247 	if (event_cond->reason_list
1248 	    && list_count(event_cond->reason_list)) {
1249 		set = 0;
1250 		if (extra)
1251 			xstrcat(extra, " && (");
1252 		else
1253 			xstrcat(extra, " where (");
1254 		itr = list_iterator_create(event_cond->reason_list);
1255 		while ((object = list_next(itr))) {
1256 			if (set)
1257 				xstrcat(extra, " || ");
1258 			xstrfmtcat(extra, "reason like '%%%s%%'", object);
1259 			set = 1;
1260 		}
1261 		list_iterator_destroy(itr);
1262 		xstrcat(extra, ")");
1263 	}
1264 
1265 	if (event_cond->reason_uid_list
1266 	    && list_count(event_cond->reason_uid_list)) {
1267 		set = 0;
1268 		if (extra)
1269 			xstrcat(extra, " && (");
1270 		else
1271 			xstrcat(extra, " where (");
1272 		itr = list_iterator_create(event_cond->reason_uid_list);
1273 		while ((object = list_next(itr))) {
1274 			if (set)
1275 				xstrcat(extra, " || ");
1276 			xstrfmtcat(extra, "reason_uid='%s'", object);
1277 			set = 1;
1278 		}
1279 		list_iterator_destroy(itr);
1280 		xstrcat(extra, ")");
1281 	}
1282 
1283 	if (event_cond->state_list
1284 	    && list_count(event_cond->state_list)) {
1285 		set = 0;
1286 		if (extra)
1287 			xstrcat(extra, " && (");
1288 		else
1289 			xstrcat(extra, " where (");
1290 		itr = list_iterator_create(event_cond->state_list);
1291 		while ((object = list_next(itr))) {
1292 			uint32_t tmp_state = strtol(object, NULL, 10);
1293 			if (set)
1294 				xstrcat(extra, " || ");
1295 			if (tmp_state & NODE_STATE_BASE)
1296 				xstrfmtcat(extra, "(state&%u)=%u",
1297 					   NODE_STATE_BASE,
1298 					   tmp_state & NODE_STATE_BASE);
1299 			else
1300 				xstrfmtcat(extra, "state&%u", tmp_state);
1301 			set = 1;
1302 		}
1303 		list_iterator_destroy(itr);
1304 		xstrcat(extra, ")");
1305 	}
1306 
1307 	if (event_cond->cluster_list && list_count(event_cond->cluster_list))
1308 		use_cluster_list = event_cond->cluster_list;
1309 empty:
1310 	xfree(tmp);
1311 	xstrfmtcat(tmp, "%s", event_req_inx[0]);
1312 	for(i=1; i<EVENT_REQ_COUNT; i++) {
1313 		xstrfmtcat(tmp, ", %s", event_req_inx[i]);
1314 	}
1315 
1316 	if (use_cluster_list == as_mysql_cluster_list)
1317 		slurm_mutex_lock(&as_mysql_cluster_list_lock);
1318 
1319 	ret_list = list_create(slurmdb_destroy_event_rec);
1320 
1321 	itr = list_iterator_create(use_cluster_list);
1322 	while ((object = list_next(itr))) {
1323 		query = xstrdup_printf("select %s from \"%s_%s\"",
1324 				       tmp, object, event_table);
1325 		if (extra)
1326 			xstrfmtcat(query, " %s", extra);
1327 
1328 		if (debug_flags & DEBUG_FLAG_DB_ASSOC)
1329 			DB_DEBUG(mysql_conn->conn, "query\n%s", query);
1330 		if (!(result = mysql_db_query_ret(
1331 			      mysql_conn, query, 0))) {
1332 			xfree(query);
1333 			if (mysql_errno(mysql_conn->db_conn)
1334 			    != ER_NO_SUCH_TABLE) {
1335 				FREE_NULL_LIST(ret_list);
1336 				ret_list = NULL;
1337 			}
1338 			break;
1339 		}
1340 		xfree(query);
1341 
1342 		while ((row = mysql_fetch_row(result))) {
1343 			slurmdb_event_rec_t *event =
1344 				xmalloc(sizeof(slurmdb_event_rec_t));
1345 
1346 			list_append(ret_list, event);
1347 
1348 			event->cluster = xstrdup(object);
1349 
1350 			if (row[EVENT_REQ_NODE] && row[EVENT_REQ_NODE][0]) {
1351 				event->node_name = xstrdup(row[EVENT_REQ_NODE]);
1352 				event->event_type = SLURMDB_EVENT_NODE;
1353 			} else
1354 				event->event_type = SLURMDB_EVENT_CLUSTER;
1355 
1356 			event->state = slurm_atoul(row[EVENT_REQ_STATE]);
1357 			event->period_start = slurm_atoul(row[EVENT_REQ_START]);
1358 			event->period_end = slurm_atoul(row[EVENT_REQ_END]);
1359 
1360 			if (row[EVENT_REQ_REASON] && row[EVENT_REQ_REASON][0])
1361 				event->reason = xstrdup(row[EVENT_REQ_REASON]);
1362 			event->reason_uid =
1363 				slurm_atoul(row[EVENT_REQ_REASON_UID]);
1364 
1365 			if (row[EVENT_REQ_CNODES] && row[EVENT_REQ_CNODES][0])
1366 				event->cluster_nodes =
1367 					xstrdup(row[EVENT_REQ_CNODES]);
1368 
1369 			if (row[EVENT_REQ_TRES] && row[EVENT_REQ_TRES][0])
1370 				event->tres_str = xstrdup(row[EVENT_REQ_TRES]);
1371 		}
1372 		mysql_free_result(result);
1373 	}
1374 	list_iterator_destroy(itr);
1375 	xfree(tmp);
1376 	xfree(extra);
1377 
1378 	if (use_cluster_list == as_mysql_cluster_list)
1379 		slurm_mutex_unlock(&as_mysql_cluster_list_lock);
1380 
1381 	return ret_list;
1382 }
1383 
as_mysql_node_down(mysql_conn_t * mysql_conn,node_record_t * node_ptr,time_t event_time,char * reason,uint32_t reason_uid)1384 extern int as_mysql_node_down(mysql_conn_t *mysql_conn,
1385 			      node_record_t *node_ptr,
1386 			      time_t event_time, char *reason,
1387 			      uint32_t reason_uid)
1388 {
1389 	int rc = SLURM_SUCCESS;
1390 	char *query = NULL;
1391 	char *my_reason;
1392 	MYSQL_RES *result = NULL;
1393 	MYSQL_ROW row;
1394 
1395 	if (check_connection(mysql_conn) != SLURM_SUCCESS)
1396 		return ESLURM_DB_CONNECTION;
1397 
1398 	if (!mysql_conn->cluster_name) {
1399 		error("%s:%d no cluster name", THIS_FILE, __LINE__);
1400 		return SLURM_ERROR;
1401 	}
1402 
1403 	if (!node_ptr) {
1404 		error("No node_ptr given!");
1405 		return SLURM_ERROR;
1406 	}
1407 
1408 	if (!node_ptr->tres_str) {
1409 		error("node ptr has no tres_list!");
1410 		return SLURM_ERROR;
1411 	}
1412 
1413 	query = xstrdup_printf("select state, reason, time_start from \"%s_%s\" where "
1414 			       "time_end=0 and node_name='%s';",
1415 			       mysql_conn->cluster_name, event_table,
1416 			       node_ptr->name);
1417 	/* info("%d(%s:%d) query\n%s", */
1418 	/*        mysql_conn->conn, THIS_FILE, __LINE__, query); */
1419 	result = mysql_db_query_ret(mysql_conn, query, 0);
1420 	xfree(query);
1421 
1422 	if (!result)
1423 		return SLURM_ERROR;
1424 
1425 	if (reason)
1426 		my_reason = reason;
1427 	else
1428 		my_reason = node_ptr->reason;
1429 
1430 	if (!my_reason)
1431 		my_reason = "";
1432 
1433 	row = mysql_fetch_row(result);
1434 	if (row && (node_ptr->node_state == slurm_atoul(row[0])) &&
1435 	    !xstrcasecmp(my_reason, row[1])) {
1436 		if (debug_flags & DEBUG_FLAG_DB_EVENT)
1437 			DB_DEBUG(mysql_conn->conn,
1438 				 "no change to %s(%s) needed %u == %s and %s == %s",
1439 				 node_ptr->name, mysql_conn->cluster_name,
1440 				 node_ptr->node_state, row[0],
1441 				 my_reason, row[1]);
1442 		mysql_free_result(result);
1443 		return SLURM_SUCCESS;
1444 	}
1445 
1446 	if (row && (event_time == slurm_atoul(row[2]))) {
1447 		/*
1448 		 * If you are clean-restarting the controller over and over
1449 		 * again you could get records that are duplicates in the
1450 		 * database. If this is the case we will zero out the time_end
1451 		 * we are just filled in. This will cause the last time to be
1452 		 * erased from the last restart, but if you are restarting
1453 		 * things this often the pervious one didn't mean anything
1454 		 * anyway. This way we only get one for the last time we let it
1455 		 * run.
1456 		 */
1457 		query = xstrdup_printf(
1458 			"update \"%s_%s\" set reason='%s' where "
1459 			"time_start=%ld and node_name='%s';",
1460 			mysql_conn->cluster_name, event_table,
1461 			my_reason, event_time, node_ptr->name);
1462 		if (debug_flags & DEBUG_FLAG_DB_EVENT)
1463 			DB_DEBUG(mysql_conn->conn, "query\n%s", query);
1464 		rc = mysql_db_query(mysql_conn, query);
1465 		xfree(query);
1466 
1467 		mysql_free_result(result);
1468 		return rc;
1469 	}
1470 
1471 	mysql_free_result(result);
1472 
1473 	if (debug_flags & DEBUG_FLAG_DB_EVENT)
1474 		DB_DEBUG(mysql_conn->conn,
1475 			 "inserting %s(%s) with tres of '%s'",
1476 			 node_ptr->name, mysql_conn->cluster_name,
1477 			 node_ptr->tres_str);
1478 
1479 	query = xstrdup_printf(
1480 		"update \"%s_%s\" set time_end=%ld where "
1481 		"time_end=0 and node_name='%s';",
1482 		mysql_conn->cluster_name, event_table,
1483 		event_time, node_ptr->name);
1484 	xstrfmtcat(query,
1485 		   "insert into \"%s_%s\" "
1486 		   "(node_name, state, tres, time_start, "
1487 		   "reason, reason_uid) "
1488 		   "values ('%s', %u, '%s', %ld, '%s', %u);",
1489 		   mysql_conn->cluster_name, event_table,
1490 		   node_ptr->name, node_ptr->node_state,
1491 		   node_ptr->tres_str, event_time, my_reason, reason_uid);
1492 	if (debug_flags & DEBUG_FLAG_DB_EVENT)
1493 		DB_DEBUG(mysql_conn->conn, "query\n%s", query);
1494 	rc = mysql_db_query(mysql_conn, query);
1495 	xfree(query);
1496 
1497 	return rc;
1498 }
1499 
as_mysql_node_up(mysql_conn_t * mysql_conn,node_record_t * node_ptr,time_t event_time)1500 extern int as_mysql_node_up(mysql_conn_t *mysql_conn,
1501 			    node_record_t *node_ptr,
1502 			    time_t event_time)
1503 {
1504 	char* query;
1505 	int rc = SLURM_SUCCESS;
1506 
1507 	if (check_connection(mysql_conn) != SLURM_SUCCESS)
1508 		return ESLURM_DB_CONNECTION;
1509 
1510 	if (!mysql_conn->cluster_name) {
1511 		error("%s:%d no cluster name", THIS_FILE, __LINE__);
1512 		return SLURM_ERROR;
1513 	}
1514 
1515 	query = xstrdup_printf(
1516 		"update \"%s_%s\" set time_end=%ld where "
1517 		"time_end=0 and node_name='%s';",
1518 		mysql_conn->cluster_name, event_table,
1519 		event_time, node_ptr->name);
1520 	if (debug_flags & DEBUG_FLAG_DB_EVENT)
1521 		DB_DEBUG(mysql_conn->conn, "query\n%s", query);
1522 	rc = mysql_db_query(mysql_conn, query);
1523 	xfree(query);
1524 	return rc;
1525 }
1526 
1527 /* This function is not used in the slurmdbd. */
as_mysql_register_ctld(mysql_conn_t * mysql_conn,char * cluster,uint16_t port)1528 extern int as_mysql_register_ctld(mysql_conn_t *mysql_conn,
1529 				  char *cluster, uint16_t port)
1530 {
1531 	return SLURM_ERROR;
1532 }
1533 
as_mysql_fini_ctld(mysql_conn_t * mysql_conn,slurmdb_cluster_rec_t * cluster_rec)1534 extern int as_mysql_fini_ctld(mysql_conn_t *mysql_conn,
1535 			      slurmdb_cluster_rec_t *cluster_rec)
1536 {
1537 	int rc = SLURM_SUCCESS;
1538 	time_t now = time(NULL);
1539 	char *query = NULL;
1540 	bool free_it = false;
1541 
1542 	if (check_connection(mysql_conn) != SLURM_SUCCESS)
1543 		return ESLURM_DB_CONNECTION;
1544 
1545 	/* Here we need to check make sure we are updating the entry
1546 	   correctly just in case the backup has already gained
1547 	   control.  If we check the ip and port it is a pretty safe
1548 	   bet we have the right ctld.
1549 	*/
1550 	query = xstrdup_printf(
1551 		"update %s set mod_time=%ld, control_host='', "
1552 		"control_port=0 where name='%s' && "
1553 		"control_host='%s' && control_port=%u;",
1554 		cluster_table, now, cluster_rec->name,
1555 		cluster_rec->control_host, cluster_rec->control_port);
1556 	if (debug_flags & DEBUG_FLAG_DB_EVENT)
1557 		DB_DEBUG(mysql_conn->conn, "query\n%s", query);
1558 	rc = mysql_db_query(mysql_conn, query);
1559 	xfree(query);
1560 
1561 	if (rc != SLURM_SUCCESS)
1562 		return SLURM_ERROR;
1563 
1564 	if (!last_affected_rows(mysql_conn) || !slurmdbd_conf->track_ctld)
1565 		return rc;
1566 
1567 	/* If tres is NULL we can get the current number of tres by
1568 	   sending NULL for the tres param in the as_mysql_cluster_tres
1569 	   function.
1570 	*/
1571 	if (!cluster_rec->tres_str) {
1572 		free_it = true;
1573 		as_mysql_cluster_tres(
1574 			mysql_conn, cluster_rec->control_host,
1575 			&cluster_rec->tres_str, now,
1576 			cluster_rec->rpc_version);
1577 	}
1578 
1579 	/* Since as_mysql_cluster_tres could change the
1580 	   last_affected_rows we can't group this with the above
1581 	   return.
1582 	*/
1583 	if (!cluster_rec->tres_str)
1584 		return rc;
1585 
1586 	/* If we affected things we need to now drain the nodes in the
1587 	 * cluster.  This is to give better stats on accounting that
1588 	 * the ctld was gone so no jobs were able to be scheduled.  We
1589 	 * drain the nodes since the rollup functionality understands
1590 	 * how to deal with that and running jobs so we don't get bad
1591 	 * info.
1592 	 */
1593 	query = xstrdup_printf(
1594 		"insert into \"%s_%s\" (tres, state, time_start, reason) "
1595 		"values ('%s', %u, %ld, 'slurmctld disconnect');",
1596 		cluster_rec->name, event_table,
1597 		cluster_rec->tres_str, NODE_STATE_DOWN, (long)now);
1598 
1599 	if (free_it)
1600 		xfree(cluster_rec->tres_str);
1601 
1602 	if (debug_flags & DEBUG_FLAG_DB_EVENT)
1603 		DB_DEBUG(mysql_conn->conn, "query\n%s", query);
1604 	rc = mysql_db_query(mysql_conn, query);
1605 	xfree(query);
1606 
1607 	return rc;
1608 }
1609 
as_mysql_cluster_tres(mysql_conn_t * mysql_conn,char * cluster_nodes,char ** tres_str_in,time_t event_time,uint16_t rpc_version)1610 extern int as_mysql_cluster_tres(mysql_conn_t *mysql_conn,
1611 				 char *cluster_nodes, char **tres_str_in,
1612 				 time_t event_time, uint16_t rpc_version)
1613 {
1614 	char* query;
1615 	int rc = SLURM_SUCCESS;
1616 	int response = 0;
1617 	MYSQL_RES *result = NULL;
1618 	MYSQL_ROW row;
1619 	bool handle_disconnect = true;
1620 
1621 	xassert(tres_str_in);
1622 
1623  	if (check_connection(mysql_conn) != SLURM_SUCCESS)
1624 		return ESLURM_DB_CONNECTION;
1625 
1626 	if (!mysql_conn->cluster_name) {
1627 		error("%s:%d no cluster name", THIS_FILE, __LINE__);
1628 		return SLURM_ERROR;
1629 	}
1630 
1631 	/* Record the processor count */
1632 	query = xstrdup_printf(
1633 		"select tres, cluster_nodes from \"%s_%s\" where "
1634 		"time_end=0 and node_name='' and state=0 limit 1",
1635 		mysql_conn->cluster_name, event_table);
1636 	if (!(result = mysql_db_query_ret(mysql_conn, query, 0))) {
1637 		xfree(query);
1638 		if (mysql_errno(mysql_conn->db_conn) == ER_NO_SUCH_TABLE)
1639 			rc = ESLURM_ACCESS_DENIED;
1640 		else
1641 			rc = SLURM_ERROR;
1642 		return rc;
1643 	}
1644 	xfree(query);
1645 
1646 	/* we only are checking the first one here */
1647 	if (!(row = mysql_fetch_row(result))) {
1648 		debug("We don't have an entry for this machine %s "
1649 		      "most likely a first time running.",
1650 		      mysql_conn->cluster_name);
1651 
1652 		/* Get all nodes in a down state and jobs pending or running.
1653 		 * This is for the first time a cluster registers
1654 		 *
1655 		 * We will return ACCOUNTING_FIRST_REG so this
1656 		 * is taken care of since the message thread
1657 		 * may not be up when we run this in the controller or
1658 		 * in the slurmdbd.
1659 		 */
1660 		if (!*tres_str_in) {
1661 			rc = 0;
1662 			goto end_it;
1663 		}
1664 
1665 		response = ACCOUNTING_FIRST_REG;
1666 		goto add_it;
1667 	}
1668 
1669 	/* If tres is NULL we want to return the tres for this cluster */
1670 	if (!*tres_str_in) {
1671 		*tres_str_in = xstrdup(row[0]);
1672 		goto end_it;
1673 	} else if (xstrcmp(*tres_str_in, row[0])) {
1674 		debug("%s has changed tres from %s to %s",
1675 		      mysql_conn->cluster_name,
1676 		      row[0], *tres_str_in);
1677 
1678 		/*
1679 		 * Reset all the entries for this cluster since the tres changed
1680 		 * some of the downed nodes may have gone away.
1681 		 * Request them again with ACCOUNTING_NODES_CHANGE_DB
1682 		 */
1683 
1684 		if (xstrcmp(cluster_nodes, row[1])) {
1685 			if (debug_flags & DEBUG_FLAG_DB_EVENT)
1686 				DB_DEBUG(mysql_conn->conn,
1687 					 "Nodes on the cluster have changed.");
1688 			response = ACCOUNTING_NODES_CHANGE_DB;
1689 		} else
1690 			response = ACCOUNTING_TRES_CHANGE_DB;
1691 	} else if (xstrcmp(cluster_nodes, row[1])) {
1692 		if (debug_flags & DEBUG_FLAG_DB_EVENT)
1693 			DB_DEBUG(mysql_conn->conn,
1694 				 "Node names on the cluster have changed.");
1695 		response = ACCOUNTING_NODES_CHANGE_DB;
1696 	} else {
1697 		if (debug_flags & DEBUG_FLAG_DB_EVENT)
1698 			DB_DEBUG(mysql_conn->conn,
1699 				 "We have the same TRES and node names as before for %s, no need to update the database.",
1700 				 mysql_conn->cluster_name);
1701 		goto remove_disconnect;
1702 	}
1703 
1704 	query = xstrdup_printf(
1705 		"update \"%s_%s\" set time_end=%ld where time_end=0",
1706 		mysql_conn->cluster_name, event_table, event_time);
1707 
1708 	rc = mysql_db_query(mysql_conn, query);
1709 	xfree(query);
1710 	handle_disconnect = false;
1711 
1712 	if (rc != SLURM_SUCCESS)
1713 		goto end_it;
1714 add_it:
1715 	query = xstrdup_printf(
1716 		"insert into \"%s_%s\" (cluster_nodes, tres, "
1717 		"time_start, reason) "
1718 		"values ('%s', '%s', %ld, 'Cluster Registered TRES');",
1719 		mysql_conn->cluster_name, event_table,
1720 		cluster_nodes, *tres_str_in, event_time);
1721 
1722 	rc = mysql_db_query(mysql_conn, query);
1723 	xfree(query);
1724 
1725 	if (rc != SLURM_SUCCESS)
1726 		goto end_it;
1727 
1728 remove_disconnect:
1729 	/*
1730 	 * The above update clears all with time_end=0, so no
1731 	 * need to do this again.
1732 	 */
1733 	if (handle_disconnect) {
1734 		query = xstrdup_printf(
1735 			"update \"%s_%s\" set time_end=%ld where time_end=0 and state=%u and node_name='';",
1736 			mysql_conn->cluster_name,
1737 			event_table, event_time,
1738 			NODE_STATE_DOWN);
1739 		(void) mysql_db_query(mysql_conn, query);
1740 		xfree(query);
1741 	}
1742 
1743 end_it:
1744 	mysql_free_result(result);
1745 	if (response && rc == SLURM_SUCCESS)
1746 		rc = response;
1747 
1748 	return rc;
1749 }
1750