blob: fcd7cc66e7d1b98e0d7462e68ce4c0845ad17480 [file] [edit]
############################################################################
# Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
############################################################################
"""Verify a step create carrying an array task id is served by the stepmgr.
A create request whose array_task_id is set is resolved through
stepmgr_ops->find_job_array_rec(). stepmgr_init() fills in no default for that
op, so each daemon that serves creates installs its own. slurmctld does, and so
must slurmstepd, which serves the create under
SlurmctldParameters=enable_stepmgr. Without the op the stepd would crash rather
than find the task.
srun sets array_task_id from --jobid=<array_job_id>_<task_id>. A plain step
never carries it to the create: existing_allocation() clears the global copy
once it has resolved the allocation. A heterogeneous step does carry it,
because each component's options were snapshotted while the arguments were
parsed, which is before that clear. So the het invocation below is not
incidental -- it is the shape that reaches the array lookup.
"""
import pytest
import atf
@pytest.fixture(scope="module", autouse=True)
def setup():
# stepmgr is enabled when both SlurmctldParameters=enable_stepmgr and
# PrologFlags=Contain are set.
atf.require_config_parameter_includes("SlurmctldParameters", "enable_stepmgr")
atf.require_config_parameter_includes("PrologFlags", "Contain")
# A het step's components do not share a node, so the two below need one
# node each.
atf.require_nodes(2, [("CPUs", 1)])
# Both components are submitted as one het step, which SchedulerParameters
# can turn off.
atf.require_config_parameter_excludes("SchedulerParameters", "disable_hetjob_steps")
atf.require_version(
(26, 5, 6),
"sbin/slurmd",
reason="Issue 50940: stepd find_job_array_rec added in 26.05.6",
)
atf.require_slurm_running()
@pytest.mark.xfail(
atf.get_version("bin/srun") < (26, 5, 3),
reason="Ticket 25328: srun --jobid cannot attach to an array task. Fixed in 26.05.3.",
)
def test_het_step_on_array_task_launches_under_stepmgr():
"""A het step attached to an array task runs, and the stepmgr survives it.
--jobid is named on both components deliberately. heterogeneous_jobs.html
does not list it among the options propagated to later components, so the
test does not rely on it carrying over."""
# Every array task but the last to start runs under its own job id, so a
# lookup by the array's id alone would not find it. Two nodes fit only one
# task, so task 0 exits to let task 1 start while task 2 is still pending.
script = '[ "$SLURM_ARRAY_TASK_ID" = 0 ] || sleep infinity'
job_id = atf.submit_job_sbatch(
f"--array=0-2 -N2 -n2 -t5 --wrap '{script}'", fatal=True
)
assert job_id != 0, "sbatch should submit the array job"
# A task only gets its own record once it is scheduled
task_job_id = 0
for t in atf.timer(fatal=True):
task_job_id = atf.get_job_id_from_array_task(job_id, 1)
if task_job_id != 0:
break
atf.wait_for_job_state(task_job_id, "RUNNING", fatal=True)
assert (
task_job_id != job_id
), f"array task {job_id}_1 should run under its own job id"
target = f"--jobid={job_id}_1"
component = f"{target} -N1 -n1 printenv SLURM_JOB_ID"
result = atf.run_command(f"srun {component} : {component}", quiet=True)
assert result["exit_code"] == 0, (
f"the het step on array task {job_id}_1 should run, got exit "
f"{result['exit_code']}: {result['stderr']!r}"
)
assert result["stdout"].split() == [str(task_job_id)] * 2, (
f"both components should run in job {task_job_id}, got: "
f"{result['stdout']!r}"
)
# The step manager served that create rather than dying on it: it is still
# there to serve another one.
result = atf.run_command(f"srun {target} -N1 -n1 true", quiet=True)
assert result["exit_code"] == 0, (
f"the stepmgr for array task {job_id}_1 should still serve step "
f"creates, got exit {result['exit_code']}: {result['stderr']!r}"
)