| /*****************************************************************************\ |
| * hostlist.c - Convert hostlist expressions between Slurm and Moab formats |
| ***************************************************************************** |
| * Copyright (C) 2007 The Regents of the University of California. |
| * Copyright (C) 2008 Lawrence Livermore National Security. |
| * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). |
| * Written by Morris Jette <jette1@llnl.gov> |
| * CODE-OCEC-09-009. All rights reserved. |
| * |
| * This file is part of SLURM, a resource management program. |
| * For details, see <https://computing.llnl.gov/linux/slurm/>. |
| * Please also read the included file: DISCLAIMER. |
| * |
| * SLURM is free software; you can redistribute it and/or modify it under |
| * the terms of the GNU General Public License as published by the Free |
| * Software Foundation; either version 2 of the License, or (at your option) |
| * any later version. |
| * |
| * In addition, as a special exception, the copyright holders give permission |
| * to link the code of portions of this program with the OpenSSL library under |
| * certain conditions as described in each individual source file, and |
| * distribute linked combinations including the two. You must obey the GNU |
| * General Public License in all respects for all of the code used other than |
| * OpenSSL. If you modify file(s) with this exception, you may extend this |
| * exception to your version of the file(s), but you are not obligated to do |
| * so. If you do not wish to do so, delete this exception statement from your |
| * version. If you delete this exception statement from all source files in |
| * the program, then also delete it here. |
| * |
| * SLURM is distributed in the hope that it will be useful, but WITHOUT ANY |
| * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
| * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
| * details. |
| * |
| * You should have received a copy of the GNU General Public License along |
| * with SLURM; if not, write to the Free Software Foundation, Inc., |
| * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
| \*****************************************************************************/ |
| |
| #if HAVE_CONFIG_H |
| # include "config.h" |
| # if HAVE_INTTYPES_H |
| # include <inttypes.h> |
| # else |
| # if HAVE_STDINT_H |
| # include <stdint.h> |
| # endif |
| # endif /* HAVE_INTTYPES_H */ |
| #else /* !HAVE_CONFIG_H */ |
| # include <inttypes.h> |
| #endif /* HAVE_CONFIG_H */ |
| |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #include "./msg.h" |
| #include "src/common/hostlist.h" |
| #include "src/common/node_select.h" |
| #include "src/common/xmalloc.h" |
| #include "src/common/xstring.h" |
| |
| static void _append_hl_buf(char **buf, hostlist_t *hl_tmp, int *reps); |
| static char * _task_list(struct job_record *job_ptr); |
| static char * _task_list_exp(struct job_record *job_ptr); |
| |
| /* |
| * Convert Moab supplied TASKLIST expression into a SLURM hostlist expression |
| * |
| * Moab format 1: tux0:tux0:tux1:tux1:tux2 (list host for each cpu) |
| * Moab format 2: tux[0-1]*2:tux2 (list cpu count after host name) |
| * |
| * SLURM format: tux0,tux0,tux1,tux1,tux2 (if consumable resources enabled) |
| * SLURM format: tux0,tux1,tux2 (if consumable resources disabled) |
| * |
| * NOTE: returned string must be released with xfree() |
| */ |
| extern char * moab2slurm_task_list(char *moab_tasklist, int *task_cnt) |
| { |
| char *slurm_tasklist = NULL, *host = NULL, *tmp1 = NULL, |
| *tmp2 = NULL, *tok = NULL, *tok_p = NULL; |
| int i, reps; |
| hostlist_t hl; |
| static uint32_t cr_test = 0, cr_enabled = 0; |
| |
| if (cr_test == 0) { |
| select_g_get_info_from_plugin(SELECT_CR_PLUGIN, NULL, |
| &cr_enabled); |
| cr_test = 1; |
| } |
| |
| *task_cnt = 0; |
| |
| /* Moab format 2 if string contains '*' or '[' */ |
| tmp1 = strchr(moab_tasklist, (int) '*'); |
| if (tmp1 == NULL) |
| tmp1 = strchr(moab_tasklist, (int) '['); |
| |
| if (tmp1 == NULL) { /* Moab format 1 */ |
| slurm_tasklist = xstrdup(moab_tasklist); |
| if (moab_tasklist[0]) |
| *task_cnt = 1; |
| for (i=0; slurm_tasklist[i]!='\0'; i++) { |
| if (slurm_tasklist[i] == ':') { |
| slurm_tasklist[i] = ','; |
| (*task_cnt)++; |
| } else if (slurm_tasklist[i] == ',') |
| (*task_cnt)++; |
| } |
| return slurm_tasklist; |
| } |
| |
| /* Moab format 2 */ |
| slurm_tasklist = xstrdup(""); |
| tmp1 = xstrdup(moab_tasklist); |
| tok = strtok_r(tmp1, ":", &tok_p); |
| while (tok) { |
| /* find task count, assume 1 if no "*" */ |
| tmp2 = strchr(tok, (int) '*'); |
| if (tmp2) { |
| reps = atoi(tmp2 + 1); |
| tmp2[0] = '\0'; |
| } else |
| reps = 1; |
| |
| /* find host expression */ |
| hl = hostlist_create(tok); |
| while ((host = hostlist_shift(hl))) { |
| for (i=0; i<reps; i++) { |
| if (slurm_tasklist[0]) |
| xstrcat(slurm_tasklist, ","); |
| xstrcat(slurm_tasklist, host); |
| if (!cr_enabled) |
| break; |
| } |
| free(host); |
| (*task_cnt) += reps; |
| } |
| hostlist_destroy(hl); |
| |
| /* get next token */ |
| tok = strtok_r(NULL, ":", &tok_p); |
| } |
| xfree(tmp1); |
| return slurm_tasklist; |
| } |
| |
| /* |
| * Report a job's tasks a a MOAB TASKLIST expression |
| * |
| * Moab format 1: tux0:tux0:tux1:tux1:tux2 (list host for each cpu) |
| * Moab format 2: tux[0-1]*2:tux2 (list cpu count after host name) |
| * |
| * NOTE: returned string must be released with xfree() |
| */ |
| extern char * slurm_job2moab_task_list(struct job_record *job_ptr) |
| { |
| if (use_host_exp) |
| return _task_list_exp(job_ptr); |
| else |
| return _task_list(job_ptr); |
| } |
| |
| /* Return task list in Moab format 1: tux0:tux0:tux1:tux1:tux2 */ |
| static char * _task_list(struct job_record *job_ptr) |
| { |
| int i, j, node_inx = 0, task_cnt; |
| char *buf = NULL, *host; |
| job_resources_t *job_resrcs_ptr = job_ptr->job_resrcs; |
| |
| xassert(job_resrcs_ptr); |
| #ifdef HAVE_BG |
| if(job_ptr->node_cnt) { |
| task_cnt = job_resrcs_ptr->cpu_array_value[0]; |
| } else |
| task_cnt = 1; |
| #endif |
| for (i=0; i<job_resrcs_ptr->nhosts; i++) { |
| if (i == 0) { |
| xassert(job_resrcs_ptr->cpus && |
| job_resrcs_ptr->node_bitmap); |
| node_inx = bit_ffs(job_resrcs_ptr->node_bitmap); |
| } else { |
| for (node_inx++; node_inx<node_record_count; |
| node_inx++) { |
| if (bit_test(job_resrcs_ptr->node_bitmap, |
| node_inx)) |
| break; |
| } |
| if (node_inx >= node_record_count) { |
| error("Improperly formed job_resrcs for %u", |
| job_ptr->job_id); |
| break; |
| } |
| } |
| host = node_record_table_ptr[node_inx].name; |
| |
| #ifndef HAVE_BG |
| task_cnt = job_resrcs_ptr->cpus[i]; |
| if (job_ptr->details && job_ptr->details->cpus_per_task) |
| task_cnt /= job_ptr->details->cpus_per_task; |
| if (task_cnt < 1) { |
| error("Invalid task_cnt for job %u on node %s", |
| job_ptr->job_id, host); |
| task_cnt = 1; |
| } |
| #endif |
| for (j=0; j<task_cnt; j++) { |
| if (buf) |
| xstrcat(buf, ":"); |
| xstrcat(buf, host); |
| } |
| } |
| return buf; |
| } |
| |
| /* Append to buf a compact tasklist expression (e.g. "tux[0-1]*2") |
| * Prepend ":" to expression as needed */ |
| static void _append_hl_buf(char **buf, hostlist_t *hl_tmp, int *reps) |
| { |
| char *host_str; |
| char *tok, *sep; |
| int i, in_bracket = 0, fini = 0; |
| |
| hostlist_uniq(*hl_tmp); |
| host_str = hostlist_ranged_string_xmalloc(*hl_tmp); |
| |
| /* Note that host_str may be of this form "alpha,beta". We want |
| * to record this as "alpha*#:beta*#" and NOT "alpha,beta*#". |
| * NOTE: Do not break up command within brackets (e.g. "tux[1,2-4]") */ |
| if (*buf) |
| sep = ":"; |
| else |
| sep = ""; |
| tok = host_str; |
| for (i=0; (fini == 0) ; i++) { |
| switch (tok[i]) { |
| case '[': |
| in_bracket = 1; |
| break; |
| case ']': |
| in_bracket = 0; |
| break; |
| case '\0': |
| fini = 1; |
| if (in_bracket) |
| error("badly formed hostlist %s", tok); |
| case ',': |
| if (in_bracket) |
| break; |
| tok[i] = '\0'; |
| xstrfmtcat(*buf, "%s%s*%d", sep, tok, *reps); |
| sep = ":"; |
| tok += (i + 1); |
| i = -1; |
| break; |
| } |
| } |
| xfree(host_str); |
| hostlist_destroy(*hl_tmp); |
| *hl_tmp = (hostlist_t) NULL; |
| *reps = 0; |
| } |
| |
| /* Return task list in Moab format 2: tux[0-1]*2:tux2 */ |
| static char * _task_list_exp(struct job_record *job_ptr) |
| { |
| int i, node_inx = 0, reps = -1, task_cnt; |
| char *buf = NULL, *host; |
| hostlist_t hl_tmp = (hostlist_t) NULL; |
| job_resources_t *job_resrcs_ptr = job_ptr->job_resrcs; |
| |
| xassert(job_resrcs_ptr); |
| #ifdef HAVE_BG |
| if(job_ptr->node_cnt) { |
| task_cnt = job_resrcs_ptr->cpu_array_value[0]; |
| } else |
| task_cnt = 1; |
| #endif |
| for (i=0; i<job_resrcs_ptr->nhosts; i++) { |
| if (i == 0) { |
| xassert(job_resrcs_ptr->cpus && |
| job_resrcs_ptr->node_bitmap); |
| node_inx = bit_ffs(job_resrcs_ptr->node_bitmap); |
| } else { |
| for (node_inx++; node_inx<node_record_count; |
| node_inx++) { |
| if (bit_test(job_resrcs_ptr->node_bitmap, |
| node_inx)) |
| break; |
| } |
| if (node_inx >= node_record_count) { |
| error("Improperly formed job_resrcs for %u", |
| job_ptr->job_id); |
| break; |
| } |
| } |
| host = node_record_table_ptr[node_inx].name; |
| |
| #ifndef HAVE_BG |
| task_cnt = job_resrcs_ptr->cpus[i]; |
| if (job_ptr->details && job_ptr->details->cpus_per_task) |
| task_cnt /= job_ptr->details->cpus_per_task; |
| if (task_cnt < 1) { |
| error("Invalid task_cnt for job %u on node %s", |
| job_ptr->job_id, host); |
| task_cnt = 1; |
| } |
| #endif |
| if (reps == task_cnt) { |
| /* append to existing hostlist record */ |
| if (hostlist_push(hl_tmp, host) == 0) |
| error("hostlist_push failure"); |
| } else { |
| if (hl_tmp) |
| _append_hl_buf(&buf, &hl_tmp, &reps); |
| |
| /* start new hostlist record */ |
| hl_tmp = hostlist_create(host); |
| if (hl_tmp) |
| reps = task_cnt; |
| else |
| error("hostlist_create failure"); |
| } |
| } |
| if (hl_tmp) |
| _append_hl_buf(&buf, &hl_tmp, &reps); |
| return buf; |
| } |