sqtop 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
sqtop-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Yanting Yang
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
sqtop-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,324 @@
1
+ Metadata-Version: 2.4
2
+ Name: sqtop
3
+ Version: 0.1.0
4
+ Summary: Slurm dashboard: partitions, account/QOS limits and estimated start times
5
+ Keywords: slurm,hpc,tui,dashboard,squeue,sbatch
6
+ License-Expression: MIT
7
+ License-File: LICENSE
8
+ Classifier: Development Status :: 4 - Beta
9
+ Classifier: Environment :: Console
10
+ Classifier: Intended Audience :: Science/Research
11
+ Classifier: Operating System :: POSIX :: Linux
12
+ Classifier: Programming Language :: Python :: 3.13
13
+ Classifier: Topic :: System :: Clustering
14
+ Classifier: Topic :: System :: Monitoring
15
+ Requires-Dist: textual>=8,<9
16
+ Requires-Python: ==3.13.*
17
+ Project-URL: Homepage, https://github.com/yanting-yang/sqtop
18
+ Project-URL: Issues, https://github.com/yanting-yang/sqtop/issues
19
+ Description-Content-Type: text/markdown
20
+
21
+ # sqtop
22
+
23
+ A terminal dashboard for Slurm, in the spirit of
24
+ [hunoutl/slurmtop](https://github.com/hunoutl/slurmtop) and
25
+ [slmtop](https://github.com/dawnmy/slmtop), that answers "where can I run, under which
26
+ account/QOS, and when would it start?", and can email you that report every hour while
27
+ it runs.
28
+
29
+ ```
30
+ killarney fir tamia
31
+ ╸━━━━━━━━━╺━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
32
+ someone@klogin02 killarney Slurm 25.05.9 updated 13:49:06 (10s ago) · probed 13:49:06
33
+ ╭─ Partitions ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
34
+ │ Partition Use CPU/node Mem/node GPU/node Load Free Idle Pend Max │
35
+ │ gpubase_h100_b1 yes 48/96 2011G 8x h100 ██████▒▒ 75% 4/80 h100 0/10 117 0-03:00:00 │
36
+ │ gpubase_h100_b2 yes 48/96 2011G 8x h100 ███████▒ 86% 1/64 h100 0/8 63 0-12:00:00 │
37
+ │ gpubase_interac yes 64 502G 4x l40s ███████░ 90% 6/100 l40s 0/25 1 0-03:00:00 │
38
+ │ gpubase_l40s_b1 yes 64 502G 4x l40s ███████▒ 90% 18/672 l40s 1/168 2571 0-03:00:00 │
39
+ │ gpubase_l40s_b3 yes 64 502G 4x l40s ███████▒ 88% 8/336 l40s 1/84 90 1-00:00:00 │
40
+ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
41
+ ╭─ Start estimates · aip-abc123/normal ──────────────────────────────────────────────────╮╭─ Accounts & QOS ─────────────────────────────────────────╮
42
+ │ Request 0-03:00:00 0-12:00:00 1-00:00:00 3-00:00:00 7-00:00:00 ││ Account QOS Jobs GPUs Wall FS │
43
+ │ 1x h100 0-22:05:00 1-05:38:00 3-02:01:00 7-23:35:00 39-23:02:00 ││ aip-abc123* interac 0/1 ∞ 0-03:00:00 0.14 │
44
+ │ 8x h100 1-04:16:00 1-10:21:00 3-06:44:00 8-11:56:00 40-05:13:00 ││ aip-abc123* normal* 0/∞ ∞ ∞ 0.14 │
45
+ │ 1x l40s 0-02:12:00 0-01:27:00 0-01:48:00 0-19:17:00 7-15:47:00 │╰────────────────── * left out of commands · enter: probe ─╯
46
+ │ 4x l40s 0-02:12:00 0-02:17:00 0-01:48:00 0-21:38:00 7-18:03:00 │╭─ My jobs ────────────────────────────────────────────────╮
47
+ │ CPU only 0-02:12:00 0-01:27:00 0-01:48:00 0-19:17:00 7-15:47:00 ││ Job Name St Start Time Nodes Reason │
48
+ │ ││ No jobs in the queue │
49
+ ╰───────────────────────────────────────────────────────────────── sbatch -c4 --mem=32G ─╯╰──────────────────────────────────────────────────────────╯
50
+ ╭─ Details ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
51
+ │ gpubase_h100_b1 UP usable · max time 0-03:00:00 · priority tier 1 │
52
+ │ Nodes 10 (5 mixed, 3 allocated, 2 unavailable) · pending jobs 117 │
53
+ │ 9 nodes: 48 CPUs / 2011 GB / 8x h100 · 1 node: 96 CPUs / 2011 GB / 8x h100 │
54
+ │ CPUs 104 free / 328 allocated / 96 unavailable of 528 · Memory 12045 GB free of 20117 GB │
55
+ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
56
+ Refresh Re-probe Edit request Email: off Config Quit
57
+ ```
58
+
59
+ ## Requirements
60
+
61
+ - [uv](https://docs.astral.sh/uv/) (it fetches Python 3.13 if you do not have it)
62
+ - A Slurm login node (`scontrol` and `squeue` on `PATH`; estimates need `sbatch`/`srun`)
63
+
64
+ ## Usage
65
+
66
+ ```bash
67
+ uvx sqtop # run it from PyPI: one tab per cluster, this one first
68
+ uvx sqtop > report.txt # text report of this cluster when not a terminal
69
+ uv tool install sqtop # or install it once, then just type `sqtop`
70
+
71
+ # From a checkout
72
+ uv run sqtop
73
+ ```
74
+
75
+ There are no options. The first tab is the cluster you are logged in to (from
76
+ `ClusterName` in `scontrol show config`), and it is the only one checked at startup.
77
+ Every other cluster with an `ssh` host in the [config file](#configuration) (Fir,
78
+ Killarney and Tamia) gets a tab too, but is only contacted, over ssh, once you open its
79
+ tab (click it). Every probe starts as 4 CPUs and 32 GB at each partition
80
+ time limit with `sbatch`; change a tab's request with `Edit request` (CPUs, memory,
81
+ walltimes, extra `sbatch` options), and click the command under Start estimates to
82
+ switch between `sbatch` and `srun`. The visible tab refreshes every minute and re-probes
83
+ every five. Click `Email: off` to email the report every hour until you quit
84
+ ([Hourly email](#hourly-email)). Everything is a click: see [Mouse](#mouse).
85
+
86
+ Probes use `--test-only`, so nothing is submitted or cancelled until you confirm a
87
+ Submit or Release (see below), and it is safe to run on a shared login node. Run it on
88
+ the login node: on a compute node, such as inside an interactive job, `srun` probes fail
89
+ with "Requested operation is presently disabled".
90
+
91
+ ### Remote clusters
92
+
93
+ Remote tabs run Slurm on the cluster's login node through `ssh`, with your
94
+ `~/.ssh/config` (aliases, users, keys). Nothing is sent to a remote cluster until you
95
+ open its tab, and only the visible tab refreshes.
96
+
97
+ sqtop never asks for a password or MFA. For a cluster that needs one, let ssh share
98
+ connections and log in with `ssh` in another terminal first; sqtop reuses that
99
+ connection:
100
+
101
+ ```
102
+ # ~/.ssh/config
103
+ Host *.alliancecan.ca
104
+ ControlMaster auto
105
+ ControlPath ~/.ssh/cm-%C
106
+ ControlPersist 4h
107
+ ```
108
+
109
+ After `ssh fir.alliancecan.ca`, the connection stays open for 4 hours after your last
110
+ session there closes. With keys that log in without a prompt, nothing is needed:
111
+ sqtop opens its own shared connection, which closes 10 minutes after the last use.
112
+ A tab that cannot connect shows ssh's error and how to connect; then click `Refresh`.
113
+
114
+ The text report printed when output is not a terminal covers the cluster you are on;
115
+ it covers the remote ones only where Slurm is not available.
116
+
117
+ Each refresh of a remote tab is one ssh session, and the cache of limits is fetched
118
+ only for your accounts and QOS, because login nodes can take seconds to start a
119
+ session and the full cache can be tens of MB. Commands for a remote cluster are shown
120
+ with its host as the prompt (`fir.alliancecan.ca$ sbatch ...`): run them there.
121
+
122
+ ### Mouse
123
+
124
+ sqtop has no keyboard shortcuts: everything is a click. The bar at the bottom holds
125
+ the dashboard's actions, and each dialog closes with its own buttons.
126
+
127
+ | Click | Action |
128
+ | --- | --- |
129
+ | a cluster tab | Show that cluster |
130
+ | a row or cell | Select it; the Details panel follows the selection |
131
+ | a highlighted Accounts & QOS row | Probe with that account/QOS |
132
+ | the Partitions title | Switch between Partitions and Nodes by hardware |
133
+ | the account/QOS above the estimates | Probe with the next account/QOS |
134
+ | the command under Start estimates | Switch between `sbatch` and `srun` probes |
135
+ | `Submit` in Details | Submit the selected `sbatch` request, after you confirm |
136
+ | `Copy` after a command in Details | Copy that command |
137
+ | `Release` in a job's Details | Release the job with `scancel`, after you confirm |
138
+ | `Refresh` / `Re-probe` | Refresh everything / re-probe now |
139
+ | `Edit request` | Edit the probe request: whole node, CPUs, memory, walltimes, extra options such as `--partition` or `--constraint`, and the email address (blank for none) |
140
+ | `Email: off` / `Email: on` | Email the report now and every hour until you quit / stop; the label says which is on |
141
+ | `Config` | Edit the [config](#configuration) in a form; saving writes the file and applies it |
142
+ | `Quit` | Quit |
143
+
144
+ ## Reading the dashboard
145
+
146
+ - **Partitions**: `Use` says whether your accounts, QOS and Unix groups may submit
147
+ there. `CPU/node`, `Mem/node` and `GPU/node` are each node's hardware (CPUs exclude
148
+ reserved cores); a partition with two kinds of node shows both (`48/96`), with more
149
+ a range. `Load` is the share of GPUs (CPUs on CPU-only partitions) in use: `█`
150
+ allocated, `▒` unavailable (down, drained, reserved), `░` free. `Free` is free/total
151
+ per GPU type, `Idle` counts wholly idle nodes, `Pend` counts pending jobs from all
152
+ users, and `Max` is the time limit. Details list each kind of node with its count,
153
+ then memory, `AllowAccounts`/`AllowQos`, the partition QOS and priority tier.
154
+ - **Accounts & QOS**: every account/QOS pair you can submit under; `*` marks what
155
+ commands leave out because Slurm picks it anyway: the account when it is your only
156
+ one (with several, commands always name it, since sites such as Fir require that
157
+ for GPU jobs) and the account's default QOS. `Jobs` is your running jobs over the tightest per-user
158
+ cap, `GPUs` and `Wall` are the tightest GPU and wall-time caps from any scope, and
159
+ `FS` is your fair-share factor. Details list every cap with its usage: QOS per user,
160
+ per account, per job and per node, plus association limits on you and your account.
161
+ `∞` means no cap, `?` unknown.
162
+ - **Start estimates** (titled with the account/QOS probed): rows are request sizes
163
+ (each GPU type up to the tightest GPU cap, then CPU only), columns are walltimes. Each
164
+ cell is how long until the scheduler expects the job to start: `now`, a wait such as
165
+ `0-10:54:00`, `no` when it cannot run (Details gives the reason), or `-` above the
166
+ account/QOS wall-time cap. The earliest start in each row is underlined. Here a 1-day
167
+ L40S job starts sooner than a 3-hour one, because short jobs queue in the busier `b1`
168
+ partition.
169
+ - **Details** for an estimate shows the partition it would land in, the probe that
170
+ produced the estimate with the scheduler's reply, and the command to submit the same
171
+ request; click `Copy` after either to copy it. The probe has only what the estimate
172
+ depends on; the submit command drops `--test-only` and adds your email options and
173
+ `--output=/dev/null`, so no `slurm-<jobid>.out` file is left behind.
174
+ `--account`/`--qos` are left out when Slurm would pick them anyway.
175
+ - **Submit**: click the `Submit` label in Details to run an `sbatch` command, after a
176
+ dialog shows it and asks you to confirm; `Cancel` submits nothing. The job is submitted in the background (on the cluster's login node
177
+ for a remote tab), its id pops up, and My jobs refreshes; it holds the allocation with
178
+ `sleep infinity` until the time limit; its details in My jobs show the `scancel`
179
+ command that releases it. An `srun` command opens an interactive shell, so `Submit`
180
+ is not clickable for it: click `Copy` and run it in a terminal. A request the probe
181
+ was refused for is not submitted.
182
+ - **My jobs** lists your queued and running jobs: `Partition` is where it runs or was
183
+ submitted to (several for some pending jobs), `Start` is when a job started, or when
184
+ the scheduler expects a pending one to (`-` until it has planned it), `Nodes` is where
185
+ it runs, or the nodes planned for a pending job (dimmed, as they can still change),
186
+ and `Reason` is why a pending job waits. A job's details show the command that
187
+ attaches a shell to it (`srun --jobid=<id> --overlap --pty <shell>`, once it runs)
188
+ and the one that releases it (`scancel <id>`), each with `Copy`. Clicking `Release`
189
+ runs `scancel` after a dialog asks you to confirm (Cancel is selected first): a
190
+ running job is killed and its allocation released, a pending one leaves the queue.
191
+
192
+ ## Configuration
193
+
194
+ All settings live in one file, `~/.config/sqtop/config.toml`
195
+ (`$XDG_CONFIG_HOME/sqtop/config.toml` if you set that): email (`[mail]`, `[smtp]`)
196
+ and cluster profiles (`[clusters.<name>]`). Click `Config` in the dashboard to edit it
197
+ in a form: the job email address and states, the hourly email's server, port, sender,
198
+ password and recipient, and one row per cluster (name, GPU counts picked from `pow2`,
199
+ `all` and `whole_node`, and ssh host) with `Add cluster` and `Remove`. `Cancel` closes
200
+ it unchanged. `Save` checks the settings (a problem is shown under the form, which stays
201
+ open), writes the file privately (`chmod 600`, since it holds the SMTP password) and
202
+ applies what changed without a restart: clusters added get tabs and removed ones lose
203
+ theirs, changed profiles and `[mail]` settings are probed again, and the hourly email
204
+ switches to a changed `[smtp]`.
205
+
206
+ Without the file, `sqtop` shows only the cluster you are logged in to, with no email
207
+ settings. The file is plain TOML, so you can also edit it by hand; the form rewrites it
208
+ without comments.
209
+
210
+ ### Other clusters
211
+
212
+ Nothing is hardcoded per cluster: partitions, GPU types, time tiers, accounts and
213
+ limits are read from Slurm. A profile records only what Slurm cannot report, keyed by
214
+ the cluster name. To add a cluster, or a tab for one you are not logged in to, add a
215
+ row in the config form (`Config`); in the file it is a table:
216
+
217
+ ```toml
218
+ [clusters.mycluster] # the ClusterName from `scontrol show config`
219
+ gpu_counts = "all" # "pow2" (1, 2, 4, ... default), "all" or "whole_node"
220
+ # (GPU jobs then take whole nodes: --exclusive --mem=0)
221
+ ssh = "login.mycluster.org" # a tab for it from anywhere; host or ~/.ssh/config alias
222
+ ```
223
+
224
+ For example, the Alliance clusters this was written on need no rules, only an `ssh`
225
+ host for a tab, except `tamia`, which allocates GPUs by whole node:
226
+
227
+ ```toml
228
+ [clusters.fir]
229
+ ssh = "fir.alliancecan.ca"
230
+
231
+ [clusters.killarney]
232
+ ssh = "killarney.alliancecan.ca"
233
+
234
+ [clusters.tamia]
235
+ ssh = "tamia.alliancecan.ca"
236
+ gpu_counts = "whole_node"
237
+ ```
238
+
239
+ The walltimes probed are not a setting: they are the distinct time limits of the
240
+ partitions you can use, which is where routing changes, and `Edit request` overrides
241
+ them for a session.
242
+
243
+ ### Whole nodes
244
+
245
+ Tick "Whole node" in the request editor (`Edit request`) to probe whole nodes: every command then
246
+ asks for `--exclusive --mem=0` (every CPU and all the memory) instead of `-c`/`--mem`,
247
+ and the GPU rows are each type's full node, such as `8x h100` or `4x l40s`. Untick it
248
+ to go back to the CPUs and memory in the editor.
249
+
250
+ On clusters that only allocate GPUs by node (`gpu_counts = "whole_node"`, such as
251
+ Tamia) the GPU rows always ask for the whole node this way, so a job that holds all of
252
+ a node's GPUs also gets its CPUs and memory; the CPU-only row keeps the editor's
253
+ values. The estimates panel then notes "GPUs by whole node".
254
+
255
+ ### Email notifications
256
+
257
+ The `[mail]` table in your config (`Config`) adds `--mail-type` and `--mail-user` to every
258
+ command, so a job you submit from one emails you when it starts, ends or fails:
259
+
260
+ ```toml
261
+ [mail]
262
+ user = "you@example.com"
263
+ type = "BEGIN,END,FAIL" # any of sbatch's --mail-type values
264
+ ```
265
+
266
+ Only the commands to submit carry them: probes leave them out, since email does not
267
+ change when a job would start. Remove the table to leave email out, or clear the
268
+ address for a session with `Edit request`.
269
+
270
+ ### Hourly email
271
+
272
+ Click `Email: off` in the dashboard to email the report now and every hour until you
273
+ quit `sqtop` (or click `Email: on` to stop). Each email covers the cluster you are on and every
274
+ remote tab you have opened, with each tab's current request. Every email has the subject
275
+ `sqtop` and replies to the first one, so your mail client keeps them in one
276
+ conversation instead of separate emails. Each starts with your job counts and what
277
+ happened since the previous email, followed by the text report:
278
+
279
+ ```
280
+ killarney: 1 running, 2 pending. Since 14:00:
281
+ 6284538 (wrap) started on kn021
282
+ 6233018 (wrap) ended: TIMEOUT
283
+ 6290112 (eval) submitted (Priority), expected to start 10-17 09:30
284
+ ```
285
+
286
+ The server is read from `[smtp]` in your config (`Config`) when you turn it on:
287
+
288
+ ```toml
289
+ [smtp]
290
+ server = "mail.example.org"
291
+ port = 587 # STARTTLS; 465 for SSL
292
+ sender = "you@example.org" # also the login
293
+ password = "..."
294
+ recipient = "you@example.org" # default: user in [mail]
295
+ ```
296
+
297
+ The bottom bar says whether the hourly email is on (`Email: on`) or off (`Email: off`).
298
+ While it is on, the top bar shows when the next email goes out (`next email 16:37`),
299
+ and marks it `(last failed)` if one could not be sent. The thread and the jobs last reported are kept
300
+ in `~/.local/state/sqtop/mail.json`, so the next run of the dashboard continues the
301
+ same conversation; delete the file to start a new one. Gmail starts a new conversation
302
+ after 100 emails, about every four days.
303
+
304
+ ### When `sacctmgr` fails
305
+
306
+ On some login nodes `sacctmgr` fails with "Connection refused" because `slurmdbd` runs
307
+ on the controller. Accounting is still enforced: `sqtop` reads limits from
308
+ `scontrol show assoc_mgr` instead. Without `sacctmgr` it cannot list your QOS
309
+ assignments, so it test-submits each cached account/QOS pair and hides the ones Slurm
310
+ rejects.
311
+
312
+ ## Development
313
+
314
+ ```bash
315
+ uv sync
316
+ uv run python -m unittest discover -s tests -t tests
317
+ uvx ruff check src tests
318
+ ```
319
+
320
+ See [AGENTS.md](AGENTS.md) for the code layout and site-specific Slurm quirks.
321
+
322
+ ## License
323
+
324
+ MIT; see [LICENSE](LICENSE).
sqtop-0.1.0/README.md ADDED
@@ -0,0 +1,304 @@
1
+ # sqtop
2
+
3
+ A terminal dashboard for Slurm, in the spirit of
4
+ [hunoutl/slurmtop](https://github.com/hunoutl/slurmtop) and
5
+ [slmtop](https://github.com/dawnmy/slmtop), that answers "where can I run, under which
6
+ account/QOS, and when would it start?", and can email you that report every hour while
7
+ it runs.
8
+
9
+ ```
10
+ killarney fir tamia
11
+ ╸━━━━━━━━━╺━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
12
+ someone@klogin02 killarney Slurm 25.05.9 updated 13:49:06 (10s ago) · probed 13:49:06
13
+ ╭─ Partitions ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
14
+ │ Partition Use CPU/node Mem/node GPU/node Load Free Idle Pend Max │
15
+ │ gpubase_h100_b1 yes 48/96 2011G 8x h100 ██████▒▒ 75% 4/80 h100 0/10 117 0-03:00:00 │
16
+ │ gpubase_h100_b2 yes 48/96 2011G 8x h100 ███████▒ 86% 1/64 h100 0/8 63 0-12:00:00 │
17
+ │ gpubase_interac yes 64 502G 4x l40s ███████░ 90% 6/100 l40s 0/25 1 0-03:00:00 │
18
+ │ gpubase_l40s_b1 yes 64 502G 4x l40s ███████▒ 90% 18/672 l40s 1/168 2571 0-03:00:00 │
19
+ │ gpubase_l40s_b3 yes 64 502G 4x l40s ███████▒ 88% 8/336 l40s 1/84 90 1-00:00:00 │
20
+ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
21
+ ╭─ Start estimates · aip-abc123/normal ──────────────────────────────────────────────────╮╭─ Accounts & QOS ─────────────────────────────────────────╮
22
+ │ Request 0-03:00:00 0-12:00:00 1-00:00:00 3-00:00:00 7-00:00:00 ││ Account QOS Jobs GPUs Wall FS │
23
+ │ 1x h100 0-22:05:00 1-05:38:00 3-02:01:00 7-23:35:00 39-23:02:00 ││ aip-abc123* interac 0/1 ∞ 0-03:00:00 0.14 │
24
+ │ 8x h100 1-04:16:00 1-10:21:00 3-06:44:00 8-11:56:00 40-05:13:00 ││ aip-abc123* normal* 0/∞ ∞ ∞ 0.14 │
25
+ │ 1x l40s 0-02:12:00 0-01:27:00 0-01:48:00 0-19:17:00 7-15:47:00 │╰────────────────── * left out of commands · enter: probe ─╯
26
+ │ 4x l40s 0-02:12:00 0-02:17:00 0-01:48:00 0-21:38:00 7-18:03:00 │╭─ My jobs ────────────────────────────────────────────────╮
27
+ │ CPU only 0-02:12:00 0-01:27:00 0-01:48:00 0-19:17:00 7-15:47:00 ││ Job Name St Start Time Nodes Reason │
28
+ │ ││ No jobs in the queue │
29
+ ╰───────────────────────────────────────────────────────────────── sbatch -c4 --mem=32G ─╯╰──────────────────────────────────────────────────────────╯
30
+ ╭─ Details ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
31
+ │ gpubase_h100_b1 UP usable · max time 0-03:00:00 · priority tier 1 │
32
+ │ Nodes 10 (5 mixed, 3 allocated, 2 unavailable) · pending jobs 117 │
33
+ │ 9 nodes: 48 CPUs / 2011 GB / 8x h100 · 1 node: 96 CPUs / 2011 GB / 8x h100 │
34
+ │ CPUs 104 free / 328 allocated / 96 unavailable of 528 · Memory 12045 GB free of 20117 GB │
35
+ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
36
+ Refresh Re-probe Edit request Email: off Config Quit
37
+ ```
38
+
39
+ ## Requirements
40
+
41
+ - [uv](https://docs.astral.sh/uv/) (it fetches Python 3.13 if you do not have it)
42
+ - A Slurm login node (`scontrol` and `squeue` on `PATH`; estimates need `sbatch`/`srun`)
43
+
44
+ ## Usage
45
+
46
+ ```bash
47
+ uvx sqtop # run it from PyPI: one tab per cluster, this one first
48
+ uvx sqtop > report.txt # text report of this cluster when not a terminal
49
+ uv tool install sqtop # or install it once, then just type `sqtop`
50
+
51
+ # From a checkout
52
+ uv run sqtop
53
+ ```
54
+
55
+ There are no options. The first tab is the cluster you are logged in to (from
56
+ `ClusterName` in `scontrol show config`), and it is the only one checked at startup.
57
+ Every other cluster with an `ssh` host in the [config file](#configuration) (Fir,
58
+ Killarney and Tamia) gets a tab too, but is only contacted, over ssh, once you open its
59
+ tab (click it). Every probe starts as 4 CPUs and 32 GB at each partition
60
+ time limit with `sbatch`; change a tab's request with `Edit request` (CPUs, memory,
61
+ walltimes, extra `sbatch` options), and click the command under Start estimates to
62
+ switch between `sbatch` and `srun`. The visible tab refreshes every minute and re-probes
63
+ every five. Click `Email: off` to email the report every hour until you quit
64
+ ([Hourly email](#hourly-email)). Everything is a click: see [Mouse](#mouse).
65
+
66
+ Probes use `--test-only`, so nothing is submitted or cancelled until you confirm a
67
+ Submit or Release (see below), and it is safe to run on a shared login node. Run it on
68
+ the login node: on a compute node, such as inside an interactive job, `srun` probes fail
69
+ with "Requested operation is presently disabled".
70
+
71
+ ### Remote clusters
72
+
73
+ Remote tabs run Slurm on the cluster's login node through `ssh`, with your
74
+ `~/.ssh/config` (aliases, users, keys). Nothing is sent to a remote cluster until you
75
+ open its tab, and only the visible tab refreshes.
76
+
77
+ sqtop never asks for a password or MFA. For a cluster that needs one, let ssh share
78
+ connections and log in with `ssh` in another terminal first; sqtop reuses that
79
+ connection:
80
+
81
+ ```
82
+ # ~/.ssh/config
83
+ Host *.alliancecan.ca
84
+ ControlMaster auto
85
+ ControlPath ~/.ssh/cm-%C
86
+ ControlPersist 4h
87
+ ```
88
+
89
+ After `ssh fir.alliancecan.ca`, the connection stays open for 4 hours after your last
90
+ session there closes. With keys that log in without a prompt, nothing is needed:
91
+ sqtop opens its own shared connection, which closes 10 minutes after the last use.
92
+ A tab that cannot connect shows ssh's error and how to connect; then click `Refresh`.
93
+
94
+ The text report printed when output is not a terminal covers the cluster you are on;
95
+ it covers the remote ones only where Slurm is not available.
96
+
97
+ Each refresh of a remote tab is one ssh session, and the cache of limits is fetched
98
+ only for your accounts and QOS, because login nodes can take seconds to start a
99
+ session and the full cache can be tens of MB. Commands for a remote cluster are shown
100
+ with its host as the prompt (`fir.alliancecan.ca$ sbatch ...`): run them there.
101
+
102
+ ### Mouse
103
+
104
+ sqtop has no keyboard shortcuts: everything is a click. The bar at the bottom holds
105
+ the dashboard's actions, and each dialog closes with its own buttons.
106
+
107
+ | Click | Action |
108
+ | --- | --- |
109
+ | a cluster tab | Show that cluster |
110
+ | a row or cell | Select it; the Details panel follows the selection |
111
+ | a highlighted Accounts & QOS row | Probe with that account/QOS |
112
+ | the Partitions title | Switch between Partitions and Nodes by hardware |
113
+ | the account/QOS above the estimates | Probe with the next account/QOS |
114
+ | the command under Start estimates | Switch between `sbatch` and `srun` probes |
115
+ | `Submit` in Details | Submit the selected `sbatch` request, after you confirm |
116
+ | `Copy` after a command in Details | Copy that command |
117
+ | `Release` in a job's Details | Release the job with `scancel`, after you confirm |
118
+ | `Refresh` / `Re-probe` | Refresh everything / re-probe now |
119
+ | `Edit request` | Edit the probe request: whole node, CPUs, memory, walltimes, extra options such as `--partition` or `--constraint`, and the email address (blank for none) |
120
+ | `Email: off` / `Email: on` | Email the report now and every hour until you quit / stop; the label says which is on |
121
+ | `Config` | Edit the [config](#configuration) in a form; saving writes the file and applies it |
122
+ | `Quit` | Quit |
123
+
124
+ ## Reading the dashboard
125
+
126
+ - **Partitions**: `Use` says whether your accounts, QOS and Unix groups may submit
127
+ there. `CPU/node`, `Mem/node` and `GPU/node` are each node's hardware (CPUs exclude
128
+ reserved cores); a partition with two kinds of node shows both (`48/96`), with more
129
+ a range. `Load` is the share of GPUs (CPUs on CPU-only partitions) in use: `█`
130
+ allocated, `▒` unavailable (down, drained, reserved), `░` free. `Free` is free/total
131
+ per GPU type, `Idle` counts wholly idle nodes, `Pend` counts pending jobs from all
132
+ users, and `Max` is the time limit. Details list each kind of node with its count,
133
+ then memory, `AllowAccounts`/`AllowQos`, the partition QOS and priority tier.
134
+ - **Accounts & QOS**: every account/QOS pair you can submit under; `*` marks what
135
+ commands leave out because Slurm picks it anyway: the account when it is your only
136
+ one (with several, commands always name it, since sites such as Fir require that
137
+ for GPU jobs) and the account's default QOS. `Jobs` is your running jobs over the tightest per-user
138
+ cap, `GPUs` and `Wall` are the tightest GPU and wall-time caps from any scope, and
139
+ `FS` is your fair-share factor. Details list every cap with its usage: QOS per user,
140
+ per account, per job and per node, plus association limits on you and your account.
141
+ `∞` means no cap, `?` unknown.
142
+ - **Start estimates** (titled with the account/QOS probed): rows are request sizes
143
+ (each GPU type up to the tightest GPU cap, then CPU only), columns are walltimes. Each
144
+ cell is how long until the scheduler expects the job to start: `now`, a wait such as
145
+ `0-10:54:00`, `no` when it cannot run (Details gives the reason), or `-` above the
146
+ account/QOS wall-time cap. The earliest start in each row is underlined. Here a 1-day
147
+ L40S job starts sooner than a 3-hour one, because short jobs queue in the busier `b1`
148
+ partition.
149
+ - **Details** for an estimate shows the partition it would land in, the probe that
150
+ produced the estimate with the scheduler's reply, and the command to submit the same
151
+ request; click `Copy` after either to copy it. The probe has only what the estimate
152
+ depends on; the submit command drops `--test-only` and adds your email options and
153
+ `--output=/dev/null`, so no `slurm-<jobid>.out` file is left behind.
154
+ `--account`/`--qos` are left out when Slurm would pick them anyway.
155
+ - **Submit**: click the `Submit` label in Details to run an `sbatch` command, after a
156
+ dialog shows it and asks you to confirm; `Cancel` submits nothing. The job is submitted in the background (on the cluster's login node
157
+ for a remote tab), its id pops up, and My jobs refreshes; it holds the allocation with
158
+ `sleep infinity` until the time limit; its details in My jobs show the `scancel`
159
+ command that releases it. An `srun` command opens an interactive shell, so `Submit`
160
+ is not clickable for it: click `Copy` and run it in a terminal. A request the probe
161
+ was refused for is not submitted.
162
+ - **My jobs** lists your queued and running jobs: `Partition` is where it runs or was
163
+ submitted to (several for some pending jobs), `Start` is when a job started, or when
164
+ the scheduler expects a pending one to (`-` until it has planned it), `Nodes` is where
165
+ it runs, or the nodes planned for a pending job (dimmed, as they can still change),
166
+ and `Reason` is why a pending job waits. A job's details show the command that
167
+ attaches a shell to it (`srun --jobid=<id> --overlap --pty <shell>`, once it runs)
168
+ and the one that releases it (`scancel <id>`), each with `Copy`. Clicking `Release`
169
+ runs `scancel` after a dialog asks you to confirm (Cancel is selected first): a
170
+ running job is killed and its allocation released, a pending one leaves the queue.
171
+
172
+ ## Configuration
173
+
174
+ All settings live in one file, `~/.config/sqtop/config.toml`
175
+ (`$XDG_CONFIG_HOME/sqtop/config.toml` if you set that): email (`[mail]`, `[smtp]`)
176
+ and cluster profiles (`[clusters.<name>]`). Click `Config` in the dashboard to edit it
177
+ in a form: the job email address and states, the hourly email's server, port, sender,
178
+ password and recipient, and one row per cluster (name, GPU counts picked from `pow2`,
179
+ `all` and `whole_node`, and ssh host) with `Add cluster` and `Remove`. `Cancel` closes
180
+ it unchanged. `Save` checks the settings (a problem is shown under the form, which stays
181
+ open), writes the file privately (`chmod 600`, since it holds the SMTP password) and
182
+ applies what changed without a restart: clusters added get tabs and removed ones lose
183
+ theirs, changed profiles and `[mail]` settings are probed again, and the hourly email
184
+ switches to a changed `[smtp]`.
185
+
186
+ Without the file, `sqtop` shows only the cluster you are logged in to, with no email
187
+ settings. The file is plain TOML, so you can also edit it by hand; the form rewrites it
188
+ without comments.
189
+
190
+ ### Other clusters
191
+
192
+ Nothing is hardcoded per cluster: partitions, GPU types, time tiers, accounts and
193
+ limits are read from Slurm. A profile records only what Slurm cannot report, keyed by
194
+ the cluster name. To add a cluster, or a tab for one you are not logged in to, add a
195
+ row in the config form (`Config`); in the file it is a table:
196
+
197
+ ```toml
198
+ [clusters.mycluster] # the ClusterName from `scontrol show config`
199
+ gpu_counts = "all" # "pow2" (1, 2, 4, ... default), "all" or "whole_node"
200
+ # (GPU jobs then take whole nodes: --exclusive --mem=0)
201
+ ssh = "login.mycluster.org" # a tab for it from anywhere; host or ~/.ssh/config alias
202
+ ```
203
+
204
+ For example, the Alliance clusters this was written on need no rules, only an `ssh`
205
+ host for a tab, except `tamia`, which allocates GPUs by whole node:
206
+
207
+ ```toml
208
+ [clusters.fir]
209
+ ssh = "fir.alliancecan.ca"
210
+
211
+ [clusters.killarney]
212
+ ssh = "killarney.alliancecan.ca"
213
+
214
+ [clusters.tamia]
215
+ ssh = "tamia.alliancecan.ca"
216
+ gpu_counts = "whole_node"
217
+ ```
218
+
219
+ The walltimes probed are not a setting: they are the distinct time limits of the
220
+ partitions you can use, which is where routing changes, and `Edit request` overrides
221
+ them for a session.
222
+
223
+ ### Whole nodes
224
+
225
+ Tick "Whole node" in the request editor (`Edit request`) to probe whole nodes: every command then
226
+ asks for `--exclusive --mem=0` (every CPU and all the memory) instead of `-c`/`--mem`,
227
+ and the GPU rows are each type's full node, such as `8x h100` or `4x l40s`. Untick it
228
+ to go back to the CPUs and memory in the editor.
229
+
230
+ On clusters that only allocate GPUs by node (`gpu_counts = "whole_node"`, such as
231
+ Tamia) the GPU rows always ask for the whole node this way, so a job that holds all of
232
+ a node's GPUs also gets its CPUs and memory; the CPU-only row keeps the editor's
233
+ values. The estimates panel then notes "GPUs by whole node".
234
+
235
+ ### Email notifications
236
+
237
+ The `[mail]` table in your config (`Config`) adds `--mail-type` and `--mail-user` to every
238
+ command, so a job you submit from one emails you when it starts, ends or fails:
239
+
240
+ ```toml
241
+ [mail]
242
+ user = "you@example.com"
243
+ type = "BEGIN,END,FAIL" # any of sbatch's --mail-type values
244
+ ```
245
+
246
+ Only the commands to submit carry them: probes leave them out, since email does not
247
+ change when a job would start. Remove the table to leave email out, or clear the
248
+ address for a session with `Edit request`.
249
+
250
+ ### Hourly email
251
+
252
+ Click `Email: off` in the dashboard to email the report now and every hour until you
253
+ quit `sqtop` (or click `Email: on` to stop). Each email covers the cluster you are on and every
254
+ remote tab you have opened, with each tab's current request. Every email has the subject
255
+ `sqtop` and replies to the first one, so your mail client keeps them in one
256
+ conversation instead of separate emails. Each starts with your job counts and what
257
+ happened since the previous email, followed by the text report:
258
+
259
+ ```
260
+ killarney: 1 running, 2 pending. Since 14:00:
261
+ 6284538 (wrap) started on kn021
262
+ 6233018 (wrap) ended: TIMEOUT
263
+ 6290112 (eval) submitted (Priority), expected to start 10-17 09:30
264
+ ```
265
+
266
+ The server is read from `[smtp]` in your config (`Config`) when you turn it on:
267
+
268
+ ```toml
269
+ [smtp]
270
+ server = "mail.example.org"
271
+ port = 587 # STARTTLS; 465 for SSL
272
+ sender = "you@example.org" # also the login
273
+ password = "..."
274
+ recipient = "you@example.org" # default: user in [mail]
275
+ ```
276
+
277
+ The bottom bar says whether the hourly email is on (`Email: on`) or off (`Email: off`).
278
+ While it is on, the top bar shows when the next email goes out (`next email 16:37`),
279
+ and marks it `(last failed)` if one could not be sent. The thread and the jobs last reported are kept
280
+ in `~/.local/state/sqtop/mail.json`, so the next run of the dashboard continues the
281
+ same conversation; delete the file to start a new one. Gmail starts a new conversation
282
+ after 100 emails, about every four days.
283
+
284
+ ### When `sacctmgr` fails
285
+
286
+ On some login nodes `sacctmgr` fails with "Connection refused" because `slurmdbd` runs
287
+ on the controller. Accounting is still enforced: `sqtop` reads limits from
288
+ `scontrol show assoc_mgr` instead. Without `sacctmgr` it cannot list your QOS
289
+ assignments, so it test-submits each cached account/QOS pair and hides the ones Slurm
290
+ rejects.
291
+
292
+ ## Development
293
+
294
+ ```bash
295
+ uv sync
296
+ uv run python -m unittest discover -s tests -t tests
297
+ uvx ruff check src tests
298
+ ```
299
+
300
+ See [AGENTS.md](AGENTS.md) for the code layout and site-specific Slurm quirks.
301
+
302
+ ## License
303
+
304
+ MIT; see [LICENSE](LICENSE).