12. NERSC SFAPI Remote Submission
12.1. Overview
The SFAPI integration enables Pegasus/HTCondor to submit jobs to the Perlmutter supercomputer at NERSC without a shared filesystem between the submit host and the remote cluster.
The integration is implemented as a BLAHP (Batch Local ASCII Helper Protocol) plugin — a set of shell scripts that sit between HTCondor’s Grid Universe and the remote batch system. The NERSC Superfacility API (SFAPI) Python client handles all communication with Perlmutter: file upload/download, job submission, status queries, and cancellation.
Since there is no shared filesystem between the submit host and Perlmutter, all files are transferred explicitly:
Upload (submit time): executable, input sandbox files, and stdin — via NERSC DTNs (Data Transfer Nodes).
Download (job completion): stdout, stderr, and output sandbox files — via the SFAPI download API.
12.2. Architecture
HTCondor / Pegasus submit host
|
| Grid Universe (batch_system = sfapi)
v
blahpd daemon
|
|────|──────────────────|
│ BLAHP Shell Scripts │
│ sfapi_submit.sh │ ──> sfapi_helpers.py submit
│ sfapi_status.sh │ ──> sfapi_helpers.py status / download
│ sfapi_cancel.sh │ ──> sfapi_helpers.py cancel
│ sfapi_ping.sh │ ──> sfapi_helpers.py status --type resource
|───────────────────────|
|
NERSC SFAPI (HTTPS)
|
|───────|────────|
│ Perlmutter │
│ /pscratch/… │
│ Slurm queue │
|────────────────|
The BLAHP scripts are installed alongside other batch plugin scripts in the Pegasus
glite/ directory. They are selected by HTCondor when batch_system = sfapi is set in
the site catalog or job ClassAd.
12.3. Components
12.3.1. sfapi_helpers.py
The Python module that wraps the sfapi_client library. All BLAHP shell scripts invoke it
as a CLI tool. It exposes four subcommands and a set of helper functions.
12.3.1.1. Credentials
Credentials are read from ~/.superfacility/ by load_sflapi_client_secret():
File |
Purpose |
|---|---|
|
SFAPI client ID (plain text, whitespace-stripped) |
|
JWK-encoded RSA private key (JSON Web Key format) |
The function sets module-level globals client_id and client_secret that all helper
functions use when constructing an sfapi_client.Client instance. File permissions on
~/.superfacility/ are enforced to 0600 at load time.
12.3.1.2. Key Functions
Function |
Description |
|---|---|
|
Reads credentials from |
|
Creates remote scratch directory, uploads input files, prepends
|
|
Creates |
|
Uploads a local file to a remote directory on Perlmutter via NERSC DTNs |
|
Downloads a remote file to a local path; raises |
|
Reads the jobstate file for the given blahp job ID and downloads all listed remote files to their local destination paths |
|
Checks a NERSC resource status via the public SFAPI; raises |
|
Prints |
|
Cancels a job by bare numeric Slurm ID or blahp ID ( |
12.3.1.3. CLI Subcommands
python3 sfapi_helpers.py submit -n NAME -i FILE1,FILE2,... -s SCRIPT_PATH
python3 sfapi_helpers.py status -t resource|job -v VALUE
python3 sfapi_helpers.py download BLAHP_JOB_ID
python3 sfapi_helpers.py cancel JOB_ID
12.3.2. sfapi_setup.sh
Sourced by every BLAHP script before any Python invocation. Activates the
~/superfacility-env Python virtual environment, which must contain the sfapi_client
package. If the virtual environment is missing, the script prints installation instructions
and exits non-zero.
. `dirname $0`/sfapi_setup.sh
12.3.3. sfapi_submit.sh
The main submission script, called by blahpd when a new HTCondor job arrives.
12.3.3.1. Flow
Parse blahp options via
bls_parse_submit_options(fromblah_common_submit_functions.sh).Set up temp files via
bls_setup_temp_files. The fullbls_setup_all_filesis deliberately avoided because stdin/stdout/stderr are handled remotely via the SFAPI, not through a local sandbox.Build the SBATCH script in the temp file:
Standard SBATCH directives:
-A(project/account),-t(runtime),--mem(memory),-p(partition/queue),-N/-n(nodes/tasks),--exclusive,--gres(GPU/MIC resources).Local submit attributes appended via
bls_set_up_local_and_extra_args, sourcingsfapi_local_submit_attributes.sh.HTCondor job environment variables injected by
bls_set_job_env. As a side effect, if_CONDOR_TRANSFER_EXECUTABLE=falseis found in the environment,bls_opt_stgcmdis set to"no"to suppress executable staging.
Note
#SBATCH --output,--error, and--chdirare not written here — they are prepended bysubmit_remote_slurm_job()insfapi_helpers.pyonce the remote directory path is known.Collect input files:
If
bls_opt_stgcmd == "yes": resolve the executable path againstbls_opt_workdirand add it to the upload list.Read
$bls_opt_inputflstring(the blahptransfer_input_fileslist) and add each file, resolving relative paths againstbls_opt_workdir.If stdin is specified and is not
/dev/null, resolve and add it too.
Collect output files from
$bls_opt_outputflstring, again resolving relative paths.Submit the job:
python3 sfapi_helpers.py submit \ --job-name "$bls_tmp_name" \ --input-files "$sfapi_input_files_csv" \ --script "$bls_tmp_file"
Parse the result line from stdout:
SFAPI_RESULT:<jobid>:<remote_stdout>:<remote_stderr>
Write the jobstate file at
~/.blah/sfapi_jobs/<date>_<jobID>:# type::<local file on submit host>:<remote file to retrieve via sfapi> stdout::/abs/path/to/local.out:/pscratch/.../job.out stderr::/abs/path/to/local.err:/pscratch/.../job.err output::/abs/path/to/local/result.dat:/pscratch/.../result.dat
Output files are assumed to land in
dirname(remote_stdout)under their local basename.Output the blahp job ID for
blahpd:BLAHP_JOBID_PREFIXsfapi/<YYYYMMDD>/<jobID>
12.3.3.2. transfer_executable Handling
BLAHP does not support the transfer_executable submit attribute natively. The integration
uses a convention: set _CONDOR_TRANSFER_EXECUTABLE=false as an HTCondor environment
variable (or via the Pegasus HTCondor_SFAPI style class). The bls_set_job_env
function detects this string and sets bls_opt_stgcmd="no", preventing executable upload.
12.3.4. sfapi_status.sh
Called by blahpd to poll job state. Accepts one or more blahp job IDs on the command
line.
12.3.4.1. Flow (per job)
Strip the path prefix:
sfapi/YYYYMMDD/JOBID→JOBID.Query the remote state:
python3 sfapi_helpers.py status --type job --value $JOBID
Parse the
"Job <id> state: <state>"line from output.Map the Slurm/SFAPI state to a blahp status code:
SFAPI / Slurm State
blahp Code
Meaning
PENDING,CONFIGURING1
Idle / Queued
RUNNING,COMPLETING,STOPPED,SUSPENDED2
Running
CANCELLED3
Cancelled
COMPLETED,FAILED,BOOT_FAIL,NODE_FAIL,PREEMPTED,SPECIAL_EXIT,TIMEOUT4
Done / Failed
(unknown)
1
Treated as queued
When status is 4 (Done):
Call
python3 sfapi_helpers.py download sfapi/<date>/<jobID>to retrieve all output files listed in the jobstate file.Delete the jobstate file
~/.blah/sfapi_jobs/<date>_<jobID>.
Output the blahp result line:
0[BatchJobId="JOBID";JobStatus=N;ExitCode=0;]
On error:
1Error: <message>
12.3.5. sfapi_cancel.sh
Called by blahpd to cancel one or more jobs. Output format mirrors slurm_cancel.sh.
12.3.5.1. Flow (per job)
Call
python3 sfapi_helpers.py cancel "$job".If the output contains
sfapi_client.exceptions.SfApiError: Job not found:, treat the result as success — the job has already completed or never existed.Output (single job):
0 No\ error
Output (multiple jobs, zero-indexed):
.0 0 No\ error .1 0 No\ error
12.3.6. sfapi_ping.sh
Called by blahpd to check whether the backend is available before submitting jobs.
Invokes sfapi_helpers.py status --type resource --value <resource> and maps the result
to blahp ping convention: "0 No error" if active, "1 SFAPI status error: ..."
otherwise. Always exits with code 0.
The resource name defaults to perlmutter and can be overridden in blah.config via
the sfapi_resource variable.
12.3.7. sfapi_local_submit_attributes.sh
A helper script sourced during submission that translates HTCondor job attributes
into #SBATCH directives. It is invoked via bls_set_up_local_and_extra_args.
Environment Variable (ClassAd) |
|
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Verbatim |
12.4. Job Lifecycle
submit host Perlmutter (NERSC)
───────────────────────────────────── ──────────────────────────────────
sfapi_submit.sh
|_ sfapi_helpers.py submit
├─ create_remote_blahp_directory ──> mkdir $PSCRATCH/.blah/<name>
├─ upload_file (per input file) ──> DTN upload
├─ prepend SBATCH output/error/chdir
└─ perlmutter.submit_job() ──> Slurm job queued
|_ write ~/.blah/sfapi_jobs/<date>_<id>
|_ output: BLAHP_JOBID_PREFIXsfapi/<date>/<id>
[polling interval]
sfapi_status.sh sfapi/<date>/<id>
|_ sfapi_helpers.py status --type job ──> query Slurm state
|_ map state → blahp code (1/2/3/4)
|_ if blahp_status == 4:
|_ sfapi_helpers.py download ──> download stdout/stderr/outputs
|_ rm ~/.blah/sfapi_jobs/<date>_<id>
|_ output: 0[BatchJobId=...;JobStatus=N;ExitCode=0;]
[on user request or workflow abort]
sfapi_cancel.sh sfapi/<date>/<id>
|_ sfapi_helpers.py cancel ──> job.cancel()
|_ output: 0 No\ error
12.5. Remote Directory Structure on Perlmutter
Each submitted job gets its own working directory under the user’s scratch filesystem:
/pscratch/sd/<first_char>/<username>/.blah/<job_name>/
|── <job_name>.sh ← SBATCH script (submitted via SFAPI)
|── <executable> ← uploaded if transfer_executable=true
|── <stdin_file> ← uploaded if stdin != /dev/null
|── <input_file_1> ← from transfer_input_files
|── ...
|── <job_name>.out ← Slurm stdout → downloaded to local stdout path
|── <job_name>.err ← Slurm stderr → downloaded to local stderr path
The create_remote_blahp_directory function resolves the scratch path dynamically by
calling client.user() to obtain the NERSC username for the authenticated client.
12.6. Jobstate File
The jobstate file is the bridge between submission and retrieval. It is written by
sfapi_submit.sh at ~/.blah/sfapi_jobs/<YYYYMMDD>_<jobID> and read by
sfapi_helpers.py download_job_outputs() when the job finishes.
Format:
# type::<local file on submit host>:<remote file to retrieve via sfapi>
stdout::/abs/local/path/job.out:/pscratch/sd/v/vahi/.blah/bl_ABC123/bl_ABC123.out
stderr::/abs/local/path/job.err:/pscratch/sd/v/vahi/.blah/bl_ABC123/bl_ABC123.err
output::/abs/local/path/result.dat:/pscratch/sd/v/vahi/.blah/bl_ABC123/result.dat
Each data line has the form <type>::<local_path>:<remote_path>. Lines beginning with
# and blank lines are ignored. The file is deleted by sfapi_status.sh after all
outputs have been successfully downloaded.
12.7. Blahp Job ID Format
sfapi/<YYYYMMDD>/<numeric_slurm_jobid>
Example: sfapi/20260511/52833290
The date component is the submission date (date +%Y%m%d). It is used by
sfapi_status.sh to reconstruct the jobstate file path:
sfapi/20260511/52833290 → ~/.blah/sfapi_jobs/20260511_52833290
12.8. Setup Requirements
12.8.1. On the Submit Host
Python 3.10 or later virtual environment at
~/superfacility-envcontaining thesfapi_clientpackage:$ python3 -m venv ~/superfacility-env $ source ~/superfacility-env/bin/activate (superfacility-env) $ pip install sfapi_client
SFAPI credentials registered at https://iris.nersc.gov with the required compute scopes, stored at:
~/.superfacility/clientid.txt ← SFAPI client ID string ~/.superfacility/priv_key.jwk ← JWK-format RSA private key (JSON)
HTCondor configured with a Grid Universe site entry pointing to this plugin (
batch_system = sfapi).
12.8.2. Pegasus HTCondor Style Class
Pegasus uses an Glite style class to inject the following into submitted jobs:
batch_system = sfapi— selects this BLAHP plugin._CONDOR_TRANSFER_EXECUTABLE=falsein the job environment — suppresses executable upload when the executable is pre-installed on Perlmutter or managed by another mechanism.
12.9. Development History
The capability was implemented as part of Pegasus issue #2186: