LSF Job Modification Workflow (bmod)

Administrator Guide

LSF Job Modification Workflow (bmod)

Spillbox LSF Job Modification Workflow (bmod)

Overview

This workflow lets you take an existing LSF job that was submitted on the local (client) cluster and redirect it to run on the remote Spillbox cluster — without the user having to resubmit. The job is modified in-place using bmod, transparently wiring in Spillbox’s pre/post exec hooks, snapshot, and download steps.

Submit Job→Modify Job→Snapshot→Run on Remote Cluster→Download Results

Two scripts drive this:

Script Purpose
modify_lsf_job.sh Modifies held jobs to run via Spillbox on the remote cluster
reverse_modify_lsf.sh Undoes a modification and restores jobs to their original state

Prerequisites

  • LSF must be sourced (LSF_ENVDIR set)
  • bsub, bmod, bresume must be in PATH
  • A Spillbox project config file must exist on both the client side and the server side — see Client vs. server config files below for the two paths and which values each one needs
  • The operator must have sudo access to run commands as other users (or run as the same user)

Client vs. server config files

Two separate copies of the same-named config file (spillbox_singularity_config.<project>) are needed — one in each data center — and each only needs the values that data center’s scripts actually use.

Client side (where you run modify_lsf_job.sh)

Path: terraform/etc/spillbox_singularity_config.<project>

bash
export SPILLBOX_SERVER=S10.10.10.10:8080:C192.168.1.1:8080
export SPILLBOX_SITE=mysite
export SPILLBOX_REMOTE_WORKDIR=/workdir

Used by: modify_lsf_job.sh only. The client side never actually runs a job’s container — it just submits jobs and tells them where to go, so it only needs to know which server to burst to (SPILLBOX_SERVER), which site this is (SPILLBOX_SITE), and the remote working directory (SPILLBOX_REMOTE_WORKDIR).

Server side (where the job actually runs)

Path: $SPILLBOX_REMOTE_WORKDIR/.spillbox/sites/local/<SITE>/terraform/etc/spillbox_singularity_config.<project>

bash
export SPILLBOX_SERVER=S10.10.10.10:8080:C192.168.1.1:8080
export SPILLBOX_SITE=mysite
export SPILLBOX_SINGULARITY_EXECUTABLE=/workdir/singularity_wrapper
export SPILLBOX_HYBRID_MOUNT_PATHS=ALL
export SPILLBOX_HYBRID_OPTION="-download"
export SPILLBOX_REMOTE_WORKDIR=/workdir
# Other optional variables (see Environment Variables Reference below for the full list)
#export SPILLBOX_DOWNLOAD_INTERVAL=10
#export SPILLBOX_HOLD_EXCLUDE_SIZE=1024

Used by: singularity_preexec.sh, singularity_jobstarter.sh, singularity_postexec.sh — the three scripts that actually run when a job executes in its container, so this copy needs the extra runtime/mount settings the client-side copy doesn’t.

Note: “client” and “server” are roles for a given burst direction, not fixed machine identities — if jobs ever burst the other way, the roles and the S/C prefixes in SPILLBOX_SERVER flip accordingly.


Quick example

# 1. User submits job on hold
bsub -H -q normal -o /results/myjob.out myjob.sh
# Job <54321> is submitted to queue <normal>.

# 2. Operator modifies it to run via Spillbox
terraform/bin/modify_lsf_job.sh \
  -project default \
  -localq normal \
  -sendq sendq \
  54321

# Output:
# Processing Input: 54321
# SUCCESS: Job 54321 updated.
# USER: jsmith
# Job <54322> is submitted to queue <normal>.   <- download job

# 3. If something goes wrong and you want to revert
terraform/bin/reverse_modify_lsf.sh -localq normal 54321
# SUCCESS: 54321 reversed

That’s the whole workflow in miniature. The sections below walk through each step in detail, and the Reference section at the bottom covers every flag, environment variable, and internal detail for when you need to look something up.


Workflow

Step 1 — User submits job in HOLD state

The user (or an automated system) submits the job to the local queue with -H to hold it before it runs:

bash
bsub -H -q normal myjob.sh

The job sits in PSUSP/PEND state — it will not run until resumed.


Step 2 — Operator runs modify_lsf_job.sh

Script location: terraform/bin/modify_lsf_job.sh

bash
terraform/bin/modify_lsf_job.sh \
  -project <projectname> \
  -localq <local_queue> \
  -sendq <send_queue> \
  [-queue <queue_names>] \
  [<job_ids_or_usernames>]

This is the minimum you need: a project name, a local queue, a remote send queue, and the job IDs (or usernames) to modify. There are more optional flags for less common cases — interactive jobs, archiving for later resubmission, delaying the start, and so on — covered in the full flag reference below.

Job targets (comma-separated, mixed is OK):

# By job ID
terraform/bin/modify_lsf_job.sh -project default -localq normal -sendq sendq 12345

# By job array
terraform/bin/modify_lsf_job.sh -project default -localq normal -sendq sendq "12345[1-10]"

# By username (modifies all jobs for that user in the local queue)
terraform/bin/modify_lsf_job.sh -project default -localq normal -sendq sendq jsmith

# By queue name (modifies EVERY job currently in that queue, across ALL
# users — not just the one running the script). A queue must be given
# with -queue; a bare name is always taken as a username.
terraform/bin/modify_lsf_job.sh -project default -localq normal -sendq sendq -queue batchq

# Multiple, mixed (comma-separated, no spaces)
terraform/bin/modify_lsf_job.sh -project default -localq normal -sendq sendq 12345,67890,jsmith

# Job IDs or usernames together with a queue
terraform/bin/modify_lsf_job.sh -project default -localq normal -sendq sendq -queue batchq 12345,jsmith

A queue given with -queue modifies every job currently in that queue.


Step 3 — Job runs on remote cluster

Spillbox’s singularity_preexec.sh and singularity_postexec.sh handle:

  • Staging input data to the remote site
  • Running the job inside a container (Singularity/Docker)
  • Writing output/error back to the original paths

The download job then pulls results back automatically.


Automatic mode: the hold queue and the crontab

Instead of running modify_lsf_job.sh by hand, the customer can have Spillbox run it on a schedule. Users submit jobs to a hold queue, and a crontab entry moves every job in that queue to the send queue. Users only change the queue name; nothing else.

Turning it on. It is off by default.

  1. In etc/spillbox_config set cron_lockfile to an absolute path the user running run_server.sh can write to, for example cron_lockfile="/var/lib/spillbox/cron.lock".
  2. Start (or restart) the server with run_server.sh. It copies etc/spillbox_cron.crontab.example to <cron_lockfile>.crontab the first time, with every entry commented out.
  3. Edit <cron_lockfile>.crontab: take the # off the modify_lsf_job.sh line and set the project name, queues and schedule. The scheduler runs in the web GUI spld (gui.log), not the main one.

The crontab is an ordinary crontab: one job per line as <schedule> <command>, # starts a comment, NAME=value sets an environment variable for every job. The schedule is five fields (minute hour day-of-month month day-of-week) or @hourly, @daily; there is no seconds field. The customer decides how often by editing the schedule. A # in front of a line turns it off.

# every 5 minutes
*/5 * * * * sudo -n -u lsfadmin /opt/spillbox/terraform/bin/modify_lsf_job.sh -project amd -localq normal -sendq send_queue -queue hold

Changing it while running. The file is re-read every second; no restart is needed. A job that is still running when the file changes is not started a second time, and a run is skipped while the last one is still going. If an edit leaves a line that is not valid, the scheduler keeps the previous schedule and logs the line.

Times are UTC; set CRONWRAPPER_TZ=America/Los_Angeles (or another zone) in the environment of spld to change that. The user running spld needs passwordless sudo to the LSF account if the entry uses sudo -n -u. Every run (start, finish time, exit code, output) is logged in gui.log. More detail: pkg/cronwrapper/README.md in splpipe.


Reversing a modification

If a modified job gets stuck in pending state on the remote cluster and you want to undo the modification and bring it back to its original local state:

Script location: terraform/bin/reverse_modify_lsf.sh

bash
terraform/bin/reverse_modify_lsf.sh -localq <local_queue> <job_ids>

Example:

bash
terraform/bin/reverse_modify_lsf.sh -localq normal 12345,67890

reverse_modify_lsf.sh reads the encoded original values from the job’s current pre-exec command (where modify_lsf_job.sh stored them) and uses bmod to restore:

  • Original job name
  • Queue set to the -localq value
  • Original working directory
  • Original output/error files
  • Original pre/post exec commands
  • Original resource requirements
  • Original dependencies
  • Original app profile

It skips internal Spillbox jobs (spillbox_snapshot, spillbox_download_*) automatically.


Reference

modify_lsf_job.sh flag reference

Flag Required Description
-project Yes Spillbox project name (used to find config file terraform/etc/spillbox_singularity_config.<project>)
-localq Yes Local LSF queue to use for the download and sleep jobs, and to find a user’s jobs when a username is given (the snapshot job goes to -sendq)
-sendq Yes Queue to send jobs to on the remote Spillbox cluster
-app No LSF application profile to apply on the remote cluster
-localR No Resource requirement for local snapshot/download jobs
-remoteR No Resource requirement to set on the remote job (replaces the job’s original -R)
-remoteRA No Resource requirement to append to the job’s original -R instead of replacing it; takes priority over -remoteR if both are given
-spillboxlocalq Only for interactive jobs Local queue override for interactive (bsub -I) jobs — script exits with an error if an interactive job is modified without this
-spillboxsendq Only for interactive jobs Send queue override for interactive (bsub -I) jobs — same requirement as -spillboxlocalq
-archive No Before running bmod, capture the job’s original state (queue, name, output/error paths, cwd, resource requirement, app, dependencies) and, once bmod succeeds, write it to ~/.config/spillbox/archive/job_<id>_<timestamp>.sh, so it can later be resubmitted as a fresh job via -resubmit
-delay <seconds> No After the snapshot job is submitted, also submit a sleep <seconds> job (depending on the first modified user job starting) that the remaining modified jobs wait on; default 0 skips this sleep job entirely
-nosla No After a successful bmod, also run bmod -slan to remove any SLA attached to the job
-queue <queue_names> No Modify every job currently in these LSF queues (comma-separated), from all users. The script exits with an error if a name is not a queue. A queue name given without -queue is taken as a username
-downloadinterval <minutes\|hold> No Overrides SPILLBOX_DOWNLOAD_INTERVAL for this run only, taking priority over the project config file’s value; hold = hold until explicitly released, a number = hold that many minutes

Skipping users with recent jobs — SPLAPP_SUBMIT_TIME. Set this in the environment of the run (or in the project config file) to a job age, not a clock time: seconds, or a number with an m, h or d suffix (3600, 30m, 2h, 1d). If any of a user’s jobs selected in this run was submitted more recently than that, all of that user’s jobs are skipped — including their older ones — and no snapshot, sleep or download job is submitted for them. Other users are modified as usual, so only users whose jobs are all older than the age are processed. Unset or empty = no limit (default); any other value stops the script with an error. modify_slurm_job.sh reads it the same way.

SPLAPP_SUBMIT_TIME=2h terraform/bin/modify_lsf_job.sh -project default -localq normal -sendq sendq -queue holdq
# USER=jsmith has a job submitted less than SPLAPP_SUBMIT_TIME=2h ago; skipping all their jobs
# Skipping 12345 : user jsmith has a job submitted less than SPLAPP_SUBMIT_TIME=2h ago

Separate mode — resubmitting an archived job (not part of the normal modify flow above; mutually exclusive with <job_ids_or_usernames> and -queue):

bash
terraform/bin/modify_lsf_job.sh -project <projectname> -resubmit <job_id> [-hold]
Flag Required Description
-resubmit <job_id> — Resubmits a job previously saved by -archive, reading it back from ~/.config/spillbox/archive/job_<job_id>_*.sh and submitting it as a brand-new job via bsub, with the original queue/output/error/cwd/resource/app restored (dependencies are not restored) from the archive file
-hold No Only meaningful with -resubmit — submits the resubmitted job with bsub -H (held) instead of letting it run immediately

Options updated by modify_lsf_job.sh on the job

When modify_lsf_job.sh runs bmod, it updates the following on each job:

Option What gets set Notes
-q sendq (remote queue) Changed from local queue to Spillbox send queue
-J <project>_<rand>_<original_name> Renamed to track the project; skipped for job arrays
-app Value from -app flag Optional; if not provided, app is left unchanged
-E singularity_preexec.sh with all env vars Replaces any existing pre-exec with Spillbox’s wrapper
-Ep singularity_postexec.sh Replaces any existing post-exec with Spillbox’s wrapper
-o $REMOTEDIR/%J_%I.out Remote log directory on the Spillbox cluster
-e $REMOTEDIR/%J_%I.err Remote log directory on the Spillbox cluster
-cwd /tmp Actual working dir is passed via SPLAPP_EXEC_CWD in pre-exec
-w done(<snapshot_job>) && <original_deps> Adds dependency on snapshot job; preserves existing deps
-R Value from -remoteR flag, or original -R plus -remoteRA Optional; only set if -remoteR or -remoteRA is provided

All original values (name, pre/post exec, output, cwd, dependencies, resource requirements, app) are encoded into the pre-exec command so they can be restored by reverse_modify_lsf.sh.

What modify_lsf_job.sh does internally

For each job it processes:

  1. Reads existing job state via bjobs:
  2. Job name, pre/post exec commands, output/error files, working directory
  3. Existing dependencies (bjdepinfo)
  4. App profile, resource requirements
  5. Whether it’s interactive

  6. Submits a snapshot job (spillbox_snapshot) — this runs first on the remote cluster and snapshots the project filesystem so the job has the right data when it starts. Only one snapshot job is submitted per user per run (shared across all that user’s jobs).

  7. Encodes original job state — all original values (name, pre/post exec, output, cwd, dependencies, resource requirements, app) are encoded as environment variables inside the new pre-exec command string, so they can be recovered later by reverse_modify_lsf.sh.

  8. Calls bmod to modify the held job:

  9. Changes queue to sendq (remote cluster)
  10. Replaces pre-exec with Spillbox’s singularity_preexec.sh (carries all encoded original values)
  11. Replaces post-exec with Spillbox’s singularity_postexec.sh
  12. Redirects output/error to remote log directory: $SPILLBOX_REMOTE_WORKDIR/.spillbox/customers/<user>/<project>-<site>/stacks/modules/docker-nfs/install/spillbox_splfs/LOG/LSFLOG/
  13. Sets working directory to /tmp (actual workdir restored at runtime via SPLAPP_EXEC_CWD)
  14. Adds dependency on the snapshot job

  15. Calls bresume — releases the job so it can now run via Spillbox.

  16. Submits a download job (spillbox_download_<project>) — runs after all modified jobs complete, downloads results back to the local cluster.

Environment Variables Reference

Variables set in the project config file (terraform/etc/spillbox_singularity_config.<projectname>):

Variable Description
SPILLBOX_SERVER Connection string to the Spillbox server, required. Either plain <host>:<port> (single-server), or S<host>:<port>:C<host>:<port> for two-server setups (S = server side, C = client side)
SPILLBOX_SITE Unique on-prem site ID, used in .spillbox/sites/local/<SITE>/ paths
SPILLBOX_SINGULARITY_EXECUTABLE Path to the compiled, setuid singularity_wrapper binary built from site/singularity_wrapper.c by lsf_bapp_setup.sh, used when runtime_container=singularity
SPILLBOX_DOCKER_EXECUTABLE Path to the compiled, setuid docker_wrapper binary built from site/docker_wrapper.c by lsf_bapp_setup.sh, used when runtime_container=docker
SPILLBOX_HYBRID_MOUNT_PATHS Which on-prem paths get hybrid-mounted into the job (ALL = everything)
SPILLBOX_HYBRID_OPTION How hybrid paths behave; -download = fetch data in, not just reference it
SPILLBOX_REMOTE_WORKDIR Cloud-side job workdir; required — modify_lsf_job.sh/modify_slurm_job.sh error out if it’s unset
SPILLBOX_DOWNLOAD_INTERVAL hold = hold until explicitly released; a number = hold that many minutes then auto-release; unset/0 = no hold, sync immediately (default). See Notes below.
SPILLBOX_LARGEDIR_OPT Entry-count threshold for “large directory” handling
SPILLBOX_LARGEDIR_ENTRY_SIZE Expected avg entry size hint for large-directory handling
SPILLBOX_LARGEDIR_PAT Regex matching dirs that get large-directory handling
SPILLBOX_HOLD_EXCLUDE_PAT Regex of files excluded from project snapshot/hold
SPILLBOX_HOLD_EXCLUDE_SIZE Size threshold above which files are excluded from snapshot/hold
SPLAPP_SUBMIT_TIME Job age (seconds, or m/h/d suffix); modify_lsf_job.sh and modify_slurm_job.sh skip every job of a user who has any job submitted more recently than this. Unset = no limit. See the flag reference above.

Example (terraform/etc/spillbox_singularity_config.<projectname>):

# --- Required ---

# Single server
export SPILLBOX_SERVER=10.10.10.10:8080

# Two-server setup: S<remote_ip>:<port>:C<client_ip>:<port>
# S = remote/server-side IP, C = local/client-side IP
# Use when the Spillbox server has separate IPs for each network
# export SPILLBOX_SERVER=S10.10.10.10:8080:C192.168.1.1:8080

export SPILLBOX_SITE=mysite                   # unique on-prem site ID
export SPILLBOX_REMOTE_WORKDIR=/workdir       # cloud-side job working directory, required

# --- Container executable: set the one matching runtime_container ---

export SPILLBOX_SINGULARITY_EXECUTABLE=/workdir/singularity_wrapper   # compiled setuid wrapper, used when runtime_container=singularity
export SPILLBOX_DOCKER_EXECUTABLE=/workdir/docker_wrapper              # compiled setuid wrapper, used when runtime_container=docker

# --- Hybrid mount behavior ---

export SPILLBOX_HYBRID_MOUNT_PATHS=ALL        # which on-prem paths get hybrid-mounted ("ALL" = everything)
export SPILLBOX_HYBRID_OPTION="-download"     # fetch data in, not just reference it

# --- Optional: uncomment and adjust only if the defaults don't fit ---

#export SPILLBOX_DOWNLOAD_INTERVAL=10                         # "hold" = hold until released, a number = hold that many minutes, unset/0 = no hold (default)
#export SPILLBOX_LARGEDIR_OPT=100000                          # entry-count threshold for "large directory" handling
#export SPILLBOX_LARGEDIR_ENTRY_SIZE=88                       # expected avg entry size hint for large-directory handling
#export SPILLBOX_LARGEDIR_PAT='/cache/sync[0-9]+/[^/]+'       # regex matching dirs that get large-directory handling
#export SPILLBOX_HOLD_EXCLUDE_PAT='/runpointsfile|/foo_\d+$'  # regex of files excluded from project snapshot/hold
#export SPILLBOX_HOLD_EXCLUDE_SIZE=1024                       # size (bytes) above which files are excluded from snapshot/hold
#export SPLAPP_SUBMIT_TIME=300                                # skip all jobs of a user with any job newer than this (seconds, or 30m/2h/1d)

Notes

  • Jobs are processed in parallel — large batches of job IDs run concurrently.
  • A queue name as input modifies every job currently in that queue, including other users’ jobs, not just the one running the script — the operator needs sudo rights to act as those other users. Each affected user gets their own separate snapshot/download job (never combined), and if a user doesn’t already have the given -project set up, it’s created for them on first use.
  • If a user already has a spillbox_download_<project> job running, that job is skipped (incremental mode, currently disabled).
  • Interactive jobs (bsub -I) require -spillboxlocalq and -spillboxsendq to be explicitly set.
  • Spaces in resource requirements, pre/post exec, and dependencies are encoded as -s_b-o_x- internally to survive shell quoting through bmod.
  • The SNAPSHOT_TIME is stamped at the start of the modify_lsf_job.sh run and shared across all jobs in that batch so they all refer to the same snapshot.
  • Hold time is a minimum, not exact — files release within N to N+1 minutes (checked once a minute).
💬 Ask a Question