blob: 9098baa180522b0f501c15a27321e2447c801550 [file] [edit]
############################################################################
# Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
############################################################################
"""Test that a cancelled stepmgr job refuses new steps."""
import pytest
import atf
@pytest.fixture(scope="module", autouse=True)
def setup():
atf.require_version(
(26, 11),
"sbin/slurmd",
reason="MR 4653: stepmgr refuses steps after the job ends since 26.11",
)
# --stepmgr is silently dropped without it, see --stepmgr in sbatch(1)
atf.require_config_parameter_includes("PrologFlags", "Contain")
atf.require_slurm_running()
def submit_stepmgr_job(script):
"""Submit a --stepmgr job and wait until its script signals it is ready.
slurmctld also refuses steps of an ending job, with a different error,
so make sure the stepmgr is really the one in use.
"""
job_id = atf.submit_job_sbatch(f"--stepmgr -t2 {script}", fatal=True)
atf.wait_for_file("ready", fatal=True)
assert (
atf.get_job_parameter(job_id, "StepMgrEnabled", fatal=True) == "Yes"
), f"job {job_id} is not managed by a stepmgr"
return job_id
def test_step_after_cancel():
"""A step started from a SIGTERM trap after scancel is refused."""
atf.make_bash_script(
"cancel.sh",
"""#SBATCH -o cancel.out
trap 'srun --overlap true; echo "srun rc=$?"' TERM
touch ready
sleep infinity &
wait
""",
)
job_id = submit_stepmgr_job("cancel.sh")
atf.run_command(f"scancel {job_id}", fatal=True)
atf.assert_file_contents(
"cancel.out", "Job/step already completing or completed", contains=True
)
atf.assert_file_contents("cancel.out", "srun rc=1", contains=True)
def test_pending_step_after_cancel():
"""A step still pending when the job is cancelled is refused when woken."""
atf.make_bash_script(
"pending.sh",
"""#SBATCH -n1 -o pending.out
srun -n1 sleep infinity &
touch ready
wait
""",
)
job_id = submit_stepmgr_job("pending.sh")
atf.wait_for_step(job_id, 0, fatal=True)
atf.run_command(
f"srun --jobid={job_id} -n1 true >pending_srun.out 2>&1 &", fatal=True
)
assert atf.get_pending_async_step(job_id), "no pending step to wake"
atf.run_command(f"scancel {job_id}", fatal=True)
atf.assert_file_contents(
"pending_srun.out", "Job/step already completing or completed", contains=True
)
def test_step_after_signal_term():
"""scancel --signal=TERM does not end the job, so steps still start."""
atf.make_bash_script(
"signal.sh",
"""#SBATCH -o signal.out
srun --overlap bash -c 'trap "touch got_term" TERM; touch ready; sleep infinity & wait' &
while [ ! -e go ]; do sleep 0.2; done
srun --overlap true; echo "srun rc=$?"
sleep infinity
""",
)
job_id = submit_stepmgr_job("signal.sh")
atf.run_command(f"scancel --signal=TERM {job_id}", fatal=True)
# The running step trapping SIGTERM shows slurmd delivered the signal
atf.wait_for_file("got_term", fatal=True)
atf.run_command("touch go", fatal=True)
atf.assert_file_contents("signal.out", "srun rc=0", contains=True)