blob: 2f531dabb94ea5ceb946e49d69b6177d3a88498d [file] [edit]
############################################################################
# Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
############################################################################
"""Test sbcast to a heterogeneous job with namespace/tmpfs."""
import pytest
import atf
ns_dir = "/tmp/test_106_5_private"
file_content = "sbcast_hetjob_tmpfs"
@pytest.fixture(scope="module", autouse=True)
def setup():
atf.require_version(
(25, 11),
component="bin/sbcast",
reason="Issue 50542: NamespaceType was added in 25.11",
)
atf.require_nodes(2)
# Only backfill starts hetjobs
atf.require_config_parameter("SchedulerType", "sched/backfill")
atf.require_config_parameter_includes("SchedulerParameters", ("bf_interval", 1))
atf.require_config_parameter("NamespaceType", "namespace/tmpfs")
atf.require_config_parameter_includes("PrologFlags", "Contain")
# BasePath must be unique per node, also with multiple slurmd
atf.require_config_file(
"job_container.conf",
f"AutoBasePath=true\nBasePath=/tmp/%h_%n_base_path Dirs={ns_dir}\n",
)
# Assume the slurmd sees the same file system as us
atf.run_command(f"mkdir -p {ns_dir}", fatal=True)
atf.require_slurm_running()
yield
atf.run_command(f"rm -rf {ns_dir}", fatal=True)
@pytest.fixture(autouse=True)
def clean_ns_dir():
atf.run_command(f"rm -rf {ns_dir}/*", fatal=True)
@pytest.fixture
def het_job_id():
"""Start a running hetjob with two components on different nodes."""
job_id = atf.submit_job_sbatch(
"-N1 --exclusive : -N1 --exclusive --wrap 'sleep infinity'", fatal=True
)
atf.wait_for_job_state(job_id, "RUNNING", fatal=True)
jobs = atf.get_jobs(job_id, fatal=True)
components = sorted(
atf.range_to_list(jobs[job_id]["HetJobIdSet"]),
key=lambda component_id: jobs[component_id]["HetJobOffset"],
)
nodes = [jobs[component_id]["NodeList"] for component_id in components]
if nodes[0] == nodes[1]:
pytest.fail(f"The components should be on different nodes, got {nodes}")
# Steps wait for the prolog, so after them each namespace exists
for het_group in (0, 1):
atf.run_command(
f"srun --jobid={job_id} --het-group={het_group} true", fatal=True
)
atf.run_command(f"echo {file_content} > file", fatal=True)
return job_id
def test_sbcast_hetjob_leader(het_job_id):
"""Verify sbcast to the hetjob leader puts the file in all components."""
result = atf.run_command(f"sbcast --jobid={het_job_id} file {ns_dir}/file")
assert result["exit_code"] == 0, f"sbcast should succeed: {result['stderr']}"
for het_group in (0, 1):
output = atf.run_command_output(
f"srun --jobid={het_job_id} --het-group={het_group} cat {ns_dir}/file"
)
assert (
output.strip() == file_content
), f"Component {het_group} should have the file in its namespace"
assert not atf.run_command_output(
f"ls -A {ns_dir}", fatal=True
), "The file should be only inside the job namespaces, not on the host"
def test_sbcast_hetjob_offset(het_job_id):
"""Verify sbcast to <leader>+<offset> puts the file only in that component."""
for het_group in (0, 1):
result = atf.run_command(
f"sbcast --jobid={het_job_id}+{het_group} file {ns_dir}/file{het_group}"
)
assert (
result["exit_code"] == 0
), f"sbcast to component {het_group} should succeed: {result['stderr']}"
for het_group in (0, 1):
output = atf.run_command_output(
f"srun --jobid={het_job_id} --het-group={het_group} ls -A {ns_dir}"
)
assert output.split() == [
f"file{het_group}"
], f"Component {het_group} should have only its own file, got {output}"
output = atf.run_command_output(
f"srun --jobid={het_job_id} --het-group={het_group}"
f" cat {ns_dir}/file{het_group}"
)
assert (
output.strip() == file_content
), f"Component {het_group} should have the file content, got {output}"
assert not atf.run_command_output(
f"ls -A {ns_dir}", fatal=True
), "The files should be only inside the job namespaces, not on the host"
def test_sbcast_hetjob_in_allocation():
"""Verify sbcast without --jobid in a hetjob allocation reaches all components."""
atf.run_command(f"echo {file_content} > file", fatal=True)
result = atf.run_command(
f'salloc -N1 : -N1 bash -c "sbcast file {ns_dir}/file_alloc'
f" && srun --het-group=0 cat {ns_dir}/file_alloc"
f' && srun --het-group=1 cat {ns_dir}/file_alloc"'
)
assert (
result["exit_code"] == 0
), f"sbcast and both reads should succeed: {result['stderr']}"
assert result["stdout"].split() == [
file_content,
file_content,
], f"Both components should have the file, got {result['stdout']}"
assert not atf.run_command_output(
f"ls -A {ns_dir}", fatal=True
), "The file should be only inside the job namespaces, not on the host"