1 /*****************************************************************************\
2  *  node_conf.c - partially manage the node records of slurm
3  *                (see src/slurmctld/node_mgr.c for the set of functionalities
4  *                 related to slurmctld usage of nodes)
5  *	Note: there is a global node table (node_record_table_ptr), its
6  *	hash table (node_hash_table), time stamp (last_node_update) and
7  *	configuration list (config_list)
8  *****************************************************************************
9  *  Copyright (C) 2002-2007 The Regents of the University of California.
10  *  Copyright (C) 2008-2010 Lawrence Livermore National Security.
11  *  Copyright (C) 2010-2017 SchedMD LLC.
12  *  Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
13  *  Written by Morris Jette <jette1@llnl.gov> et. al.
14  *  CODE-OCEC-09-009. All rights reserved.
15  *
16  *  This file is part of Slurm, a resource management program.
17  *  For details, see <https://slurm.schedmd.com/>.
18  *  Please also read the included file: DISCLAIMER.
19  *
20  *  Slurm is free software; you can redistribute it and/or modify it under
21  *  the terms of the GNU General Public License as published by the Free
22  *  Software Foundation; either version 2 of the License, or (at your option)
23  *  any later version.
24  *
25  *  In addition, as a special exception, the copyright holders give permission
26  *  to link the code of portions of this program with the OpenSSL library under
27  *  certain conditions as described in each individual source file, and
28  *  distribute linked combinations including the two. You must obey the GNU
29  *  General Public License in all respects for all of the code used other than
30  *  OpenSSL. If you modify file(s) with this exception, you may extend this
31  *  exception to your version of the file(s), but you are not obligated to do
32  *  so. If you do not wish to do so, delete this exception statement from your
33  *  version.  If you delete this exception statement from all source files in
34  *  the program, then also delete it here.
35  *
36  *  Slurm is distributed in the hope that it will be useful, but WITHOUT ANY
37  *  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
38  *  FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
39  *  details.
40  *
41  *  You should have received a copy of the GNU General Public License along
42  *  with Slurm; if not, write to the Free Software Foundation, Inc.,
43  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA.
44 \*****************************************************************************/
45 
46 #include "config.h"
47 
48 #include <ctype.h>
49 #include <errno.h>
50 #include <fcntl.h>
51 #include <inttypes.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <sys/types.h>
56 #include <sys/stat.h>
57 #include <time.h>
58 
59 #include "src/common/assoc_mgr.h"
60 #include "src/common/gres.h"
61 #include "src/common/hostlist.h"
62 #include "src/common/macros.h"
63 #include "src/common/node_select.h"
64 #include "src/common/pack.h"
65 #include "src/common/parse_time.h"
66 #include "src/common/read_config.h"
67 #include "src/common/slurm_accounting_storage.h"
68 #include "src/common/slurm_acct_gather_energy.h"
69 #include "src/common/slurm_ext_sensors.h"
70 #include "src/common/slurm_topology.h"
71 #include "src/common/xassert.h"
72 #include "src/common/xmalloc.h"
73 #include "src/common/xstring.h"
74 
75 #define _DEBUG 0
76 
77 strong_alias(init_node_conf, slurm_init_node_conf);
78 strong_alias(build_all_nodeline_info, slurm_build_all_nodeline_info);
79 strong_alias(rehash_node, slurm_rehash_node);
80 strong_alias(hostlist2bitmap, slurm_hostlist2bitmap);
81 
82 /* Global variables */
83 List config_list  = NULL;	/* list of config_record entries */
84 List front_end_list = NULL;	/* list of slurm_conf_frontend_t entries */
85 time_t last_node_update = (time_t) 0;	/* time of last update */
86 node_record_t *node_record_table_ptr = NULL;	/* node records */
87 xhash_t* node_hash_table = NULL;
88 int node_record_count = 0;		/* count in node_record_table_ptr */
89 uint16_t *cr_node_num_cores = NULL;
90 uint32_t *cr_node_cores_offset = NULL;
91 
92 /* Local function definitions */
93 static int	_delete_config_record (void);
94 #if _DEBUG
95 static void	_dump_hash (void);
96 #endif
97 static node_record_t *_find_node_record(char *name, bool test_alias,
98 					bool log_missing);
99 static void	_list_delete_config (void *config_entry);
100 static void _node_record_hash_identity (void* item, const char** key,
101 					uint32_t* key_len);
102 
103 /*
104  * _delete_config_record - delete all configuration records
105  * RET 0 if no error, errno otherwise
106  * global: config_list - list of all configuration records
107  */
_delete_config_record(void)108 static int _delete_config_record (void)
109 {
110 	last_node_update = time (NULL);
111 	list_flush(config_list);
112 	list_flush(front_end_list);
113 
114 	return SLURM_SUCCESS;
115 }
116 
117 
118 #if _DEBUG
119 /*
120  * helper function used by _dump_hash to print the hash table elements
121  */
xhash_walk_helper_cbk(void * item,void * arg)122 static void xhash_walk_helper_cbk (void* item, void* arg)
123 {
124 	static int i = 0; /* sequential walk, so just update a static i */
125 	int inx;
126 	node_record_t *node_ptr = (node_record_t *) item;
127 
128 	inx = node_ptr -  node_record_table_ptr;
129 	debug3("node_hash[%d]:%d(%s)", i++, inx, node_ptr->name);
130 }
131 /*
132  * _dump_hash - print the node_hash_table contents, used for debugging
133  *	or analysis of hash technique
134  * global: node_record_table_ptr - pointer to global node table
135  *         node_hash_table - table of hash indexes
136  */
_dump_hash(void)137 static void _dump_hash (void)
138 {
139 	if (node_hash_table == NULL)
140 		return;
141 	debug2("node_hash: indexing %ld elements",
142 	      xhash_count(node_hash_table));
143 	xhash_walk(node_hash_table, xhash_walk_helper_cbk, NULL);
144 }
145 #endif
146 
147 /* _list_delete_config - delete an entry from the config list,
148  *	see list.h for documentation */
_list_delete_config(void * config_entry)149 static void _list_delete_config (void *config_entry)
150 {
151 	config_record_t *config_ptr = (config_record_t *) config_entry;
152 
153 	xassert(config_ptr);
154 	xassert(config_ptr->magic == CONFIG_MAGIC);
155 	xfree(config_ptr->cpu_spec_list);
156 	xfree(config_ptr->feature);
157 	xfree(config_ptr->gres);
158 	xfree (config_ptr->nodes);
159 	FREE_NULL_BITMAP (config_ptr->node_bitmap);
160 	xfree(config_ptr->tres_weights);
161 	xfree(config_ptr->tres_weights_str);
162 	xfree (config_ptr);
163 }
164 
165 /*
166  * xhash helper function to index node_record per name field
167  * in node_hash_table
168  */
_node_record_hash_identity(void * item,const char ** key,uint32_t * key_len)169 static void _node_record_hash_identity (void* item, const char** key,
170 					uint32_t* key_len)
171 {
172 	node_record_t *node_ptr = (node_record_t *) item;
173 	*key = node_ptr->name;
174 	*key_len = strlen(node_ptr->name);
175 }
176 
177 /*
178  * bitmap2hostlist - given a bitmap, build a hostlist
179  * IN bitmap - bitmap pointer
180  * RET pointer to hostlist or NULL on error
181  * globals: node_record_table_ptr - pointer to node table
182  * NOTE: the caller must xfree the memory at node_list when no longer required
183  */
bitmap2hostlist(bitstr_t * bitmap)184 hostlist_t bitmap2hostlist (bitstr_t *bitmap)
185 {
186 	int i, first, last;
187 	hostlist_t hl;
188 
189 	if (bitmap == NULL)
190 		return NULL;
191 
192 	first = bit_ffs(bitmap);
193 	if (first == -1)
194 		return NULL;
195 
196 	last  = bit_fls(bitmap);
197 	hl = hostlist_create(NULL);
198 	for (i = first; i <= last; i++) {
199 		if (bit_test(bitmap, i) == 0)
200 			continue;
201 		hostlist_push_host(hl, node_record_table_ptr[i].name);
202 	}
203 	return hl;
204 
205 }
206 
207 /*
208  * bitmap2node_name_sortable - given a bitmap, build a list of comma
209  *	separated node names. names may include regular expressions
210  *	(e.g. "lx[01-10]")
211  * IN bitmap - bitmap pointer
212  * IN sort   - returned sorted list or not
213  * RET pointer to node list or NULL on error
214  * globals: node_record_table_ptr - pointer to node table
215  * NOTE: the caller must xfree the memory at node_list when no longer required
216  */
bitmap2node_name_sortable(bitstr_t * bitmap,bool sort)217 char * bitmap2node_name_sortable (bitstr_t *bitmap, bool sort)
218 {
219 	hostlist_t hl;
220 	char *buf;
221 
222 	hl = bitmap2hostlist (bitmap);
223 	if (hl == NULL)
224 		return xstrdup("");
225 	if (sort)
226 		hostlist_sort(hl);
227 	buf = hostlist_ranged_string_xmalloc(hl);
228 	hostlist_destroy(hl);
229 	return buf;
230 }
231 
232 /*
233  * bitmap2node_name - given a bitmap, build a list of sorted, comma
234  *	separated node names. names may include regular expressions
235  *	(e.g. "lx[01-10]")
236  * IN bitmap - bitmap pointer
237  * RET pointer to node list or NULL on error
238  * globals: node_record_table_ptr - pointer to node table
239  * NOTE: the caller must xfree the memory at node_list when no longer required
240  */
bitmap2node_name(bitstr_t * bitmap)241 char * bitmap2node_name (bitstr_t *bitmap)
242 {
243 	return bitmap2node_name_sortable(bitmap, 1);
244 }
245 
246 #ifdef HAVE_FRONT_END
247 /* Log the contents of a frontend record */
_dump_front_end(slurm_conf_frontend_t * fe_ptr)248 static void _dump_front_end(slurm_conf_frontend_t *fe_ptr)
249 {
250 	info("fe name:%s addr:%s port:%u state:%u reason:%s "
251 	     "allow_groups:%s allow_users:%s "
252 	     "deny_groups:%s deny_users:%s",
253 	     fe_ptr->frontends, fe_ptr->addresses,
254 	     fe_ptr->port, fe_ptr->node_state, fe_ptr->reason,
255 	     fe_ptr->allow_groups, fe_ptr->allow_users,
256 	     fe_ptr->deny_groups, fe_ptr->deny_users);
257 }
258 #endif
259 
260 /*
261  * build_all_frontend_info - get a array of slurm_conf_frontend_t structures
262  *	from the slurm.conf reader, build table, and set values
263  * is_slurmd_context: set to true if run from slurmd
264  * RET 0 if no error, error code otherwise
265  */
build_all_frontend_info(bool is_slurmd_context)266 extern int build_all_frontend_info (bool is_slurmd_context)
267 {
268 	slurm_conf_frontend_t **ptr_array;
269 #ifdef HAVE_FRONT_END
270 	slurm_conf_frontend_t *fe_single, *fe_line;
271 	int i, count, max_rc = SLURM_SUCCESS;
272 	bool front_end_debug;
273 
274 	if (slurm_get_debug_flags() & DEBUG_FLAG_FRONT_END)
275 		front_end_debug = true;
276 	else
277 		front_end_debug = false;
278 	count = slurm_conf_frontend_array(&ptr_array);
279 	if (count == 0)
280 		fatal("No FrontendName information available!");
281 
282 	for (i = 0; i < count; i++) {
283 		hostlist_t hl_name, hl_addr;
284 		char *fe_name, *fe_addr;
285 
286 		fe_line = ptr_array[i];
287 		hl_name = hostlist_create(fe_line->frontends);
288 		if (hl_name == NULL)
289 			fatal("Invalid FrontendName:%s", fe_line->frontends);
290 		hl_addr = hostlist_create(fe_line->addresses);
291 		if (hl_addr == NULL)
292 			fatal("Invalid FrontendAddr:%s", fe_line->addresses);
293 		if (hostlist_count(hl_name) != hostlist_count(hl_addr)) {
294 			fatal("Inconsistent node count between "
295 			      "FrontendName(%s) and FrontendAddr(%s)",
296 			      fe_line->frontends, fe_line->addresses);
297 		}
298 		while ((fe_name = hostlist_shift(hl_name))) {
299 			fe_addr = hostlist_shift(hl_addr);
300 			fe_single = xmalloc(sizeof(slurm_conf_frontend_t));
301 			list_append(front_end_list, fe_single);
302 			fe_single->frontends = xstrdup(fe_name);
303 			fe_single->addresses = xstrdup(fe_addr);
304 			free(fe_name);
305 			free(fe_addr);
306 			if (fe_line->allow_groups && fe_line->allow_groups[0]) {
307 				fe_single->allow_groups =
308 					xstrdup(fe_line->allow_groups);
309 			}
310 			if (fe_line->allow_users && fe_line->allow_users[0]) {
311 				fe_single->allow_users =
312 					xstrdup(fe_line->allow_users);
313 			}
314 			if (fe_line->deny_groups && fe_line->deny_groups[0]) {
315 				fe_single->deny_groups =
316 					xstrdup(fe_line->deny_groups);
317 			}
318 			if (fe_line->deny_users && fe_line->deny_users[0]) {
319 				fe_single->deny_users =
320 					xstrdup(fe_line->deny_users);
321 			}
322 			fe_single->port = fe_line->port;
323 			if (fe_line->reason && fe_line->reason[0])
324 				fe_single->reason = xstrdup(fe_line->reason);
325 			fe_single->node_state = fe_line->node_state;
326 			if (front_end_debug && !is_slurmd_context)
327 				_dump_front_end(fe_single);
328 		}
329 		hostlist_destroy(hl_addr);
330 		hostlist_destroy(hl_name);
331 	}
332 	return max_rc;
333 #else
334 	if (slurm_conf_frontend_array(&ptr_array) != 0)
335 		fatal("FrontendName information configured!");
336 	return SLURM_SUCCESS;
337 #endif
338 }
339 
_check_callback(char * alias,char * hostname,char * address,char * bcast_address,uint16_t port,int state_val,slurm_conf_node_t * node_ptr,config_record_t * config_ptr)340 static void _check_callback(char *alias, char *hostname,
341 			    char *address, char *bcast_address,
342 			    uint16_t port, int state_val,
343 			    slurm_conf_node_t *node_ptr,
344 			    config_record_t *config_ptr)
345 {
346 	node_record_t *node_rec;
347 
348 	if ((node_rec = find_node_record2(alias)))
349 		fatal("Duplicated NodeHostName %s in config file", alias);
350 
351 	node_rec = create_node_record(config_ptr, alias);
352 	if ((state_val != NO_VAL) &&
353 	    (state_val != NODE_STATE_UNKNOWN))
354 		node_rec->node_state = state_val;
355 	node_rec->last_response = (time_t) 0;
356 	node_rec->comm_name = xstrdup(address);
357 	node_rec->cpu_bind  = node_ptr->cpu_bind;
358 	node_rec->node_hostname = xstrdup(hostname);
359 	node_rec->bcast_address = xstrdup(bcast_address);
360 	node_rec->port      = port;
361 	node_rec->weight    = node_ptr->weight;
362 	node_rec->features  = xstrdup(node_ptr->feature);
363 	node_rec->reason    = xstrdup(node_ptr->reason);
364 }
365 
366 /*
367  * build_all_nodeline_info - get a array of slurm_conf_node_t structures
368  *	from the slurm.conf reader, build table, and set values
369  * IN set_bitmap - if true then set node_bitmap in config record (used by
370  *		    slurmd), false is used by slurmctld and testsuite
371  * IN tres_cnt - number of TRES configured on system (used on controller side)
372  * RET 0 if no error, error code otherwise
373  */
build_all_nodeline_info(bool set_bitmap,int tres_cnt)374 extern int build_all_nodeline_info(bool set_bitmap, int tres_cnt)
375 {
376 	slurm_conf_node_t *node, **ptr_array;
377 	config_record_t *config_ptr = NULL;
378 	int count;
379 	int i, rc, max_rc = SLURM_SUCCESS;
380 	bool in_daemon;
381 	static bool daemon_run = false, daemon_set = false;
382 
383 	in_daemon = run_in_daemon(&daemon_run, &daemon_set, "slurmctld,slurmd");
384 
385 	count = slurm_conf_nodename_array(&ptr_array);
386 	if (count == 0)
387 		fatal("No NodeName information available!");
388 
389 	for (i = 0; i < count; i++) {
390 		node = ptr_array[i];
391 
392 		config_ptr = create_config_record();
393 		config_ptr->nodes = xstrdup(node->nodenames);
394 		config_ptr->cpu_bind = node->cpu_bind;
395 		config_ptr->cpus = node->cpus;
396 		config_ptr->boards = node->boards;
397 		config_ptr->sockets = node->sockets;
398 		config_ptr->cores = node->cores;
399 		config_ptr->core_spec_cnt = node->core_spec_cnt;
400 		config_ptr->cpu_spec_list = xstrdup(node->cpu_spec_list);
401 		config_ptr->threads = node->threads;
402 		config_ptr->real_memory = node->real_memory;
403 		config_ptr->mem_spec_limit = node->mem_spec_limit;
404 		config_ptr->tmp_disk = node->tmp_disk;
405 
406 		if (tres_cnt) {
407 			config_ptr->tres_weights_str =
408 				xstrdup(node->tres_weights_str);
409 			config_ptr->tres_weights =
410 				slurm_get_tres_weight_array(
411 						node->tres_weights_str,
412 						tres_cnt, true);
413 		}
414 
415 		config_ptr->weight = node->weight;
416 		if (node->feature && node->feature[0])
417 			config_ptr->feature = xstrdup(node->feature);
418 		if (in_daemon) {
419 			config_ptr->gres = gres_plugin_name_filter(node->gres,
420 							       node->nodenames);
421 		}
422 
423 		rc = check_nodeline_info(node, config_ptr, LOG_LEVEL_FATAL,
424 					 _check_callback);
425 		max_rc = MAX(max_rc, rc);
426 	}
427 
428 	if (set_bitmap) {
429 		ListIterator config_iterator;
430 		config_iterator = list_iterator_create(config_list);
431 		while ((config_ptr = list_next(config_iterator))) {
432 			node_name2bitmap(config_ptr->nodes, true,
433 					 &config_ptr->node_bitmap);
434 		}
435 		list_iterator_destroy(config_iterator);
436 	}
437 
438 	return max_rc;
439 }
440 
441 /*
442  * check_nodeline_info - From the slurm.conf reader, build table,
443  * 	and set values
444  * RET 0 if no error, error code otherwise
445  * Note: Operates on common variables
446  *	default_node_record - default node configuration values
447  */
check_nodeline_info(slurm_conf_node_t * node_ptr,config_record_t * config_ptr,log_level_t lvl,void (* _callback)(char * alias,char * hostname,char * address,char * bcast_address,uint16_t port,int state_val,slurm_conf_node_t * node_ptr,config_record_t * config_ptr))448 extern int check_nodeline_info(slurm_conf_node_t *node_ptr,
449 			       config_record_t *config_ptr,
450 			       log_level_t lvl,
451 			       void (*_callback) (
452 				       char *alias, char *hostname,
453 				       char *address, char *bcast_address,
454 				       uint16_t port, int state_val,
455 				       slurm_conf_node_t *node_ptr,
456 				       config_record_t *config_ptr))
457 {
458 	int error_code = SLURM_SUCCESS;
459 	hostlist_t address_list = NULL;
460 	hostlist_t alias_list = NULL;
461 	hostlist_t bcast_list = NULL;
462 	hostlist_t hostname_list = NULL;
463 	hostlist_t port_list = NULL;
464 	char *address = NULL;
465 	char *alias = NULL;
466 	char *bcast_address = NULL;
467 	char *hostname = NULL;
468 	char *port_str = NULL;
469 	int state_val = NODE_STATE_UNKNOWN;
470 	int address_count, alias_count, bcast_count, hostname_count, port_count;
471 	uint16_t port = 0;
472 
473 	if ((node_ptr->nodenames == NULL) || (node_ptr->nodenames[0] == '\0'))
474 		return -1;
475 
476 	if (node_ptr->state != NULL) {
477 		state_val = state_str2int(node_ptr->state, node_ptr->nodenames);
478 		if (state_val == NO_VAL)
479 			fatal("Invalid state %s from %s",
480 			      node_ptr->state, node_ptr->nodenames);
481 	}
482 
483 	if (!(address_list = hostlist_create(node_ptr->addresses)))
484 		fatal("Unable to create NodeAddr list from %s",
485 		      node_ptr->addresses);
486 
487 	if (!(alias_list = hostlist_create(node_ptr->nodenames)))
488 		fatal("Unable to create NodeName list from %s",
489 		      node_ptr->nodenames);
490 
491 	if (!(bcast_list = hostlist_create(node_ptr->bcast_addresses)))
492 		fatal("Unable to create BcastAddr list from %s",
493 		      node_ptr->bcast_addresses);
494 
495 	if (!(hostname_list = hostlist_create(node_ptr->hostnames)))
496 		fatal("Unable to create NodeHostname list from %s",
497 		      node_ptr->hostnames);
498 
499 	if (node_ptr->port_str && node_ptr->port_str[0] &&
500 	    (node_ptr->port_str[0] != '[') &&
501 	    (strchr(node_ptr->port_str, '-') ||
502 	     strchr(node_ptr->port_str, ','))) {
503 		xstrfmtcat(port_str, "[%s]", node_ptr->port_str);
504 		port_list = hostlist_create(port_str);
505 		xfree(port_str);
506 	} else
507 		port_list = hostlist_create(node_ptr->port_str);
508 
509 	if (!port_list)
510 		fatal("Unable to create Port list from %s",
511 		      node_ptr->port_str);
512 
513 	/* some sanity checks */
514 	address_count  = hostlist_count(address_list);
515 	bcast_count = hostlist_count(bcast_list);
516 	alias_count    = hostlist_count(alias_list);
517 	hostname_count = hostlist_count(hostname_list);
518 	port_count     = hostlist_count(port_list);
519 #ifdef HAVE_FRONT_END
520 	if ((hostname_count != alias_count) && (hostname_count != 1))
521 		fatal("NodeHostname count must equal that of NodeName records of there must be no more than one");
522 
523 	if ((address_count != alias_count) && (address_count != 1))
524 		fatal("NodeAddr count must equal that of NodeName records of there must be no more than one");
525 #else
526 #ifdef MULTIPLE_SLURMD
527 	if ((address_count != alias_count) && (address_count != 1))
528 		fatal("NodeAddr count must equal that of NodeName records of there must be no more than one");
529 	if (bcast_count && (bcast_count != alias_count) && (bcast_count != 1))
530 		fatal("BcastAddr count must equal that of NodeName records, or there must be no more than one");
531 #else
532 	if (address_count < alias_count)
533 		fatal("At least as many NodeAddr are required as NodeName");
534 
535 	if (bcast_count && (bcast_count < alias_count))
536 		fatal("At least as many BcastAddr are required as NodeName");
537 
538 	if (hostname_count < alias_count)
539 		fatal("At least as many NodeHostname are required as NodeName");
540 #endif	/* MULTIPLE_SLURMD */
541 #endif	/* HAVE_FRONT_END */
542 	if ((port_count != alias_count) && (port_count > 1))
543 		fatal("Port count must equal that of NodeName records or there must be no more than one (%u != %u)",
544 		      port_count, alias_count);
545 
546 	/* now build the individual node structures */
547 	while ((alias = hostlist_shift(alias_list))) {
548 		if (address_count > 0) {
549 			address_count--;
550 			if (address)
551 				free(address);
552 			address = hostlist_shift(address_list);
553 		}
554 		if (bcast_count > 0) {
555 			bcast_count--;
556 			if (bcast_address)
557 				free(bcast_address);
558 			bcast_address = hostlist_shift(bcast_list);
559 		}
560 		if (hostname_count > 0) {
561 			hostname_count--;
562 			if (hostname)
563 				free(hostname);
564 			hostname = hostlist_shift(hostname_list);
565 		}
566 		if (port_count > 0) {
567 			int port_int;
568 			port_count--;
569 			if (port_str)
570 				free(port_str);
571 			port_str = hostlist_shift(port_list);
572 			port_int = atoi(port_str);
573 			if ((port_int <= 0) || (port_int > 0xffff)) {
574 				log_var(lvl, "Invalid Port %s",
575 					node_ptr->port_str);
576 			}
577 			port = port_int;
578 		}
579 
580 		(*_callback)(alias, hostname, address, bcast_address,
581 			     port, state_val, node_ptr, config_ptr);
582 
583 		free(alias);
584 	}
585 	/* free allocated storage */
586 	if (address)
587 		free(address);
588 	if (bcast_address)
589 		free(bcast_address);
590 	if (hostname)
591 		free(hostname);
592 	if (port_str)
593 		free(port_str);
594 	if (address_list)
595 		hostlist_destroy(address_list);
596 	if (alias_list)
597 		hostlist_destroy(alias_list);
598 	if (bcast_list)
599 		hostlist_destroy(bcast_list);
600 	if (hostname_list)
601 		hostlist_destroy(hostname_list);
602 	if (port_list)
603 		hostlist_destroy(port_list);
604 	return error_code;
605 }
606 
607 /*
608  * create_config_record - create a config_record entry and set is values to
609  *	the defaults. each config record corresponds to a line in the
610  *	slurm.conf file and typically describes the configuration of a
611  *	large number of nodes
612  * RET pointer to the config_record
613  * NOTE: memory allocated will remain in existence until
614  *	_delete_config_record() is called to delete all configuration records
615  */
create_config_record(void)616 extern config_record_t *create_config_record(void)
617 {
618 	config_record_t *config_ptr = xmalloc(sizeof(*config_ptr));
619 
620 	last_node_update = time (NULL);
621 
622 	config_ptr->nodes = NULL;
623 	config_ptr->node_bitmap = NULL;
624 	xassert (config_ptr->magic = CONFIG_MAGIC);  /* set value */
625 
626 	list_append(config_list, config_ptr);
627 
628 	return config_ptr;
629 }
630 
631 /*
632  * create_node_record - create a node record and set its values to defaults
633  * IN config_ptr - pointer to node's configuration information
634  * IN node_name - name of the node
635  * RET pointer to the record or NULL if error
636  * NOTE: allocates memory at node_record_table_ptr that must be xfreed when
637  *	the global node table is no longer required
638  */
create_node_record(config_record_t * config_ptr,char * node_name)639 extern node_record_t *create_node_record(config_record_t *config_ptr,
640 					 char *node_name)
641 {
642 	node_record_t *node_ptr;
643 	int old_buffer_size, new_buffer_size;
644 
645 	last_node_update = time (NULL);
646 	xassert(config_ptr);
647 	xassert(node_name);
648 
649 	/* round up the buffer size to reduce overhead of xrealloc */
650 	old_buffer_size = (node_record_count) * sizeof(node_record_t);
651 	old_buffer_size =
652 		((int) ((old_buffer_size / BUF_SIZE) + 1)) * BUF_SIZE;
653 	new_buffer_size =
654 		(node_record_count + 1) * sizeof(node_record_t);
655 	new_buffer_size =
656 		((int) ((new_buffer_size / BUF_SIZE) + 1)) * BUF_SIZE;
657 	if (!node_record_table_ptr) {
658 		node_record_table_ptr = xmalloc(new_buffer_size);
659 	} else if (old_buffer_size != new_buffer_size) {
660 		xrealloc (node_record_table_ptr, new_buffer_size);
661 		/*
662 		 * You need to rehash the hash after we realloc or we will have
663 		 * only bad memory references in the hash.
664 		 */
665 		rehash_node();
666 	}
667 	node_ptr = node_record_table_ptr + (node_record_count++);
668 	node_ptr->name = xstrdup(node_name);
669 	if (!node_hash_table)
670 		node_hash_table = xhash_init(_node_record_hash_identity, NULL);
671 	xhash_add(node_hash_table, node_ptr);
672 
673 	node_ptr->config_ptr = config_ptr;
674 	/* these values will be overwritten when the node actually registers */
675 	node_ptr->cpus = config_ptr->cpus;
676 	node_ptr->cpu_load = NO_VAL;
677 	node_ptr->free_mem = NO_VAL64;
678 	node_ptr->cpu_spec_list = xstrdup(config_ptr->cpu_spec_list);
679 	node_ptr->boards = config_ptr->boards;
680 	node_ptr->sockets = config_ptr->sockets;
681 	node_ptr->cores = config_ptr->cores;
682 	node_ptr->core_spec_cnt = config_ptr->core_spec_cnt;
683 	node_ptr->threads = config_ptr->threads;
684 	node_ptr->mem_spec_limit = config_ptr->mem_spec_limit;
685 	node_ptr->real_memory = config_ptr->real_memory;
686 	node_ptr->node_spec_bitmap = NULL;
687 	node_ptr->tmp_disk = config_ptr->tmp_disk;
688 	node_ptr->select_nodeinfo = select_g_select_nodeinfo_alloc();
689 	node_ptr->energy = acct_gather_energy_alloc(1);
690 	node_ptr->ext_sensors = ext_sensors_alloc();
691 	node_ptr->owner = NO_VAL;
692 	node_ptr->mcs_label = NULL;
693 	node_ptr->next_state = NO_VAL;
694 	node_ptr->protocol_version = SLURM_MIN_PROTOCOL_VERSION;
695 	xassert (node_ptr->magic = NODE_MAGIC)  /* set value */;
696 	return node_ptr;
697 }
698 
699 /*
700  * find_node_record - find a record for node with specified name
701  * IN: name - name of the desired node
702  * RET: pointer to node record or NULL if not found
703  * NOTE: Logs an error if the node name is NOT found
704  */
find_node_record(char * name)705 extern node_record_t *find_node_record(char *name)
706 {
707 	return _find_node_record(name, true, true);
708 }
709 
710 /*
711  * find_node_record2 - find a record for node with specified name
712  * IN: name - name of the desired node
713  * RET: pointer to node record or NULL if not found
714  * NOTE: Does not log an error if the node name is NOT found
715  */
find_node_record2(char * name)716 extern node_record_t *find_node_record2(char *name)
717 {
718 	return _find_node_record(name, true, false);
719 }
720 
721 /*
722  * find_node_record_no_alias - find a record for node with specified name
723  * without looking at the node's alias (NodeHostName).
724  * IN: name - name of the desired node
725  * RET: pointer to node record or NULL if not found
726  * NOTE: Logs an error if the node name is NOT found
727  */
find_node_record_no_alias(char * name)728 extern node_record_t *find_node_record_no_alias(char *name)
729 {
730 	return _find_node_record(name, false, true);
731 }
732 
733 /*
734  * _find_node_record - find a record for node with specified name
735  * IN: name - name of the desired node
736  * IN: test_alias - if set, also test NodeHostName value
737  * IN: log_missing - if set, then print an error message if the node is not found
738  * RET: pointer to node record or NULL if not found
739  */
_find_node_record(char * name,bool test_alias,bool log_missing)740 static node_record_t *_find_node_record(char *name, bool test_alias,
741 					bool log_missing)
742 {
743 	node_record_t *node_ptr;
744 
745 	if ((name == NULL) || (name[0] == '\0')) {
746 		info("%s: passed NULL node name", __func__);
747 		return NULL;
748 	}
749 
750 	/* nothing added yet */
751 	if (!node_hash_table)
752 		return NULL;
753 
754 	/* try to find via hash table, if it exists */
755 	if ((node_ptr = xhash_get_str(node_hash_table, name))) {
756 		xassert(node_ptr->magic == NODE_MAGIC);
757 		return node_ptr;
758 	}
759 
760 	if ((node_record_count == 1) &&
761 	    (xstrcmp(node_record_table_ptr[0].name, "localhost") == 0))
762 		return (&node_record_table_ptr[0]);
763 
764 	if (log_missing)
765 		error("%s(%d): lookup failure for %s",
766 		      __func__, __LINE__, name);
767 
768 	if (test_alias) {
769 		char *alias = slurm_conf_get_nodename(name);
770 		/* look for the alias node record if the user put this in
771 	 	 * instead of what slurm sees the node name as */
772 		if (!alias)
773 			return NULL;
774 
775 		node_ptr = xhash_get_str(node_hash_table, alias);
776 		if (log_missing)
777 			error("%s(%d): lookup failure for %s alias %s",
778 			      __func__, __LINE__, name, alias);
779 		xfree(alias);
780 		return node_ptr;
781 	}
782 
783 	return NULL;
784 }
785 
786 /*
787  * init_node_conf - initialize the node configuration tables and values.
788  *	this should be called before creating any node or configuration
789  *	entries.
790  * RET 0 if no error, otherwise an error code
791  */
init_node_conf(void)792 extern int init_node_conf (void)
793 {
794 	last_node_update = time (NULL);
795 	int i;
796 	node_record_t *node_ptr;
797 
798 	node_ptr = node_record_table_ptr;
799 	for (i = 0; i < node_record_count; i++, node_ptr++)
800 		purge_node_rec(node_ptr);
801 
802 	node_record_count = 0;
803 	xfree(node_record_table_ptr);
804 	xhash_free(node_hash_table);
805 
806 	if (config_list)	/* delete defunct configuration entries */
807 		(void) _delete_config_record ();
808 	else {
809 		config_list    = list_create (_list_delete_config);
810 		front_end_list = list_create (destroy_frontend);
811 	}
812 
813 	return SLURM_SUCCESS;
814 }
815 
816 
817 /* node_fini2 - free memory associated with node records (except bitmaps) */
node_fini2(void)818 extern void node_fini2 (void)
819 {
820 	int i;
821 	node_record_t *node_ptr;
822 
823 	if (config_list) {
824 		FREE_NULL_LIST(config_list);
825 		FREE_NULL_LIST(front_end_list);
826 	}
827 
828 	xhash_free(node_hash_table);
829 	node_ptr = node_record_table_ptr;
830 	for (i = 0; i < node_record_count; i++, node_ptr++)
831 		purge_node_rec(node_ptr);
832 
833 	xfree(node_record_table_ptr);
834 	node_record_count = 0;
835 }
836 
837 
838 /*
839  * node_name2bitmap - given a node name regular expression, build a bitmap
840  *	representation
841  * IN node_names  - list of nodes
842  * IN best_effort - if set don't return an error on invalid node name entries
843  * OUT bitmap     - set to bitmap, may not have all bits set on error
844  * RET 0 if no error, otherwise EINVAL
845  * NOTE: call FREE_NULL_BITMAP() to free bitmap memory when no longer required
846  */
node_name2bitmap(char * node_names,bool best_effort,bitstr_t ** bitmap)847 extern int node_name2bitmap (char *node_names, bool best_effort,
848 			     bitstr_t **bitmap)
849 {
850 	int rc = SLURM_SUCCESS;
851 	char *this_node_name;
852 	bitstr_t *my_bitmap;
853 	hostlist_t host_list;
854 
855 	my_bitmap = (bitstr_t *) bit_alloc (node_record_count);
856 	*bitmap = my_bitmap;
857 
858 	if (node_names == NULL) {
859 		info("node_name2bitmap: node_names is NULL");
860 		return rc;
861 	}
862 
863 	if ( (host_list = hostlist_create (node_names)) == NULL) {
864 		/* likely a badly formatted hostlist */
865 		error ("hostlist_create on %s error:", node_names);
866 		if (!best_effort)
867 			rc = EINVAL;
868 		return rc;
869 	}
870 
871 	while ( (this_node_name = hostlist_shift (host_list)) ) {
872 		node_record_t *node_ptr;
873 		node_ptr = _find_node_record(this_node_name, best_effort, true);
874 		if (node_ptr) {
875 			bit_set (my_bitmap, (bitoff_t) (node_ptr -
876 							node_record_table_ptr));
877 		} else {
878 			error ("node_name2bitmap: invalid node specified %s",
879 			       this_node_name);
880 			if (!best_effort)
881 				rc = EINVAL;
882 		}
883 		free (this_node_name);
884 	}
885 	hostlist_destroy (host_list);
886 
887 	return rc;
888 }
889 
890 /*
891  * hostlist2bitmap - given a hostlist, build a bitmap representation
892  * IN hl          - hostlist
893  * IN best_effort - if set don't return an error on invalid node name entries
894  * OUT bitmap     - set to bitmap, may not have all bits set on error
895  * RET 0 if no error, otherwise EINVAL
896  */
hostlist2bitmap(hostlist_t hl,bool best_effort,bitstr_t ** bitmap)897 extern int hostlist2bitmap (hostlist_t hl, bool best_effort, bitstr_t **bitmap)
898 {
899 	int rc = SLURM_SUCCESS;
900 	bitstr_t *my_bitmap;
901 	char *name;
902 	hostlist_iterator_t hi;
903 
904 	FREE_NULL_BITMAP(*bitmap);
905 	my_bitmap = (bitstr_t *) bit_alloc (node_record_count);
906 	*bitmap = my_bitmap;
907 
908 	hi = hostlist_iterator_create(hl);
909 	while ((name = hostlist_next(hi))) {
910 		node_record_t *node_ptr;
911 		node_ptr = _find_node_record(name, best_effort, true);
912 		if (node_ptr) {
913 			bit_set (my_bitmap, (bitoff_t) (node_ptr -
914 							node_record_table_ptr));
915 		} else {
916 			error ("hostlist2bitmap: invalid node specified %s",
917 			       name);
918 			if (!best_effort)
919 				rc = EINVAL;
920 		}
921 		free (name);
922 	}
923 
924 	hostlist_iterator_destroy(hi);
925 	return rc;
926 
927 }
928 
929 /* Purge the contents of a node record */
purge_node_rec(node_record_t * node_ptr)930 extern void purge_node_rec(node_record_t *node_ptr)
931 {
932 	xfree(node_ptr->arch);
933 	xfree(node_ptr->comm_name);
934 	xfree(node_ptr->cpu_spec_list);
935 	xfree(node_ptr->features);
936 	xfree(node_ptr->features_act);
937 	xfree(node_ptr->gres);
938 	FREE_NULL_LIST(node_ptr->gres_list);
939 	xfree(node_ptr->name);
940 	xfree(node_ptr->node_hostname);
941 	FREE_NULL_BITMAP(node_ptr->node_spec_bitmap);
942 	xfree(node_ptr->os);
943 	xfree(node_ptr->part_pptr);
944 	xfree(node_ptr->power);
945 	xfree(node_ptr->reason);
946 	xfree(node_ptr->version);
947 	acct_gather_energy_destroy(node_ptr->energy);
948 	ext_sensors_destroy(node_ptr->ext_sensors);
949 	select_g_select_nodeinfo_free(node_ptr->select_nodeinfo);
950 	xfree(node_ptr->tres_str);
951 	xfree(node_ptr->tres_fmt_str);
952 	xfree(node_ptr->tres_cnt);
953 }
954 
955 /*
956  * rehash_node - build a hash table of the node_record entries.
957  * NOTE: using xhash implementation
958  */
rehash_node(void)959 extern void rehash_node (void)
960 {
961 	int i;
962 	node_record_t *node_ptr = node_record_table_ptr;
963 
964 	xhash_free (node_hash_table);
965 	node_hash_table = xhash_init(_node_record_hash_identity, NULL);
966 	for (i = 0; i < node_record_count; i++, node_ptr++) {
967 		if ((node_ptr->name == NULL) ||
968 		    (node_ptr->name[0] == '\0'))
969 			continue;	/* vestigial record */
970 		xhash_add(node_hash_table, node_ptr);
971 	}
972 
973 #if _DEBUG
974 	_dump_hash();
975 #endif
976 	return;
977 }
978 
979 /* Convert a node state string to it's equivalent enum value */
state_str2int(const char * state_str,char * node_name)980 extern int state_str2int(const char *state_str, char *node_name)
981 {
982 	int state_val = NO_VAL;
983 	int i;
984 
985 	for (i = 0; i <= NODE_STATE_END; i++) {
986 		if (xstrcasecmp(node_state_string(i), "END") == 0)
987 			break;
988 		if (xstrcasecmp(node_state_string(i), state_str) == 0) {
989 			state_val = i;
990 			break;
991 		}
992 	}
993 	if (i >= NODE_STATE_END) {
994 		if (xstrncasecmp("CLOUD", state_str, 5) == 0)
995 			state_val = NODE_STATE_IDLE | NODE_STATE_CLOUD |
996 				    NODE_STATE_POWER_SAVE;
997 		else if (xstrncasecmp("DRAIN", state_str, 5) == 0)
998 			state_val = NODE_STATE_UNKNOWN | NODE_STATE_DRAIN;
999 		else if (xstrncasecmp("FAIL", state_str, 4) == 0)
1000 			state_val = NODE_STATE_IDLE | NODE_STATE_FAIL;
1001 	}
1002 	if (state_val == NO_VAL) {
1003 		error("node %s has invalid state %s", node_name, state_str);
1004 		errno = EINVAL;
1005 	}
1006 	return state_val;
1007 }
1008 
1009 /* (re)set cr_node_num_cores arrays */
cr_init_global_core_data(node_record_t * node_ptr,int node_cnt)1010 extern void cr_init_global_core_data(node_record_t *node_ptr, int node_cnt)
1011 {
1012 	uint32_t n;
1013 
1014 	cr_fini_global_core_data();
1015 
1016 	cr_node_num_cores = xmalloc(node_cnt * sizeof(uint16_t));
1017 	cr_node_cores_offset = xmalloc((node_cnt+1) * sizeof(uint32_t));
1018 
1019 	for (n = 0; n < node_cnt; n++) {
1020 		uint16_t cores = node_ptr[n].config_ptr->cores;
1021 		cores *= node_ptr[n].config_ptr->sockets;
1022 
1023 		cr_node_num_cores[n] = cores;
1024 		if (n > 0) {
1025 			cr_node_cores_offset[n] = cr_node_cores_offset[n-1] +
1026 						  cr_node_num_cores[n-1] ;
1027 		} else
1028 			cr_node_cores_offset[0] = 0;
1029 	}
1030 
1031 	/* an extra value is added to get the total number of cores */
1032 	/* as cr_get_coremap_offset is sometimes used to get the total */
1033 	/* number of cores in the cluster */
1034 	cr_node_cores_offset[node_cnt] = cr_node_cores_offset[node_cnt-1] +
1035 					 cr_node_num_cores[node_cnt-1] ;
1036 
1037 }
1038 
cr_fini_global_core_data(void)1039 extern void cr_fini_global_core_data(void)
1040 {
1041 	xfree(cr_node_num_cores);
1042 	xfree(cr_node_cores_offset);
1043 }
1044 
1045 /* return the coremap index to the first core of the given node */
1046 
cr_get_coremap_offset(uint32_t node_index)1047 extern uint32_t cr_get_coremap_offset(uint32_t node_index)
1048 {
1049 	xassert(cr_node_cores_offset);
1050 	return cr_node_cores_offset[node_index];
1051 }
1052 
1053 /* Return a bitmap the size of the machine in cores. On a Bluegene
1054  * system it will return a bitmap in cnodes. */
cr_create_cluster_core_bitmap(int core_mult)1055 extern bitstr_t *cr_create_cluster_core_bitmap(int core_mult)
1056 {
1057 	/* DEF_TIMERS; */
1058 	/* START_TIMER; */
1059 	bitstr_t *core_bitmap;
1060 	static int cnt = 0;
1061 
1062 	if (!cnt) {
1063 		cnt = cr_get_coremap_offset(node_record_count);
1064 		if (core_mult)
1065 			cnt *= core_mult;
1066 	}
1067 	core_bitmap = bit_alloc(cnt);
1068 	/* END_TIMER; */
1069 	/* info("creating of core bitmap of %d took %s", cnt, TIME_STR); */
1070 	return core_bitmap;
1071 }
1072 
1073 /*
1074  * Determine maximum number of CPUs on this node usable by a job
1075  * ntasks_per_core IN - tasks-per-core to be launched by this job
1076  * cpus_per_task IN - number of required  CPUs per task for this job
1077  * total_cores IN - total number of cores on this node
1078  * total_cpus IN - total number of CPUs on this node
1079  * RET count of usable CPUs on this node usable by this job
1080  */
adjust_cpus_nppcu(uint16_t ntasks_per_core,int cpus_per_task,int total_cores,int total_cpus)1081 extern int adjust_cpus_nppcu(uint16_t ntasks_per_core, int cpus_per_task,
1082 			     int total_cores, int total_cpus)
1083 {
1084 	int cpus = total_cpus;
1085 
1086 //FIXME: This function ignores tasks-per-socket and tasks-per-node checks.
1087 // Those parameters are tested later
1088 	if ((ntasks_per_core != 0) && (ntasks_per_core != 0xffff) &&
1089 	    (cpus_per_task != 0)) {
1090 		cpus = MAX((total_cores * ntasks_per_core * cpus_per_task),
1091 			   total_cpus);
1092 	}
1093 
1094 	return cpus;
1095 }
1096 
find_hostname(uint32_t pos,char * hosts)1097 extern char *find_hostname(uint32_t pos, char *hosts)
1098 {
1099 	hostlist_t hostlist = NULL;
1100 	char *temp = NULL, *host = NULL;
1101 
1102 	if (!hosts || (pos == NO_VAL) || (pos == INFINITE))
1103 		return NULL;
1104 
1105 	hostlist = hostlist_create(hosts);
1106 	temp = hostlist_nth(hostlist, pos);
1107 	if (temp) {
1108 		host = xstrdup(temp);
1109 		free(temp);
1110 	}
1111 	hostlist_destroy(hostlist);
1112 	return host;
1113 }
1114