diff --git a/batchspawner/batchspawner.py b/batchspawner/batchspawner.py index 7c2836c..f16f3e9 100644 --- a/batchspawner/batchspawner.py +++ b/batchspawner/batchspawner.py @@ -666,12 +666,12 @@ def get_env(self): class SlurmSpawner(UserEnvMixin, BatchSpawnerRegexStates): batch_script = Unicode( """#!/bin/bash -#SBATCH --output={{homedir}}/jupyterhub_slurmspawner_%j.log #SBATCH --job-name=spawner-jupyterhub #SBATCH --chdir={{homedir}} #SBATCH --export={{keepvars}} #SBATCH --get-user-env=L -{% if partition %}#SBATCH --partition={{partition}} +{% if output %}#SBATCH --output={% if not output.startswith('/') %}{{homedir}}/{% endif %}{{output}} +{% endif %}{% if partition %}#SBATCH --partition={{partition}} {% endif %}{% if runtime %}#SBATCH --time={{runtime}} {% endif %}{% if memory %}#SBATCH --mem={{memory}} {% endif %}{% if gres %}#SBATCH --gres={{gres}} @@ -717,6 +717,12 @@ class SlurmSpawner(UserEnvMixin, BatchSpawnerRegexStates): help="Additional resources (e.g. GPUs) requested", ).tag(config=True) + req_output = Unicode( + "jupyterhub_slurmspawner_%j.log", + help="Batch script standard output and standard error filename pattern." + "Relative filename is considered relative to ``req_homedir``.", + ).tag(config=True) + # outputs line like "Submitted batch job 209" batch_submit_cmd = Unicode("sbatch --parsable").tag(config=True) # outputs status and exec node like "RUNNING hostname" diff --git a/batchspawner/tests/test_spawners.py b/batchspawner/tests/test_spawners.py index bd378c5..6005ae0 100644 --- a/batchspawner/tests/test_spawners.py +++ b/batchspawner/tests/test_spawners.py @@ -481,6 +481,40 @@ async def test_slurm(db, event_loop): re.compile(r"^\#SBATCH \s+ some_option_asdf", re.X | re.M), re.compile(r"^\#SBATCH \s+ --reservation=RES123", re.X | re.M), re.compile(r"^\#SBATCH \s+ --gres=GRES123", re.X | re.M), + re.compile( + r"^\#SBATCH \s+ --output= .+ /jupyterhub_slurmspawner_%j.log", re.X | re.M + ), + ] + from .. import SlurmSpawner + + await run_spawner_script( + db, + SlurmSpawner, + normal_slurm_script, + batch_script_re_list=batch_script_re_list, + spawner_kwargs=spawner_kwargs, + ) + + +@pytest.mark.parametrize( + "req_output,expected_file_pattern", + [ + ("/dev/null", "/dev/null"), + ("slurm-%j.out", "{homedir}/slurm-%j.out"), + ], +) +async def test_slurm_req_output(db, event_loop, req_output, expected_file_pattern): + homedir = "/users/jhub_users" + spawner_kwargs = { + "req_output": req_output, + "req_homedir": homedir, + } + + batch_script_re_list = [ + re.compile( + r"^\#SBATCH \s+ --output=" + expected_file_pattern.format(homedir=homedir), + re.X | re.M, + ), ] from .. import SlurmSpawner