Skip to content

Report run status from Slurm exit code in SlurmContainer#964

Draft
malagows-nvd wants to merge 1 commit into
NVIDIA:mainfrom
malagows-nvd:mm/slurmcontainer-add-wasrunsuccessful
Draft

Report run status from Slurm exit code in SlurmContainer#964
malagows-nvd wants to merge 1 commit into
NVIDIA:mainfrom
malagows-nvd:mm/slurmcontainer-add-wasrunsuccessful

Conversation

@malagows-nvd

Copy link
Copy Markdown

Grade the run via was_run_successful() - currently based on the exit_code field in slurm-job.toml (non-zero = FAILED).

Summary

Every run of SlurmContainer workload was reported PASSED in the scenario summary regardless of what the
container actually did. Grade the run from the container real exit code, which CloudAI already records in slurm-job.toml

Test Plan

Scenario TOML
name = "Verify.SlurmContainer.StatusCheck"
job_status_check = false

[[Tests]]
id = "Verify.SlurmContainer.Pass"
name = "slurmcontainer-pass"
description = "Should be reported PASSED (exit 0)"
test_template_name = "SlurmContainer"
num_nodes = 1
time_limit = "00:05:00"
extra_srun_args = "--ntasks-per-node=1"

  [Tests.cmd_args]
  docker_image_url = "nvcr.io#nvidia/cuda:12.9.1-devel-ubuntu24.04"
  cmd = "bash -c 'echo PASS-CASE; exit 0'"

[[Tests]]
id = "Verify.SlurmContainer.Fail"
name = "slurmcontainer-fail"
description = "Should be reported FAILED (exit 42)"
test_template_name = "SlurmContainer"
num_nodes = 1
time_limit = "00:05:00"
extra_srun_args = "--ntasks-per-node=1"

  [Tests.cmd_args]
  docker_image_url = "nvcr.io#nvidia/cuda:12.9.1-devel-ubuntu24.04"
  cmd = "bash -c 'echo FAIL-CASE; exit 42'"
CloudAI run
[INFO] System Name: ...
[INFO] Scheduler: slurm
[INFO] Test Scenario Name: Verify.SlurmContainer.StatusCheck
[INFO] Checking if workloads components are installed.
[INFO] Test Scenario: Verify.SlurmContainer.StatusCheck

Section Name: Verify.SlurmContainer.Pass
  Test Name: slurmcontainer-pass
  Description: Should be reported PASSED (exit 0)
  No dependencies
Section Name: Verify.SlurmContainer.Fail
  Test Name: slurmcontainer-fail
  Description: Should be reported FAILED (exit 42)
  No dependencies
[INFO] Initializing Runner [RUN] mode
[INFO] Creating SlurmRunner
[INFO] Scenario results will be stored at: results/Verify.SlurmContainer.StatusCheck_2026-07-08_15-13-24
[INFO] Starting test: Verify.SlurmContainer.Pass (results at: results/Verify.SlurmContainer.StatusCheck_2026-07-08_15-13-24/Verify.SlurmContainer.Pass/0)
[INFO] Running test: Verify.SlurmContainer.Pass
[INFO] Submitted slurm job: 62105
[INFO] Starting test: Verify.SlurmContainer.Fail (results at: results/Verify.SlurmContainer.StatusCheck_2026-07-08_15-13-24/Verify.SlurmContainer.Fail/0)
[INFO] Running test: Verify.SlurmContainer.Fail
[INFO] Submitted slurm job: 62106
[INFO] Job completed: Verify.SlurmContainer.Pass (iteration 1 of 1)
[ERROR] Job 62106 for test Verify.SlurmContainer.Fail failed: Container command exited with a non-zero exit code for results/Verify.SlurmContainer.StatusCheck_2026-07-08_15-13-24/Verify.SlurmContainer.Fail/0: state=FAILED, exit_code=42:0.
[INFO] Job completed: Verify.SlurmContainer.Fail (iteration 1 of 1)
[INFO] Generated scenario report at results/Verify.SlurmContainer.StatusCheck_2026-07-08_15-13-24/Verify.SlurmContainer.StatusCheck.html
[INFO] Scenario results                                                                                                                                                                                                          
╔════════════════════════════╤════════╤══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╗
║ Case                       │ Status │ Details                                                                                                                                                                          ║
╟────────────────────────────┼────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╢
║ Verify.SlurmContainer.Pass │ PASSED │ results/Verify.SlurmContainer.StatusCheck_2026-07-08_15-13-24/Verify.SlurmContainer.Pass/0                                                                                       ║
╟────────────────────────────┼────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╢
║ Verify.SlurmContainer.Fail │ FAILED │ results/Verify.SlurmContainer.StatusCheck_2026-07-08_15-13-24/Verify.SlurmContainer.Fail/0                                                                                       ║
║                            │        │ Container command exited with a non-zero exit code for results/Verify.SlurmContainer.StatusCheck_2026-07-08_15-13-24/Verify.SlurmContainer.Fail/0: state=FAILED, exit_code=42:0. ║
╚════════════════════════════╧════════╧══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╝

[INFO] Created tarball at results/Verify.SlurmContainer.StatusCheck_2026-07-08_15-13-24.tgz
[INFO] All jobs are complete.
PyTest
$ uv run pytest tests/workloads/slurm_container/ -v
... cut ...
tests/workloads/slurm_container/test_command_gen_strategy_slurm.py::test_default PASSED                                                                                                                                                            [  9%]
tests/workloads/slurm_container/test_command_gen_strategy_slurm.py::test_with_nsys PASSED                                                                                                                                                          [ 18%]
tests/workloads/slurm_container/test_command_gen_strategy_slurm.py::test_with_extra_srun_args PASSED                                                                                                                                               [ 27%]
tests/workloads/slurm_container/test_slurm_container.py::TestSlurmContainerSuccessCheck::test_missing_slurm_metadata_fails PASSED                                                                                                                  [ 36%]
tests/workloads/slurm_container/test_slurm_container.py::TestSlurmContainerSuccessCheck::test_exit_code_is_honored[0:0-True] PASSED                                                                                                                [ 45%]
tests/workloads/slurm_container/test_slurm_container.py::TestSlurmContainerSuccessCheck::test_exit_code_is_honored[0-True] PASSED                                                                                                                  [ 54%]
tests/workloads/slurm_container/test_slurm_container.py::TestSlurmContainerSuccessCheck::test_exit_code_is_honored[0:15-True] PASSED                                                                                                               [ 63%]
tests/workloads/slurm_container/test_slurm_container.py::TestSlurmContainerSuccessCheck::test_exit_code_is_honored[1:0-False] PASSED                                                                                                               [ 72%]
tests/workloads/slurm_container/test_slurm_container.py::TestSlurmContainerSuccessCheck::test_exit_code_is_honored[15:0-False] PASSED                                                                                                              [ 81%]
tests/workloads/slurm_container/test_slurm_container.py::TestSlurmContainerSuccessCheck::test_exit_code_is_honored[42:0-False] PASSED                                                                                                              [ 90%]
tests/workloads/slurm_container/test_slurm_container.py::TestSlurmContainerSuccessCheck::test_exit_code_is_honored[137:0-False] PASSED                                                                                                             [100%]

=================================================================================================================== 11 passed in 0.10s ==================================================================================================================

Additional Notes

None

@malagows-nvd malagows-nvd marked this pull request as draft July 8, 2026 12:24
@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 2b8ef46c-e310-4d88-af25-115776a23ed4

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a was_run_successful method to SlurmContainerTestDefinition that reads and validates slurm-job.toml from the test run output directory, determines success based on exit_code, and returns a structured JobStatusResult. Includes new tests covering missing metadata and various exit code formats.

Changes

Slurm container success grading

Layer / File(s) Summary
was_run_successful implementation
src/cloudai/workloads/slurm_container/slurm_container.py
Adds toml and SlurmJobMetadata imports, updates copyright year, and implements was_run_successful to parse slurm-job.toml, treat exit codes of "0" or prefixed "0:" as success, and return failure/missing-file JobStatusResult otherwise.
Success grading tests
tests/workloads/slurm_container/test_slurm_container.py
Adds TestSlurmContainerSuccessCheck with a _write_slurm_metadata helper and tests verifying missing-file failure and correct success/failure across various SLURM exit code formats.

Estimated code review effort: 2 (Simple) | ~10 minutes

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: grading SlurmContainer runs from the recorded exit code.
Description check ✅ Passed The description matches the change and test coverage for grading SlurmContainer runs from slurm-job.toml exit codes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/cloudai/workloads/slurm_container/slurm_container.py`:
- Around line 71-72: The `was_run_successful` path in `SlurmContainer` currently
lets `toml.load()` and `SlurmJobMetadata.model_validate()` throw on bad or
partial `slurm-job.toml`, which can abort `Reporter.is_successful()` and
`BaseRunner.get_job_status()`. Wrap the metadata parsing in this method with
error handling, catch parse/validation failures, and return a failed
`JobStatusResult` that includes an error message instead of propagating the
exception. Keep the fix localized to the `was_run_successful` logic and preserve
the existing success path when metadata loads correctly.

In `@tests/workloads/slurm_container/test_slurm_container.py`:
- Around line 62-73: The pytest parametrization in the test module uses a tuple
of tuples for `pytest.mark.parametrize`, which triggers PT007. Update the
`parametrize` values in `test_slurm_container.py` to use a list of tuples
instead of a tuple, keeping the existing `exit_code` and `is_successful` cases
unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: ff3fb4b3-e0f1-470e-8df6-c60fa43d873b

📥 Commits

Reviewing files that changed from the base of the PR and between 2dd195d and a286eb8.

📒 Files selected for processing (2)
  • src/cloudai/workloads/slurm_container/slurm_container.py
  • tests/workloads/slurm_container/test_slurm_container.py

Comment on lines +71 to +72
with slurm_job_path.open("r", encoding="utf-8") as file:
metadata = SlurmJobMetadata.model_validate(toml.load(file))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Confirm whether callers of was_run_successful catch exceptions.
rg -n -B3 -A6 'def is_successful' src/cloudai/reporter.py
rg -n -B3 -A10 'was_run_successful' src/cloudai/reporter.py src/cloudai/_core

Repository: NVIDIA/cloudai

Length of output: 3665


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the workload loader and the metadata model.
sed -n '1,180p' src/cloudai/workloads/slurm_container/slurm_container.py
printf '\n---\n'
rg -n -A6 -B6 'class SlurmJobMetadata|model_config|extra\s*=' src/cloudai/workloads/slurm_container src/cloudai/_core

# Inspect the runner/status plumbing around the call sites.
printf '\n=== reporter.py ===\n'
sed -n '100,240p' src/cloudai/reporter.py
printf '\n=== base_runner.py ===\n'
sed -n '260,320p' src/cloudai/_core/base_runner.py

Repository: NVIDIA/cloudai

Length of output: 12906


Catch Slurm metadata parse errors in was_run_successful (src/cloudai/workloads/slurm_container/slurm_container.py:71-72) toml.load() and SlurmJobMetadata.model_validate() can raise on malformed or partially written slurm-job.toml, and the result is consumed directly by Reporter.is_successful() and BaseRunner.get_job_status(). Return a failed JobStatusResult with an error message instead of letting the exception abort grading/reporting for the whole scenario.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/cloudai/workloads/slurm_container/slurm_container.py` around lines 71 -
72, The `was_run_successful` path in `SlurmContainer` currently lets
`toml.load()` and `SlurmJobMetadata.model_validate()` throw on bad or partial
`slurm-job.toml`, which can abort `Reporter.is_successful()` and
`BaseRunner.get_job_status()`. Wrap the metadata parsing in this method with
error handling, catch parse/validation failures, and return a failed
`JobStatusResult` that includes an error message instead of propagating the
exception. Keep the fix localized to the `was_run_successful` logic and preserve
the existing success path when metadata loads correctly.

Comment on lines +62 to +73
@pytest.mark.parametrize(
("exit_code", "is_successful"),
(
("0:0", True),
("0", True),
("0:15", True),
("1:0", False),
("15:0", False),
("42:0", False),
("137:0", False),
),
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use list of tuples for parametrize values.

Static analysis flags PT007: the values collection should be a list, not a tuple, of tuples.

🎨 Proposed fix
     `@pytest.mark.parametrize`(
         ("exit_code", "is_successful"),
-        (
+        [
             ("0:0", True),
             ("0", True),
             ("0:15", True),
             ("1:0", False),
             ("15:0", False),
             ("42:0", False),
             ("137:0", False),
-        ),
+        ],
     )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@pytest.mark.parametrize(
("exit_code", "is_successful"),
(
("0:0", True),
("0", True),
("0:15", True),
("1:0", False),
("15:0", False),
("42:0", False),
("137:0", False),
),
)
`@pytest.mark.parametrize`(
("exit_code", "is_successful"),
[
("0:0", True),
("0", True),
("0:15", True),
("1:0", False),
("15:0", False),
("42:0", False),
("137:0", False),
],
)
🧰 Tools
🪛 Ruff (0.15.20)

[warning] 64-72: Wrong values type in pytest.mark.parametrize expected list of tuple

Use list of tuple for parameter values

(PT007)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/workloads/slurm_container/test_slurm_container.py` around lines 62 -
73, The pytest parametrization in the test module uses a tuple of tuples for
`pytest.mark.parametrize`, which triggers PT007. Update the `parametrize` values
in `test_slurm_container.py` to use a list of tuples instead of a tuple, keeping
the existing `exit_code` and `is_successful` cases unchanged.

Source: Linters/SAST tools

Grade the run via was_run_successful() - currently based on
the exit_code field in slurm-job.toml (non-zero = FAILED).

Signed-off-by: Marcin Malagowski <mmalagowski@nvidia.com>
@malagows-nvd malagows-nvd force-pushed the mm/slurmcontainer-add-wasrunsuccessful branch from a286eb8 to 3081afb Compare July 8, 2026 13:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant