1 /*
2 * Copyright (c) 2004-2005 The Trustees of Indiana University and Indiana
3 * University Research and Technology
4 * Corporation. All rights reserved.
5 * Copyright (c) 2004-2005 The University of Tennessee and The University
6 * of Tennessee Research Foundation. All rights
7 * reserved.
8 * Copyright (c) 2004-2005 High Performance Computing Center Stuttgart,
9 * University of Stuttgart. All rights reserved.
10 * Copyright (c) 2004-2005 The Regents of the University of California.
11 * All rights reserved.
12 * Copyright (c) 2013 Cisco Systems, Inc. All rights reserved.
13 * Copyright (c) 2014-2017 Intel, Inc. All rights reserved.
14 * Copyright (c) 2015 Research Organization for Information Science
15 * and Technology (RIST). All rights reserved.
16 * Copyright (c) 2016 IBM Corporation. All rights reserved.
17 * $COPYRIGHT$
18 *
19 * Additional copyrights may follow
20 *
21 * $HEADER$
22 */
23
24 #include "orte_config.h"
25
26 #include <string.h>
27 #include <ctype.h>
28
29 #include "orte/constants.h"
30 #include "orte/types.h"
31
32 #include "orte/util/show_help.h"
33 #include "opal/util/argv.h"
34 #include "opal/util/if.h"
35 #include "opal/util/net.h"
36
37 #include "orte/mca/ras/base/base.h"
38 #include "orte/mca/plm/plm_types.h"
39 #include "orte/mca/errmgr/errmgr.h"
40 #include "orte/util/proc_info.h"
41 #include "orte/runtime/orte_globals.h"
42
43 #include "dash_host.h"
44
45 /* we can only enter this routine if no other allocation
46 * was found, so we only need to know that finding any
47 * relative node syntax should generate an immediate error
48 */
orte_util_add_dash_host_nodes(opal_list_t * nodes,char * hosts,bool allocating)49 int orte_util_add_dash_host_nodes(opal_list_t *nodes,
50 char *hosts, bool allocating)
51 {
52 opal_list_item_t *item, *itm;
53 orte_std_cntr_t i, j, k;
54 int rc, nodeidx;
55 char **host_argv=NULL;
56 char **mapped_nodes = NULL, **mini_map, *ndname;
57 orte_node_t *node, *nd;
58 opal_list_t adds;
59 bool found;
60 int slots=0;
61 bool slots_given;
62 char *cptr, *ptr;
63
64 OPAL_OUTPUT_VERBOSE((1, orte_ras_base_framework.framework_output,
65 "%s dashhost: parsing args %s",
66 ORTE_NAME_PRINT(ORTE_PROC_MY_NAME), hosts));
67
68 OBJ_CONSTRUCT(&adds, opal_list_t);
69 host_argv = opal_argv_split(hosts, ',');
70
71 /* Accumulate all of the host name mappings */
72 for (j = 0; j < opal_argv_count(host_argv); ++j) {
73 mini_map = opal_argv_split(host_argv[j], ',');
74
75 if (mapped_nodes == NULL) {
76 mapped_nodes = mini_map;
77 } else {
78 for (k = 0; NULL != mini_map[k]; ++k) {
79 rc = opal_argv_append_nosize(&mapped_nodes,
80 mini_map[k]);
81 if (OPAL_SUCCESS != rc) {
82 opal_argv_free(host_argv);
83 opal_argv_free(mini_map);
84 goto cleanup;
85 }
86 }
87 opal_argv_free(mini_map);
88 }
89 }
90 opal_argv_free(host_argv);
91 mini_map = NULL;
92
93 /* Did we find anything? If not, then do nothing */
94 if (NULL == mapped_nodes) {
95 rc = ORTE_SUCCESS;
96 goto cleanup;
97 }
98
99 for (i = 0; NULL != mapped_nodes[i]; ++i) {
100 /* if the specified node contains a relative node syntax,
101 * and we are allocating, then ignore it
102 */
103 if ('+' == mapped_nodes[i][0]) {
104 if (!allocating) {
105 if ('e' == mapped_nodes[i][1] ||
106 'E' == mapped_nodes[i][1]) {
107 /* request for empty nodes - do they want
108 * all of them?
109 */
110 if (NULL != (cptr = strchr(mapped_nodes[i], ':'))) {
111 /* the colon indicates a specific # are requested */
112 ++cptr;
113 j = strtoul(cptr, NULL, 10);
114 } else if ('\0' != mapped_nodes[0][2]) {
115 j = strtoul(&mapped_nodes[0][2], NULL, 10);
116 } else {
117 /* add them all */
118 j = orte_node_pool->size;
119 }
120 for (k=0; 0 < j && k < orte_node_pool->size; k++) {
121 if (NULL != (node = (orte_node_t*)opal_pointer_array_get_item(orte_node_pool, k))) {
122 if (0 == node->num_procs) {
123 opal_argv_append_nosize(&mini_map, node->name);
124 --j;
125 }
126 }
127 }
128 } else if ('n' == mapped_nodes[i][1] ||
129 'N' == mapped_nodes[i][1]) {
130 /* they want a specific relative node #, so
131 * look it up on global pool
132 */
133 if ('\0' == mapped_nodes[i][2]) {
134 /* they forgot to tell us the # */
135 orte_show_help("help-dash-host.txt", "dash-host:invalid-relative-node-syntax",
136 true, mapped_nodes[i]);
137 rc = ORTE_ERR_SILENT;
138 goto cleanup;
139 }
140 nodeidx = strtol(&mapped_nodes[i][2], NULL, 10);
141 if (nodeidx < 0 ||
142 nodeidx > (int)orte_node_pool->size) {
143 /* this is an error */
144 orte_show_help("help-dash-host.txt", "dash-host:relative-node-out-of-bounds",
145 true, nodeidx, mapped_nodes[i]);
146 rc = ORTE_ERR_SILENT;
147 goto cleanup;
148 }
149 /* if the HNP is not allocated, then we need to
150 * adjust the index as the node pool is offset
151 * by one
152 */
153 if (!orte_hnp_is_allocated) {
154 nodeidx++;
155 }
156 /* see if that location is filled */
157
158 if (NULL == (node = (orte_node_t *) opal_pointer_array_get_item(orte_node_pool, nodeidx))) {
159 /* this is an error */
160 orte_show_help("help-dash-host.txt", "dash-host:relative-node-not-found",
161 true, nodeidx, mapped_nodes[i]);
162 rc = ORTE_ERR_SILENT;
163 goto cleanup;
164 }
165 /* add this node to the list */
166 opal_argv_append_nosize(&mini_map, node->name);
167 } else {
168 /* invalid relative node syntax */
169 orte_show_help("help-dash-host.txt", "dash-host:invalid-relative-node-syntax",
170 true, mapped_nodes[i]);
171 rc = ORTE_ERR_SILENT;
172 goto cleanup;
173 }
174 }
175 } else {
176 /* just one node was given */
177 opal_argv_append_nosize(&mini_map, mapped_nodes[i]);
178 }
179 }
180 if (NULL == mini_map) {
181 rc = ORTE_SUCCESS;
182 goto cleanup;
183 }
184
185 /* go through the names found and
186 add them to the host list. If they're not unique, then
187 bump the slots count for each duplicate */
188 for (i=0; NULL != mini_map[i]; i++) {
189 OPAL_OUTPUT_VERBOSE((1, orte_ras_base_framework.framework_output,
190 "%s dashhost: working node %s",
191 ORTE_NAME_PRINT(ORTE_PROC_MY_NAME), mini_map[i]));
192
193 /* see if the node contains the number of slots */
194 slots_given = false;
195 if (NULL != (cptr = strchr(mini_map[i], ':'))) {
196 *cptr = '\0';
197 ++cptr;
198 if ('*' == *cptr || 0 == strcmp(cptr, "auto")) {
199 /* auto-detect #slots */
200 slots = -1;
201 slots_given = false;
202 } else {
203 slots = strtol(cptr, NULL, 10);
204 slots_given = true;
205 }
206 }
207
208 /* check for local name */
209 if (orte_ifislocal(mini_map[i])) {
210 ndname = orte_process_info.nodename;
211 } else {
212 ndname = mini_map[i];
213 }
214
215 // Strip off the FQDN if present, ignore IP addresses
216 if( !orte_keep_fqdn_hostnames && !opal_net_isaddr(ndname) ) {
217 if (NULL != (ptr = strchr(ndname, '.'))) {
218 *ptr = '\0';
219 }
220 }
221 /* remove any modifier */
222 if (NULL != (ptr = strchr(ndname, ':'))) {
223 *ptr = '\0';
224 }
225 /* see if the node is already on the list */
226 found = false;
227 OPAL_LIST_FOREACH(node, &adds, orte_node_t) {
228 if (0 == strcmp(node->name, ndname)) {
229 found = true;
230 if (slots_given) {
231 node->slots += slots;
232 if (0 < slots) {
233 ORTE_FLAG_SET(node, ORTE_NODE_FLAG_SLOTS_GIVEN);
234 }
235 } else {
236 ++node->slots;
237 ORTE_FLAG_SET(node, ORTE_NODE_FLAG_SLOTS_GIVEN);
238 }
239 OPAL_OUTPUT_VERBOSE((1, orte_ras_base_framework.framework_output,
240 "%s dashhost: node %s already on list - slots %d",
241 ORTE_NAME_PRINT(ORTE_PROC_MY_NAME), node->name, node->slots));
242 break;
243 }
244 }
245
246 /* If we didn't find it, add it to the list */
247 if (!found) {
248 node = OBJ_NEW(orte_node_t);
249 if (NULL == node) {
250 opal_argv_free(mapped_nodes);
251 return ORTE_ERR_OUT_OF_RESOURCE;
252 }
253 node->name = strdup(ndname);
254 OPAL_OUTPUT_VERBOSE((1, orte_ras_base_framework.framework_output,
255 "%s dashhost: added node %s to list - slots %d",
256 ORTE_NAME_PRINT(ORTE_PROC_MY_NAME), node->name, slots));
257 node->state = ORTE_NODE_STATE_UP;
258 node->slots_inuse = 0;
259 node->slots_max = 0;
260 if (slots_given) {
261 node->slots = slots;
262 if (0 < slots) {
263 ORTE_FLAG_SET(node, ORTE_NODE_FLAG_SLOTS_GIVEN);
264 }
265 } else if (slots < 0) {
266 node->slots = 0;
267 ORTE_FLAG_UNSET(node, ORTE_NODE_FLAG_SLOTS_GIVEN);
268 } else {
269 node->slots = 1;
270 ORTE_FLAG_SET(node, ORTE_NODE_FLAG_SLOTS_GIVEN);
271 }
272 opal_list_append(&adds, &node->super);
273 }
274 }
275 opal_argv_free(mini_map);
276
277 /* transfer across all unique nodes */
278 while (NULL != (item = opal_list_remove_first(&adds))) {
279 nd = (orte_node_t*)item;
280 found = false;
281 for (itm = opal_list_get_first(nodes);
282 itm != opal_list_get_end(nodes);
283 itm = opal_list_get_next(itm)) {
284 node = (orte_node_t*)itm;
285 if (0 == strcmp(nd->name, node->name)) {
286 found = true;
287 OPAL_OUTPUT_VERBOSE((1, orte_ras_base_framework.framework_output,
288 "%s dashhost: found existing node %s on input list - adding slots",
289 ORTE_NAME_PRINT(ORTE_PROC_MY_NAME), node->name));
290 if (ORTE_FLAG_TEST(nd, ORTE_NODE_FLAG_SLOTS_GIVEN)) {
291 /* transfer across the number of slots */
292 node->slots = nd->slots;
293 ORTE_FLAG_SET(node, ORTE_NODE_FLAG_SLOTS_GIVEN);
294 }
295 break;
296 }
297 }
298 if (!found) {
299 OPAL_OUTPUT_VERBOSE((1, orte_ras_base_framework.framework_output,
300 "%s dashhost: adding node %s with %d slots to final list",
301 ORTE_NAME_PRINT(ORTE_PROC_MY_NAME), nd->name, nd->slots));
302 opal_list_append(nodes, &nd->super);
303 } else {
304 OBJ_RELEASE(item);
305 }
306 }
307
308 // Managed allocation: Update the node pool slots
309 // with what was asked for in the host list.
310 if(orte_managed_allocation) {
311 orte_node_t *node_from_pool = NULL;
312 for (i = 0; i < orte_node_pool->size; i++) {
313 if (NULL == (node_from_pool = (orte_node_t*)opal_pointer_array_get_item(orte_node_pool, i))) {
314 continue;
315 }
316 for (itm = opal_list_get_first(nodes);
317 itm != opal_list_get_end(nodes);
318 itm = opal_list_get_next(itm)) {
319 node = (orte_node_t*) itm;
320 if (0 == strcmp(node_from_pool->name, node->name)) {
321 if(node->slots < node_from_pool -> slots) {
322 node_from_pool->slots = node->slots;
323 }
324 break;
325 }
326 // There's no need to check that this host exists in the pool. That
327 // should have already been checked at this point.
328 }
329 }
330 }
331 rc = ORTE_SUCCESS;
332
333 cleanup:
334 if (NULL != mapped_nodes) {
335 opal_argv_free(mapped_nodes);
336 }
337 OPAL_LIST_DESTRUCT(&adds);
338
339 return rc;
340 }
341
342
343 /* the -host option can always be used in both absolute
344 * and relative mode, so we have to check for pre-existing
345 * allocations if we are to use relative node syntax
346 */
parse_dash_host(char *** mapped_nodes,char * hosts)347 static int parse_dash_host(char ***mapped_nodes, char *hosts)
348 {
349 orte_std_cntr_t j, k;
350 int rc=ORTE_SUCCESS;
351 char **mini_map=NULL, *cptr;
352 int nodeidx;
353 orte_node_t *node;
354 char **host_argv=NULL;
355
356 host_argv = opal_argv_split(hosts, ',');
357
358 /* Accumulate all of the host name mappings */
359 for (j = 0; j < opal_argv_count(host_argv); ++j) {
360 mini_map = opal_argv_split(host_argv[j], ',');
361
362 for (k = 0; NULL != mini_map[k]; ++k) {
363 if ('+' == mini_map[k][0]) {
364 /* see if we specified empty nodes */
365 if ('e' == mini_map[k][1] ||
366 'E' == mini_map[k][1]) {
367 /* request for empty nodes - do they want
368 * all of them?
369 */
370 if (NULL != (cptr = strchr(mini_map[k], ':'))) {
371 /* the colon indicates a specific # are requested */
372 *cptr = '*';
373 opal_argv_append_nosize(mapped_nodes, cptr);
374 } else {
375 /* add a marker to the list */
376 opal_argv_append_nosize(mapped_nodes, "*");
377 }
378 } else if ('n' == mini_map[k][1] ||
379 'N' == mini_map[k][1]) {
380 /* they want a specific relative node #, so
381 * look it up on global pool
382 */
383 nodeidx = strtol(&mini_map[k][2], NULL, 10);
384 if (nodeidx < 0 ||
385 nodeidx > (int)orte_node_pool->size) {
386 /* this is an error */
387 orte_show_help("help-dash-host.txt", "dash-host:relative-node-out-of-bounds",
388 true, nodeidx, mini_map[k]);
389 rc = ORTE_ERR_SILENT;
390 goto cleanup;
391 }
392 /* if the HNP is not allocated, then we need to
393 * adjust the index as the node pool is offset
394 * by one
395 */
396 if (!orte_hnp_is_allocated) {
397 nodeidx++;
398 }
399 /* see if that location is filled */
400
401 if (NULL == (node = (orte_node_t *) opal_pointer_array_get_item(orte_node_pool, nodeidx))) {
402 /* this is an error */
403 orte_show_help("help-dash-host.txt", "dash-host:relative-node-not-found",
404 true, nodeidx, mini_map[k]);
405 rc = ORTE_ERR_SILENT;
406 goto cleanup;
407 }
408 /* add this node to the list */
409 opal_argv_append_nosize(mapped_nodes, node->name);
410 } else {
411 /* invalid relative node syntax */
412 orte_show_help("help-dash-host.txt", "dash-host:invalid-relative-node-syntax",
413 true, mini_map[k]);
414 rc = ORTE_ERR_SILENT;
415 goto cleanup;
416 }
417 } else { /* non-relative syntax - add to list */
418 /* remove any modifier */
419 if (NULL != (cptr = strchr(mini_map[k], ':'))) {
420 *cptr = '\0';
421 }
422 /* check for local alias */
423 if (orte_ifislocal(mini_map[k])) {
424 opal_argv_append_nosize(mapped_nodes, orte_process_info.nodename);
425 } else {
426 opal_argv_append_nosize(mapped_nodes, mini_map[k]);
427 }
428 }
429 }
430 opal_argv_free(mini_map);
431 mini_map = NULL;
432 }
433
434 cleanup:
435 if (NULL != host_argv) {
436 opal_argv_free(host_argv);
437 }
438 if (NULL != mini_map) {
439 opal_argv_free(mini_map);
440 }
441 return rc;
442 }
443
orte_util_filter_dash_host_nodes(opal_list_t * nodes,char * hosts,bool remove)444 int orte_util_filter_dash_host_nodes(opal_list_t *nodes,
445 char *hosts,
446 bool remove)
447 {
448 opal_list_item_t* item;
449 opal_list_item_t *next;
450 orte_std_cntr_t i, j, len_mapped_node=0;
451 int rc, test;
452 char **mapped_nodes = NULL;
453 orte_node_t *node;
454 int num_empty=0;
455 opal_list_t keep;
456 bool want_all_empty=false;
457 char *cptr;
458 size_t lst, lmn;
459
460 /* if the incoming node list is empty, then there
461 * is nothing to filter!
462 */
463 if (opal_list_is_empty(nodes)) {
464 return ORTE_SUCCESS;
465 }
466
467 if (ORTE_SUCCESS != (rc = parse_dash_host(&mapped_nodes, hosts))) {
468 ORTE_ERROR_LOG(rc);
469 return rc;
470 }
471 /* Did we find anything? If not, then do nothing */
472 if (NULL == mapped_nodes) {
473 return ORTE_SUCCESS;
474 }
475
476 /* NOTE: The following logic is based on knowing that
477 * any node can only be included on the incoming
478 * nodes list ONCE.
479 */
480
481 len_mapped_node = opal_argv_count(mapped_nodes);
482 /* setup a working list so we can put the final list
483 * of nodes in order. This way, if the user specifies a
484 * set of nodes, we will use them in the order in which
485 * they were specifed. Note that empty node requests
486 * will always be appended to the end
487 */
488 OBJ_CONSTRUCT(&keep, opal_list_t);
489
490 for (i = 0; i < len_mapped_node; ++i) {
491 /* check if we are supposed to add some number of empty
492 * nodes here
493 */
494 if ('*' == mapped_nodes[i][0]) {
495 /* if there is a number after the '*', then we are
496 * to insert a specific # of nodes
497 */
498 if ('\0' == mapped_nodes[i][1]) {
499 /* take all empty nodes from the list */
500 num_empty = INT_MAX;
501 want_all_empty = true;
502 } else {
503 /* extract number of nodes to take */
504 num_empty = strtol(&mapped_nodes[i][1], NULL, 10);
505 }
506 /* search for empty nodes and take them */
507 item = opal_list_get_first(nodes);
508 while (0 < num_empty && item != opal_list_get_end(nodes)) {
509 next = opal_list_get_next(item); /* save this position */
510 node = (orte_node_t*)item;
511 /* see if this node is empty */
512 if (0 == node->slots_inuse) {
513 /* check to see if it is specified later */
514 for (j=i+1; j < len_mapped_node; j++) {
515 if (0 == strcmp(mapped_nodes[j], node->name)) {
516 /* specified later - skip this one */
517 goto skipnode;
518 }
519 }
520 if (remove) {
521 /* remove item from list */
522 opal_list_remove_item(nodes, item);
523 /* xfer to keep list */
524 opal_list_append(&keep, item);
525 } else {
526 /* mark the node as found */
527 ORTE_FLAG_SET(node, ORTE_NODE_FLAG_MAPPED);
528 }
529 --num_empty;
530 }
531 skipnode:
532 item = next;
533 }
534 } else {
535 /* remove any modifier */
536 if (NULL != (cptr = strchr(mapped_nodes[i], ':'))) {
537 *cptr = '\0';
538 }
539 /* we are looking for a specific node on the list. The
540 * parser will have substituted our local name for any
541 * alias, so we only have to do a strcmp here. */
542 cptr = NULL;
543 lmn = strtoul(mapped_nodes[i], &cptr, 10);
544 item = opal_list_get_first(nodes);
545 while (item != opal_list_get_end(nodes)) {
546 next = opal_list_get_next(item); /* save this position */
547 node = (orte_node_t*)item;
548 /* search -host list to see if this one is found */
549 if (orte_managed_allocation &&
550 (NULL == cptr || 0 == strlen(cptr))) {
551 /* if we are only given a number, then we test the
552 * value against the number in the node name. This allows support for
553 * launch_id-based environments. For example, a hostname
554 * of "nid0015" can be referenced by "--host 15" */
555 for (j=strlen(node->name)-1; 0 < j; j--) {
556 if (!isdigit(node->name[j])) {
557 j++;
558 break;
559 }
560 }
561 if (j >= (int)(strlen(node->name) - 1)) {
562 test = 0;
563 } else {
564 lst = strtoul(&node->name[j], NULL, 10);
565 test = (lmn == lst) ? 0 : 1;
566 }
567 } else {
568 test = strcmp(node->name, mapped_nodes[i]);
569 }
570 if (0 == test) {
571 if (remove) {
572 /* remove item from list */
573 opal_list_remove_item(nodes, item);
574 /* xfer to keep list */
575 opal_list_append(&keep, item);
576 } else {
577 /* mark the node as found */
578 ORTE_FLAG_SET(node, ORTE_NODE_FLAG_MAPPED);
579 }
580 break;
581 }
582 item = next;
583 }
584 }
585 /* done with the mapped entry */
586 free(mapped_nodes[i]);
587 mapped_nodes[i] = NULL;
588 }
589
590 /* was something specified that was -not- found? */
591 for (i=0; i < len_mapped_node; i++) {
592 if (NULL != mapped_nodes[i]) {
593 orte_show_help("help-dash-host.txt", "not-all-mapped-alloc",
594 true, mapped_nodes[i]);
595 rc = ORTE_ERR_SILENT;
596 goto cleanup;
597 }
598 }
599
600 if (!remove) {
601 /* all done */
602 rc = ORTE_SUCCESS;
603 goto cleanup;
604 }
605
606 /* clear the rest of the nodes list */
607 while (NULL != (item = opal_list_remove_first(nodes))) {
608 OBJ_RELEASE(item);
609 }
610
611 /* the nodes list has been cleared - rebuild it in order */
612 while (NULL != (item = opal_list_remove_first(&keep))) {
613 opal_list_append(nodes, item);
614 }
615
616 /* did they ask for more than we could provide */
617 if (!want_all_empty && 0 < num_empty) {
618 orte_show_help("help-dash-host.txt", "dash-host:not-enough-empty",
619 true, num_empty);
620 rc = ORTE_ERR_SILENT;
621 goto cleanup;
622 }
623
624 rc = ORTE_SUCCESS;
625 /* done filtering existing list */
626
627 cleanup:
628 for (i=0; i < len_mapped_node; i++) {
629 if (NULL != mapped_nodes[i]) {
630 free(mapped_nodes[i]);
631 mapped_nodes[i] = NULL;
632 }
633 }
634 if (NULL != mapped_nodes) {
635 free(mapped_nodes);
636 }
637
638 return rc;
639 }
640
orte_util_get_ordered_dash_host_list(opal_list_t * nodes,char * hosts)641 int orte_util_get_ordered_dash_host_list(opal_list_t *nodes,
642 char *hosts)
643 {
644 int rc, i;
645 char **mapped_nodes = NULL;
646 orte_node_t *node;
647
648 if (ORTE_SUCCESS != (rc = parse_dash_host(&mapped_nodes, hosts))) {
649 ORTE_ERROR_LOG(rc);
650 }
651
652 /* for each entry, create a node entry on the list */
653 for (i=0; NULL != mapped_nodes[i]; i++) {
654 node = OBJ_NEW(orte_node_t);
655 node->name = strdup(mapped_nodes[i]);
656 opal_list_append(nodes, &node->super);
657 }
658
659 /* cleanup */
660 opal_argv_free(mapped_nodes);
661 return rc;
662 }
663