From 3b5ad76736a48d3d7e8acb6c992f13fb90493d8e Mon Sep 17 00:00:00 2001 From: Moe Jette <jette1@llnl.gov> Date: Tue, 27 Nov 2007 19:59:55 +0000 Subject: [PATCH] Added support for richer job dependency specification. --- NEWS | 2 + RELEASE_NOTES | 3 +- doc/html/news.shtml | 4 +- doc/man/man1/salloc.1 | 29 +++- doc/man/man1/sbatch.1 | 27 +++- doc/man/man1/srun.1 | 34 ++++- slurm/slurm.h.in | 4 +- src/api/init_msg.c | 2 +- src/api/job_info.c | 33 +++-- src/common/slurm_errno.c | 2 +- src/common/slurm_protocol_defs.c | 2 + src/common/slurm_protocol_pack.c | 24 ++- src/plugins/sched/wiki/job_modify.c | 43 ++---- src/plugins/sched/wiki2/get_jobs.c | 2 +- src/plugins/sched/wiki2/job_modify.c | 43 ++---- src/salloc/opt.c | 14 +- src/salloc/opt.h | 2 +- src/salloc/salloc.c | 3 +- src/sbatch/opt.c | 14 +- src/sbatch/opt.h | 2 +- src/sbatch/sbatch.c | 3 +- src/scontrol/update_job.c | 4 +- src/slurmctld/job_mgr.c | 74 +++++----- src/slurmctld/job_scheduler.c | 213 +++++++++++++++++++++++++++ src/slurmctld/read_config.c | 29 ++++ src/slurmctld/slurmctld.h | 35 ++++- src/squeue/print.c | 9 +- src/srun/opt.c | 17 +-- src/srun/opt.h | 2 +- src/sview/job_info.c | 21 +-- testsuite/expect/test1.42 | 6 +- testsuite/expect/test15.14 | 6 +- testsuite/expect/test17.18 | 6 +- 33 files changed, 515 insertions(+), 199 deletions(-) diff --git a/NEWS b/NEWS index c5c4b663c18..6d5c8e37bae 100644 --- a/NEWS +++ b/NEWS @@ -22,6 +22,8 @@ documents those changes that are of interest to users and admins. information is not found in the "slurm.conf" file, but a scheduler plugin specific configuration (e.g. "wiki.conf"). -- sview partition information reported now includes partition priority. + -- Expand job dependency specification to support concurrent execution, + testing of job exit status and multiple job IDs. * Changes in SLURM 1.3.0-pre4 ============================= diff --git a/RELEASE_NOTES b/RELEASE_NOTES index 6ba5ad4c19d..57e4daac9ca 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -40,6 +40,8 @@ COMMAND CHANGES * sacct -c can now be used to view job completion data * scontrol "notify" command added to send message to stdout of srun for specified job id. +* Support has been added for a much richer job dependency specification + including testing of exit codes and multiple dependencies. CONFIGURATION FILE CHANGES @@ -73,7 +75,6 @@ CONFIGURATION FILE CHANGES * The partition MaxTime format now accepts minutes, minutes:seconds, hours:minutes:seconds, days-hours, days-hours:minutes, days-hours:minutes:seconds or "UNLIMITED". - * See "man slurm.conf" for more information. OTHER CHANGES diff --git a/doc/html/news.shtml b/doc/html/news.shtml index 0b2f792e767..9d3a675c198 100644 --- a/doc/html/news.shtml +++ b/doc/html/news.shtml @@ -80,6 +80,8 @@ without an external scheduler).</li> <i>salloc</i>, <i>sattach</i> or <i>sbatch</i> commands instead).</li> <li><i>srun --pty</i> option added to support remote pseudo terminial for spawned tasks.</li> +<li>Support added for a much richer job dependency specification +including testing of exit codes and multiple dependencies.</li> </ul> <h2><a name="14">Major Updates in SLURM Version 1.4 and beyond</a></h2> @@ -93,6 +95,6 @@ to coordinate activies. Future development plans includes: and refresh.</li> </ul> -<p style="text-align:center;">Last modified 24 October 2007</p> +<p style="text-align:center;">Last modified 27 November 2007</p> <!--#include virtual="footer.txt"--> diff --git a/doc/man/man1/salloc.1 b/doc/man/man1/salloc.1 index 97679079d54..31e63022595 100644 --- a/doc/man/man1/salloc.1 +++ b/doc/man/man1/salloc.1 @@ -1,4 +1,4 @@ -.TH "salloc" "1" "SLURM 1.3" "September 2007" "SLURM Commands" +.TH "salloc" "1" "SLURM 1.3" "November 2007" "SLURM Commands" .SH "NAME" .LP salloc \- Obtain a SLURM job allocation (a set of nodes), execute a command, and then release the allocation when the command is finished. @@ -117,11 +117,32 @@ the \-\-cpus\-per\-task=3 options, the controller knows that each task requires of 4 nodes, one for each of the 4 tasks. .TP -\fB\-d\fR, \fB\-\-dependency\fR[=]<\fIjobid\fR> -Defer the start of this job until the specified \fIjobid\fR has completed. +\fB\-d\fR, \fB\-\-dependency\fR[=]<\fIdependency_list\fR> +Defer the start of this job until the specified dependencies have been +satisfied completed. +<\fIdependency_list\fR> is of the form +<\fItype:job_id[:job_id][,type:job_id[:job_id]]\fR>. Many jobs can share the same dependency and these jobs may even belong to -different users. The value may be changed after job submission using the +different users. The value may be changed after job submission using the scontrol command. +.PD +.RS +.TP +\fBafter:job_id[:jobid...]\fR +This job can begin execution after the specified jobs have begun +execution. +.TP +\fBafterany:job_id[:jobid...]\fR +This job can begin execution after the specified jobs have terminated. +.TP +\fBafternotok:job_id[:jobid...\fR +This job can begin execution after the specified jobs have terminated +in some failed state (non-zero exit code, node failure, timed out, etc). +.TP +\fBafterok:job_id[:jobid...]\fR +This job can begin execution after the specified jobs have successfully +executed (ran to completion with non-zero exit code). +.RE .TP \fB\-\-exclusive\fR diff --git a/doc/man/man1/sbatch.1 b/doc/man/man1/sbatch.1 index 382717546d8..f2410b8c666 100644 --- a/doc/man/man1/sbatch.1 +++ b/doc/man/man1/sbatch.1 @@ -125,11 +125,32 @@ Set the working directory of the batch script to \fIdirectory\fR before it it executed. .TP -\fB\-d\fR, \fB\-\-dependency\fR[=]<\fIjobid\fR> -Defer the start of this job until the specified \fIjobid\fR has completed. +\fB\-d\fR, \fB\-\-dependency\fR[=]<\fIdependency_list\fR> +Defer the start of this job until the specified dependencies have been +satisfied completed. +<\fIdependency_list\fR> is of the form +<\fItype:job_id[:job_id][,type:job_id[:job_id]]\fR>. Many jobs can share the same dependency and these jobs may even belong to -different users. The value may be changed after job submission using the +different users. The value may be changed after job submission using the scontrol command. +.PD +.RS +.TP +\fBafter:job_id[:jobid...]\fR +This job can begin execution after the specified jobs have begun +execution. +.TP +\fBafterany:job_id[:jobid...]\fR +This job can begin execution after the specified jobs have terminated. +.TP +\fBafternotok:job_id[:jobid...\fR +This job can begin execution after the specified jobs have terminated +in some failed state (non-zero exit code, node failure, timed out, etc). +.TP +\fBafterok:job_id[:jobid...]\fR +This job can begin execution after the specified jobs have successfully +executed (ran to completion with non-zero exit code). +.RE .TP \fB\-e\fR, \fB\-\-error\fR[=]<\fIfilename pattern\fR> diff --git a/doc/man/man1/srun.1 b/doc/man/man1/srun.1 index 2fd5e5dca8b..438014bc6c4 100644 --- a/doc/man/man1/srun.1 +++ b/doc/man/man1/srun.1 @@ -1,6 +1,6 @@ \." $Id$ .\" -.TH SRUN "1" "September 2007" "srun 1.3" "slurm components" +.TH SRUN "1" "November 2007" "srun 1.3" "slurm components" .SH "NAME" srun \- run parallel jobs @@ -657,13 +657,33 @@ If the specified file already exists, it will be overwritten. If \fB\-\-error\fR is not also specified on the command line, both stdout and stderr will directed to the file specified by \fB\-\-output\fR. +.TP +\fB\-P\fR, \fB\-\-dependency\fR[=]<\fIdependency_list\fR> +Defer the start of this job until the specified dependencies have been +satisfied completed. +<\fIdependency_list\fR> is of the form +<\fItype:job_id[:job_id][,type:job_id[:job_id]]\fR>. +Many jobs can share the same dependency and these jobs may even belong to +different users. The value may be changed after job submission using the +scontrol command. +.PD +.RS .TP -\fB\-P\fR, \fB\-\-dependency\fR=\fIjobid\fR -Defer initiation of this job until the specified jobid -has completed execution. Many jobs can share the same -dependency and these jobs may belong to different users. -The value may be changed after job submission using the -\fBscontrol\fR command. +\fBafter:job_id[:jobid...]\fR +This job can begin execution after the specified jobs have begun +execution. +.TP +\fBafterany:job_id[:jobid...]\fR +This job can begin execution after the specified jobs have terminated. +.TP +\fBafternotok:job_id[:jobid...\fR +This job can begin execution after the specified jobs have terminated +in some failed state (non-zero exit code, node failure, timed out, etc). +.TP +\fBafterok:job_id[:jobid...]\fR +This job can begin execution after the specified jobs have successfully +executed (ran to completion with non-zero exit code). +.RE .TP \fB\-p\fR, \fB\-\-partition\fR=\fIpartition\fR diff --git a/slurm/slurm.h.in b/slurm/slurm.h.in index 6941a01c38e..7fe25cb1773 100644 --- a/slurm/slurm.h.in +++ b/slurm/slurm.h.in @@ -527,7 +527,7 @@ typedef struct job_descriptor { /* For submit, allocate, and update requests */ uint16_t alloc_resp_port; uint16_t other_port; - uint32_t dependency; /* defer until specified job completes */ + char *dependency; /* syncrhonize job execution with other jobs */ uint16_t overcommit; /* over subscribe resources, for batch only */ uint32_t num_tasks; /* number of tasks to be started, for batch only */ uint16_t nice; /* requested priority change, @@ -623,7 +623,7 @@ typedef struct job_info { * start_range_1, end_range_1, * start_range_2, .., -1 */ char *features; /* comma separated list of required features */ - uint32_t dependency; /* defer until specified job completes */ + char *dependency; /* syncrhonize job execution with other jobs */ uint32_t exit_code; /* exit code for job (status from wait call) */ char *account; /* charge to specified account */ uint16_t state_reason; /* reason job still pending or failed, see diff --git a/src/api/init_msg.c b/src/api/init_msg.c index 96a7044495f..bca6a1b800c 100644 --- a/src/api/init_msg.c +++ b/src/api/init_msg.c @@ -64,7 +64,7 @@ void slurm_init_job_desc_msg(job_desc_msg_t * job_desc_msg) job_desc_msg->ntasks_per_node = (uint16_t) NO_VAL; job_desc_msg->ntasks_per_socket = (uint16_t) NO_VAL; job_desc_msg->ntasks_per_core = (uint16_t) NO_VAL; - job_desc_msg->dependency = NO_VAL; + job_desc_msg->dependency = NULL; job_desc_msg->environment = ((char **) NULL); job_desc_msg->env_size = 0; job_desc_msg->features = NULL; diff --git a/src/api/job_info.c b/src/api/job_info.c index ea19c5b38ff..6e755812f2b 100644 --- a/src/api/job_info.c +++ b/src/api/job_info.c @@ -378,8 +378,17 @@ slurm_sprint_job_info ( job_info_t * job_ptr, int one_liner ) /****** Line 11 ******/ snprintf(tmp_line, sizeof(tmp_line), - "Dependency=%u Account=%s Reason=%s Network=%s", - job_ptr->dependency, job_ptr->account, + "Dependency=%s Account=%s", + job_ptr->dependency, job_ptr->account); + xstrcat(out, tmp_line); + if (one_liner) + xstrcat(out, " "); + else + xstrcat(out, "\n "); + + /****** Line 12 ******/ + snprintf(tmp_line, sizeof(tmp_line), + "Reason=%s Network=%s", job_reason_string(job_ptr->state_reason), job_ptr->network); xstrcat(out, tmp_line); @@ -388,7 +397,7 @@ slurm_sprint_job_info ( job_info_t * job_ptr, int one_liner ) else xstrcat(out, "\n "); - /****** Line 12 ******/ + /****** Line 13 ******/ snprintf(tmp_line, sizeof(tmp_line), "Req%s=%s Req%sIndices=", nodelist, job_ptr->req_nodes, nodelist); xstrcat(out, tmp_line); @@ -405,7 +414,7 @@ slurm_sprint_job_info ( job_info_t * job_ptr, int one_liner ) else xstrcat(out, "\n "); - /****** Line 13 ******/ + /****** Line 14 ******/ snprintf(tmp_line, sizeof(tmp_line), "Exc%s=%s Exc%sIndices=", nodelist, job_ptr->exc_nodes, nodelist); xstrcat(out, tmp_line); @@ -422,7 +431,7 @@ slurm_sprint_job_info ( job_info_t * job_ptr, int one_liner ) else xstrcat(out, "\n "); - /****** Line 14 ******/ + /****** Line 15 ******/ slurm_make_time_str((time_t *)&job_ptr->submit_time, time_str, sizeof(time_str)); sprintf(tmp_line, "SubmitTime=%s ", time_str); @@ -437,7 +446,7 @@ slurm_sprint_job_info ( job_info_t * job_ptr, int one_liner ) time_str, (long int)job_ptr->pre_sus_time); xstrcat(out, tmp_line); - /****** Lines 15, 16 (optional, batch only) ******/ + /****** Lines 16, 17 (optional, batch only) ******/ if (job_ptr->batch_flag) { if (one_liner) xstrcat(out, " "); @@ -454,7 +463,7 @@ slurm_sprint_job_info ( job_info_t * job_ptr, int one_liner ) xstrcat(out, tmp_line); } - /****** Line 17 (optional) ******/ + /****** Line 18 (optional) ******/ if (job_ptr->comment) { if (one_liner) xstrcat(out, " "); @@ -464,7 +473,7 @@ slurm_sprint_job_info ( job_info_t * job_ptr, int one_liner ) xstrcat(out, tmp_line); } - /****** Line 18 (optional) ******/ + /****** Line 19 (optional) ******/ select_g_sprint_jobinfo(job_ptr->select_jobinfo, select_buf, sizeof(select_buf), SELECT_PRINT_MIXED); @@ -475,7 +484,7 @@ slurm_sprint_job_info ( job_info_t * job_ptr, int one_liner ) xstrcat(out, "\n "); xstrcat(out, select_buf); } - /****** Line 19 (optional) ******/ + /****** Line 20 (optional) ******/ select_g_sprint_jobinfo(job_ptr->select_jobinfo, select_buf, sizeof(select_buf), SELECT_PRINT_BLRTS_IMAGE); @@ -488,7 +497,7 @@ slurm_sprint_job_info ( job_info_t * job_ptr, int one_liner ) "BlrtsImage=%s", select_buf); xstrcat(out, tmp_line); } - /****** Line 20 (optional) ******/ + /****** Line 21 (optional) ******/ select_g_sprint_jobinfo(job_ptr->select_jobinfo, select_buf, sizeof(select_buf), SELECT_PRINT_LINUX_IMAGE); @@ -501,7 +510,7 @@ slurm_sprint_job_info ( job_info_t * job_ptr, int one_liner ) "LinuxImage=%s", select_buf); xstrcat(out, tmp_line); } - /****** Line 21 (optional) ******/ + /****** Line 22 (optional) ******/ select_g_sprint_jobinfo(job_ptr->select_jobinfo, select_buf, sizeof(select_buf), SELECT_PRINT_MLOADER_IMAGE); @@ -514,7 +523,7 @@ slurm_sprint_job_info ( job_info_t * job_ptr, int one_liner ) "MloaderImage=%s", select_buf); xstrcat(out, tmp_line); } - /****** Line 22 (optional) ******/ + /****** Line 23 (optional) ******/ select_g_sprint_jobinfo(job_ptr->select_jobinfo, select_buf, sizeof(select_buf), SELECT_PRINT_RAMDISK_IMAGE); diff --git a/src/common/slurm_errno.c b/src/common/slurm_errno.c index 8977b79a352..c30dfb8e0c1 100644 --- a/src/common/slurm_errno.c +++ b/src/common/slurm_errno.c @@ -189,7 +189,7 @@ static slurm_errtab_t slurm_errtab[] = { { ESLURM_DISABLED, "Requested operation is presently disabled" }, { ESLURM_DEPENDENCY, - "Immediate execution impossible, job dependency problem"}, + "Job dependency problem" }, { ESLURM_BATCH_ONLY, "Only batch jobs are accepted or processed" }, { ESLURM_TASKDIST_ARBITRARY_UNSUPPORTED, diff --git a/src/common/slurm_protocol_defs.c b/src/common/slurm_protocol_defs.c index 9ce114419b7..aa77dbcc285 100644 --- a/src/common/slurm_protocol_defs.c +++ b/src/common/slurm_protocol_defs.c @@ -200,6 +200,7 @@ void slurm_free_job_desc_msg(job_desc_msg_t * msg) xfree(msg->account); xfree(msg->network); xfree(msg->comment); + xfree(msg->dependency); xfree(msg->resp_host); xfree(msg->blrtsimage); xfree(msg->linuximage); @@ -267,6 +268,7 @@ void slurm_free_job_info_members(job_info_t * job) xfree(job->exc_node_inx); xfree(job->network); xfree(job->comment); + xfree(job->dependency); xfree(job->work_dir); xfree(job->command); } diff --git a/src/common/slurm_protocol_pack.c b/src/common/slurm_protocol_pack.c index 85f18c10b72..c84028281d0 100644 --- a/src/common/slurm_protocol_pack.c +++ b/src/common/slurm_protocol_pack.c @@ -2035,10 +2035,9 @@ _unpack_job_info_members(job_info_t * job, Buf buffer) safe_unpackstr_xmalloc(&job->account, &uint16_tmp, buffer); safe_unpackstr_xmalloc(&job->network, &uint16_tmp, buffer); safe_unpackstr_xmalloc(&job->comment, &uint16_tmp, buffer); + safe_unpackstr_xmalloc(&job->dependency, &uint16_tmp, buffer); - safe_unpack32(&job->dependency, buffer); safe_unpack32(&job->exit_code, buffer); - safe_unpack16(&job->num_cpu_groups, buffer); safe_unpack32_array(&job->cpus_per_node, &uint32_tmp, buffer); safe_unpack32_array(&job->cpu_count_reps, &uint32_tmp, buffer); @@ -2117,17 +2116,23 @@ unpack_error: xfree(job->nodes); xfree(job->partition); xfree(job->account); + xfree(job->network); + xfree(job->comment); + xfree(job->dependency); + xfree(job->cpus_per_node); + xfree(job->cpu_count_reps); xfree(job->name); xfree(job->alloc_node); xfree(job->node_inx); select_g_free_jobinfo(&job->select_jobinfo); xfree(job->features); + xfree(job->work_dir); + xfree(job->command); xfree(job->req_nodes); xfree(job->req_node_inx); xfree(job->exc_nodes); xfree(job->exc_node_inx); - xfree(job->network); - xfree(job->comment); + return SLURM_ERROR; } @@ -2495,7 +2500,7 @@ _pack_job_desc_msg(job_desc_msg_t * job_desc_ptr, Buf buffer) packstr(job_desc_ptr->partition, buffer); pack32(job_desc_ptr->priority, buffer); - pack32(job_desc_ptr->dependency, buffer); + packstr(job_desc_ptr->dependency, buffer); packstr(job_desc_ptr->account, buffer); packstr(job_desc_ptr->comment, buffer); pack16(job_desc_ptr->nice, buffer); @@ -2624,7 +2629,7 @@ _unpack_job_desc_msg(job_desc_msg_t ** job_desc_buffer_ptr, Buf buffer) safe_unpackstr_xmalloc(&job_desc_ptr->partition, &uint16_tmp, buffer); safe_unpack32(&job_desc_ptr->priority, buffer); - safe_unpack32(&job_desc_ptr->dependency, buffer); + safe_unpackstr_xmalloc(&job_desc_ptr->dependency, &uint16_tmp, buffer); safe_unpackstr_xmalloc(&job_desc_ptr->account, &uint16_tmp, buffer); safe_unpackstr_xmalloc(&job_desc_ptr->comment, &uint16_tmp, buffer); safe_unpack16(&job_desc_ptr->nice, buffer); @@ -2688,11 +2693,15 @@ _unpack_job_desc_msg(job_desc_msg_t ** job_desc_buffer_ptr, Buf buffer) return SLURM_SUCCESS; unpack_error: - select_g_free_jobinfo(&job_desc_ptr->select_jobinfo); + xfree(job_desc_ptr->features); xfree(job_desc_ptr->name); xfree(job_desc_ptr->partition); + xfree(job_desc_ptr->dependency); + xfree(job_desc_ptr->account); + xfree(job_desc_ptr->comment); xfree(job_desc_ptr->req_nodes); + xfree(job_desc_ptr->exc_nodes); xfree(job_desc_ptr->environment); xfree(job_desc_ptr->script); xfree(job_desc_ptr->argv); @@ -2702,6 +2711,7 @@ unpack_error: xfree(job_desc_ptr->work_dir); xfree(job_desc_ptr->network); xfree(job_desc_ptr->mail_user); + select_g_free_jobinfo(&job_desc_ptr->select_jobinfo); xfree(job_desc_ptr); *job_desc_buffer_ptr = NULL; return SLURM_ERROR; diff --git a/src/plugins/sched/wiki/job_modify.c b/src/plugins/sched/wiki/job_modify.c index e3a62654951..72030e90aea 100644 --- a/src/plugins/sched/wiki/job_modify.c +++ b/src/plugins/sched/wiki/job_modify.c @@ -53,24 +53,8 @@ static void _null_term(char *str) } } -/* return -1 on error */ -static int32_t _get_depend_id(char *str) -{ - /* stand-alone job_id */ - if (isdigit(str[0])) - return (int32_t) atol(str); - - if (strncasecmp(str, "afterany:", 9) != 0) /* invalid spec */ - return (int32_t) -1; - - str += 9; - if (!isdigit(str[0])) - return (int32_t) -1; - return (int32_t) atol(str); -} - static int _job_modify(uint32_t jobid, char *bank_ptr, - int32_t depend_id, char *new_hostlist, + char *depend_ptr, char *new_hostlist, uint32_t new_node_cnt, char *part_name_ptr, uint32_t new_time_limit) { @@ -86,9 +70,16 @@ static int _job_modify(uint32_t jobid, char *bank_ptr, return ESLURM_DISABLED; } - if (depend_id != -1) { - info("wiki: changing job dependency to %d", depend_id); - job_ptr->dependency = depend_id; + if (depend_ptr) { + int rc = update_job_dependency(job_ptr, depend_ptr); + if (rc == SLURM_SUCCESS) { + info("wiki: changed job %u dependency to %s", + jobid, depend_ptr); + } else { + error("wiki: changing job %u dependency to %s", + jobid, depend_ptr); + return EINVAL; + } } if (new_time_limit) { @@ -207,7 +198,6 @@ extern int job_modify_wiki(char *cmd_ptr, int *err_code, char **err_msg) char *arg_ptr, *bank_ptr, *depend_ptr, *nodes_ptr; char *host_ptr, *part_ptr, *time_ptr, *tmp_char; int slurm_rc; - int depend_id = -1; uint32_t jobid, new_node_cnt = 0, new_time_limit = 0; static char reply_msg[128]; /* Locks: write job, read node and partition info */ @@ -246,14 +236,7 @@ extern int job_modify_wiki(char *cmd_ptr, int *err_code, char **err_msg) if (depend_ptr) { depend_ptr[6] = ':'; depend_ptr += 7; - depend_id = _get_depend_id(depend_ptr); - if (depend_id == -1) { - *err_code = -300; - *err_msg = "MODIFYJOB has invalid DEPEND specificiation"; - error("wiki: MODIFYJOB has invalid DEPEND spec: %s", - depend_ptr); - return -1; - } + _null_term(depend_ptr); } if (host_ptr) { host_ptr[8] = ':'; @@ -286,7 +269,7 @@ extern int job_modify_wiki(char *cmd_ptr, int *err_code, char **err_msg) } lock_slurmctld(job_write_lock); - slurm_rc = _job_modify(jobid, bank_ptr, depend_id, host_ptr, + slurm_rc = _job_modify(jobid, bank_ptr, depend_ptr, host_ptr, new_node_cnt, part_ptr, new_time_limit); unlock_slurmctld(job_write_lock); if (slurm_rc != SLURM_SUCCESS) { diff --git a/src/plugins/sched/wiki2/get_jobs.c b/src/plugins/sched/wiki2/get_jobs.c index 847cb86ff36..bd51ec31b39 100644 --- a/src/plugins/sched/wiki2/get_jobs.c +++ b/src/plugins/sched/wiki2/get_jobs.c @@ -372,7 +372,7 @@ static void _get_job_comment(struct job_record *job_ptr, if (job_ptr->dependency) { /* Kludge for job dependency set via srun */ size += snprintf((buffer + size), (buf_size - size), - "DEPEND=afterany:%u", job_ptr->dependency); + "DEPEND=%s", job_ptr->dependency); field_sep = "?"; } diff --git a/src/plugins/sched/wiki2/job_modify.c b/src/plugins/sched/wiki2/job_modify.c index e3a62654951..72030e90aea 100644 --- a/src/plugins/sched/wiki2/job_modify.c +++ b/src/plugins/sched/wiki2/job_modify.c @@ -53,24 +53,8 @@ static void _null_term(char *str) } } -/* return -1 on error */ -static int32_t _get_depend_id(char *str) -{ - /* stand-alone job_id */ - if (isdigit(str[0])) - return (int32_t) atol(str); - - if (strncasecmp(str, "afterany:", 9) != 0) /* invalid spec */ - return (int32_t) -1; - - str += 9; - if (!isdigit(str[0])) - return (int32_t) -1; - return (int32_t) atol(str); -} - static int _job_modify(uint32_t jobid, char *bank_ptr, - int32_t depend_id, char *new_hostlist, + char *depend_ptr, char *new_hostlist, uint32_t new_node_cnt, char *part_name_ptr, uint32_t new_time_limit) { @@ -86,9 +70,16 @@ static int _job_modify(uint32_t jobid, char *bank_ptr, return ESLURM_DISABLED; } - if (depend_id != -1) { - info("wiki: changing job dependency to %d", depend_id); - job_ptr->dependency = depend_id; + if (depend_ptr) { + int rc = update_job_dependency(job_ptr, depend_ptr); + if (rc == SLURM_SUCCESS) { + info("wiki: changed job %u dependency to %s", + jobid, depend_ptr); + } else { + error("wiki: changing job %u dependency to %s", + jobid, depend_ptr); + return EINVAL; + } } if (new_time_limit) { @@ -207,7 +198,6 @@ extern int job_modify_wiki(char *cmd_ptr, int *err_code, char **err_msg) char *arg_ptr, *bank_ptr, *depend_ptr, *nodes_ptr; char *host_ptr, *part_ptr, *time_ptr, *tmp_char; int slurm_rc; - int depend_id = -1; uint32_t jobid, new_node_cnt = 0, new_time_limit = 0; static char reply_msg[128]; /* Locks: write job, read node and partition info */ @@ -246,14 +236,7 @@ extern int job_modify_wiki(char *cmd_ptr, int *err_code, char **err_msg) if (depend_ptr) { depend_ptr[6] = ':'; depend_ptr += 7; - depend_id = _get_depend_id(depend_ptr); - if (depend_id == -1) { - *err_code = -300; - *err_msg = "MODIFYJOB has invalid DEPEND specificiation"; - error("wiki: MODIFYJOB has invalid DEPEND spec: %s", - depend_ptr); - return -1; - } + _null_term(depend_ptr); } if (host_ptr) { host_ptr[8] = ':'; @@ -286,7 +269,7 @@ extern int job_modify_wiki(char *cmd_ptr, int *err_code, char **err_msg) } lock_slurmctld(job_write_lock); - slurm_rc = _job_modify(jobid, bank_ptr, depend_id, host_ptr, + slurm_rc = _job_modify(jobid, bank_ptr, depend_ptr, host_ptr, new_node_cnt, part_ptr, new_time_limit); unlock_slurmctld(job_write_lock); if (slurm_rc != SLURM_SUCCESS) { diff --git a/src/salloc/opt.c b/src/salloc/opt.c index 24b7ca9ebf2..0c7c846c369 100644 --- a/src/salloc/opt.c +++ b/src/salloc/opt.c @@ -235,7 +235,7 @@ static void _opt_default() opt.job_name = NULL; opt.jobid = NO_VAL; - opt.dependency = NO_VAL; + opt.dependency = NULL; opt.account = NULL; opt.comment = NULL; @@ -564,7 +564,8 @@ void set_options(const int argc, char **argv) opt.constraints = xstrdup(optarg); break; case 'd': - opt.dependency = _get_int(optarg, "dependency"); + xfree(opt.dependency); + opt.dependency = xstrdup(optarg); break; case 'F': xfree(opt.nodelist); @@ -1215,10 +1216,7 @@ static void _opt_list() info("nice : %d", opt.nice); info("account : %s", opt.account); info("comment : %s", opt.comment); - if (opt.dependency == NO_VAL) - info("dependency : none"); - else - info("dependency : %u", opt.dependency); + info("dependency : %s", opt.dependency); str = print_constraints(); info("constraints : %s", str); xfree(str); @@ -1270,7 +1268,7 @@ static void _usage(void) " [--verbose] [--gid=group] [--uid=user]\n" " [-W sec] [--minsockets=n] [--mincores=n] [--minthreads=n]\n" " [--contiguous] [--mincpus=n] [--mem=MB] [--tmp=MB] [-C list]\n" -" [--account=name] [--dependency=jobid] [--comment=name]\n" +" [--account=name] [--dependency=type:jobid] [--comment=name]\n" #ifdef HAVE_BG /* Blue gene specific options */ " [--geometry=XxYxZ] [--conn-type=type] [--no-rotate] [ --reboot]\n" " [--blrts-image=path] [--linux-image=path]\n" @@ -1310,7 +1308,7 @@ static void _help(void) " immediately available\n" " -v, --verbose verbose mode (multiple -v's increase verbosity)\n" " -q, --quiet quiet mode (suppress informational messages)\n" -" -d, --dependency=jobid defer job until specified jobid completes\n" +" -d, --dependency=type:jobid defer job until condition on jobid is satisfied\n" " --nice[=value] decrease secheduling priority by value\n" " -U, --account=name charge job to specified account\n" " --begin=time defer job until HH:MM DD/MM/YY\n" diff --git a/src/salloc/opt.h b/src/salloc/opt.h index ea5fb69d57e..bdf773d9248 100644 --- a/src/salloc/opt.h +++ b/src/salloc/opt.h @@ -83,7 +83,7 @@ typedef struct salloc_options { * plane> */ char *job_name; /* --job-name=, -J name */ unsigned int jobid; /* --jobid=jobid */ - unsigned int dependency;/* --dependency, -P jobid */ + char *dependency; /* --dependency, -P type:jobid */ int nice; /* --nice */ char *account; /* --account, -U acct_name */ char *comment; /* --comment */ diff --git a/src/salloc/salloc.c b/src/salloc/salloc.c index 25262bdb010..f08d2752dd0 100644 --- a/src/salloc/salloc.c +++ b/src/salloc/salloc.c @@ -250,7 +250,8 @@ static int fill_job_desc_from_opts(job_desc_msg_t *desc) desc->max_nodes = opt.max_nodes; desc->user_id = opt.uid; desc->group_id = opt.gid; - desc->dependency = opt.dependency; + if (opt.dependency) + desc->dependency = xstrdup(opt.dependency); desc->task_dist = opt.distribution; if (opt.plane_size != NO_VAL) desc->plane_size = opt.plane_size; diff --git a/src/sbatch/opt.c b/src/sbatch/opt.c index 0caba426496..7eecac2d9b7 100644 --- a/src/sbatch/opt.c +++ b/src/sbatch/opt.c @@ -234,7 +234,7 @@ static void _opt_default() opt.job_name = NULL; opt.jobid = NO_VAL; opt.jobid_set = false; - opt.dependency = NO_VAL; + opt.dependency = NULL; opt.account = NULL; opt.comment = NULL; @@ -892,7 +892,8 @@ static void _set_options(int argc, char **argv) opt.constraints = xstrdup(optarg); break; case 'd': - opt.dependency = _get_int(optarg, "dependency"); + xfree(opt.dependency); + opt.dependency = xstrdup(optarg); break; case 'D': xfree(opt.cwd); @@ -1928,10 +1929,7 @@ static void _opt_list() info("nice : %d", opt.nice); info("account : %s", opt.account); info("comment : %s", opt.comment); - if (opt.dependency == NO_VAL) - info("dependency : none"); - else - info("dependency : %u", opt.dependency); + info("dependency : %s", opt.dependency); str = print_constraints(); info("constraints : %s", str); xfree(str); @@ -1986,7 +1984,7 @@ static void _usage(void) " [--jobid=id] [--verbose] [--gid=group] [--uid=user]\n" " [-W sec] [--minsockets=n] [--mincores=n] [--minthreads=n]\n" " [--contiguous] [--mincpus=n] [--mem=MB] [--tmp=MB] [-C list]\n" -" [--account=name] [--dependency=jobid] [--comment=name]\n" +" [--account=name] [--dependency=type:jobid] [--comment=name]\n" #ifdef HAVE_BG /* Blue gene specific options */ " [--geometry=XxYxZ] [--conn-type=type] [--no-rotate] [ --reboot]\n" " [--blrts-image=path] [--linux-image=path]\n" @@ -2024,7 +2022,7 @@ static void _help(void) " --jobid=id run under already allocated job\n" " -v, --verbose verbose mode (multiple -v's increase verbosity)\n" " -q, --quiet quiet mode (suppress informational messages)\n" -" -d, --dependency=jobid defer job until specified jobid completes\n" +" -d, --dependency=type:jobid defer job until condition on jobid is satisfied\n" " -D, --workdir=directory set working directory for batch script\n" " --nice[=value] decrease secheduling priority by value\n" " -O, --overcommit overcommit resources\n" diff --git a/src/sbatch/opt.h b/src/sbatch/opt.h index 1c572e5f6e3..cb8097a05ad 100644 --- a/src/sbatch/opt.h +++ b/src/sbatch/opt.h @@ -87,7 +87,7 @@ typedef struct sbatch_options { unsigned int jobid; /* --jobid=jobid */ bool jobid_set; /* true of jobid explicitly set */ char *mpi_type; /* --mpi=type */ - unsigned int dependency;/* --dependency, -P jobid */ + char *dependency; /* --dependency, -P type:jobid */ int nice; /* --nice */ char *account; /* --account, -U acct_name */ char *comment; /* --comment */ diff --git a/src/sbatch/sbatch.c b/src/sbatch/sbatch.c index f608b538739..75f0082e0de 100644 --- a/src/sbatch/sbatch.c +++ b/src/sbatch/sbatch.c @@ -127,7 +127,8 @@ static int fill_job_desc_from_opts(job_desc_msg_t *desc) desc->max_nodes = opt.max_nodes; desc->user_id = opt.uid; desc->group_id = opt.gid; - desc->dependency = opt.dependency; + if (opt.dependency) + desc->dependency = xstrdup(opt.dependency); desc->task_dist = opt.distribution; if (opt.plane_size != NO_VAL) desc->plane_size = opt.plane_size; diff --git a/src/scontrol/update_job.c b/src/scontrol/update_job.c index 1a03924bb6e..fed7bcc7d51 100644 --- a/src/scontrol/update_job.c +++ b/src/scontrol/update_job.c @@ -366,9 +366,7 @@ scontrol_update_job (int argc, char *argv[]) update_cnt++; } else if (strncasecmp(argv[i], "Dependency=", 11) == 0) { - job_msg.dependency = - (uint32_t) strtol(&argv[i][11], - (char **) NULL, 10); + job_msg.dependency = &argv[i][11]; update_cnt++; } #ifdef HAVE_BG diff --git a/src/slurmctld/job_mgr.c b/src/slurmctld/job_mgr.c index 74d69516e46..ce91288fd33 100644 --- a/src/slurmctld/job_mgr.c +++ b/src/slurmctld/job_mgr.c @@ -357,7 +357,7 @@ int dump_all_job_state(void) * checkpoint. Execute this after loading the configuration file data. * RET 0 or error code */ -int load_all_job_state(void) +extern int load_all_job_state(void) { int data_allocated, data_read = 0, error_code = 0; uint32_t data_size = 0; @@ -484,7 +484,6 @@ static void _dump_job_state(struct job_record *dump_job_ptr, Buf buffer) pack32(dump_job_ptr->time_limit, buffer); pack32(dump_job_ptr->priority, buffer); pack32(dump_job_ptr->alloc_sid, buffer); - pack32(dump_job_ptr->dependency, buffer); pack32(dump_job_ptr->num_procs, buffer); pack32(dump_job_ptr->exit_code, buffer); pack32(dump_job_ptr->db_index, buffer); @@ -520,6 +519,7 @@ static void _dump_job_state(struct job_record *dump_job_ptr, Buf buffer) packstr(dump_job_ptr->alloc_node, buffer); packstr(dump_job_ptr->account, buffer); packstr(dump_job_ptr->comment, buffer); + packstr(dump_job_ptr->dependency, buffer); packstr(dump_job_ptr->network, buffer); packstr(dump_job_ptr->mail_user, buffer); @@ -550,7 +550,7 @@ static void _dump_job_state(struct job_record *dump_job_ptr, Buf buffer) static int _load_job_state(Buf buffer) { uint32_t job_id, user_id, group_id, time_limit, priority, alloc_sid; - uint32_t dependency, exit_code, num_procs, db_index; + uint32_t exit_code, num_procs, db_index; time_t start_time, end_time, suspend_time, pre_sus_time; uint16_t job_state, next_step_id, details, batch_flag, step_flag; uint16_t kill_on_node_fail, kill_on_step_done, name_len; @@ -558,6 +558,7 @@ static int _load_job_state(Buf buffer) char *nodes = NULL, *partition = NULL, *name = NULL, *resp_host = NULL; char *account = NULL, *network = NULL, *mail_user = NULL; char *comment = NULL, *nodes_completing = NULL, *alloc_node = NULL; + char *dependency = NULL; struct job_record *job_ptr; struct part_record *part_ptr; int error_code; @@ -569,7 +570,6 @@ static int _load_job_state(Buf buffer) safe_unpack32(&time_limit, buffer); safe_unpack32(&priority, buffer); safe_unpack32(&alloc_sid, buffer); - safe_unpack32(&dependency, buffer); safe_unpack32(&num_procs, buffer); safe_unpack32(&exit_code, buffer); safe_unpack32(&db_index, buffer); @@ -601,6 +601,7 @@ static int _load_job_state(Buf buffer) safe_unpackstr_xmalloc(&alloc_node, &name_len, buffer); safe_unpackstr_xmalloc(&account, &name_len, buffer); safe_unpackstr_xmalloc(&comment, &name_len, buffer); + safe_unpackstr_xmalloc(&dependency, &name_len, buffer); safe_unpackstr_xmalloc(&network, &name_len, buffer); safe_unpackstr_xmalloc(&mail_user, &name_len, buffer); @@ -738,6 +739,7 @@ unpack_error: xfree(alloc_node); xfree(account); xfree(comment); + xfree(dependency); xfree(resp_host); xfree(mail_user); select_g_free_jobinfo(&select_jobinfo); @@ -1133,7 +1135,7 @@ void dump_job_desc(job_desc_msg_t * job_specs) long job_min_procs, job_min_sockets, job_min_cores, job_min_threads; long job_min_memory, job_max_memory, job_min_tmp_disk, num_procs; long time_limit, priority, contiguous; - long kill_on_node_fail, shared, immediate, dependency; + long kill_on_node_fail, shared, immediate; long cpus_per_task, no_requeue, num_tasks, overcommit; long ntasks_per_node, ntasks_per_socket, ntasks_per_core; char buf[100]; @@ -1239,13 +1241,12 @@ void dump_job_desc(job_desc_msg_t * job_specs) job_specs->work_dir, job_specs->alloc_node, job_specs->alloc_sid); - dependency = (job_specs->dependency != NO_VAL) ? - (long) job_specs->dependency : -1L; debug3(" resp_host=%s alloc_resp_port=%u other_port=%u", job_specs->resp_host, job_specs->alloc_resp_port, job_specs->other_port); - debug3(" dependency=%ld account=%s comment=%s", - dependency, job_specs->account, job_specs->comment); + debug3(" dependency=%s account=%s comment=%s", + job_specs->dependency, job_specs->account, + job_specs->comment); num_tasks = (job_specs->num_tasks != (uint16_t) NO_VAL) ? (long) job_specs->num_tasks : -1L; @@ -1926,10 +1927,9 @@ static int _job_create(job_desc_msg_t * job_desc, int allocate, int will_run, goto cleanup; } - if ((error_code =_validate_job_create_req(job_desc))) goto cleanup; - + if ((error_code = _copy_job_desc_to_job_record(job_desc, job_pptr, part_ptr, @@ -1938,10 +1938,9 @@ static int _job_create(job_desc_msg_t * job_desc, int allocate, int will_run, error_code = ESLURM_ERROR_ON_DESC_TO_RECORD_COPY; goto cleanup; } - + job_ptr = *job_pptr; - if (job_ptr->dependency == job_ptr->job_id) { - info("User specified self as dependent job"); + if (update_job_dependency(job_ptr, job_desc->dependency)) { error_code = ESLURM_DEPENDENCY; goto cleanup; } @@ -2434,8 +2433,6 @@ _copy_job_desc_to_job_record(job_desc_msg_t * job_desc, job_ptr->account = xstrdup(job_desc->account); job_ptr->network = xstrdup(job_desc->network); job_ptr->comment = xstrdup(job_desc->comment); - if (job_desc->dependency != NO_VAL) /* leave as zero */ - job_ptr->dependency = job_desc->dependency; if (job_desc->priority != NO_VAL) /* already confirmed submit_uid==0 */ job_ptr->priority = job_desc->priority; @@ -2516,7 +2513,6 @@ _copy_job_desc_to_job_record(job_desc_msg_t * job_desc, job_ptr->select_jobinfo = select_g_copy_jobinfo(job_desc->select_jobinfo); detail_ptr->mc_ptr = _set_multi_core_data(job_desc); - *job_rec_ptr = job_ptr; return SLURM_SUCCESS; } @@ -2781,6 +2777,9 @@ static void _list_delete_job(void *job_entry) xfree(job_ptr->alloc_lps); xfree(job_ptr->used_lps); xfree(job_ptr->comment); + xfree(job_ptr->dependency); + if (job_ptr->depend_list) + list_destroy(job_ptr->depend_list); select_g_free_jobinfo(&job_ptr->select_jobinfo); if (job_ptr->step_list) { delete_step_records(job_ptr, 0); @@ -2952,8 +2951,8 @@ void pack_job(struct job_record *dump_job_ptr, Buf buffer) packstr(dump_job_ptr->account, buffer); packstr(dump_job_ptr->network, buffer); packstr(dump_job_ptr->comment, buffer); + packstr(dump_job_ptr->dependency, buffer); - pack32(dump_job_ptr->dependency, buffer); pack32(dump_job_ptr->exit_code, buffer); pack16(dump_job_ptr->num_cpu_groups, buffer); @@ -3788,14 +3787,14 @@ int update_job(job_desc_msg_t * job_specs, uid_t uid) } } - if (job_specs->dependency != NO_VAL) { + if (job_specs->dependency) { if (!IS_JOB_PENDING(job_ptr)) error_code = ESLURM_DISABLED; - else if (job_specs->dependency == job_ptr->job_id) + else if (update_job_dependency(job_ptr, job_specs->dependency) + != SLURM_SUCCESS) { error_code = ESLURM_DEPENDENCY; - else { - job_ptr->dependency = job_specs->dependency; - info("update_job: setting dependency to %u for " + } else { + info("update_job: setting dependency to %s for " "job_id %u", job_ptr->dependency, job_ptr->job_id); } @@ -4424,27 +4423,30 @@ extern void job_completion_logger(struct job_record *job_ptr) */ extern bool job_independent(struct job_record *job_ptr) { - struct job_record *dep_ptr; struct job_details *detail_ptr = job_ptr->details; + int rc; if (detail_ptr && (detail_ptr->begin_time > time(NULL))) { job_ptr->state_reason = WAIT_TIME; return false; /* not yet time */ } - - if (job_ptr->dependency == 0) - return true; - dep_ptr = find_job_record(job_ptr->dependency); - if (dep_ptr == NULL) + rc = test_job_dependency(job_ptr); + if (rc == 0) return true; - - if (((dep_ptr->job_state & JOB_COMPLETING) == 0) && - (dep_ptr->job_state >= JOB_COMPLETE)) - return true; - - job_ptr->state_reason = WAIT_DEPENDENCY; - return false; /* job exists and incomplete */ + else if (rc == 1) { + job_ptr->state_reason = WAIT_DEPENDENCY; + return false; + } else { /* rc == 2 */ + time_t now = time(NULL); + info("Job dependency can't be satisfied, cancelling job %u", + job_ptr->job_id); + job_ptr->job_state = JOB_CANCELLED; + job_ptr->start_time = now; + job_ptr->end_time = now; + job_completion_logger(job_ptr); + return false; + } } /* * determine if job is ready to execute per the node select plugin diff --git a/src/slurmctld/job_scheduler.c b/src/slurmctld/job_scheduler.c index 5b341537a47..9e99c7530ee 100644 --- a/src/slurmctld/job_scheduler.c +++ b/src/slurmctld/job_scheduler.c @@ -58,6 +58,7 @@ #include "src/slurmctld/slurmctld.h" #include "src/slurmctld/srun_comm.h" +#define _DEBUG 0 #define MAX_RETRIES 10 struct job_queue { @@ -67,6 +68,7 @@ struct job_queue { }; static int _build_job_queue(struct job_queue **job_queue); +static void _depend_list_del(void *dep_ptr); static void _launch_job(struct job_record *job_ptr); static void _sort_job_queue(struct job_queue *job_queue, int job_queue_size); @@ -440,3 +442,214 @@ extern int make_batch_job_cred(batch_job_launch_msg_t *launch_msg_ptr) error("slurm_cred_create failure for batch job %u", cred_arg.jobid); return SLURM_ERROR; } + +static void _depend_list_del(void *dep_ptr) +{ + xfree(dep_ptr); +} + +/* Print a job's dependency information based upon job_ptr->depend_list */ +extern void print_job_dependency(struct job_record *job_ptr) +{ + ListIterator depend_iter; + struct depend_spec *dep_ptr; + char *dep_str; + + info("Dependency information for job %u", job_ptr->job_id); + if (!job_ptr->depend_list) + return; + + depend_iter = list_iterator_create(job_ptr->depend_list); + if (!depend_iter) + fatal("list_iterator_create memory allocation failure"); + while ((dep_ptr = list_next(depend_iter))) { + if (dep_ptr->depend_type == SLURM_DEPEND_AFTER) + dep_str = "after"; + else if (dep_ptr->depend_type == SLURM_DEPEND_AFTER_ANY) + dep_str = "afterany"; + else if (dep_ptr->depend_type == SLURM_DEPEND_AFTER_NOT_OK) + dep_str = "afternotok"; + else if (dep_ptr->depend_type == SLURM_DEPEND_AFTER_OK) + dep_str = "afterok"; + else + dep_str = "unknown"; + info(" %s:%u", dep_str, dep_ptr->job_id); + } + list_iterator_destroy(depend_iter); +} + +/* + * Determine if a job's dependencies are met + * RET: 0 = no dependencies + * 1 = dependencies remain + * 2 = failure (job completion code not per dependency), delete the job + */ +extern int test_job_dependency(struct job_record *job_ptr) +{ + ListIterator depend_iter; + struct depend_spec *dep_ptr; + bool failure = false; + + if (!job_ptr->depend_list) + return 0; + + depend_iter = list_iterator_create(job_ptr->depend_list); + if (!depend_iter) + fatal("list_iterator_create memory allocation failure"); + while ((dep_ptr = list_next(depend_iter))) { + if (dep_ptr->job_ptr->job_id != dep_ptr->job_id) { + /* job is gone, dependency lifted */ + list_delete_item(depend_iter); + } else if (dep_ptr->depend_type == SLURM_DEPEND_AFTER) { + if (!IS_JOB_PENDING(dep_ptr->job_ptr)) + list_delete_item(depend_iter); + else + break; + } else if (dep_ptr->depend_type == SLURM_DEPEND_AFTER_ANY) { + if (IS_JOB_FINISHED(dep_ptr->job_ptr)) + list_delete_item(depend_iter); + else + break; + } else if (dep_ptr->depend_type == SLURM_DEPEND_AFTER_NOT_OK) { + if (!IS_JOB_FINISHED(dep_ptr->job_ptr)) + break; + if ((dep_ptr->job_ptr->job_state & (~JOB_COMPLETING)) + != JOB_COMPLETE) + list_delete_item(depend_iter); + else { + failure = true; + break; + } + } else if (dep_ptr->depend_type == SLURM_DEPEND_AFTER_OK) { + if (!IS_JOB_FINISHED(dep_ptr->job_ptr)) + break; + if ((dep_ptr->job_ptr->job_state & (~JOB_COMPLETING)) + == JOB_COMPLETE) + list_delete_item(depend_iter); + else { + failure = true; + break; + } + } else + failure = true; + } + list_iterator_destroy(depend_iter); + + if (failure) + return 2; + if (dep_ptr) + return 1; + return 0; +} + +/* + * Parse a job dependency string and use it to establish a "depend_spec" + * list of dependencies. We accept both old format (a single job ID) and + * new format (e.g. "afterok:123:124,after:128"). + * IN job_ptr - job record to have dependency and depend_list updated + * IN new_depend - new dependency description + * RET returns an error code from slurm_errno.h + */ +extern int update_job_dependency(struct job_record *job_ptr, char *new_depend) +{ + int rc = SLURM_SUCCESS; + uint16_t depend_type = 0; + uint32_t job_id = 0; + char *tok = new_depend, *sep_ptr, *sep_ptr2; + List new_depend_list = NULL; + struct depend_spec *dep_ptr; + struct job_record *dep_job_ptr; + char dep_buf[32]; + + /* Clear dependencies on NULL or empty dependency input */ + if ((new_depend == NULL) || (new_depend[0] == '\0')) { + xfree(job_ptr->dependency); + if (job_ptr->depend_list) + list_destroy(job_ptr->depend_list); + return rc; + + } + + new_depend_list = list_create(_depend_list_del); + /* validate new dependency string */ + while (rc == SLURM_SUCCESS) { + sep_ptr = strchr(tok, ':'); + if ((sep_ptr == NULL) && (job_id == 0)) { + job_id = strtol(tok, &sep_ptr, 10); + if ((sep_ptr == NULL) || (sep_ptr[0] != '\0') || + (job_id <= 0) || (job_id == job_ptr->job_id)) { + rc = EINVAL; + break; + } + /* old format, just a single job_id */ + dep_job_ptr = find_job_record(job_id); + if (!dep_job_ptr) /* assume already done */ + break; + snprintf(dep_buf, sizeof(dep_buf), "afterany:%u", job_id); + new_depend = dep_buf; + dep_ptr = xmalloc(sizeof(struct depend_spec)); + dep_ptr->depend_type = SLURM_DEPEND_AFTER_ANY; + dep_ptr->job_id = job_id; + dep_ptr->job_ptr = dep_job_ptr; + if (!list_append(new_depend_list, dep_ptr)) + fatal("list_append memory allocation failure"); + break; + } + + if (strncasecmp(tok, "afternotok", 10) == 0) + depend_type = SLURM_DEPEND_AFTER_NOT_OK; + else if (strncasecmp(tok, "afterany", 8) == 0) + depend_type = SLURM_DEPEND_AFTER_ANY; + else if (strncasecmp(tok, "afterok", 7) == 0) + depend_type = SLURM_DEPEND_AFTER_OK; + else if (strncasecmp(tok, "after", 5) == 0) + depend_type = SLURM_DEPEND_AFTER; + else { + rc = EINVAL; + break; + } + sep_ptr++; /* skip over ":" */ + while (rc == SLURM_SUCCESS) { + job_id = strtol(sep_ptr, &sep_ptr2, 10); + if ((sep_ptr2 == NULL) || + (job_id <= 0) || (job_id == job_ptr->job_id) || + ((sep_ptr2[0] != '\0') && (sep_ptr2[0] != ',') && + (sep_ptr2[0] != ':'))) { + rc = EINVAL; + break; + } + dep_job_ptr = find_job_record(job_id); + if (dep_job_ptr) { /* job still active */ + dep_ptr = xmalloc(sizeof(struct depend_spec)); + dep_ptr->depend_type = depend_type; + dep_ptr->job_id = job_id; + dep_ptr->job_ptr = dep_job_ptr; + if (!list_append(new_depend_list, dep_ptr)) { + fatal("list_append memory allocation " + "failure"); + } + } + if (sep_ptr2[0] != ':') + break; + sep_ptr = sep_ptr2 + 1; /* skip over ":" */ + } + if (sep_ptr2[0] == ',') + tok = sep_ptr2 + 1; + else + break; + } + + if (rc == SLURM_SUCCESS) { + xfree(job_ptr->dependency); + job_ptr->dependency = xstrdup(new_depend); + if (job_ptr->depend_list) + list_destroy(job_ptr->depend_list); + job_ptr->depend_list = new_depend_list; +#if _DEBUG + print_job_dependency(job_ptr); +#endif + } else { + list_destroy(new_depend_list); + } + return rc; +} diff --git a/src/slurmctld/read_config.c b/src/slurmctld/read_config.c index 514c56a3fec..f2097b66e87 100644 --- a/src/slurmctld/read_config.c +++ b/src/slurmctld/read_config.c @@ -76,6 +76,7 @@ static int _build_bitmaps(void); static int _init_all_slurm_conf(void); static void _purge_old_node_state(struct node_record *old_node_table_ptr, int old_node_record_count); +static int _restore_job_dependencies(void); static void _restore_node_state(struct node_record *old_node_table_ptr, int old_node_record_count); static int _preserve_select_type_param(slurm_ctl_conf_t * ctl_conf_ptr, @@ -784,6 +785,7 @@ int read_slurm_conf(int recover) if ((error_code = _build_bitmaps())) return error_code; + _restore_job_dependencies(); restore_node_features(); #ifdef HAVE_ELAN _validate_node_proc_count(); @@ -1114,3 +1116,30 @@ static void _validate_node_proc_count(void) } #endif +/* + * _restore_job_dependencies - Build depend_list for every job + */ +static int _restore_job_dependencies(void) +{ + int error_code = SLURM_SUCCESS, rc; + struct job_record *job_ptr; + ListIterator job_iterator; + char *new_depend; + + job_iterator = list_iterator_create(job_list); + while ((job_ptr = (struct job_record *) list_next(job_iterator))) { + if (job_ptr->dependency == NULL) + continue; + new_depend = job_ptr->dependency; + job_ptr->dependency = NULL; + rc = update_job_dependency(job_ptr, new_depend); + if (rc != SLURM_SUCCESS) { + error("Invalid dependencies discarded for job %u: %s", + job_ptr->job_id, new_depend); + error_code = rc; + } + xfree(new_depend); + } + list_iterator_destroy(job_iterator); + return error_code; +} diff --git a/src/slurmctld/slurmctld.h b/src/slurmctld/slurmctld.h index 66360f0b17b..f8ffb35c278 100644 --- a/src/slurmctld/slurmctld.h +++ b/src/slurmctld/slurmctld.h @@ -374,7 +374,8 @@ struct job_record { uint16_t other_port; /* port for client communications */ char *account; /* account number to charge */ char *comment; /* arbitrary comment */ - uint32_t dependency; /* defer until this job completes */ + char *dependency; /* wait for other jobs */ + List depend_list; /* list of job_ptr:state pairs */ char *network; /* network/switch requirement spec */ struct job_record *job_next; /* next entry with same hash index */ uint16_t cr_enabled; /* specify if if Consumable @@ -406,6 +407,17 @@ struct job_record { plugins */ }; +/* Job dependency specification, used in "depend_list" within job_record */ +#define SLURM_DEPEND_AFTER 1 +#define SLURM_DEPEND_AFTER_ANY 2 +#define SLURM_DEPEND_AFTER_NOT_OK 3 +#define SLURM_DEPEND_AFTER_OK 4 +struct depend_spec { + uint16_t depend_type; /* SLURM_DEPEND_* type */ + uint32_t job_id; /* SLURM job_id */ + struct job_record *job_ptr; /* pointer to this job */ +}; + struct step_record { struct job_record* job_ptr; /* ptr to the job that owns the step */ uint16_t step_id; /* step number */ @@ -1153,6 +1165,9 @@ extern void part_filter_set(uid_t uid); /* part_fini - free all memory associated with partition records */ void part_fini (void); +/* Print a job's dependency information based upon job_ptr->depend_list */ +extern void print_job_dependency(struct job_record *job_ptr); + /* * purge_old_job - purge old job records. * the jobs must have completed at least MIN_JOB_AGE minutes ago @@ -1328,6 +1343,14 @@ extern void suspend_job_step(struct job_record *job_ptr); */ extern int sync_job_files(void); +/* + * Determine if a job's dependencies are met + * RET: 0 = no dependencies + * 1 = dependencies remain + * 2 = failure (job completion code not per dependency), delete the job + */ +extern int test_job_dependency(struct job_record *job_ptr); + /* * update_job - update a job's parameters per the supplied specifications * IN job_specs - a job's specification @@ -1338,6 +1361,16 @@ extern int sync_job_files(void); */ extern int update_job (job_desc_msg_t * job_specs, uid_t uid); +/* + * Parse a job dependency string and use it to establish a "depend_spec" + * list of dependencies. We accept both old format (a single job ID) and + * new format (e.g. "afterok:123:124,after:128"). + * IN job_ptr - job record to have dependency and depend_list updated + * IN new_depend - new dependency description + * RET returns an error code from slurm_errno.h + */ +extern int update_job_dependency(struct job_record *job_ptr, char *new_depend); + /* Reset nodes_completing field for all jobs */ extern void update_job_nodes_completing(void); diff --git a/src/squeue/print.c b/src/squeue/print.c index d15d0424dd5..904a60f58a3 100644 --- a/src/squeue/print.c +++ b/src/squeue/print.c @@ -1022,11 +1022,10 @@ int _print_job_dependency(job_info_t * job, int width, bool right_justify, { if (job == NULL) /* Print the Header instead */ _print_str("DEPENDENCY", width, right_justify, true); - else { - char id[FORMAT_STRING_SIZE]; - snprintf(id, FORMAT_STRING_SIZE, "%u", job->dependency); - _print_str(id, width, right_justify, true); - } + else if (job->dependency) + _print_str(job->dependency, width, right_justify, true); + else + _print_str("", width, right_justify, true); if (suffix) printf("%s", suffix); return SLURM_SUCCESS; diff --git a/src/srun/opt.c b/src/srun/opt.c index a68e04c88b9..c882777aca8 100644 --- a/src/srun/opt.c +++ b/src/srun/opt.c @@ -612,7 +612,7 @@ static void _opt_default() opt.job_name_set = false; opt.jobid = NO_VAL; opt.jobid_set = false; - opt.dependency = NO_VAL; + opt.dependency = NULL; opt.account = NULL; opt.comment = NULL; @@ -728,7 +728,7 @@ env_vars_t env_vars[] = { {"SLURM_CORE_FORMAT", OPT_CORE, NULL, NULL }, {"SLURM_CPU_BIND", OPT_CPU_BIND, NULL, NULL }, {"SLURM_MEM_BIND", OPT_MEM_BIND, NULL, NULL }, -{"SLURM_DEPENDENCY", OPT_INT, &opt.dependency, NULL }, +{"SLURM_DEPENDENCY", OPT_STRING, &opt.dependency, NULL }, {"SLURM_DISTRIBUTION", OPT_DISTRIB, NULL, NULL }, {"SLURM_GEOMETRY", OPT_GEOMETRY, NULL, NULL }, {"SLURM_IMMEDIATE", OPT_INT, &opt.immediate, NULL }, @@ -1175,7 +1175,8 @@ static void set_options(const int argc, char **argv) opt.partition = xstrdup(optarg); break; case (int)'P': - opt.dependency = _get_int(optarg, "dependency", true); + xfree(opt.dependency); + opt.dependency = xstrdup(optarg); break; case (int)'q': opt.quit_on_intr = true; @@ -2068,10 +2069,8 @@ static void _opt_list() info("nice : %d", opt.nice); info("account : %s", opt.account); info("comment : %s", opt.comment); - if (opt.dependency == NO_VAL) - info("dependency : none"); - else - info("dependency : %u", opt.dependency); + + info("dependency : %s", opt.dependency); info("exclusive : %s", tf_(opt.exclusive)); if (opt.shared != (uint16_t) NO_VAL) info("shared : %u", opt.shared); @@ -2143,7 +2142,7 @@ static void _usage(void) " [--jobid=id] [--verbose] [--slurmd_debug=#]\n" " [--core=type] [-T threads] [-W sec] [--checkpoint=time]\n" " [--contiguous] [--mincpus=n] [--mem=MB] [--tmp=MB] [-C list]\n" -" [--mpi=type] [--account=name] [--dependency=jobid]\n" +" [--mpi=type] [--account=name] [--dependency=type:jobid]\n" " [--kill-on-bad-exit] [--propagate[=rlimits] [--comment=name]\n" " [--cpu_bind=...] [--mem_bind=...]\n" " [--ntasks-per-node=n] [--ntasks-per-socket=n]\n" @@ -2204,7 +2203,7 @@ static void _help(void) " -d, --slurmd-debug=level slurmd debug level\n" " --core=type change default corefile format type\n" " (type=\"list\" to list of valid formats)\n" -" -P, --dependency=jobid defer job until specified jobid completes\n" +" -P, --dependency=type:jobid defer job until condition on jobid is satisfied\n" " --nice[=value] decrease secheduling priority by value\n" " -U, --account=name charge job to specified account\n" " --comment=name arbitrary comment\n" diff --git a/src/srun/opt.h b/src/srun/opt.h index 2e150aaf2e8..2eb2e6713be 100644 --- a/src/srun/opt.h +++ b/src/srun/opt.h @@ -123,7 +123,7 @@ typedef struct srun_options { unsigned int jobid; /* --jobid=jobid */ bool jobid_set; /* true if jobid explicitly set */ char *mpi_type; /* --mpi=type */ - unsigned int dependency;/* --dependency, -P jobid */ + char *dependency; /* --dependency, -P type:jobid */ int nice; /* --nice */ char *account; /* --account, -U acct_name */ char *comment; /* --comment */ diff --git a/src/sview/job_info.c b/src/sview/job_info.c index e92bfd1888e..d9085eeb080 100644 --- a/src/sview/job_info.c +++ b/src/sview/job_info.c @@ -696,12 +696,8 @@ static const char *_set_job_msg(job_desc_msg_t *job_msg, const char *new_text, type = "account"; break; case SORTID_DEPENDENCY: - temp_int = strtol(new_text, (char **)NULL, 10); - + job_msg->dependency = xstrdup(new_text); type = "dependency"; - if(temp_int <= 0) - goto return_error; - job_msg->dependency = (uint32_t)temp_int; break; #ifdef HAVE_BG case SORTID_GEOMETRY: @@ -1396,14 +1392,10 @@ static void _layout_job_record(GtkTreeView *treeview, SORTID_FEATURES), job_ptr->features); - if(job_ptr->dependency > 0) - sprintf(tmp_char, "%u", job_ptr->dependency); - else - sprintf(tmp_char, " "); add_display_treestore_line(update, treestore, &iter, find_col_name(display_data_job, SORTID_DEPENDENCY), - tmp_char); + job_ptr->dependency); add_display_treestore_line(update, treestore, &iter, find_col_name(display_data_job, @@ -1689,11 +1681,10 @@ static void _update_job_record(sview_job_info_t *sview_job_info_ptr, gtk_tree_store_set(treestore, iter, SORTID_ACCOUNT, job_ptr->account, -1); - if(job_ptr->dependency > 0) { - sprintf(tmp_char, "%u", job_ptr->dependency); - gtk_tree_store_set(treestore, iter, - SORTID_DEPENDENCY, tmp_char, -1); - } + + gtk_tree_store_set(treestore, iter, + SORTID_DEPENDENCY, job_ptr->dependency, -1); + sprintf(tmp_char, "%u", job_ptr->priority); gtk_tree_store_set(treestore, iter, SORTID_PRIORITY, tmp_char, -1); diff --git a/testsuite/expect/test1.42 b/testsuite/expect/test1.42 index be5795cef2f..f090db99e38 100755 --- a/testsuite/expect/test1.42 +++ b/testsuite/expect/test1.42 @@ -8,7 +8,7 @@ # "FAILURE: ..." otherwise with an explanation of the failure, OR # anything else indicates a failure mode that must be investigated. ############################################################################ -# Copyright (C) 2004 The Regents of the University of California. +# Copyright (C) 2004-2007 The Regents of the University of California. # Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). # Written by Morris Jette <jette1@llnl.gov> # UCRL-CODE-226842. @@ -77,7 +77,7 @@ if {$job_id1 == 0} { # set match_acct 0 set match_state 0 -set srun_pid [spawn $srun -v --dependency=$job_id1 $scontrol show job $job_id1] +set srun_pid [spawn $srun -v --dependency=afterany:$job_id1 $scontrol show job $job_id1] expect { -re "launching ($number).0" { set job_id2 $expect_out(1,string) @@ -117,7 +117,7 @@ set match_acct 0 set match_jobid 0 spawn $scontrol show job $job_id2 expect { - -re "Dependency=($number)" { + -re "Dependency=afterany:($number)" { set match_jobid $expect_out(1,string) exp_continue } diff --git a/testsuite/expect/test15.14 b/testsuite/expect/test15.14 index 3a775ace17b..c3761158d18 100755 --- a/testsuite/expect/test15.14 +++ b/testsuite/expect/test15.14 @@ -8,7 +8,7 @@ # "FAILURE: ..." otherwise with an explanation of the failure, OR # anything else indicates a failure mode that must be investigated. ############################################################################ -# Copyright (C) 2004-2006 The Regents of the University of California. +# Copyright (C) 2004-2007 The Regents of the University of California. # Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). # Written by Morris Jette <jette1@llnl.gov> # UCRL-CODE-226842. @@ -76,7 +76,7 @@ if {$job_id1 == 0} { # set match_acct 0 set match_state 0 -set salloc_pid [spawn $salloc --dependency=$job_id1 $srun $scontrol show job $job_id1] +set salloc_pid [spawn $salloc --dependency=afterany:$job_id1 $srun $scontrol show job $job_id1] expect { -re "Granted job allocation ($number)" { set job_id2 $expect_out(1,string) @@ -119,7 +119,7 @@ set match_acct 0 set match_jobid 0 spawn $scontrol show job $job_id2 expect { - -re "Dependency=($number)" { + -re "Dependency=afterany:($number)" { set match_jobid $expect_out(1,string) exp_continue } diff --git a/testsuite/expect/test17.18 b/testsuite/expect/test17.18 index 78974a8c0c6..def936b5fe9 100755 --- a/testsuite/expect/test17.18 +++ b/testsuite/expect/test17.18 @@ -8,7 +8,7 @@ # "FAILURE: ..." otherwise with an explanation of the failure, OR # anything else indicates a failure mode that must be investigated. ############################################################################ -# Copyright (C) 2004-2006 The Regents of the University of California. +# Copyright (C) 2004-2007 The Regents of the University of California. # Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). # Written by Morris Jette <jette1@llnl.gov> # UCRL-CODE-226842. @@ -82,7 +82,7 @@ make_bash_script $file_in "$scontrol show job $job_id1" set match_acct 0 set match_state 0 set timeout 30 -spawn $sbatch --dependency=$job_id1 --output=$file_out $file_in +spawn $sbatch --dependency=afterany:$job_id1 --output=$file_out $file_in expect { -re "Submitted batch job ($number)" { set job_id2 $expect_out(1,string) @@ -145,7 +145,7 @@ set match_acct 0 set match_jobid 0 spawn $scontrol show job $job_id2 expect { - -re "Dependency=($number)" { + -re "Dependency=afterany:($number)" { set match_jobid $expect_out(1,string) exp_continue } -- GitLab