Source code for romtools.hpc.components.slurm
import re
import textwrap
import shlex
from typing import Optional, Tuple
SLURM_TERMINAL_STATES = {
"BOOT_FAIL",
"CANCELLED",
"COMPLETED",
"DEADLINE",
"FAILED",
"NODE_FAIL",
"OUT_OF_MEMORY",
"PREEMPTED",
"TIMEOUT",
}
DEFAULT_SLURM_OUTFILE = "slurm.out"
DEFAULT_SLURM_ERRFILE = "slurm.err"
# Job ended but not successfully
FAILED_EXIT_CODE = "1:0"
# sacct reports an exit code as "<status>:<signal>"
_EXITCODE_PATTERN = re.compile(r"^\d+:\d+$")
[docs]
def is_slurm_exitcode(value: Optional[str]) -> bool:
"""Whether sacct gave us a code we can read; its columns are often blank."""
return bool(value) and _EXITCODE_PATTERN.match(value) is not None
[docs]
def create_slurm_script(job_name: str, num_nodes: int, tasks_per_node: int, wall_time: str, wcid: str, partition: str, command: str) -> str:
"""
Create a SLURM job script with the specified parameters.
Parameters:
- job_name: Name of the SLURM job
- num_nodes: Number of nodes to request
- tasks_per_node: Number of tasks to run on each node
- wall_time: Maximum wall time for the job (format: HH:MM:SS)
- wcid: The account to charge for the job
- partition: The partition to submit the job to ("batch", "short")
- command: The command to execute in the job
Returns:
A string containing the SLURM job script.
"""
slurm_script = textwrap.dedent(f"""#!/bin/bash
#SBATCH --job-name={job_name}
#SBATCH --nodes={num_nodes}
#SBATCH --ntasks-per-node={tasks_per_node}
#SBATCH --time={wall_time}
#SBATCH --account={wcid}
#SBATCH -p {partition}
{command}
""")
return slurm_script
def slurm_exitcode_to_python_style(exitcode: str) -> int:
if exitcode is None:
return None
if not is_slurm_exitcode(exitcode):
raise ValueError(f"Malformed SLURM exit code {exitcode!r}; expected '<status>:<signal>'.")
status_str, signal_str = exitcode.split(":")
status = int(status_str)
signal = int(signal_str)
if signal != 0:
return -signal
return status
def parse_sbatch_out_args(script: str) -> Tuple[Optional[str], Optional[str]]:
if not script:
return None, None
specified_out, specified_err = None, None
with open(script, "r") as runscript:
for line in runscript:
line = line.strip()
if not line.startswith("#SBATCH"):
continue
args = shlex.split(line[len("#SBATCH"):].strip())
i = 0
while i < len(args):
arg = args[i]
if arg.startswith("--output="):
specified_out = arg.split("=", 1)[1]
elif arg == "--output" and i + 1 < len(args):
specified_out = args[i + 1]
i += 1
elif arg.startswith("-o") and arg != "-o":
specified_out = arg[2:]
elif arg == "-o" and i + 1 < len(args):
specified_out = args[i + 1]
i += 1
elif arg.startswith("--error="):
specified_err = arg.split("=", 1)[1]
elif arg == "--error" and i + 1 < len(args):
specified_err = args[i + 1]
i += 1
elif arg.startswith("-e") and arg != "-e":
specified_err = arg[2:]
elif arg == "-e" and i + 1 < len(args):
specified_err = args[i + 1]
i += 1
i += 1
if specified_out is not None and specified_err is not None:
break
return specified_out, specified_err