/* * Copyright (c) 2016-2018 Intel, Inc. All rights reserved. * Copyright (c) 2018-2019 Research Organization for Information Science * and Technology (RIST). All rights reserved. * $COPYRIGHT$ * * Additional copyrights may follow * * $HEADER$ * */ #include "orte_config.h" #include "orte/types.h" #include "opal/types.h" #ifdef HAVE_UNISTD_H #include #endif #include #include "opal/util/argv.h" #include "opal/util/basename.h" #include "opal/util/opal_environ.h" #include "orte/runtime/orte_globals.h" #include "orte/util/name_fns.h" #include "orte/util/show_help.h" #include "orte/mca/errmgr/errmgr.h" #include "orte/mca/rmaps/base/base.h" #include "orte/mca/routed/routed.h" #include "orte/mca/regx/base/base.h" static void range_construct(orte_regex_range_t *ptr) { ptr->vpid = 0; ptr->cnt = 0; } OBJ_CLASS_INSTANCE(orte_regex_range_t, opal_list_item_t, range_construct, NULL); static void orte_regex_node_construct(orte_regex_node_t *ptr) { ptr->prefix = NULL; ptr->suffix = NULL; ptr->num_digits = 0; OBJ_CONSTRUCT(&ptr->ranges, opal_list_t); } static void orte_regex_node_destruct(orte_regex_node_t *ptr) { opal_list_item_t *item; if (NULL != ptr->prefix) { free(ptr->prefix); } if (NULL != ptr->suffix) { free(ptr->suffix); } while (NULL != (item = opal_list_remove_first(&ptr->ranges))) { OBJ_RELEASE(item); } OBJ_DESTRUCT(&ptr->ranges); } OBJ_CLASS_INSTANCE(orte_regex_node_t, opal_list_item_t, orte_regex_node_construct, orte_regex_node_destruct); int orte_regx_base_nidmap_parse(char *regex) { char *nodelist, *vpids, *ptr; char **nodes, **dvpids; int rc, n, cnt; orte_regex_range_t *rng; opal_list_t dids; orte_job_t *daemons; orte_node_t *nd; orte_proc_t *proc; /* if we are the HNP, we don't need to parse this */ if (ORTE_PROC_IS_HNP) { return ORTE_SUCCESS; } /* split the regex into its node and vpid parts */ nodelist = regex; vpids = strchr(regex, '@'); if (NULL == vpids) { /* indicates the regex got mangled somewhere */ return ORTE_ERR_BAD_PARAM; } *vpids = '\0'; // terminate the nodelist string ++vpids; // step over the separator if (NULL == vpids || '\0' == *vpids) { /* indicates the regex got mangled somewhere */ return ORTE_ERR_BAD_PARAM; } /* decompress the nodes regex */ nodes = NULL; if (ORTE_SUCCESS != (rc = orte_regx.extract_node_names(nodelist, &nodes))) { ORTE_ERROR_LOG(rc); return rc; } if (NULL == nodes) { /* should not happen */ ORTE_ERROR_LOG(ORTE_ERR_NOT_FOUND); return ORTE_ERR_NOT_FOUND; } /* decompress the vpids */ OBJ_CONSTRUCT(&dids, opal_list_t); dvpids = opal_argv_split(vpids, ','); for (n=0; NULL != dvpids[n]; n++) { rng = OBJ_NEW(orte_regex_range_t); opal_list_append(&dids, &rng->super); /* check for a count */ if (NULL != (ptr = strchr(dvpids[n], '('))) { dvpids[n][strlen(dvpids[n])-1] = '\0'; // remove trailing paren *ptr = '\0'; ++ptr; rng->cnt = strtoul(ptr, NULL, 10); } else { rng->cnt = 1; } /* convert the number */ rng->vpid = strtoul(dvpids[n], NULL, 10); } opal_argv_free(dvpids); /* get the daemon job object */ daemons = orte_get_job_data_object(ORTE_PROC_MY_NAME->jobid); /* create the node pool array - this will include * _all_ nodes known to the allocation */ rng = (orte_regex_range_t*)opal_list_get_first(&dids); cnt = 0; for (n=0; NULL != nodes[n]; n++) { nd = OBJ_NEW(orte_node_t); nd->name = nodes[n]; opal_pointer_array_set_item(orte_node_pool, n, nd); /* see if it has a daemon on it */ if (-1 != rng->vpid) { /* we have a daemon, so let's create the tracker for it */ if (NULL == (proc = (orte_proc_t*)opal_pointer_array_get_item(daemons->procs, rng->vpid+cnt))) { proc = OBJ_NEW(orte_proc_t); proc->name.jobid = ORTE_PROC_MY_NAME->jobid; proc->name.vpid = rng->vpid + cnt; proc->state = ORTE_PROC_STATE_RUNNING; ORTE_FLAG_SET(proc, ORTE_PROC_FLAG_ALIVE); daemons->num_procs++; opal_pointer_array_set_item(daemons->procs, proc->name.vpid, proc); } nd->index = proc->name.vpid; OBJ_RETAIN(nd); proc->node = nd; OBJ_RETAIN(proc); nd->daemon = proc; } ++cnt; if (rng->cnt <= cnt) { rng = (orte_regex_range_t*)opal_list_get_next(&rng->super); if (NULL == rng) { ORTE_ERROR_LOG(ORTE_ERR_NOT_FOUND); return ORTE_ERR_NOT_FOUND; } cnt = 0; } } /* update num procs */ if (orte_process_info.num_procs != daemons->num_procs) { orte_process_info.num_procs = daemons->num_procs; /* need to update the routing plan */ orte_routed.update_routing_plan(NULL); } if (orte_process_info.max_procs < orte_process_info.num_procs) { orte_process_info.max_procs = orte_process_info.num_procs; } if (0 < opal_output_get_verbosity(orte_regx_base_framework.framework_output)) { int i; for (i=0; i < orte_node_pool->size; i++) { if (NULL == (nd = (orte_node_t*)opal_pointer_array_get_item(orte_node_pool, i))) { continue; } opal_output(0, "%s node[%d].name %s daemon %s", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME), i, (NULL == nd->name) ? "NULL" : nd->name, (NULL == nd->daemon) ? "NONE" : ORTE_VPID_PRINT(nd->daemon->name.vpid)); } } return ORTE_SUCCESS; } int orte_regx_base_encode_nodemap(opal_buffer_t *buffer) { int n; bool test; orte_regex_range_t *rng, *slt, *tp, *flg; opal_list_t slots, topos, flags; opal_list_item_t *item; char *tmp, *tmp2; orte_node_t *nptr; int rc; uint8_t ui8; orte_topology_t *ortetopo; /* setup the list of results */ OBJ_CONSTRUCT(&slots, opal_list_t); OBJ_CONSTRUCT(&topos, opal_list_t); OBJ_CONSTRUCT(&flags, opal_list_t); slt = NULL; tp = NULL; flg = NULL; /* pack a flag indicating if the HNP was included in the allocation */ if (orte_hnp_is_allocated) { ui8 = 1; } else { ui8 = 0; } if (ORTE_SUCCESS != (rc = opal_dss.pack(buffer, &ui8, 1, OPAL_UINT8))) { ORTE_ERROR_LOG(rc); return rc; } /* pack a flag indicating if we are in a managed allocation */ if (orte_managed_allocation) { ui8 = 1; } else { ui8 = 0; } if (ORTE_SUCCESS != (rc = opal_dss.pack(buffer, &ui8, 1, OPAL_UINT8))) { ORTE_ERROR_LOG(rc); return rc; } /* handle the topologies - as the most common case by far * is to have homogeneous topologies, we only send them * if something is different. We know that the HNP is * the first topology, and that any differing topology * on the compute nodes must follow. So send the topologies * if and only if: * * (a) the HNP is being used to house application procs and * there is more than one topology on our list; or * * (b) the HNP is not being used, but there are more than * two topologies on our list, thus indicating that * there are multiple topologies on the compute nodes */ nptr = (orte_node_t*)opal_pointer_array_get_item(orte_node_pool, 0); if (!orte_hnp_is_allocated || (ORTE_GET_MAPPING_DIRECTIVE(orte_rmaps_base.mapping) & ORTE_MAPPING_NO_USE_LOCAL)) { /* assign a NULL topology so we still account for our presence, * but don't cause us to send topology info when not needed */ tp = OBJ_NEW(orte_regex_range_t); tp->t = NULL; tp->cnt = 1; } else { /* there is always one topology - our own - so start with it */ tp = OBJ_NEW(orte_regex_range_t); tp->t = nptr->topology; tp->cnt = 1; } opal_list_append(&topos, &tp->super); opal_output_verbose(5, orte_regx_base_framework.framework_output, "%s STARTING WITH TOPOLOGY FOR NODE %s: %s", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME), nptr->name, (NULL == tp->t) ? "NULL" : tp->t->sig); /* likewise, we have slots */ slt = OBJ_NEW(orte_regex_range_t); slt->slots = nptr->slots; slt->cnt = 1; opal_list_append(&slots, &slt->super); /* and flags */ flg = OBJ_NEW(orte_regex_range_t); if (ORTE_FLAG_TEST(nptr, ORTE_NODE_FLAG_SLOTS_GIVEN)) { flg->slots = 1; } else { flg->slots = 0; } flg->cnt = 1; opal_list_append(&flags, &flg->super); for (n=1; n < orte_node_pool->size; n++) { if (NULL == (nptr = (orte_node_t*)opal_pointer_array_get_item(orte_node_pool, n))) { continue; } /* check the #slots */ /* is this the next in line */ if (nptr->slots == slt->slots) { slt->cnt++; } else { /* need to start another range */ slt = OBJ_NEW(orte_regex_range_t); slt->slots = nptr->slots; slt->cnt = 1; opal_list_append(&slots, &slt->super); } /* check the topologies */ if (NULL != tp->t && NULL == nptr->topology) { /* we don't know this topology, likely because * we don't have a daemon on the node */ tp = OBJ_NEW(orte_regex_range_t); tp->t = NULL; tp->cnt = 1; opal_output_verbose(5, orte_regx_base_framework.framework_output, "%s ADD TOPOLOGY FOR NODE %s: NULL", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME), nptr->name); opal_list_append(&topos, &tp->super); } else { /* is this the next in line */ if (tp->t == nptr->topology) { tp->cnt++; opal_output_verbose(5, orte_regx_base_framework.framework_output, "%s CONTINUE TOPOLOGY RANGE (%d) WITH NODE %s: %s", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME), tp->cnt, nptr->name, (NULL == tp->t) ? "N/A" : tp->t->sig); } else { /* need to start another range */ tp = OBJ_NEW(orte_regex_range_t); tp->t = nptr->topology; tp->cnt = 1; opal_output_verbose(5, orte_regx_base_framework.framework_output, "%s STARTING NEW TOPOLOGY RANGE WITH NODE %s: %s", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME), nptr->name, tp->t->sig); opal_list_append(&topos, &tp->super); } } /* check the flags */ test = ORTE_FLAG_TEST(nptr, ORTE_NODE_FLAG_SLOTS_GIVEN); /* is this the next in line */ if ((test && 1 == flg->slots) || (!test && 0 == flg->slots)) { flg->cnt++; } else { /* need to start another range */ flg = OBJ_NEW(orte_regex_range_t); if (test) { flg->slots = 1; } else { flg->slots = 0; } flg->cnt = 1; opal_list_append(&flags, &flg->super); } } /* pass #slots on each node */ tmp = NULL; while (NULL != (item = opal_list_remove_first(&slots))) { rng = (orte_regex_range_t*)item; if (NULL == tmp) { asprintf(&tmp, "%d[%d]", rng->cnt, rng->slots); } else { asprintf(&tmp2, "%s,%d[%d]", tmp, rng->cnt, rng->slots); free(tmp); tmp = tmp2; } OBJ_RELEASE(rng); } OPAL_LIST_DESTRUCT(&slots); opal_output_verbose(1, orte_regx_base_framework.framework_output, "%s SLOT ASSIGNMENTS: %s", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME), tmp); /* pack the string */ if (ORTE_SUCCESS != (rc = opal_dss.pack(buffer, &tmp, 1, OPAL_STRING))) { ORTE_ERROR_LOG(rc); return rc; } if (NULL != tmp) { free(tmp); } /* do the same to pass the flags for each node */ tmp = NULL; while (NULL != (item = opal_list_remove_first(&flags))) { rng = (orte_regex_range_t*)item; if (NULL == tmp) { asprintf(&tmp, "%d[%d]", rng->cnt, rng->slots); } else { asprintf(&tmp2, "%s,%d[%d]", tmp, rng->cnt, rng->slots); free(tmp); tmp = tmp2; } OBJ_RELEASE(rng); } OPAL_LIST_DESTRUCT(&flags); /* pack the string */ opal_output_verbose(1, orte_regx_base_framework.framework_output, "%s FLAG ASSIGNMENTS: %s", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME), tmp); if (ORTE_SUCCESS != (rc = opal_dss.pack(buffer, &tmp, 1, OPAL_STRING))) { ORTE_ERROR_LOG(rc); return rc; } if (NULL != tmp) { free(tmp); } /* don't try to be cute - there aren't going to be that many * topologies, so just scan the list and see if they are the * same, excluding any NULL values */ ortetopo = NULL; test = false; OPAL_LIST_FOREACH(rng, &topos, orte_regex_range_t) { if (NULL == rng->t) { continue; } if (NULL == ortetopo) { ortetopo = rng->t; } else if (0 != strcmp(ortetopo->sig, rng->t->sig)) { /* we have a difference, so send them */ test = true; } } tmp = NULL; if (test) { opal_buffer_t bucket, *bptr; OBJ_CONSTRUCT(&bucket, opal_buffer_t); while (NULL != (item = opal_list_remove_first(&topos))) { rng = (orte_regex_range_t*)item; opal_output_verbose(5, orte_regx_base_framework.framework_output, "%s PASSING TOPOLOGY %s RANGE %d", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME), (NULL == rng->t) ? "NULL" : rng->t->sig, rng->cnt); if (NULL == tmp) { asprintf(&tmp, "%d", rng->cnt); } else { asprintf(&tmp2, "%s,%d", tmp, rng->cnt); free(tmp); tmp = tmp2; } if (NULL == rng->t) { /* need to account for NULL topology */ opal_output_verbose(1, orte_regx_base_framework.framework_output, "%s PACKING NULL TOPOLOGY", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME)); tmp2 = NULL; if (ORTE_SUCCESS != (rc = opal_dss.pack(&bucket, &tmp2, 1, OPAL_STRING))) { ORTE_ERROR_LOG(rc); OBJ_RELEASE(rng); OPAL_LIST_DESTRUCT(&topos); OBJ_DESTRUCT(&bucket); free(tmp); return rc; } } else { opal_output_verbose(1, orte_regx_base_framework.framework_output, "%s PACKING TOPOLOGY: %s", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME), rng->t->sig); /* pack this topology string */ if (ORTE_SUCCESS != (rc = opal_dss.pack(&bucket, &rng->t->sig, 1, OPAL_STRING))) { ORTE_ERROR_LOG(rc); OBJ_RELEASE(rng); OPAL_LIST_DESTRUCT(&topos); OBJ_DESTRUCT(&bucket); free(tmp); return rc; } /* pack the topology itself */ if (ORTE_SUCCESS != (rc = opal_dss.pack(&bucket, &rng->t->topo, 1, OPAL_HWLOC_TOPO))) { ORTE_ERROR_LOG(rc); OBJ_RELEASE(rng); OPAL_LIST_DESTRUCT(&topos); OBJ_DESTRUCT(&bucket); free(tmp); return rc; } } OBJ_RELEASE(rng); } OPAL_LIST_DESTRUCT(&topos); /* pack the string */ opal_output_verbose(1, orte_regx_base_framework.framework_output, "%s TOPOLOGY ASSIGNMENTS: %s", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME), tmp); if (ORTE_SUCCESS != (rc = opal_dss.pack(buffer, &tmp, 1, OPAL_STRING))) { ORTE_ERROR_LOG(rc); OBJ_DESTRUCT(&bucket); free(tmp); return rc; } free(tmp); /* now pack the topologies */ bptr = &bucket; if (ORTE_SUCCESS != (rc = opal_dss.pack(buffer, &bptr, 1, OPAL_BUFFER))) { ORTE_ERROR_LOG(rc); OBJ_DESTRUCT(&bucket); return rc; } OBJ_DESTRUCT(&bucket); } else { opal_output_verbose(1, orte_regx_base_framework.framework_output, "%s NOT PASSING TOPOLOGIES", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME)); /* need to pack the NULL just to terminate the region */ if (ORTE_SUCCESS != (rc = opal_dss.pack(buffer, &tmp, 1, OPAL_STRING))) { ORTE_ERROR_LOG(rc); return rc; } } return ORTE_SUCCESS; } int orte_regx_base_decode_daemon_nodemap(opal_buffer_t *buffer) { int n, nn, rc, cnt, offset; orte_node_t *node; char *slots=NULL, *topos=NULL, *flags=NULL; char *rmndr, **tmp; opal_list_t slts, flgs;; opal_buffer_t *bptr=NULL; orte_topology_t *t2; orte_regex_range_t *rng, *srng, *frng; uint8_t ui8; OBJ_CONSTRUCT(&slts, opal_list_t); OBJ_CONSTRUCT(&flgs, opal_list_t); /* unpack the flag indicating if the HNP was allocated */ n = 1; if (ORTE_SUCCESS != (rc = opal_dss.unpack(buffer, &ui8, &n, OPAL_UINT8))) { ORTE_ERROR_LOG(rc); goto cleanup; } if (0 == ui8) { orte_hnp_is_allocated = false; } else { orte_hnp_is_allocated = true; } /* unpack the flag indicating we are in a managed allocation */ n = 1; if (ORTE_SUCCESS != (rc = opal_dss.unpack(buffer, &ui8, &n, OPAL_UINT8))) { ORTE_ERROR_LOG(rc); goto cleanup; } if (0 == ui8) { orte_managed_allocation = false; } else { orte_managed_allocation = true; } /* unpack the slots regex */ n = 1; if (ORTE_SUCCESS != (rc = opal_dss.unpack(buffer, &slots, &n, OPAL_STRING))) { ORTE_ERROR_LOG(rc); goto cleanup; } /* this is not allowed to be NULL */ if (NULL == slots) { ORTE_ERROR_LOG(ORTE_ERR_BAD_PARAM); rc = ORTE_ERR_BAD_PARAM; goto cleanup; } /* unpack the flags regex */ n = 1; if (ORTE_SUCCESS != (rc = opal_dss.unpack(buffer, &flags, &n, OPAL_STRING))) { ORTE_ERROR_LOG(rc); goto cleanup; } /* this is not allowed to be NULL */ if (NULL == flags) { ORTE_ERROR_LOG(ORTE_ERR_BAD_PARAM); rc = ORTE_ERR_BAD_PARAM; goto cleanup; } /* unpack the topos regex - this may not have been * provided (e.g., for a homogeneous machine) */ n = 1; if (ORTE_SUCCESS != (rc = opal_dss.unpack(buffer, &topos, &n, OPAL_STRING))) { ORTE_ERROR_LOG(rc); goto cleanup; } if (NULL != topos) { /* need to unpack the topologies */ n = 1; if (ORTE_SUCCESS != (rc = opal_dss.unpack(buffer, &bptr, &n, OPAL_BUFFER))) { ORTE_ERROR_LOG(rc); goto cleanup; } } /* if we are the HNP, then we just discard these strings as we already * have a complete picture - but we needed to unpack them in order to * maintain sync in the unpacking order */ if (ORTE_PROC_IS_HNP) { rc = ORTE_SUCCESS; goto cleanup; } /* decompress the slots */ tmp = opal_argv_split(slots, ','); for (n=0; NULL != tmp[n]; n++) { rng = OBJ_NEW(orte_regex_range_t); opal_list_append(&slts, &rng->super); /* find the '[' as that delimits the value */ rmndr = strchr(tmp[n], '['); if (NULL == rmndr) { ORTE_ERROR_LOG(ORTE_ERR_BAD_PARAM); rc = ORTE_ERR_BAD_PARAM; opal_argv_free(tmp); goto cleanup; } *rmndr = '\0'; ++rmndr; /* convert that number as this is the number of * slots for this range */ rng->slots = strtoul(rmndr, NULL, 10); /* convert the initial number as that is the cnt */ rng->cnt = strtoul(tmp[n], NULL, 10); } opal_argv_free(tmp); /* decompress the flags */ tmp = opal_argv_split(flags, ','); for (n=0; NULL != tmp[n]; n++) { rng = OBJ_NEW(orte_regex_range_t); opal_list_append(&flgs, &rng->super); /* find the '[' as that delimits the value */ rmndr = strchr(tmp[n], '['); if (NULL == rmndr) { ORTE_ERROR_LOG(ORTE_ERR_BAD_PARAM); opal_argv_free(tmp); rc = ORTE_ERR_BAD_PARAM; goto cleanup; } *rmndr = '\0'; ++rmndr; /* check the value - it is just one character */ if ('1' == *rmndr) { rng->slots = 1; } else { rng->slots = 0; } /* convert the initial number as that is the cnt */ rng->cnt = strtoul(tmp[n], NULL, 10); } opal_argv_free(tmp); free(flags); /* update the node array */ srng = (orte_regex_range_t*)opal_list_get_first(&slts); frng = (orte_regex_range_t*)opal_list_get_first(&flgs); for (n=0; n < orte_node_pool->size; n++) { if (NULL == (node = (orte_node_t*)opal_pointer_array_get_item(orte_node_pool, n))) { continue; } /* set the number of slots */ node->slots = srng->slots; srng->cnt--; if (0 == srng->cnt) { srng = (orte_regex_range_t*)opal_list_get_next(&srng->super); } /* set the flags */ if (0 == frng->slots) { ORTE_FLAG_UNSET(node, ORTE_NODE_FLAG_SLOTS_GIVEN); } else { ORTE_FLAG_SET(node, ORTE_NODE_FLAG_SLOTS_GIVEN); } frng->cnt--; if (0 == frng->cnt) { frng = (orte_regex_range_t*)opal_list_get_next(&frng->super); } } /* if no topology info was passed, then everyone shares our topology */ if (NULL == bptr) { /* our topology is first in the array */ t2 = (orte_topology_t*)opal_pointer_array_get_item(orte_node_topologies, 0); opal_output_verbose(1, orte_regx_base_framework.framework_output, "%s ASSIGNING ALL TOPOLOGIES TO: %s", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME), t2->sig); for (n=0; n < orte_node_pool->size; n++) { if (NULL != (node = (orte_node_t*)opal_pointer_array_get_item(orte_node_pool, n))) { if (NULL == node->topology) { OBJ_RETAIN(t2); node->topology = t2; } } } } else { char *sig; hwloc_topology_t topo; /* decompress the topology regex */ tmp = opal_argv_split(topos, ','); /* there must be a topology definition for each range */ offset = 0; for (nn=0; NULL != tmp[nn]; nn++) { cnt = strtoul(tmp[nn], NULL, 10); /* unpack the signature */ n = 1; if (ORTE_SUCCESS != (rc = opal_dss.unpack(bptr, &sig, &n, OPAL_STRING))) { ORTE_ERROR_LOG(rc); opal_argv_free(tmp); OBJ_RELEASE(bptr); goto cleanup; } if (NULL == sig) { /* the nodes in this range have not reported a topology, * so skip them */ offset += cnt; continue; } n = 1; if (ORTE_SUCCESS != (rc = opal_dss.unpack(bptr, &topo, &n, OPAL_HWLOC_TOPO))) { ORTE_ERROR_LOG(rc); opal_argv_free(tmp); OBJ_RELEASE(bptr); free(sig); goto cleanup; } /* see if we already have this topology - could be an update */ t2 = NULL; for (n=0; n < orte_node_topologies->size; n++) { if (NULL == (t2 = (orte_topology_t*)opal_pointer_array_get_item(orte_node_topologies, n))) { continue; } if (0 == strcmp(t2->sig, sig)) { /* found a match */ free(sig); opal_hwloc_base_free_topology(topo); sig = NULL; break; } } if (NULL != sig || NULL == t2) { /* new topology - record it */ t2 = OBJ_NEW(orte_topology_t); t2->sig = sig; t2->topo = topo; opal_pointer_array_add(orte_node_topologies, t2); } /* point each of the nodes in this range to this topology */ n=0; while (n < cnt && (n+offset) < orte_node_pool->size) { if (NULL == (node = (orte_node_t*)opal_pointer_array_get_item(orte_node_pool, n+offset))) { continue; } opal_output_verbose(1, orte_regx_base_framework.framework_output, "%s ASSIGNING NODE %s WITH TOPO: %s", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME), node->name, t2->sig); if (NULL == node->topology) { OBJ_RETAIN(t2); node->topology = t2; } ++n; } offset += cnt; } OBJ_RELEASE(bptr); opal_argv_free(tmp); } cleanup: OPAL_LIST_DESTRUCT(&slts); OPAL_LIST_DESTRUCT(&flgs); return rc; } int orte_regx_base_generate_ppn(orte_job_t *jdata, char **ppn) { orte_nidmap_regex_t *prng, **actives; opal_list_t *prk; orte_node_t *nptr; orte_proc_t *proc; size_t n; int *cnt, i, k; char *tmp2, *ptmp, **cache = NULL; /* create an array of lists to handle the number of app_contexts in this job */ prk = (opal_list_t*)malloc(jdata->num_apps * sizeof(opal_list_t)); cnt = (int*)malloc(jdata->num_apps * sizeof(int)); actives = (orte_nidmap_regex_t**)malloc(jdata->num_apps * sizeof(orte_nidmap_regex_t*)); for (n=0; n < jdata->num_apps; n++) { OBJ_CONSTRUCT(&prk[n], opal_list_t); actives[n] = NULL; } /* we provide a complete map in the regex, with an entry for every * node in the pool */ for (i=0; i < orte_node_pool->size; i++) { if (NULL == (nptr = (orte_node_t*)opal_pointer_array_get_item(orte_node_pool, i))) { continue; } /* if a daemon has been assigned, then count how many procs * for each app_context from the specified job are assigned to this node */ memset(cnt, 0, jdata->num_apps * sizeof(int)); if (NULL != nptr->daemon) { for (k=0; k < nptr->procs->size; k++) { if (NULL != (proc = (orte_proc_t*)opal_pointer_array_get_item(nptr->procs, k))) { if (proc->name.jobid == jdata->jobid) { ++cnt[proc->app_idx]; } } } } /* track the #procs on this node */ for (n=0; n < jdata->num_apps; n++) { if (NULL == actives[n]) { /* just starting */ actives[n] = OBJ_NEW(orte_nidmap_regex_t); actives[n]->nprocs = cnt[n]; actives[n]->cnt = 1; opal_list_append(&prk[n], &actives[n]->super); } else { /* is this the next in line */ if (cnt[n] == actives[n]->nprocs) { actives[n]->cnt++; } else { /* need to start another range */ actives[n] = OBJ_NEW(orte_nidmap_regex_t); actives[n]->nprocs = cnt[n]; actives[n]->cnt = 1; opal_list_append(&prk[n], &actives[n]->super); } } } } /* construct the regex from the found ranges for each app_context */ ptmp = NULL; for (n=0; n < jdata->num_apps; n++) { OPAL_LIST_FOREACH(prng, &prk[n], orte_nidmap_regex_t) { if (1 < prng->cnt) { if (NULL == ptmp) { asprintf(&ptmp, "%u(%u)", prng->nprocs, prng->cnt); } else { asprintf(&tmp2, "%s,%u(%u)", ptmp, prng->nprocs, prng->cnt); free(ptmp); ptmp = tmp2; } } else { if (NULL == ptmp) { asprintf(&ptmp, "%u", prng->nprocs); } else { asprintf(&tmp2, "%s,%u", ptmp, prng->nprocs); free(ptmp); ptmp = tmp2; } } } OPAL_LIST_DESTRUCT(&prk[n]); // releases all the actives objects if (NULL != ptmp) { opal_argv_append_nosize(&cache, ptmp); free(ptmp); ptmp = NULL; } } free(prk); free(cnt); free(actives); *ppn = opal_argv_join(cache, '@'); opal_argv_free(cache); return ORTE_SUCCESS; } int orte_regx_base_parse_ppn(orte_job_t *jdata, char *regex) { orte_node_t *node; orte_proc_t *proc; int n, k, m, cnt; char **tmp, *ptr, **ppn; orte_nidmap_regex_t *rng; opal_list_t trk; int rc = ORTE_SUCCESS; /* split the regex by app_context */ tmp = opal_argv_split(regex, '@'); /* for each app_context, set the ppn */ for (n=0; NULL != tmp[n]; n++) { ppn = opal_argv_split(tmp[n], ','); /* decompress the ppn */ OBJ_CONSTRUCT(&trk, opal_list_t); for (m=0; NULL != ppn[m]; m++) { rng = OBJ_NEW(orte_nidmap_regex_t); opal_list_append(&trk, &rng->super); /* check for a count */ if (NULL != (ptr = strchr(ppn[m], '('))) { ppn[m][strlen(ppn[m])-1] = '\0'; // remove trailing paren *ptr = '\0'; ++ptr; rng->cnt = strtoul(ptr, NULL, 10); } else { rng->cnt = 1; } /* convert the number */ rng->nprocs = strtoul(ppn[m], NULL, 10); } opal_argv_free(ppn); /* cycle thru our node pool and add the indicated number of procs * to each node */ rng = (orte_nidmap_regex_t*)opal_list_get_first(&trk); cnt = 0; for (m=0; m < orte_node_pool->size; m++) { if (NULL == (node = (orte_node_t*)opal_pointer_array_get_item(orte_node_pool, m))) { continue; } /* see if it has any procs for this job and app_context */ if (0 < rng->nprocs) { /* add this node to the job map if it isn't already there */ if (!ORTE_FLAG_TEST(node, ORTE_NODE_FLAG_MAPPED)) { OBJ_RETAIN(node); ORTE_FLAG_SET(node, ORTE_NODE_FLAG_MAPPED); opal_pointer_array_add(jdata->map->nodes, node); } /* create a proc object for each one */ for (k=0; k < rng->nprocs; k++) { proc = OBJ_NEW(orte_proc_t); proc->name.jobid = jdata->jobid; /* leave the vpid undefined as this will be determined * later when we do the overall ranking */ proc->app_idx = n; proc->parent = node->daemon->name.vpid; OBJ_RETAIN(node); proc->node = node; /* flag the proc as ready for launch */ proc->state = ORTE_PROC_STATE_INIT; opal_pointer_array_add(node->procs, proc); /* we will add the proc to the jdata array when we * compute its rank */ } node->num_procs += rng->nprocs; } ++cnt; if (rng->cnt <= cnt) { rng = (orte_nidmap_regex_t*)opal_list_get_next(&rng->super); if (NULL == rng) { ORTE_ERROR_LOG(ORTE_ERR_NOT_FOUND); opal_argv_free(tmp); rc = ORTE_ERR_NOT_FOUND; goto complete; } cnt = 0; } } OPAL_LIST_DESTRUCT(&trk); } opal_argv_free(tmp); complete: /* reset any node map flags we used so the next job will start clean */ for (n=0; n < jdata->map->nodes->size; n++) { if (NULL != (node = (orte_node_t*)opal_pointer_array_get_item(jdata->map->nodes, n))) { ORTE_FLAG_UNSET(node, ORTE_NODE_FLAG_MAPPED); } } return rc; } static int regex_parse_node_range(char *base, char *range, int num_digits, char *suffix, char ***names); /* * Parse one or more ranges in a set * * @param base The base text of the node name * @param *ranges A pointer to a range. This can contain multiple ranges * (i.e. "1-3,10" or "5" or "9,0100-0130,250") * @param ***names An argv array to add the newly discovered nodes to */ static int regex_parse_node_ranges(char *base, char *ranges, int num_digits, char *suffix, char ***names) { int i, len, ret; char *start, *orig; /* Look for commas, the separator between ranges */ len = strlen(ranges); for (orig = start = ranges, i = 0; i < len; ++i) { if (',' == ranges[i]) { ranges[i] = '\0'; ret = regex_parse_node_range(base, start, num_digits, suffix, names); if (ORTE_SUCCESS != ret) { ORTE_ERROR_LOG(ret); return ret; } start = ranges + i + 1; } } /* Pick up the last range, if it exists */ if (start < orig + len) { OPAL_OUTPUT_VERBOSE((1, orte_debug_output, "%s regex:parse:ranges: parse range %s (2)", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME), start)); ret = regex_parse_node_range(base, start, num_digits, suffix, names); if (ORTE_SUCCESS != ret) { ORTE_ERROR_LOG(ret); return ret; } } /* All done */ return ORTE_SUCCESS; } /* * Parse a single range in a set and add the full names of the nodes * found to the names argv * * @param base The base text of the node name * @param *ranges A pointer to a single range. (i.e. "1-3" or "5") * @param ***names An argv array to add the newly discovered nodes to */ static int regex_parse_node_range(char *base, char *range, int num_digits, char *suffix, char ***names) { char *str, tmp[132]; size_t i, k, start, end; size_t base_len, len; bool found; int ret; if (NULL == base || NULL == range) { return ORTE_ERROR; } len = strlen(range); base_len = strlen(base); /* Silence compiler warnings; start and end are always assigned properly, below */ start = end = 0; /* Look for the beginning of the first number */ for (found = false, i = 0; i < len; ++i) { if (isdigit((int) range[i])) { if (!found) { start = strtol(range + i, NULL, 10); found = true; break; } } } if (!found) { ORTE_ERROR_LOG(ORTE_ERR_NOT_FOUND); return ORTE_ERR_NOT_FOUND; } /* Look for the end of the first number */ for (found = false; i < len; ++i) { if (!isdigit(range[i])) { break; } } /* Was there no range, just a single number? */ if (i >= len) { end = start; found = true; } else { /* Nope, there was a range. Look for the beginning of the second * number */ for (; i < len; ++i) { if (isdigit(range[i])) { end = strtol(range + i, NULL, 10); found = true; break; } } } if (!found) { ORTE_ERROR_LOG(ORTE_ERR_NOT_FOUND); return ORTE_ERR_NOT_FOUND; } /* Make strings for all values in the range */ len = base_len + num_digits + 32; if (NULL != suffix) { len += strlen(suffix); } str = (char *) malloc(len); if (NULL == str) { ORTE_ERROR_LOG(ORTE_ERR_OUT_OF_RESOURCE); return ORTE_ERR_OUT_OF_RESOURCE; } for (i = start; i <= end; ++i) { memset(str, 0, len); strcpy(str, base); /* we need to zero-pad the digits */ for (k=0; k < (size_t)num_digits; k++) { str[k+base_len] = '0'; } memset(tmp, 0, 132); snprintf(tmp, 132, "%lu", (unsigned long)i); for (k=0; k < strlen(tmp); k++) { str[base_len + num_digits - k - 1] = tmp[strlen(tmp)-k-1]; } /* if there is a suffix, add it */ if (NULL != suffix) { strcat(str, suffix); } ret = opal_argv_append_nosize(names, str); if(ORTE_SUCCESS != ret) { ORTE_ERROR_LOG(ret); free(str); return ret; } } free(str); /* All done */ return ORTE_SUCCESS; } static int regex_parse_node_range(char *base, char *range, int num_digits, char *suffix, char ***names); int orte_regx_base_extract_node_names(char *regexp, char ***names) { int i, j, k, len, ret; char *base; char *orig, *suffix; bool found_range = false; bool more_to_come = false; int num_digits; if (NULL == regexp) { *names = NULL; return ORTE_SUCCESS; } orig = base = strdup(regexp); if (NULL == base) { ORTE_ERROR_LOG(ORTE_ERR_OUT_OF_RESOURCE); return ORTE_ERR_OUT_OF_RESOURCE; } OPAL_OUTPUT_VERBOSE((1, orte_debug_output, "%s regex:extract:nodenames: checking nodelist: %s", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME), regexp)); do { /* Find the base */ len = strlen(base); for (i = 0; i <= len; ++i) { if (base[i] == '[') { /* we found a range. this gets dealt with below */ base[i] = '\0'; found_range = true; break; } if (base[i] == ',') { /* we found a singleton node, and there are more to come */ base[i] = '\0'; found_range = false; more_to_come = true; break; } if (base[i] == '\0') { /* we found a singleton node */ found_range = false; more_to_come = false; break; } } if (i == 0 && !found_range) { /* we found a special character at the beginning of the string */ orte_show_help("help-regex.txt", "regex:special-char", true, regexp); free(orig); return ORTE_ERR_BAD_PARAM; } if (found_range) { /* If we found a range, get the number of digits in the numbers */ i++; /* step over the [ */ for (j=i; j < len; j++) { if (base[j] == ':') { base[j] = '\0'; break; } } if (j >= len) { /* we didn't find the number of digits */ orte_show_help("help-regex.txt", "regex:num-digits-missing", true, regexp); free(orig); return ORTE_ERR_BAD_PARAM; } num_digits = strtol(&base[i], NULL, 10); i = j + 1; /* step over the : */ /* now find the end of the range */ for (j = i; j < len; ++j) { if (base[j] == ']') { base[j] = '\0'; break; } } if (j >= len) { /* we didn't find the end of the range */ orte_show_help("help-regex.txt", "regex:end-range-missing", true, regexp); free(orig); return ORTE_ERR_BAD_PARAM; } /* check for a suffix */ if (j+1 < len && base[j+1] != ',') { /* find the next comma, if present */ for (k=j+1; k < len && base[k] != ','; k++); if (k < len) { base[k] = '\0'; } suffix = strdup(&base[j+1]); if (k < len) { base[k] = ','; } j = k-1; } else { suffix = NULL; } OPAL_OUTPUT_VERBOSE((1, orte_debug_output, "%s regex:extract:nodenames: parsing range %s %s %s", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME), base, base + i, suffix)); ret = regex_parse_node_ranges(base, base + i, num_digits, suffix, names); if (NULL != suffix) { free(suffix); } if (ORTE_SUCCESS != ret) { orte_show_help("help-regex.txt", "regex:bad-value", true, regexp); free(orig); return ret; } if (j+1 < len && base[j + 1] == ',') { more_to_come = true; base = &base[j + 2]; } else { more_to_come = false; } } else { /* If we didn't find a range, just add the node */ if(ORTE_SUCCESS != (ret = opal_argv_append_nosize(names, base))) { ORTE_ERROR_LOG(ret); free(orig); return ret; } /* step over the comma */ i++; /* set base equal to the (possible) next base to look at */ base = &base[i]; } } while(more_to_come); free(orig); /* All done */ return ret; }