Skip to content

baremetal: create the raid configuration during clean, per node - #2609

Merged
ideaship merged 2 commits into
osism:mainfrom
kingspeedy95:baremetal-clean-raid-create
Aug 27, 2026
Merged

baremetal: create the raid configuration during clean, per node#2609
ideaship merged 2 commits into
osism:mainfrom
kingspeedy95:baremetal-clean-raid-create

Conversation

@kingspeedy95

@kingspeedy95 kingspeedy95 commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

The gap

osism baremetal clean already prepends the raid clean step delete_configuration when a node has a raid interface:

# NOTE: If the node has an agent raid interface, include step to delete the raid configuration
if not metadata_only and node.get("raid_interface", "no-raid") != "no-raid":
    clean_steps = [{"interface": "raid", "step": "delete_configuration"}] + clean_steps

but nothing ever adds create_configuration. Two consequences: a node that had a software mirror loses it on the first full clean and never gets it back, and a fleet whose target_raid_config was just declared cannot build its arrays with the OSISM CLI at all.

That is not an omission that can be worked around elsewhere. Ironic exposes create_configuration only as a clean step with priority 0, see AgentRAID in 2025.1, so it has to be requested explicitly. The deploy step variant is apply_configuration, which takes the configuration as a step argument through deploy templates rather than from target_raid_config.

The change

The step list now comes from _build_clean_steps(node, metadata_only, raid), which produces delete_configuration, the erase step, create_configuration. That is the order the Ironic documentation prescribes for software RAID: create does not remove existing disks and "Building RAID will fail if the target disks are already partitioned", so delete and erase have to run first. create_configuration is only added when the node actually carries a target_raid_config, so a raid capable node without a declaration keeps exactly its previous behaviour.

It also fixes an accumulation bug. The list used to be built once before the node loop and prepended to inside it, so with --all the second raid capable node got two delete_configuration steps, the third got three, and so on. Building per node removes that by construction, and a command level --all test over three different kinds of node pins it.

--raid / --no-raid make the choice explicit, defaulting to today's behaviour: raid steps on a full clean, none on --metadata-only. The new part is that --metadata-only --raid becomes expressible.

Why that last combination matters

On the Supermicro blades of one of our test pods the platform firmware freezes both ATA erase paths of the SATA SSDs during POST, so erase_devices has no working path there at all, while the OS mirror on the NVMe pair still has to be built. Today that fleet has to choose between a clean that fails and a clean that silently skips the raid steps.

Tested

  • tests/unit/commands/test_baremetal.py: 131 to 140 passing. At helper level, a node without a raid interface, a raid capable node with and without a declaration, both --metadata-only variants and --no-raid on a full clean. At command level, one --all run over three manageable nodes, one without a raid interface, one raid capable without a declaration and one with a target_raid_config, asserting the full ordered clean_steps of every call, plus a single node case each for --metadata-only --raid and --no-raid.
  • That --all test was checked against both variants of the bug. The pre change code gives the third node two delete_configuration steps, and hoisting the helper call back out of the node loop gives every node the first node's steps. Both fail it, and in the hoisted case it is the only test that fails.
  • --raid / --no-raid / neither / --metadata-only --raid verified to parse as intended.
  • The remaining collection errors under tests/unit in my environment are missing optional dependencies and are identical with and without this change, checked by stashing.

Not tested by me: a real clean against hardware. The fleet this came from is about to be rebuilt and I can report back once the arrays are built there.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've found 1 issue

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="tests/unit/commands/test_baremetal.py" line_range="283" />
<code_context>
     )


+# --- _build_clean_steps ---
+
+
</code_context>
<issue_to_address>
**issue (testing):** There are no automated tests covering the new `--raid` CLI flag wiring into `take_action`; consider adding them.

The current tests cover `_build_clean_steps`, but not the new `--raid` flag wiring into `take_action`. Please add tests that build the parser, exercise `--raid`, `--no-raid`, and the default (no flag), and assert that `take_action` calls `_build_clean_steps` with the correct `raid` value in each case, matching the documented CLI behaviour (default on for full clean, off for `--metadata-only`, with explicit overrides).
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread tests/unit/commands/test_baremetal.py
`osism baremetal clean` already prepends the raid clean step
`delete_configuration` when a node has a raid interface, but nothing ever adds
`create_configuration`. A node that had a software mirror therefore loses it on
the first full clean and never gets it back, and a fleet whose
`target_raid_config` was just declared cannot build its arrays with the OSISM
CLI at all. Ironic exposes `create_configuration` only as a clean step, priority
0, so it has to be requested explicitly; the deploy step variant
`apply_configuration` takes the configuration as a step argument through deploy
templates rather than from `target_raid_config`.

The step list now comes from `_build_clean_steps`, which builds
`delete_configuration`, the erase step, `create_configuration`, which is the
order the Ironic documentation prescribes: create does not remove existing disks
and fails outright on a partitioned target, so delete and erase have to run
first. `create_configuration` is only added when the node actually carries a
`target_raid_config`, so a raid capable node without a declaration keeps its
previous behaviour.

Building the list per node also fixes an accumulation. It used to be built once
before the node loop and then prepended to inside it, so with `--all` the second
raid capable node got two `delete_configuration` steps, the third got three, and
so on.

Finally `--raid` and `--no-raid` make the choice explicit. The default keeps the
current behaviour, raid steps on a full clean and none on `--metadata-only`, but
the combination `--metadata-only --raid` is now expressible. That is what a pod
needs whose data disks cannot be erased in band: on the Supermicro blades of one
of our test pods the platform firmware freezes both ATA erase paths of the SATA
SSDs during POST, so `erase_devices` has no working path there, while the mirror
still has to be built.

Verified locally: `tests/unit/commands/test_baremetal.py` goes from 131 to 138
passing tests, with cases for a node without a raid interface, a raid capable
node with and without a declaration, both metadata only variants, `--no-raid` on
a full clean, and a regression test for the accumulation. The remaining
collection errors in `tests/unit` are missing optional dependencies in the local
environment and are identical with and without this change.

Signed-off-by: Timon Schnell <timon.schnell@btc-it-services.com>
Assisted-by: Claude:claude-opus-5
@kingspeedy95
kingspeedy95 force-pushed the baremetal-clean-raid-create branch from 724f673 to a5f183d Compare August 20, 2026 09:27
@berendt
berendt requested a review from ideaship August 20, 2026 10:19
@osfrickler osfrickler moved this from New to Ready for review in Human Board Aug 24, 2026

@ideaship ideaship 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.

Thanks — the accumulation fix is right, and extracting _build_clean_steps is the shape that keeps it from coming back. The step order matches Ironic's software-RAID docs, and the frozen-ATA case behind --metadata-only --raid is real: ironic-python-agent raises on a frozen drive, so there's no working in-band full-erase path on that hardware.

One request, inline — the regression test can't detect the regression, and the new behaviour has no coverage above the helper.

Nothing to change here for the following, it's ours to handle: we plan to give --raid an optional value — --raid delete|keep|recreate — because "leave the existing array alone" and "tear it down without rebuilding" are different outcomes one boolean can't express, and to put the create step behind an explicit choice rather than the default. Bare --raid would keep meaning exactly what it means in your patch, so --metadata-only --raid stays valid for your pod. Two things would change: --no-raid becomes --raid keep, and a plain clean on a node with a target_raid_config goes back to delete + erase without the rebuild. We'll do that work and any compatibility handling separately.

Comment thread tests/unit/commands/test_baremetal.py Outdated
Comment on lines +348 to +357
def test_clean_steps_do_not_accumulate_across_nodes():
"""Regression: the list used to be built once and prepended to per node."""
nodes = [
FakeNode(
name=f"node{index}", raid_interface="agent", target_raid_config={"x": 1}
)
for index in range(3)
]
for node in nodes:
assert _steps(node).count(("raid", "delete_configuration")) == 1

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.

This test can't detect the regression it is named for. _build_clean_steps returns fresh list literals with no module state or mutable default, so calling it independently per node doesn't exercise the accumulation, which happened in take_action's node loop. Hoisting the helper call back out of that loop would leave this green.

The behaviour the PR adds also isn't reached at command level: FakeNode defaults target_raid_config to None (:246), so none of the clean tests from :1415 on exercise create_configuration, --raid or --no-raid.

Could you drop this test and add command-level ones next to those, via _run_baremetal_clean? For the loop, one --all run over three manageable nodes — raid_interface="no-raid", agent without a declaration, agent with one — asserting the full ordered clean_steps of every call in set_node_provision_state.call_args_list. The three kinds are the point: identical RAID nodes would also pass if the call were hoisted out of the loop. Then a single-node case each for --raid and --no-raid. Happy to sketch it if that's easier.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done in f36acff.

The helper level test is gone. You are right that it could not fail: fresh list literals, no state to carry between calls.

test_clean_all_builds_the_step_list_per_node replaces it, one --all run over the three kinds you named, no-raid, agent without a declaration, agent with a target_raid_config, asserting the full ordered clean_steps of all three entries in set_node_provision_state.call_args_list. I checked it against both variants of the bug rather than trusting the shape: with the pre change code the third node comes back with two delete_configuration steps, and with the helper call hoisted back out of the loop, built once from clean_nodes[0], every node gets the first node's steps. Both fail the test, and in the hoisted case it is the only failure in the file.

Above the helper there are now also test_clean_metadata_only_with_raid_requested and test_clean_no_raid_skips_the_raid_steps_on_a_full_clean, single node, each with a target_raid_config set so the create step is actually in play. That covers the Sourcery point on the same review as well. 138 to 140 in the file.

Understood on --raid delete|keep|recreate, and I agree the boolean cannot carry the difference between leaving an array alone and tearing it down without rebuilding. Happy to leave that and the compatibility handling to you. Bare --raid keeping its meaning is all my pod needs.

@github-project-automation github-project-automation Bot moved this from Ready for review to In review in Human Board Aug 25, 2026
The helper level regression test could not detect the regression it was named
for. `_build_clean_steps` returns fresh list literals with no module state and
no mutable default, so calling it once per node in a test never accumulates.
The accumulation happened in `take_action`'s node loop, and hoisting the helper
call back out of that loop left the test green.

Drop it and cover the loop where it runs. One `--all` run over three manageable
nodes, one without a raid interface, one raid capable without a declaration and
one with a `target_raid_config`, asserting the full ordered `clean_steps` of
every call. The three kinds have to differ: three identical raid nodes would
also pass with the call hoisted out of the loop.

`--raid` and `--no-raid` had no coverage above the helper either, because
`FakeNode` defaults `target_raid_config` to `None` and none of the command level
clean tests set it. Add a single node case for each, `--metadata-only --raid`
for the combination this change makes expressible and `--no-raid` on a full
clean.

Verified that both variants of the bug fail the new `--all` test: the pre change
code gives the third node two `delete_configuration` steps, and hoisting the
helper call out of the loop gives every node the first node's steps. The file
goes from 138 to 140 passing tests.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Timon Schnell <timon.schnell@btc-it-services.com>
@berendt
berendt requested a review from ideaship August 27, 2026 07:33

@ideaship ideaship 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.

Thanks!

@ideaship
ideaship merged commit 87de4cf into osism:main Aug 27, 2026
3 checks passed
@github-project-automation github-project-automation Bot moved this from In review to Done in Human Board Aug 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

4 participants