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