seamm_scheduler package#
Submodules#
seamm_scheduler.backend module#
QueueBackend: submit, poll and cancel jobs on a queueing system.
A backend pairs a Scheduler (which commands,
which syntax) with a transport (how to run them: on this host, or over ssh).
Command construction and output parsing live in the scheduler, so every
transport behaves identically.
- class seamm_scheduler.backend.QueueBackend(scheduler, transport=None)[source]#
Bases:
objectTalks to one queueing system through one transport.
- Parameters:
scheduler (seamm_scheduler.scheduler.Scheduler)
transport (LocalTransport or SshTransport, optional) – How commands run. A subclass may instead override
_run().
- find_jobs(job_name)[source]#
The ids of the user’s queued or running jobs called
job_name, or None if that cannot be known now (the queue could not be asked).
- property host#
- poll_many(job_ids)[source]#
The current status of a batch of jobs, in as few commands as the scheduler allows.
- Returns:
{str – Keyed by job id. A job the scheduler has no record of at all (e.g. purged from accounting) is absent; callers should treat “missing” as “can’t confirm this job still exists”.
- Return type:
JobStatus}
- submit(script, *, job_name=None)[source]#
Submit a script (its full text, directives and shebang included – see
seamm_scheduler.script.build_script), fed on the submit command’s standard input, so the script never needs to exist as a file the target host can see.- Returns:
The job id.
- Return type:
str
seamm_scheduler.config module#
Load a JobServer’s <root>/<jobserver-name>.ini: its targets.
System/machine config, not a user preference – lives at <root>
(~/SEAMM by default), alongside orca.ini/lammps.ini/
dashboards.ini, not in ~/.seamm.d/seamm.ini. Named after the JobServer
instance (its --name, default hostname), one section per target.
A target says where a job’s flowchart evaluator runs (type: local for
a subprocess of the JobServer, slurm for a batch job of its own) and, with
the keys added for the task layer, where the evaluator’s tasks run
(tasks = pool | taskserver | queue). Every key the task layer added is
optional: a section without tasks = means exactly what it meant before
(whole-flowchart submission, the codes running inside the evaluator).
A section is copied verbatim into each job’s target.json (setup text
included), which the Dashboard can show, so a section must never hold secrets.
Lives here, not in seamm_jobserver, so any dependency-light consumer –
seamm_jobserver, seamm_webui’s queue list, seamm_exec’s task layer
– can read and validate the file without pulling in the rest of the SEAMM
stack. See seamm_exec’s docs/developer_guide/campaigns/2026-10-02/.
The keys of a section#
Where the flowchart runs and how to reach the queue:
typelocal: a subprocess of the JobServer, no scheduler.queue: a batch job on the queueing system named byscheduler.slurm: the original spelling ofqueuewith SLURM. Defaultslurm.schedulerslurm(default),pbs, orseamm(the machine’s TaskServer). Also the scheduler of the tasks whentasks = queue.transportlocal: the scheduler’s commands run here.ssh: they run onhostover passwordless ssh.hostThe ssh host, for
transport = ssh.remote_rootsshonly: the directory on the remote host under which each job (and each task, fortasks = queue) gets its own directory, staged there and back with rsync. Without it a shared filesystem is assumed.remote_run_from_jobserver,remote_conda_envsshonly: how to start SEAMM on the remote host – the absolute path of itsrun_from_jobserver(preferred), or a conda environment name.setupShell lines run at the top of the batch script, before SEAMM; indented continuation lines for several. Trusted text, copied into the job’s
target.json.defaultIn
[DEFAULT]only: the section a job uses when it names none.
The JobServer’s behaviour for the section:
max_concurrent_jobsHow many of this section’s jobs are kept submitted at once (default 20).
max_resubmitsHow many times a job the queue lost is submitted again (default 3).
poll_intervalSeconds between polls of the queue (default 60).
Submission directives – every other key. Each becomes a directive of the
scheduler in its own spelling (SLURM: #SBATCH --<key>=<value> with
underscores as dashes), so any option the scheduler takes works. The portable
spellings, which PBS maps onto its own, are account, partition (PBS
queue), qos, constraint, nodes, ntasks, cpus_per_task,
mem, mem_per_cpu, time (PBS walltime), gres and
export. A blank value passes no directive. Set mem or
mem_per_cpu: a site that reserves the whole node per job by default will
otherwise run one job at a time. export = NONE makes SLURM rebuild the
job’s environment as from a fresh login, which an ssh submission needs when
the cluster loads codes as modules.
The task layer – where the flowchart’s calculations run (all optional; a
section without tasks behaves as before the task layer existed):
taskspool: in the flowchart’s own allocation (the default).taskserver: through the machine’s TaskServer aturl.queue: as batch jobs submitted by the task layer toscheduler.shared_filesystemWhether the flowchart and the cluster see the same storage, so task directories need no staging. Default yes for
transport = localor a flowchart that is itself a batch job on the cluster; no forssh.bundle_tasks,bundle_walltimeHow tasks are packed into one batch job: a fixed number, or as many as fit the walltime (seconds, or a SLURM time) from their estimates. Without either, by estimate within the section’s
time.max_walltimeThe queue’s longest walltime; a timed-out task’s retry asks for more time but never more than this.
estimate_scale,min_task_walltimeGuards on the walltime of a bundle when it comes from the tasks’ estimates: each estimate is multiplied by
estimate_scale(default 1), and each task gets at leastmin_task_walltime(seconds, or a SLURM time). Still withinbundle_walltimeandmax_walltime.max_queued_tasksThe most queued and running bundle jobs of this user at once (sites cap a user’s jobs; every array element counts). Shared by every task set a job runs at once.
inline_belowTasks estimated below this many seconds (default 60) run in the flowchart’s pool when their program is installed there.
remote_python,remote_seamm_rootsshonly: a Python withseamm_execon the cluster, which runs the task worker and configures each program from the cluster’s own<root>/<program>.ini, and that root (default: the venv’s).urltasks = taskserver: the TaskServer’s URL.
Per-job overrides: the companion section [<name>.limits] says which
directives a job may override (overridable = a, b, c) and, per directive,
<key>.choices, <key>.min and <key>.max. Without the section nothing
is overridable.
- class seamm_scheduler.config.FieldLimits(choices: list | None = None, minimum: str | None = None, maximum: str | None = None)[source]#
Bases:
objectConstraints on a per-job override of one directive.
All fields optional: a directive can be listed as overridable with no constraint at all, meaning “any value the scheduler itself accepts.”
- choices: list | None = None#
- maximum: str | None = None#
- minimum: str | None = None#
- seamm_scheduler.config.SlurmSection#
The historical name.
- class seamm_scheduler.config.TargetSection(name: str, transport: str, host: str | None, type: str = 'slurm', directives: dict = <factory>, max_concurrent_jobs: int = 20, max_resubmits: int = 3, limits: dict = <factory>, remote_root: str | None = None, remote_conda_env: str | None = None, remote_run_from_jobserver: str | None = None, setup: str | None = None, tasks: str | None = None, scheduler: str | None = None, shared_filesystem: bool | None = None, bundle_tasks: int | None = None, bundle_walltime: float | None = None, max_walltime: float | None = None, estimate_scale: float | None = None, min_task_walltime: float | None = None, max_queued_tasks: int | None = None, inline_below: float | None = None, url: str | None = None, remote_python: str | None = None, remote_seamm_root: str | None = None, poll_interval: float | None = None)[source]#
Bases:
objectOne target from a
<root>/<jobserver-name>.inifile.- property batch_scheduler#
The queueing system the evaluator jobs are submitted to.
- build_stager()[source]#
Construct the stager for this section’s evaluator jobs – paired with the transport the same way
build_backend()picks a backend.
- bundle_tasks: int | None = None#
- bundle_walltime: float | None = None#
- directives: dict#
- estimate_scale: float | None = None#
- classmethod from_settings(data)[source]#
The inverse of
task_settings().
- host: str | None#
- inline_below: float | None = None#
- property is_batch#
Whether the JobServer runs this section’s evaluators as batch jobs.
- limits: dict#
- max_concurrent_jobs: int = 20#
- max_queued_tasks: int | None = None#
- max_resubmits: int = 3#
- max_walltime: float | None = None#
- merge_overrides(requested)[source]#
Merge a job’s requested per-directive overrides on top of this section’s defaults, validating each against
self.limits.- Parameters:
requested (dict(str, any)) – Directive overrides a job asked for, e.g.
{"ntasks": 4}– typically a job’sparameters["slurm"].- Returns:
The final directives dict (this section’s defaults with the validated overrides applied), ready for
seamm_scheduler.script.build_script.- Return type:
dict
- Raises:
ValueError – If a requested field isn’t overridable for this section, or its value is outside the configured choices/bounds. Never trust a caller (e.g. a web UI) to have already enforced this – validate here regardless of what constrained the request’s origin.
- min_task_walltime: float | None = None#
- name: str#
- poll_interval: float | None = None#
- remote_conda_env: str | None = None#
- remote_python: str | None = None#
- remote_root: str | None = None#
- remote_run_from_jobserver: str | None = None#
- remote_seamm_root: str | None = None#
- remote_wdir_for(local_wdir)[source]#
The remote scratch path a
transport = sshsection’s job should run in (or, for an already-running job, where its files currently live), derived from its local working directory’s name and this section’sremote_root.Deterministic and side-effect-free – callers besides the one that originally submitted the job (e.g. a Dashboard pulling a running job’s files back on demand, rather than waiting for the job to finish) can recompute the same path independently, as long as they agree on
local_wdirand this section’s config. Job directory names (Job_NNNNNN) are unique across the whole datastore, not just within a project, so no collision risk.- Raises:
RuntimeError – If this section has no
remote_rootset – only meaningful fortransport = ssh.
- scheduler: str | None = None#
- setup: str | None = None#
- property task_scheduler#
The name of the queueing system for tasks.
- task_settings()[source]#
This section as a JSON-serializable dict, for
<job>/target.json.The JobServer writes it into each job’s directory so that an evaluator can find its target wherever it runs (on the JobServer’s host, or as a batch job on a cluster that cannot read this ini file).
- property task_transport#
How the evaluator reaches the queue for its tasks.
An evaluator that is itself a batch job (type=slurm or queue) runs inside the cluster and submits its tasks there with local commands, whatever transport the JobServer uses to reach the cluster.
- tasks: str | None = None#
Whether task directories need no staging.
- transport: str#
- type: str = 'slurm'#
- url: str | None = None#
- seamm_scheduler.config.list_sections(root, jobserver_name)[source]#
Load every routable section from
<root>/<jobserver_name>.ini, not just the oneload_target()resolves viadefault=/single-section fallback.For per-job routing (a job asks for a named queue) and for surfacing “what queues exist” to a submission UI (e.g.
seamm_webui’sGET /api/queues).- Returns:
Empty if the config file doesn’t exist.
- Return type:
dict(str, TargetSection)
- seamm_scheduler.config.load_slurm_config(root, jobserver_name, section=None)#
The historical name.
- seamm_scheduler.config.load_target(root, jobserver_name, section=None)[source]#
Load one target from
<root>/<jobserver_name>.ini, if it exists.- Parameters:
root (str or Path) – The SEAMM root directory (e.g.
~/SEAMM), same as every other per-code.inifile.jobserver_name (str) – This JobServer instance’s
--name(default: hostname).section (str or None) – Which section to use. If
None, uses[DEFAULT]’sdefault =key, falling back to the sole section if there is exactly one and nodefaultis set.
- Returns:
Nonemeans “no such file” – the caller should fall back to running jobs as local subprocesses, exactly as if this feature did not exist.- Return type:
TargetSection or None
seamm_scheduler.local module#
Run queueing-system commands directly on the current host.
seamm_scheduler.pbs module#
PBS (PBS Professional and OpenPBS): #PBS directives, qsub/qstat/qdel.
This module implements the Scheduler
interface for a second queueing system. It was validated on a real OpenPBS
23.06 site (MolSSI10, 2026-10-03), whose recorded output is replayed by the
tests, alongside mocked qsub/qstat output.
A PBS job starts in the user’s home directory, so chdir becomes a cd at
the top of the script. Dependencies and other job attributes are -W
options (SLURM’s dependency is accepted). Without -V only the
PBS_O_* variables reach the job, as with SLURM’s export=NONE.
Resources become one select statement: nodes chunks, each with its
share of the MPI ranks (mpiprocs), cores (ncpus), threads, memory and
GPUs. partition is the PBS queue (-q). PBS has no QOS, so qos is
ignored.
Status prefers qstat -x -f -F json (PBS Pro 18 and later) and falls back to
the text table of qstat -x. -x includes finished jobs, whose
Exit_status decides between completed and failed.
- class seamm_scheduler.pbs.Pbs[source]#
Bases:
SchedulerPBS Professional / OpenPBS.
- count_cmd()[source]#
A command listing the user’s own jobs, one per line, or None if the scheduler cannot. Used to respect per-user queued-job limits.
- directive_prefix = '#PBS'#
“#SBATCH”, “#PBS”, …
- directives(resources, extra=None)[source]#
PBS directives from resources and a section’s/job’s directives.
extramay use PBS names (queue,walltime,select, …) or the portable/SLURM spellings a target section uses (partition,time,ntasks,mem, …). The resources become oneselectchunk, merged into anyselectthe section gives: the section’s settings (e.g. itsmem) are kept unless the resources set them.
- env_names = {'job_id': 'PBS_JOBID', 'ncpus': 'NCPUS', 'ngpus': 'NGPUS', 'nodefile': 'PBS_NODEFILE', 'submit_dir': 'PBS_O_WORKDIR', 'threads': 'OMP_NUM_THREADS'}#
The scheduler-neutral names -> the environment variables a job sees, e.g.
{"job_id": "SLURM_JOB_ID", "ntasks": "SLURM_NTASKS"}. Used byseamm_exec.computational_environment()to recognize and read an allocation.
- env_prefixes = ('PBS_',)#
- find_cmd(job_name)[source]#
A command printing the ids of the user’s queued or running jobs called
job_name, one per line, or None if the scheduler cannot.
- name = 'pbs'#
“slurm”, “pbs”, …
- parse_status(stdout, ids)[source]#
The table of
qstat -x:Job id Name User Time Use S Queue ---------------- ---------------- ---------------- -------- - ----- 1234.pbs01 seamm-task psaxe 00:00:01 F workq
The table has no exit status, so a finished job’s category comes from
qstat -x -finpoll().
- poll(run, ids)[source]#
The status of
ids, usingrun(argv) -> (rc, out, err).- Returns:
{str – An id the scheduler has no record of is absent.
- Return type:
JobStatus}
seamm_scheduler.scheduler module#
The interface every queueing system implements, and the job status it reports.
A Scheduler knows one queueing system’s syntax and nothing else: how
to write its directives, which commands submit, poll and cancel, how to read
their output, and the names of the environment variables it sets inside a job.
It never runs anything itself. A QueueBackend
pairs a scheduler with a transport (local commands or ssh) to actually talk to
a cluster.
Adding a queueing system is one module with a Scheduler subclass, plus an
entry in SCHEDULERS.
- class seamm_scheduler.scheduler.JobStatus(job_id: str, state: str, category: str, exit_code: str | None = None, reason: str | None = None, raw: dict = <factory>)[source]#
Bases:
objectThe status of one queued job, as last polled.
- category: str#
- exit_code: str | None = None#
- property is_terminal#
Whether this job has finished (successfully or not) and is no longer pending or running.
- job_id: str#
- raw: dict#
- reason: str | None = None#
- state: str#
- property task_state#
The state in the task layer’s vocabulary (see
TASK_STATES).
- property timed_out#
Whether the queue stopped the job for running past its time limit.
SLURM says TIMEOUT; PBS gives exit status -29 (JOB_EXEC_KILL_WALLTIME) or a comment such as “job killed: walltime 610 exceeded limit 600”.
- seamm_scheduler.scheduler.SCHEDULERS = {'pbs': ('seamm_scheduler.pbs', 'Pbs'), 'seamm': ('seamm_scheduler.seamm', 'Seamm'), 'slurm': ('seamm_scheduler.slurm', 'Slurm')}#
name -> (module, class)
- class seamm_scheduler.scheduler.Scheduler[source]#
Bases:
objectOne queueing system’s syntax: directives, commands and states.
Subclasses set
name,directive_prefixandenv_names, and implement the*_cmd/parse_*methods.poll()composes them; a scheduler whose status needs more than one command (SLURM:squeuethensacct) overrides it.- count_cmd()[source]#
A command listing the user’s own jobs, one per line, or None if the scheduler cannot. Used to respect per-user queued-job limits.
- directive_prefix = None#
“#SBATCH”, “#PBS”, …
- directives(resources, extra=None)[source]#
Translate scheduler-neutral resources into this scheduler’s directive dict (the form
directive_lines()andseamm_scheduler.script.build_scripttake).- Parameters:
resources (object or dict) – Anything with the attributes (or keys)
ntasks,cpus_per_task,mem_per_cpu(bytes),ngpus,walltime(seconds),partition,account,qosandnodes, e.g.seamm_exec.Resources. Missing or None values are left out.extra (dict, optional) – Directives in this scheduler’s own spelling (a target section’s site defaults), applied first; the resources override them.
- Return type:
dict
- env_names = {}#
The scheduler-neutral names -> the environment variables a job sees, e.g.
{"job_id": "SLURM_JOB_ID", "ntasks": "SLURM_NTASKS"}. Used byseamm_exec.computational_environment()to recognize and read an allocation.
- find(run, job_name)[source]#
The ids of the user’s jobs called
job_name, queued, running or (where the scheduler remembers them) finished; None if that cannot be known now.
- find_cmd(job_name)[source]#
A command printing the ids of the user’s queued or running jobs called
job_name, one per line, or None if the scheduler cannot.
- name = None#
“slurm”, “pbs”, …
- parse_status(stdout, ids)[source]#
{id: JobStatus}from the status command’s output. An id the output does not mention is left out.
- poll(run, ids)[source]#
The status of
ids, usingrun(argv) -> (rc, out, err).- Returns:
{str – An id the scheduler has no record of is absent.
- Return type:
JobStatus}
- poll_failed = False#
True when the queue could not be asked (a failed command, e.g. ssh could not connect), so a job missing from the answer may still exist. Callers must not take “missing” as “gone” then.
- Type:
Set by
poll()
- exception seamm_scheduler.scheduler.SchedulerError[source]#
Bases:
RuntimeErrorRaised when a queueing-system command fails unexpectedly.
- exception seamm_scheduler.scheduler.SubmitError[source]#
Bases:
SchedulerErrorRaised when a job cannot be submitted.
- seamm_scheduler.scheduler.TASK_STATES = {'cancelled': 'lost', 'completed': 'finished', 'failed': 'failed', 'pending': 'queued', 'running': 'running', 'unknown': 'running'}#
The category of a job -> the task layer’s vocabulary (queued | running | finished | failed | lost). A cancelled job is “lost” to the task layer: something outside it removed the job, so the tasks in it are worth resubmitting.
- seamm_scheduler.scheduler.format_memory_mb(nbytes)[source]#
Bytes -> whole megabytes with an
Msuffix, rounded up.
seamm_scheduler.script module#
Build a full batch script from a directives dict and a payload.
Matches the <root>/<jobserver-name>.ini config shape used by
seamm_jobserver: each target section’s directive keys (partition,
account, qos, nodes, ntasks, time, mem, gpus, …)
become the scheduler’s directive lines. A blank/None value means “don’t pass
that directive” – let the scheduler’s own defaults apply.
- seamm_scheduler.script.build_script(directives, payload, *, shell='/bin/bash', scheduler='slurm')[source]#
Build a full batch script: shebang, directive lines, then payload.
- Parameters:
directives (dict(str, any)) – Submission options in the scheduler’s own vocabulary (for SLURM, the plain names such as
"partition"– seeseamm_scheduler.slurm.Slurm.directive_lines()).payload (str) – The body of the script – the commands to run. Used verbatim, after the directive block.
shell (str = "/bin/bash") – The shebang interpreter.
scheduler (str or Scheduler = "slurm") – Whose directive syntax to write.
- Returns:
The full script text, ready to hand to
QueueBackend.submit().- Return type:
str
seamm_scheduler.seamm module#
The seamm scheduler: the TaskServer (seamm_scheduler.taskserver).
Driven like SLURM or PBS – locally, or over ssh with rsync staging – by the
task layer’s SchedulerBackend and by the JobServer. Its commands run the
queue with the target machine’s own Python (python -m
seamm_scheduler.taskserver), never a command found on the PATH.
- class seamm_scheduler.seamm.Seamm(python=None, root=None)[source]#
Bases:
SchedulerThe TaskServer on a machine without a queueing system.
- count_cmd()[source]#
A command listing the user’s own jobs, one per line, or None if the scheduler cannot. Used to respect per-user queued-job limits.
- directive_prefix = '#SEAMM'#
“#SBATCH”, “#PBS”, …
- directives(resources, extra=None)[source]#
cores,memory(bytes),time(seconds) and the rest.extramay use the portable spellings of a target section (ntasks,cpus_per_task,mem,mem_per_cpu,time,job_name,chdir,output); queue settings the TaskServer does not have (partition,account,qos,export, …) are dropped.
- env_names = {'job_id': 'SEAMM_TASKSERVER_JOB_ID', 'memory': 'SEAMM_TASKSERVER_MEMORY', 'ntasks': 'SEAMM_TASKSERVER_NTASKS'}#
The scheduler-neutral names -> the environment variables a job sees, e.g.
{"job_id": "SLURM_JOB_ID", "ntasks": "SLURM_NTASKS"}. Used byseamm_exec.computational_environment()to recognize and read an allocation.
- env_prefixes = ('SEAMM_TASKSERVER_',)#
- find_cmd(job_name)[source]#
A command printing the ids of the user’s queued or running jobs called
job_name, one per line, or None if the scheduler cannot.
- name = 'seamm'#
“slurm”, “pbs”, …
- parse_status(stdout, ids)[source]#
{id: JobStatus}from the status command’s output. An id the output does not mention is left out.
- python#
The Python that runs the queue on the target machine
- root#
its default, ~/SEAMM)
- Type:
The SEAMM root there (None
seamm_scheduler.slurm module#
SLURM: #SBATCH directives, sbatch/squeue/sacct/scancel.
Not every SLURM version supports squeue --json/sacct --json: SLURM
21.08 has it, 20.11 (MolSSI10) does not. Slurm probes for it once per
instance and falls back to the text formats. SLURM 25.11 (TinkerCliffs) nests
the exit code of sacct --json one level deeper; both shapes are read.
- class seamm_scheduler.slurm.LocalSlurm(*, drop_env_prefixes=())[source]#
Bases:
SlurmBackendSLURM’s CLI directly on the current host – the case where the caller runs on a SLURM submit host with those commands on
PATH.
- class seamm_scheduler.slurm.Slurm[source]#
Bases:
SchedulerThe SLURM queueing system.
- count_cmd()[source]#
A command listing the user’s own jobs, one per line, or None if the scheduler cannot. Used to respect per-user queued-job limits.
- directive_lines(directives)[source]#
#SBATCH --flag=valuelines.Keys are the plain names in
_DIRECTIVE_FLAGS(e.g."partition", not"--partition"). A blank or None value is skipped. Other keys pass through, underscores becoming dashes ({"gres": "gpu:1"}->#SBATCH --gres=gpu:1), so a target section can carry options this module does not special-case.
- directive_prefix = '#SBATCH'#
“#SBATCH”, “#PBS”, …
- directives(resources, extra=None)[source]#
Translate scheduler-neutral resources into this scheduler’s directive dict (the form
directive_lines()andseamm_scheduler.script.build_scripttake).- Parameters:
resources (object or dict) – Anything with the attributes (or keys)
ntasks,cpus_per_task,mem_per_cpu(bytes),ngpus,walltime(seconds),partition,account,qosandnodes, e.g.seamm_exec.Resources. Missing or None values are left out.extra (dict, optional) – Directives in this scheduler’s own spelling (a target section’s site defaults), applied first; the resources override them.
- Return type:
dict
- env_names = {'cpus_per_task': 'SLURM_CPUS_PER_TASK', 'gpus': 'SLURM_JOB_GPUS', 'job_id': 'SLURM_JOB_ID', 'mem_per_cpu': 'SLURM_MEM_PER_CPU', 'mem_per_node': 'SLURM_MEM_PER_NODE', 'nnodes': 'SLURM_NNODES', 'nodelist': 'SLURM_NODELIST', 'ntasks': 'SLURM_NTASKS', 'ntasks_per_node': 'SLURM_NTASKS_PER_NODE', 'submit_dir': 'SLURM_SUBMIT_DIR'}#
The scheduler-neutral names -> the environment variables a job sees, e.g.
{"job_id": "SLURM_JOB_ID", "ntasks": "SLURM_NTASKS"}. Used byseamm_exec.computational_environment()to recognize and read an allocation.
- env_prefixes = ('SLURM_', 'SBATCH_')#
The prefixes of every variable SLURM sets in a job.
- find_cmd(job_name)[source]#
A command printing the ids of the user’s queued or running jobs called
job_name, one per line, or None if the scheduler cannot.
- name = 'slurm'#
“slurm”, “pbs”, …
- parse_status(stdout, ids)[source]#
{id: JobStatus}from the status command’s output. An id the output does not mention is left out.
- class seamm_scheduler.slurm.SlurmBackend(transport=None)[source]#
Bases:
QueueBackendA
QueueBackendfor SLURM. Subclasses either pass a transport or implement_runthemselves.
- exception seamm_scheduler.slurm.SlurmError[source]#
Bases:
SchedulerErrorRaised when a SLURM CLI command fails unexpectedly.
- exception seamm_scheduler.slurm.SlurmSubmitError[source]#
Bases:
SlurmError,SubmitErrorRaised when
sbatchfails to submit a job.
- class seamm_scheduler.slurm.SshSlurm(host, *, ssh_command='ssh', ssh_options=(), timeout=None)[source]#
Bases:
SlurmBackendSLURM’s CLI on a remote host over passwordless ssh.
- property host#
- property ssh_command#
- seamm_scheduler.slurm.classify(raw_state)[source]#
Classify a raw SLURM state string into one of this library’s categories.
- Parameters:
raw_state (str) – A SLURM job state, e.g.
"RUNNING","COMPLETED", or (assacctsometimes reports for a cancelled job)"CANCELLED by 1234".- Returns:
One of
"pending","running","completed","cancelled","failed", or"unknown".- Return type:
str
seamm_scheduler.ssh module#
Run queueing-system commands on a remote host over ssh.
- class seamm_scheduler.ssh.SshTransport(host, *, ssh_command='ssh', ssh_options=(), timeout=None)[source]#
Bases:
objectRuns commands on a remote host over passwordless ssh – the case where the caller is not on a submit host and reaches the cluster’s login node over ssh instead.
Assumes key-based, passwordless auth is already set up for
host(e.g. an~/.ssh/configalias), the same way a user would runssh <host> sbatch ...by hand.- name = 'ssh'#
- seamm_scheduler.ssh.TASK_SSH_OPTIONS = ('-o', 'BatchMode=yes', '-o', 'ConnectTimeout=30', '-o', 'ServerAliveInterval=15', '-o', 'ServerAliveCountMax=4', '-o', 'ClearAllForwardings=yes')#
ssh options for the task layer’s many short connections from a machine whose network comes and goes (a laptop that sleeps or changes networks): fail fast instead of hanging, never prompt, and do not set up the host alias’s port forwards on every command.
seamm_scheduler.stage module#
Stage a job’s working directory to/from a remote host.
Needed when a JobServer’s transport = ssh points at a host that shares
no filesystem with the JobServer itself – unlike every case validated so
far, where the JobServer runs on (or already shares storage with) the
SLURM submit host. Paired with the transport the same way
LocalSlurm/SshSlurm are: LocalStager for the shared-storage
case (a no-op) and RsyncStager for the no-shared-filesystem case.
Lives here, not in seamm_jobserver, for the same reason config.py
does: a lightweight consumer should be able to use the transport without
pulling in the rest of seamm_jobserver (psutil, GUI code, job-running
machinery). See seamm_jobserver’s docs/developer_guide/campaigns/
2026-08-05/ (Phase 8) for the full design this implements.
The task layer (seamm_exec’s scheduler back end) moves many small task
directories at once: JobStager.push() and JobStager.pull() copy a
list of paths relative to a base directory in one rsync.
- class seamm_scheduler.stage.JobStager[source]#
Bases:
ABCMoves a job’s working directory to/from wherever it actually needs to run.
- pull(remote_base, local_base, paths, *, exclude=())[source]#
Copy
paths(relative toremote_base) back underlocal_base. No-op forLocalStager.
- push(local_base, remote_base, paths, *, delete=False)[source]#
Copy
paths(relative tolocal_base) to the same places underremote_base. No-op forLocalStager.
- abstractmethod stage_in(local_wdir, remote_wdir)[source]#
Make
remote_wdirready to run the job, fromlocal_wdir.Returns the working directory the job should actually use (for the
#SBATCH --chdirdirective, and for the job’s own command line) –local_wdiritself forLocalStager,remote_wdirforRsyncStager.
- class seamm_scheduler.stage.LocalStager[source]#
Bases:
JobStagerNo-op: the JobServer already shares storage with the SLURM submit host (every case validated so far), so there’s nothing to move.
- class seamm_scheduler.stage.RsyncStager(host, *, ssh_command='ssh', rsync_command='rsync', ssh_options=(), timeout=None)[source]#
Bases:
JobStagerPushes/pulls a job’s working directory to/from a remote host with no shared filesystem, over
rsync -e ssh. Assumes the same passwordless SSH accessSshSlurmdoes.A job’s working directory is already self-contained by the time a JobServer picks it up:
flowchart.flow, thejob_data.jsonstub, and adata/directory holding every file the flowchart references – SEAMM’s existingtype: "file"control-parameter mechanism (seamm_dashboard_client.dashboard.Dashboard.submit()+safe_filename()) already stages those intodata/and rewrites the command line tojob:data/...at submission time, before this ever runs. So staging is just “copy the whole directory,” not a general arbitrary-path resolver.Known limitation, not handled here: a
job://<n>/...cross-job file reference (e.g. a restart checkpoint from another job) points outside the referencing job’s own working directory, so it isn’t staged – such a reference in a job routed through this stager will fail to resolve on the remote host.- pull(remote_base, local_base, paths, *, exclude=())[source]#
Copy
paths(relative toremote_base) back underlocal_base. No-op forLocalStager.
- push(local_base, remote_base, paths, *, delete=False)[source]#
One
rsync --files-fromfor many paths: a bundle’s task directories, say, rather than one ssh connection each.
seamm_scheduler.taskserver module#
The TaskServer: a small queueing system for a machine that has none.
It shares one machine’s cores and memory among the jobs of everything on it –
the tasks of several flowcharts’ evaluators, and the evaluators themselves when
the JobServer submits them here – so that two jobs on a laptop no longer
oversubscribe it. It is driven like SLURM or PBS, by commands, locally or over
ssh, through the seamm scheduler (seamm_scheduler.seamm):
python -m seamm_scheduler.taskserver [--root R] submit [--name N] [SCRIPT]
python -m seamm_scheduler.taskserver [--root R] status [--json] [ID ...]
python -m seamm_scheduler.taskserver [--root R] cancel ID ...
python -m seamm_scheduler.taskserver [--root R] find NAME
python -m seamm_scheduler.taskserver [--root R] count
python -m seamm_scheduler.taskserver [--root R] queue
python -m seamm_scheduler.taskserver [--root R] config
A script carries its request in #SEAMM lines, as a SLURM script does in
#SBATCH lines: --cores, --memory (bytes, or with a unit), --time
(seconds, or HH:MM:SS), --chdir, --output, --name and --kind
(task or evaluator). An evaluator – a flowchart’s own process, light and
mostly waiting on its tasks – is charged no cores and 1 GB, and tasks are
admitted against the tasks running alone, so evaluators can never hold the
cores or memory their tasks wait for; an evaluator itself starts only when
everything still fits. A job named seamm-<number> (the
JobServer’s name for an evaluator) is an evaluator unless it says otherwise.
There is no daemon. The queue is one SQLite file, <root>/taskserver/queue.db;
every submit and status call, and every job’s runner when its job ends,
makes a scheduling pass that starts the queued jobs that fit, in order. A queued
job is claimed by one guarded UPDATE in a BEGIN IMMEDIATE transaction,
so concurrent passes never start a job twice. Each job runs under its own runner
(run-job) in a session of its own, so nothing that restarts or is upgraded
touches it. The runner enforces the job’s time limit – measured with
time.monotonic, which stops while the machine sleeps – and its memory: the
job is stopped when its processes use more than 125 % of its request for 30 s,
and the newest job is stopped when the machine itself runs low on memory.
The machine’s capacity is in <root>/taskserver.ini:
[taskserver]
cores = 8
memory = 8 GB
by default its physical cores and half its memory (the rest is for the desktop, the evaluators and everything else).
- seamm_scheduler.taskserver.DEFAULT_MEMORY_PER_CORE = 2147483648#
Charged to a job that asks for no memory, per core
- seamm_scheduler.taskserver.EVALUATOR_MEMORY = 1073741824#
Charged to an evaluator
- seamm_scheduler.taskserver.KEEP_ENVIRONMENT = {'HOME', 'LANG', 'LOGNAME', 'PATH', 'SHELL', 'TMPDIR', 'TZ', 'USER'}#
The variables a job’s script starts with (and LC_*); the script sets up the rest itself, as a batch job does.
- seamm_scheduler.taskserver.KEEP_FINISHED = 2592000#
Terminal jobs older than this are removed from the queue (seconds)
- seamm_scheduler.taskserver.KILL_GRACE = 10.0#
Seconds between SIGTERM and SIGKILL
- seamm_scheduler.taskserver.MACHINE_MEMORY_FLOOR = 0.1#
Below this fraction of the machine’s memory available, the newest job stops
- seamm_scheduler.taskserver.MEMORY_GRACE = 30.0#
… for this long (seconds) is stopped
- seamm_scheduler.taskserver.MEMORY_LIMIT_FACTOR = 1.25#
A job using more than this fraction of its memory request …
- seamm_scheduler.taskserver.QUEUED = 'queued'#
claimed, its runner not yet running the script.
- Type:
Job states.
starting
- class seamm_scheduler.taskserver.Queue(root=None)[source]#
Bases:
objectThe queue in
<root>/taskserver/queue.db.- charge(row)[source]#
What a job takes from the machine while it runs: (cores, memory).
An evaluator: no cores, and 1 GB (a quarter of the memory of a queue with less than 4 GB, so evaluators can still run there).
- finish(job_id, state, exit_code=None, reason=None)[source]#
Record a job’s end, unless it has already ended.
- seamm_scheduler.taskserver.RESERVE_AFTER = 1800#
The oldest queued job reserves room after waiting this long (seconds)
- seamm_scheduler.taskserver.SCHEMA_VERSION = 2#
the job’s own pid)
- Type:
The version of the queue’s database layout (2
- seamm_scheduler.taskserver.START_TIMEOUT = 120.0#
A claimed job whose runner has not started after this long is lost
- seamm_scheduler.taskserver.capacity(root)[source]#
The machine’s
{"cores": int, "memory": bytes}for the queue.
- seamm_scheduler.taskserver.parse_directives(script)[source]#
The request in a script’s
#SEAMM --key valuelines.
- seamm_scheduler.taskserver.parse_memory(value)[source]#
Bytes from
"8 GB","512M","2gb", or a plain number of bytes.
- seamm_scheduler.taskserver.parse_time(value)[source]#
Seconds from
3600,"1:00:00","1-00:00:00"or"90s".
Module contents#
seamm_scheduler: queueing systems (SLURM, PBS) for SEAMM, with local and ssh transports.