From c49b2952d6e3910bb4852aab1112e83736cebfd8 Mon Sep 17 00:00:00 2001 From: Morris Jette <jette@schedmd.com> Date: Mon, 11 Jul 2016 14:48:36 -0700 Subject: [PATCH] Add support for sbatch --bbf option bug 2858 --- NEWS | 1 + doc/man/man1/sbatch.1 | 8 +++++++ src/sbatch/opt.c | 54 +++++++++++++++++++++++++++++++++++++++---- src/sbatch/opt.h | 1 + src/sbatch/sbatch.c | 53 +++++++++++++++++++++++++++++++++++++++++- 5 files changed, 111 insertions(+), 6 deletions(-) diff --git a/NEWS b/NEWS index ee8f0286342..aff86374ffd 100644 --- a/NEWS +++ b/NEWS @@ -18,6 +18,7 @@ documents those changes that are of interest to users and administrators. value. -- Add ConstrainKmemSpace to cgroup.conf, defaulting to yes, to allow cgroup Kmem enforcement to be disabled while still using ConstrainRAMSpace. + -- Add support for sbatch --bbf option. * Changes in Slurm 16.05.3 ========================== diff --git a/doc/man/man1/sbatch.1 b/doc/man/man1/sbatch.1 index 97b5bda9923..329c337a130 100644 --- a/doc/man/man1/sbatch.1 +++ b/doc/man/man1/sbatch.1 @@ -141,6 +141,14 @@ If not specified, the scontrol show job will display 'ReqS:C:T=*:*:*'. \fB\-\-bb\fR=<\fIspec\fR> Burst buffer specification. The form of the specification is system dependent. +Also see \fB\-\-bbf\fR. + +.TP +\fB\-\-bbf\fR=<\fIfile_name\fR> +Path of file containing burst buffer specification. +The form of the specification is system dependent. +These burst buffer directives will be inserted into the submitted batch script. +Also see \fB\-\-bb\fR. .TP \fB\-\-begin\fR=<\fItime\fR> diff --git a/src/sbatch/opt.c b/src/sbatch/opt.c index 267d3dc2b79..49c817a43a8 100644 --- a/src/sbatch/opt.c +++ b/src/sbatch/opt.c @@ -143,7 +143,6 @@ enum wrappers { #define LONG_OPT_WRAP 0x118 #define LONG_OPT_REQUEUE 0x119 #define LONG_OPT_NETWORK 0x120 -#define LONG_OPT_BURST_BUFFER 0x126 #define LONG_OPT_QOS 0x127 #define LONG_OPT_SOCKETSPERNODE 0x130 #define LONG_OPT_CORESPERSOCKET 0x131 @@ -184,6 +183,8 @@ enum wrappers { #define LONG_OPT_SPREAD_JOB 0x162 #define LONG_OPT_MCS_LABEL 0x165 #define LONG_OPT_DEADLINE 0x166 +#define LONG_OPT_BURST_BUFFER_SPEC 0x167 +#define LONG_OPT_BURST_BUFFER_FILE 0x168 /*---- global variables, defined in opt.h ----*/ opt_t opt; @@ -226,6 +227,7 @@ static uint16_t _parse_pbs_mail_type(const char *arg); static void _usage(void); static void _fullpath(char **filename, const char *cwd); +static char *_read_file(char *fname); static void _set_options(int argc, char **argv); static void _parse_pbs_resource_list(char *rl); @@ -282,7 +284,7 @@ static bool _valid_node_list(char **node_list_pptr) /* * _opt_default(): used by initialize_and_process_args to set defaults */ -static void _opt_default() +static void _opt_default(void) { char buf[MAXPATHLEN + 1]; int i; @@ -412,6 +414,40 @@ static void _opt_default() opt.mcs_label = NULL; } +/* Read specified file's contents into a buffer. + * Caller must xfree the buffer's contents */ +static char *_read_file(char *fname) +{ + int fd, i, offset = 0; + struct stat stat_buf; + char *file_buf; + + fd = open(fname, O_RDONLY); + if (fd < 0) { + fatal("Could not open burst buffer specification file %s: %m", + fname); + } + if (fstat(fd, &stat_buf) < 0) { + fatal("Could not stat burst buffer specification file %s: %m", + fname); + } + file_buf = xmalloc(stat_buf.st_size); + while (stat_buf.st_size > offset) { + i = read(fd, file_buf + offset, stat_buf.st_size - offset); + if (i < 0) { + if (errno == EAGAIN) + continue; + fatal("Could not read burst buffer specification " + "file %s: %m", fname); + } + if (i == 0) + break; /* EOF */ + offset += i; + } + close(fd); + return file_buf; +} + /*---[ env var processing ]-----------------------------------------------*/ /* @@ -744,7 +780,8 @@ static struct option long_options[] = { {"nodelist", required_argument, 0, 'w'}, {"exclude", required_argument, 0, 'x'}, {"acctg-freq", required_argument, 0, LONG_OPT_ACCTG_FREQ}, - {"bb", required_argument, 0, LONG_OPT_BURST_BUFFER}, + {"bb", required_argument, 0, LONG_OPT_BURST_BUFFER_SPEC}, + {"bbf", required_argument, 0, LONG_OPT_BURST_BUFFER_FILE}, {"begin", required_argument, 0, LONG_OPT_BEGIN}, {"blrts-image", required_argument, 0, LONG_OPT_BLRTS_IMAGE}, {"checkpoint", required_argument, 0, LONG_OPT_CHECKPOINT}, @@ -1588,10 +1625,14 @@ static void _set_options(int argc, char **argv) opt.mcs_label = xstrdup(optarg); break; } - case LONG_OPT_BURST_BUFFER: + case LONG_OPT_BURST_BUFFER_SPEC: xfree(opt.burst_buffer); opt.burst_buffer = xstrdup(optarg); break; + case LONG_OPT_BURST_BUFFER_FILE: + xfree(opt.burst_buffer_file); + opt.burst_buffer_file = _read_file(optarg); + break; case LONG_OPT_NICE: if (optarg) opt.nice = strtol(optarg, NULL, 10); @@ -3180,6 +3221,7 @@ static void _opt_list(void) } else info("core-spec : %d", opt.core_spec); info("burst_buffer : `%s'", opt.burst_buffer); + info("burst_buffer_file : `%s'", opt.burst_buffer_file); info("remote command : `%s'", str); info("power : %s", power_flags_str(opt.power_flags)); info("wait : %s", opt.wait ? "no" : "yes"); @@ -3223,7 +3265,8 @@ static void _usage(void) " [--mem_bind=...] [--reservation=name] [--mcs-label=mcs]\n" " [--cpu-freq=min[-max[:gov]] [--power=flags] [--gres-flags=opts]\n" " [--switches=max-switches{@max-time-to-wait}] [--reboot]\n" -" [--core-spec=cores] [--thread-spec=threads] [--bb=burst_buffer_spec]\n" +" [--core-spec=cores] [--thread-spec=threads]\n" +" [--bb=burst_buffer_spec] [--bbf=burst_buffer_file]\n" " [--array=index_values] [--profile=...] [--ignore-pbs] [--spread-job]\n" " [--export[=names]] [--export-file=file|fd] executable [args...]\n"); } @@ -3239,6 +3282,7 @@ static void _help(void) " -a, --array=indexes job array index values\n" " -A, --account=name charge job to specified account\n" " --bb=<spec> burst buffer specifications\n" +" --bbf=<file_name> burst buffer specification file\n" " --begin=time defer job until HH:MM MM/DD/YY\n" " -M, --clusters=names Comma separated list of clusters to issue\n" " commands to. Default is current cluster.\n" diff --git a/src/sbatch/opt.h b/src/sbatch/opt.h index 398b2890543..15883a7bb47 100644 --- a/src/sbatch/opt.h +++ b/src/sbatch/opt.h @@ -184,6 +184,7 @@ typedef struct sbatch_options { uint32_t cpu_freq_gov; /* cpu frequency governor */ bool test_only; /* --test-only */ char *burst_buffer; /* -bb */ + char *burst_buffer_file;/* -bbf */ uint8_t power_flags; /* Power management options */ char *mcs_label; /* mcs label if mcs plugin in use */ time_t deadline; /* ---deadline */ diff --git a/src/sbatch/sbatch.c b/src/sbatch/sbatch.c index a464dedf944..067c0f3dd18 100644 --- a/src/sbatch/sbatch.c +++ b/src/sbatch/sbatch.c @@ -62,6 +62,7 @@ #define MAX_RETRIES 15 +static void _add_bb_to_script(char **script_body, char *burst_buffer_file); static void _env_merge_filter(job_desc_msg_t *desc); static int _fill_job_desc_from_opts(job_desc_msg_t *desc); static int _check_cluster_specific_settings(job_desc_msg_t *desc); @@ -81,7 +82,7 @@ int main(int argc, char *argv[]) job_desc_msg_t desc; submit_response_msg_t *resp; char *script_name; - void *script_body; + char *script_body; int script_size = 0; int rc = 0, retries = 0; @@ -125,6 +126,10 @@ int main(int argc, char *argv[]) exit(error_exit); } + if (opt.burst_buffer_file) + _add_bb_to_script(&script_body, opt.burst_buffer_file); + + if (spank_init_post_opt() < 0) { error("Plugin stack post-option processing failed"); exit(error_exit); @@ -224,6 +229,52 @@ int main(int argc, char *argv[]) return rc; } +/* Insert the contents of "burst_buffer_file" into "script_body" */ +static void _add_bb_to_script(char **script_body, char *burst_buffer_file) +{ + char *orig_script = *script_body; + char *new_script, *sep, save_char; + int i; + + if (!burst_buffer_file || (burst_buffer_file[0] == '\0')) + return; /* No burst buffer file or empty file */ + + if (!orig_script) { + *script_body = xstrdup(burst_buffer_file); + return; + } + + i = strlen(burst_buffer_file) - 1; + if (burst_buffer_file[i] != '\n') /* Append new line as needed */ + xstrcat(burst_buffer_file, "\n"); + + if (orig_script[0] != '#') { + /* Prepend burst buffer file */ + new_script = xstrdup(burst_buffer_file); + xstrcat(new_script, orig_script); + *script_body = new_script; + return; + } + + sep = strchr(orig_script, '\n'); + if (sep) { + save_char = sep[1]; + sep[1] = '\0'; + new_script = xstrdup(orig_script); + xstrcat(new_script, burst_buffer_file); + sep[1] = save_char; + xstrcat(new_script, sep + 1); + *script_body = new_script; + return; + } else { + new_script = xstrdup(orig_script); + xstrcat(new_script, "\n"); + xstrcat(new_script, burst_buffer_file); + *script_body = new_script; + return; + } +} + /* Wait for specified job ID to terminate, return it's exit code */ static int _job_wait(uint32_t job_id) { -- GitLab