1 /*****************************************************************************\
2  *  licenses.c - Functions for handling cluster-wide consumable resources
3  *****************************************************************************
4  *  Copyright (C) 2008-2011 Lawrence Livermore National Security.
5  *  Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
6  *  Written by Morris Jette <jette@llnl.gov>, et. al.
7  *  CODE-OCEC-09-009. All rights reserved.
8  *
9  *  This file is part of Slurm, a resource management program.
10  *  For details, see <https://slurm.schedmd.com/>.
11  *  Please also read the included file: DISCLAIMER.
12  *
13  *  Slurm is free software; you can redistribute it and/or modify it under
14  *  the terms of the GNU General Public License as published by the Free
15  *  Software Foundation; either version 2 of the License, or (at your option)
16  *  any later version.
17  *
18  *  In addition, as a special exception, the copyright holders give permission
19  *  to link the code of portions of this program with the OpenSSL library under
20  *  certain conditions as described in each individual source file, and
21  *  distribute linked combinations including the two. You must obey the GNU
22  *  General Public License in all respects for all of the code used other than
23  *  OpenSSL. If you modify file(s) with this exception, you may extend this
24  *  exception to your version of the file(s), but you are not obligated to do
25  *  so. If you do not wish to do so, delete this exception statement from your
26  *  version.  If you delete this exception statement from all source files in
27  *  the program, then also delete it here.
28  *
29  *  Slurm is distributed in the hope that it will be useful, but WITHOUT ANY
30  *  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
31  *  FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
32  *  details.
33  *
34  *  You should have received a copy of the GNU General Public License along
35  *  with Slurm; if not, write to the Free Software Foundation, Inc.,
36  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA.
37 \*****************************************************************************/
38 
39 #include <ctype.h>
40 #include <errno.h>
41 #include <pthread.h>
42 #include <stdlib.h>
43 #include <string.h>
44 
45 #include "slurm/slurm_errno.h"
46 
47 #include "src/common/assoc_mgr.h"
48 #include "src/common/list.h"
49 #include "src/common/log.h"
50 #include "src/common/macros.h"
51 #include "src/common/xmalloc.h"
52 #include "src/common/xstring.h"
53 #include "src/slurmctld/licenses.h"
54 #include "src/slurmctld/reservation.h"
55 #include "src/slurmctld/slurmctld.h"
56 #include "src/common/slurm_accounting_storage.h"
57 
58 List license_list = (List) NULL;
59 time_t last_license_update = 0;
60 static pthread_mutex_t license_mutex = PTHREAD_MUTEX_INITIALIZER;
61 static void _pack_license(struct licenses *lic, Buf buffer, uint16_t protocol_version);
62 
63 /* Print all licenses on a list */
_licenses_print(char * header,List licenses,job_record_t * job_ptr)64 static void _licenses_print(char *header, List licenses, job_record_t *job_ptr)
65 {
66 	ListIterator iter;
67 	licenses_t *license_entry;
68 
69 	if (licenses == NULL)
70 		return;
71 	if ((slurmctld_conf.debug_flags & DEBUG_FLAG_LICENSE) == 0)
72 		return;
73 
74 	iter = list_iterator_create(licenses);
75   	while ((license_entry = list_next(iter))) {
76 		if (!job_ptr) {
77 			info("licenses: %s=%s total=%u used=%u",
78 			     header, license_entry->name,
79 			     license_entry->total, license_entry->used);
80 		} else {
81 			info("licenses: %s=%s %pJ available=%u used=%u",
82 			     header, license_entry->name, job_ptr,
83 			     license_entry->total, license_entry->used);
84 		}
85 	}
86 	list_iterator_destroy(iter);
87 }
88 
89 /* Free a license_t record (for use by FREE_NULL_LIST) */
license_free_rec(void * x)90 extern void license_free_rec(void *x)
91 {
92 	licenses_t *license_entry = (licenses_t *) x;
93 
94 	if (license_entry) {
95 		xfree(license_entry->name);
96 		xfree(license_entry);
97 	}
98 }
99 
100 /* Find a license_t record by license name (for use by list_find_first) */
_license_find_rec(void * x,void * key)101 static int _license_find_rec(void *x, void *key)
102 {
103 	licenses_t *license_entry = (licenses_t *) x;
104 	char *name = (char *) key;
105 
106 	if ((license_entry->name == NULL) || (name == NULL))
107 		return 0;
108 	if (xstrcmp(license_entry->name, name))
109 		return 0;
110 	return 1;
111 }
112 
113 /* Find a license_t record by license name (for use by list_find_first) */
_license_find_remote_rec(void * x,void * key)114 static int _license_find_remote_rec(void *x, void *key)
115 {
116 	licenses_t *license_entry = (licenses_t *) x;
117 
118 	if (!license_entry->remote)
119 		return 0;
120 	return _license_find_rec(x, key);
121 }
122 
123 /* Given a license string, return a list of license_t records */
_build_license_list(char * licenses,bool * valid)124 static List _build_license_list(char *licenses, bool *valid)
125 {
126 	int i;
127 	char *end_num, *tmp_str, *token, *last;
128 	licenses_t *license_entry;
129 	List lic_list;
130 
131 	*valid = true;
132 	if ((licenses == NULL) || (licenses[0] == '\0'))
133 		return NULL;
134 
135 	lic_list = list_create(license_free_rec);
136 	tmp_str = xstrdup(licenses);
137 	token = strtok_r(tmp_str, ",;", &last);
138 	while (token && *valid) {
139 		int32_t num = 1;
140 		for (i = 0; token[i]; i++) {
141 			if (isspace(token[i])) {
142 				*valid = false;
143 				break;
144 			}
145 
146 			if (token[i] == ':') {
147 				token[i++] = '\0';
148 				num = (int32_t)strtol(&token[i], &end_num, 10);
149 				if (*end_num != '\0')
150 					 *valid = false;
151 				break;
152 			}
153 		}
154 		if (num < 0 || !(*valid)) {
155 			*valid = false;
156 			break;
157 		}
158 
159 		license_entry = list_find_first(lic_list, _license_find_rec,
160 						token);
161 		if (license_entry) {
162 			license_entry->total += num;
163 		} else {
164 			license_entry = xmalloc(sizeof(licenses_t));
165 			license_entry->name = xstrdup(token);
166 			license_entry->total = num;
167 			list_push(lic_list, license_entry);
168 		}
169 		token = strtok_r(NULL, ",;", &last);
170 	}
171 	xfree(tmp_str);
172 
173 	if (*valid == false) {
174 		FREE_NULL_LIST(lic_list);
175 	}
176 	return lic_list;
177 }
178 
179 /*
180  * Given a list of license_t records, return a license string.
181  *
182  * This can be combined with _build_license_list() to eliminate duplicates
183  *
184  * IN license_list - list of license_t records
185  *
186  * RET string represenation of licenses. Must be destroyed by caller.
187  */
license_list_to_string(List license_list)188 extern char *license_list_to_string(List license_list)
189 {
190 	char *sep = "";
191 	char *licenses = NULL;
192 	ListIterator iter;
193 	licenses_t *license_entry;
194 
195 	if (!license_list)
196 		return licenses;
197 
198 	iter = list_iterator_create(license_list);
199 	while ((license_entry = list_next(iter))) {
200 		xstrfmtcat(licenses, "%s%s:%u",
201 			   sep, license_entry->name, license_entry->total);
202 		sep = ",";
203 	}
204 	list_iterator_destroy(iter);
205 
206 	return licenses;
207 }
208 
209 /* license_mutex should be locked before calling this. */
_add_res_rec_2_lic_list(slurmdb_res_rec_t * rec,bool sync)210 static void _add_res_rec_2_lic_list(slurmdb_res_rec_t *rec, bool sync)
211 {
212 	licenses_t *license_entry = xmalloc(sizeof(licenses_t));
213 
214 	license_entry->name = xstrdup_printf("%s@%s", rec->name, rec->server);
215 	license_entry->total = ((rec->count *
216 				 rec->clus_res_rec->percent_allowed) / 100);
217 	license_entry->remote = sync ? 2 : 1;
218 
219 	list_push(license_list, license_entry);
220 	last_license_update = time(NULL);
221 }
222 
223 /* Initialize licenses on this system based upon slurm.conf */
license_init(char * licenses)224 extern int license_init(char *licenses)
225 {
226 	bool valid = true;
227 
228 	last_license_update = time(NULL);
229 
230 	slurm_mutex_lock(&license_mutex);
231 	if (license_list)
232 		fatal("license_list already defined");
233 
234 	license_list = _build_license_list(licenses, &valid);
235 	if (!valid)
236 		fatal("Invalid configured licenses: %s", licenses);
237 
238 	_licenses_print("init_license", license_list, NULL);
239 	slurm_mutex_unlock(&license_mutex);
240 	return SLURM_SUCCESS;
241 }
242 
243 /* Update licenses on this system based upon slurm.conf.
244  * Remove all previously allocated licenses */
license_update(char * licenses)245 extern int license_update(char *licenses)
246 {
247         ListIterator iter;
248         licenses_t *license_entry, *match;
249         List new_list;
250         bool valid = true;
251 
252         new_list = _build_license_list(licenses, &valid);
253         if (!valid)
254                 fatal("Invalid configured licenses: %s", licenses);
255 
256         slurm_mutex_lock(&license_mutex);
257         if (!license_list) {        /* no licenses before now */
258                 license_list = new_list;
259                 slurm_mutex_unlock(&license_mutex);
260                 return SLURM_SUCCESS;
261         }
262 
263         iter = list_iterator_create(license_list);
264         while ((license_entry = list_next(iter))) {
265 		/* Always add the remote ones, since we handle those
266 		   else where. */
267 		if (license_entry->remote) {
268 			list_remove(iter);
269 			if (!new_list)
270 				new_list = list_create(license_free_rec);
271 			license_entry->used = 0;
272 			list_append(new_list, license_entry);
273 			continue;
274 		}
275 		if (new_list)
276 			match = list_find_first(new_list, _license_find_rec,
277 						license_entry->name);
278 		else
279 			match = NULL;
280 
281                 if (!match) {
282                         info("license %s removed with %u in use",
283                              license_entry->name, license_entry->used);
284                 } else {
285                         if (license_entry->used > match->total) {
286                                 info("license %s count decreased",
287                                      match->name);
288                         }
289                 }
290         }
291         list_iterator_destroy(iter);
292 
293         FREE_NULL_LIST(license_list);
294         license_list = new_list;
295         _licenses_print("update_license", license_list, NULL);
296         slurm_mutex_unlock(&license_mutex);
297         return SLURM_SUCCESS;
298 }
299 
license_add_remote(slurmdb_res_rec_t * rec)300 extern void license_add_remote(slurmdb_res_rec_t *rec)
301 {
302 	licenses_t *license_entry;
303 	char *name;
304 
305 
306 	xassert(rec);
307 	xassert(rec->type == SLURMDB_RESOURCE_LICENSE);
308 
309 	name = xstrdup_printf("%s@%s", rec->name, rec->server);
310 
311 	slurm_mutex_lock(&license_mutex);
312 	if (!license_list) {
313 		/* If last_license_update then init already ran and we
314 		 * don't have any licenses defined in the slurm.conf
315 		 * so make the license_list.
316 		 */
317 		xassert(last_license_update);
318 		license_list = list_create(license_free_rec);
319 	}
320 
321 	license_entry = list_find_first(
322 		license_list, _license_find_remote_rec, name);
323 
324 	if (license_entry)
325 		error("license_add_remote: license %s already exists!", name);
326 	else
327 		_add_res_rec_2_lic_list(rec, 0);
328 
329 	xfree(name);
330 
331 	slurm_mutex_unlock(&license_mutex);
332 }
333 
license_update_remote(slurmdb_res_rec_t * rec)334 extern void license_update_remote(slurmdb_res_rec_t *rec)
335 {
336 	licenses_t *license_entry;
337 	char *name;
338 
339 	xassert(rec);
340 	xassert(rec->clus_res_rec);
341 	xassert(rec->type == SLURMDB_RESOURCE_LICENSE);
342 
343 	name = xstrdup_printf("%s@%s", rec->name, rec->server);
344 
345 	slurm_mutex_lock(&license_mutex);
346 	if (!license_list) {
347 		/* If last_license_update then init already ran and we
348 		 * don't have any licenses defined in the slurm.conf
349 		 * so make the license_list.
350 		 */
351 		xassert(last_license_update);
352 		license_list = list_create(license_free_rec);
353 	}
354 
355 	license_entry = list_find_first(
356 		license_list, _license_find_remote_rec, name);
357 
358 	if (!license_entry) {
359 		debug("license_update_remote: License '%s' not found, adding",
360 		      name);
361 		_add_res_rec_2_lic_list(rec, 0);
362 	} else {
363 		license_entry->total =
364 			((rec->count *
365 			  rec->clus_res_rec->percent_allowed) / 100);
366 		if (license_entry->used > license_entry->total) {
367 			info("license %s count decreased",
368 			     license_entry->name);
369 		}
370 	}
371 	last_license_update = time(NULL);
372 
373 	xfree(name);
374 
375 	slurm_mutex_unlock(&license_mutex);
376 }
377 
license_remove_remote(slurmdb_res_rec_t * rec)378 extern void license_remove_remote(slurmdb_res_rec_t *rec)
379 {
380 	licenses_t *license_entry;
381 	ListIterator iter;
382 	char *name;
383 
384 	xassert(rec);
385 	xassert(rec->type == SLURMDB_RESOURCE_LICENSE);
386 
387 	slurm_mutex_lock(&license_mutex);
388 	if (!license_list) {
389 		xassert(last_license_update);
390 		license_list = list_create(license_free_rec);
391 	}
392 
393 	name = xstrdup_printf("%s@%s", rec->name, rec->server);
394 
395 	iter = list_iterator_create(license_list);
396 	while ((license_entry = list_next(iter))) {
397 		if (!license_entry->remote)
398 			continue;
399 		if (!xstrcmp(license_entry->name, name)) {
400 			info("license_remove_remote: license %s "
401 			     "removed with %u in use",
402 			     license_entry->name, license_entry->used);
403 			list_delete_item(iter);
404 			last_license_update = time(NULL);
405 			break;
406 		}
407 	}
408 	list_iterator_destroy(iter);
409 
410 	if (!license_entry)
411 		error("license_remote_remote: License '%s' not found", name);
412 
413 	xfree(name);
414 	slurm_mutex_unlock(&license_mutex);
415 }
416 
license_sync_remote(List res_list)417 extern void license_sync_remote(List res_list)
418 {
419 	slurmdb_res_rec_t *rec = NULL;
420 	licenses_t *license_entry;
421 	ListIterator iter;
422 
423 	slurm_mutex_lock(&license_mutex);
424 	if (res_list && !license_list) {
425 		xassert(last_license_update);
426 		license_list = list_create(license_free_rec);
427 	}
428 
429 	iter = list_iterator_create(license_list);
430 	if (res_list) {
431 		ListIterator iter2 = list_iterator_create(res_list);
432 		while ((rec = list_next(iter2))) {
433 			char *name;
434 			if (rec->type != SLURMDB_RESOURCE_LICENSE)
435 				continue;
436 			name = xstrdup_printf("%s@%s", rec->name, rec->server);
437 			while ((license_entry = list_next(iter))) {
438 				if (!license_entry->remote)
439 					continue;
440 				if (!xstrcmp(license_entry->name, name)) {
441 					license_entry->remote = 2;
442 					license_entry->total =
443 						((rec->count *
444 						  rec->clus_res_rec->
445 						  percent_allowed) / 100);
446 					if (license_entry->used >
447 					    license_entry->total) {
448 						info("license %s count "
449 						     "decreased",
450 						     license_entry->name);
451 					}
452 					last_license_update = time(NULL);
453 					break;
454 				}
455 			}
456 			xfree(name);
457 			if (!license_entry)
458 				_add_res_rec_2_lic_list(rec, 1);
459 			list_iterator_reset(iter);
460 		}
461 		list_iterator_destroy(iter2);
462 	}
463 
464 	while ((license_entry = list_next(iter))) {
465 		if (!license_entry->remote)
466 			continue;
467 		else if (license_entry->remote == 1) {
468 			info("license_remove_remote: license %s "
469 			     "removed with %u in use",
470 			     license_entry->name, license_entry->used);
471 			list_delete_item(iter);
472 			last_license_update = time(NULL);
473 		} else if (license_entry->remote == 2)
474 			license_entry->remote = 1;
475 	}
476 	list_iterator_destroy(iter);
477 
478 	slurm_mutex_unlock(&license_mutex);
479 }
480 
481 /* Free memory associated with licenses on this system */
license_free(void)482 extern void license_free(void)
483 {
484 	slurm_mutex_lock(&license_mutex);
485 	FREE_NULL_LIST(license_list);
486 	slurm_mutex_unlock(&license_mutex);
487 }
488 
489 /*
490  * license_validate - Test if the required licenses are valid
491  * IN licenses - required licenses
492  * IN validate_configured - if true, validate that there are enough configured
493  *                          licenses for the requested amount.
494  * IN validate_existing - if true, validate that licenses exist, otherwise don't
495  *                        return them in the final list.
496  * OUT tres_req_cnt - appropriate counts for each requested gres,
497  *                    since this only matters on pending jobs you can
498  *                    send in NULL otherwise
499  * OUT valid - true if required licenses are valid and a sufficient number
500  *             are configured (though not necessarily available now)
501  * RET license_list, must be destroyed by caller
502  */
license_validate(char * licenses,bool validate_configured,bool validate_existing,uint64_t * tres_req_cnt,bool * valid)503 extern List license_validate(char *licenses, bool validate_configured,
504 			     bool validate_existing,
505 			     uint64_t *tres_req_cnt, bool *valid)
506 {
507 	ListIterator iter;
508 	licenses_t *license_entry, *match;
509 	List job_license_list;
510 	static bool first_run = 1;
511 	static slurmdb_tres_rec_t tres_req;
512 	int tres_pos;
513 
514 	job_license_list = _build_license_list(licenses, valid);
515 	if (!job_license_list)
516 		return job_license_list;
517 
518 	/* we only need to init this once */
519 	if (first_run) {
520 		first_run = 0;
521 		memset(&tres_req, 0, sizeof(slurmdb_tres_rec_t));
522 		tres_req.type = "license";
523 	}
524 
525 	slurm_mutex_lock(&license_mutex);
526 	_licenses_print("request_license", job_license_list, NULL);
527 	iter = list_iterator_create(job_license_list);
528 	while ((license_entry = list_next(iter))) {
529 		if (license_list) {
530 			match = list_find_first(license_list,
531 						_license_find_rec,
532 						license_entry->name);
533 		} else
534 			match = NULL;
535 		if (!match) {
536 			debug("License name requested (%s) does not exist",
537 			      license_entry->name);
538 			if (!validate_existing) {
539 				list_delete_item(iter);
540 				continue;
541 			}
542 			*valid = false;
543 			break;
544 		} else if (validate_configured &&
545 			   (license_entry->total > match->total)) {
546 			debug("Licenses count requested higher than configured "
547 			      "(%s: %u > %u)",
548 			      match->name, license_entry->total, match->total);
549 			*valid = false;
550 			break;
551 		}
552 
553 		if (tres_req_cnt) {
554 			tres_req.name = license_entry->name;
555 			if ((tres_pos = assoc_mgr_find_tres_pos(
556 				     &tres_req, false)) != -1)
557 				tres_req_cnt[tres_pos] =
558 					(uint64_t)license_entry->total;
559 		}
560 	}
561 	list_iterator_destroy(iter);
562 	slurm_mutex_unlock(&license_mutex);
563 
564 	if (!(*valid)) {
565 		FREE_NULL_LIST(job_license_list);
566 	}
567 	return job_license_list;
568 }
569 
570 /*
571  * license_job_merge - The licenses from one job have just been merged into
572  *	another job by appending one job's licenses to another, possibly
573  *	including duplicate names. Reconstruct this job's licenses and
574  *	license_list fields to eliminate duplicates.
575  */
license_job_merge(job_record_t * job_ptr)576 extern void license_job_merge(job_record_t *job_ptr)
577 {
578 	bool valid = true;
579 
580 	FREE_NULL_LIST(job_ptr->license_list);
581 	job_ptr->license_list = _build_license_list(job_ptr->licenses, &valid);
582 	xfree(job_ptr->licenses);
583 	job_ptr->licenses = license_list_to_string(job_ptr->license_list);
584 }
585 
586 /*
587  * license_job_test - Test if the licenses required for a job are available
588  * IN job_ptr - job identification
589  * IN when    - time to check
590  * IN reboot    - true if node reboot required to start job
591  * RET: SLURM_SUCCESS, EAGAIN (not available now), SLURM_ERROR (never runnable)
592  */
license_job_test(job_record_t * job_ptr,time_t when,bool reboot)593 extern int license_job_test(job_record_t *job_ptr, time_t when, bool reboot)
594 {
595 	ListIterator iter;
596 	licenses_t *license_entry, *match;
597 	int rc = SLURM_SUCCESS, resv_licenses;
598 
599 	if (!job_ptr->license_list)	/* no licenses needed */
600 		return rc;
601 
602 	slurm_mutex_lock(&license_mutex);
603 	iter = list_iterator_create(job_ptr->license_list);
604 	while ((license_entry = list_next(iter))) {
605 		match = list_find_first(license_list, _license_find_rec,
606 			license_entry->name);
607 		if (!match) {
608 			error("could not find license %s for job %u",
609 			      license_entry->name, job_ptr->job_id);
610 			rc = SLURM_ERROR;
611 			break;
612 		} else if (license_entry->total > match->total) {
613 			info("job %u wants more %s licenses than configured",
614 			     job_ptr->job_id, match->name);
615 			rc = SLURM_ERROR;
616 			break;
617 		} else if ((license_entry->total + match->used) >
618 			   match->total) {
619 			rc = EAGAIN;
620 			break;
621 		} else {
622 			/* Assume node reboot required since we have not
623 			 * selected the compute nodes yet */
624 			resv_licenses = job_test_lic_resv(job_ptr,
625 							  license_entry->name,
626 							  when, reboot);
627 			if ((license_entry->total + match->used +
628 			     resv_licenses) > match->total) {
629 				rc = EAGAIN;
630 				break;
631 			}
632 		}
633 	}
634 	list_iterator_destroy(iter);
635 	slurm_mutex_unlock(&license_mutex);
636 	return rc;
637 }
638 
639 /*
640  * license_job_copy - create a copy of a job's license list
641  * IN license_list_src - job license list to be copied
642  * RET a copy of the original job license list
643  */
license_job_copy(List license_list_src)644 extern List license_job_copy(List license_list_src)
645 {
646 	licenses_t *license_entry_src, *license_entry_dest;
647 	ListIterator iter;
648 	List license_list_dest = NULL;
649 
650 	if (!license_list_src)
651 		return license_list_dest;
652 
653 	license_list_dest = list_create(license_free_rec);
654 	iter = list_iterator_create(license_list_src);
655 	while ((license_entry_src = list_next(iter))) {
656 		license_entry_dest = xmalloc(sizeof(licenses_t));
657 		license_entry_dest->name = xstrdup(license_entry_src->name);
658 		license_entry_dest->total = license_entry_src->total;
659 		list_push(license_list_dest, license_entry_dest);
660 	}
661 	list_iterator_destroy(iter);
662 	return license_list_dest;
663 }
664 
665 /*
666  * license_job_get - Get the licenses required for a job
667  * IN job_ptr - job identification
668  * RET SLURM_SUCCESS or failure code
669  */
license_job_get(job_record_t * job_ptr)670 extern int license_job_get(job_record_t *job_ptr)
671 {
672 	ListIterator iter;
673 	licenses_t *license_entry, *match;
674 	int rc = SLURM_SUCCESS;
675 
676 	if (!job_ptr->license_list)	/* no licenses needed */
677 		return rc;
678 
679 	last_license_update = time(NULL);
680 
681 	slurm_mutex_lock(&license_mutex);
682 	iter = list_iterator_create(job_ptr->license_list);
683 	while ((license_entry = list_next(iter))) {
684 		match = list_find_first(license_list, _license_find_rec,
685 			license_entry->name);
686 		if (match) {
687 			match->used += license_entry->total;
688 			license_entry->used += license_entry->total;
689 		} else {
690 			error("could not find license %s for job %u",
691 			      license_entry->name, job_ptr->job_id);
692 			rc = SLURM_ERROR;
693 		}
694 	}
695 	list_iterator_destroy(iter);
696 	_licenses_print("acquire_license", license_list, job_ptr);
697 	slurm_mutex_unlock(&license_mutex);
698 	return rc;
699 }
700 
701 /*
702  * license_job_return - Return the licenses allocated to a job
703  * IN job_ptr - job identification
704  * RET SLURM_SUCCESS or failure code
705  */
license_job_return(job_record_t * job_ptr)706 extern int license_job_return(job_record_t *job_ptr)
707 {
708 	ListIterator iter;
709 	licenses_t *license_entry, *match;
710 	int rc = SLURM_SUCCESS;
711 
712 	if (!job_ptr->license_list)	/* no licenses needed */
713 		return rc;
714 
715 	last_license_update = time(NULL);
716 	trace_job(job_ptr, __func__, "");
717 	slurm_mutex_lock(&license_mutex);
718 	iter = list_iterator_create(job_ptr->license_list);
719 	while ((license_entry = list_next(iter))) {
720 		match = list_find_first(license_list, _license_find_rec,
721 			license_entry->name);
722 		if (match) {
723 			if (match->used >= license_entry->total)
724 				match->used -= license_entry->total;
725 			else {
726 				error("%s: license use count underflow for %s",
727 				      __func__, match->name);
728 				match->used = 0;
729 				rc = SLURM_ERROR;
730 			}
731 			license_entry->used = 0;
732 		} else {
733 			/* This can happen after a reconfiguration */
734 			error("%s: job returning unknown license name %s",
735 			      __func__, license_entry->name);
736 		}
737 	}
738 	list_iterator_destroy(iter);
739 	_licenses_print("return_license", license_list, job_ptr);
740 	slurm_mutex_unlock(&license_mutex);
741 	return rc;
742 }
743 
744 /*
745  * license_list_overlap - test if there is any overlap in licenses
746  *	names found in the two lists
747  */
license_list_overlap(List list_1,List list_2)748 extern bool license_list_overlap(List list_1, List list_2)
749 {
750 	ListIterator iter;
751 	licenses_t *license_entry;
752 	bool match = false;
753 
754 	if (!list_1 || !list_2)
755 		return false;
756 
757 	iter = list_iterator_create(list_1);
758 	while ((license_entry = list_next(iter))) {
759 		if (list_find_first(list_2, _license_find_rec,
760 				    license_entry->name)) {
761 			match = true;
762 			break;
763 		}
764 	}
765 	list_iterator_destroy(iter);
766 
767 	return match;
768 }
769 
770 /* pack_all_licenses()
771  *
772  * Return license counters to the library.
773  */
774 extern void
get_all_license_info(char ** buffer_ptr,int * buffer_size,uid_t uid,uint16_t protocol_version)775 get_all_license_info(char **buffer_ptr,
776                      int *buffer_size,
777                      uid_t uid,
778                      uint16_t protocol_version)
779 {
780 	ListIterator iter;
781 	licenses_t *lic_entry;
782 	uint32_t lics_packed;
783 	int tmp_offset;
784 	Buf buffer;
785 	time_t now = time(NULL);
786 
787 	debug2("%s: calling for all licenses", __func__);
788 
789 	buffer_ptr[0] = NULL;
790 	*buffer_size = 0;
791 
792 	buffer = init_buf(BUF_SIZE);
793 
794 	/* write header: version and time
795 	 */
796 	lics_packed = 0;
797 	pack32(lics_packed, buffer);
798 	pack_time(now, buffer);
799 
800 	slurm_mutex_lock(&license_mutex);
801 	if (license_list) {
802 		iter = list_iterator_create(license_list);
803 		while ((lic_entry = list_next(iter))) {
804 			/* Now encode the license data structure.
805 			 */
806 			_pack_license(lic_entry, buffer, protocol_version);
807 			++lics_packed;
808 		}
809 		list_iterator_destroy(iter);
810 	}
811 
812 	slurm_mutex_unlock(&license_mutex);
813 	debug2("%s: processed %d licenses", __func__, lics_packed);
814 
815 	/* put the real record count in the message body header
816 	 */
817 	tmp_offset = get_buf_offset(buffer);
818 	set_buf_offset(buffer, 0);
819 	pack32(lics_packed, buffer);
820 	set_buf_offset(buffer, tmp_offset);
821 
822 	*buffer_size = get_buf_offset(buffer);
823 	buffer_ptr[0] = xfer_buf_data(buffer);
824 }
825 
get_total_license_cnt(char * name)826 extern uint32_t get_total_license_cnt(char *name)
827 {
828 	uint32_t count = 0;
829 	licenses_t *lic;
830 
831 	slurm_mutex_lock(&license_mutex);
832 	if (license_list) {
833 		lic = list_find_first(
834 			license_list, _license_find_rec, name);
835 
836 		if (lic)
837 			count = lic->total;
838 	}
839 	slurm_mutex_unlock(&license_mutex);
840 
841 	return count;
842 }
843 
844 /* node_read should be locked before coming in here
845  * returns 1 if change happened.
846  */
licenses_2_tres_str(List license_list)847 extern char *licenses_2_tres_str(List license_list)
848 {
849 	ListIterator itr;
850 	slurmdb_tres_rec_t *tres_rec;
851 	licenses_t *license_entry;
852 	char *tres_str = NULL;
853 	static bool first_run = 1;
854 	static slurmdb_tres_rec_t tres_req;
855 	assoc_mgr_lock_t locks = { .tres = READ_LOCK };
856 
857 	if (!license_list)
858 		return NULL;
859 
860 	/* we only need to init this once */
861 	if (first_run) {
862 		first_run = 0;
863 		memset(&tres_req, 0, sizeof(slurmdb_tres_rec_t));
864 		tres_req.type = "license";
865 	}
866 
867 	assoc_mgr_lock(&locks);
868 	itr = list_iterator_create(license_list);
869 	while ((license_entry = list_next(itr))) {
870 		tres_req.name = license_entry->name;
871 		if (!(tres_rec = assoc_mgr_find_tres_rec(&tres_req)))
872 			continue; /* not tracked */
873 
874 		if (slurmdb_find_tres_count_in_string(
875 			    tres_str, tres_rec->id) != INFINITE64)
876 			continue; /* already handled */
877 		/* New license */
878 		xstrfmtcat(tres_str, "%s%u=%"PRIu64,
879 			   tres_str ? "," : "",
880 			   tres_rec->id, (uint64_t)license_entry->total);
881 	}
882 	list_iterator_destroy(itr);
883 	assoc_mgr_unlock(&locks);
884 
885 	return tres_str;
886 }
887 
license_set_job_tres_cnt(List license_list,uint64_t * tres_cnt,bool locked)888 extern void license_set_job_tres_cnt(List license_list,
889 				     uint64_t *tres_cnt,
890 				     bool locked)
891 {
892 	ListIterator itr;
893 	licenses_t *license_entry;
894 	static bool first_run = 1;
895 	static slurmdb_tres_rec_t tres_rec;
896 	int tres_pos;
897 	assoc_mgr_lock_t locks = { .tres = READ_LOCK };
898 
899 	/* we only need to init this once */
900 	if (first_run) {
901 		first_run = 0;
902 		memset(&tres_rec, 0, sizeof(slurmdb_tres_rec_t));
903 		tres_rec.type = "license";
904 	}
905 
906 	if (!license_list || !tres_cnt)
907 		return;
908 
909 	if (!locked)
910 		assoc_mgr_lock(&locks);
911 
912 	itr = list_iterator_create(license_list);
913 	while ((license_entry = list_next(itr))) {
914 		tres_rec.name = license_entry->name;
915 		if ((tres_pos = assoc_mgr_find_tres_pos(
916 			     &tres_rec, locked)) != -1)
917 			tres_cnt[tres_pos] = (uint64_t)license_entry->total;
918 	}
919 	list_iterator_destroy(itr);
920 
921 	if (!locked)
922 		assoc_mgr_unlock(&locks);
923 
924 	return;
925 }
926 
927 /* pack_license()
928  *
929  * Encode the licenses data structure.
930  *
931  *	char *		name;
932  *	uint32_t	total;
933  *	uint32_t	used;
934  *	uint8_t 	remote;
935  *
936  */
937 static void
_pack_license(struct licenses * lic,Buf buffer,uint16_t protocol_version)938 _pack_license(struct licenses *lic, Buf buffer, uint16_t protocol_version)
939 {
940 	if (protocol_version >= SLURM_MIN_PROTOCOL_VERSION) {
941 		packstr(lic->name, buffer);
942 		pack32(lic->total, buffer);
943 		pack32(lic->used, buffer);
944 		pack8(lic->remote, buffer);
945 	} else {
946 		error("\
947 %s: protocol_version %hu not supported", __func__, protocol_version);
948 	}
949 }
950