Skip to content

fix(rest): stamp reports[] on PoolMissing so CLI renders Faulty (Bug 83)#3

Draft
kvaps wants to merge 1 commit into
mainfrom
fix/sp-reports-on-poolmissing
Draft

fix(rest): stamp reports[] on PoolMissing so CLI renders Faulty (Bug 83)#3
kvaps wants to merge 1 commit into
mainfrom
fix/sp-reports-on-poolmissing

Conversation

@kvaps
Copy link
Copy Markdown
Member

@kvaps kvaps commented May 14, 2026

Summary

Python linstor-client's storpool_cmds.py:472 derives the State column from get_replies_state(storpool.reports) — the structured ApiCallRc array — not from the bare state string field. With an empty reports[], get_replies_state returns ("Ok", green) regardless of state, so a satellite-reported Faulty pool (PoolMissing=true after zpool destroy) kept rendering as Ok in linstor sp l even though REST /v1/view/storage-pools correctly emitted state="Faulty".

This fix synthesises an ERROR-severity reports[] entry whenever PoolMissing=true, in both the k8s store's crdToWireStoragePool and the in-memory store's withDerivedState, keeping the two backends in lockstep.

Wire shape

The new ApiCallRc entry carries:

  • ret_code = MASK_ERROR | MASK_STOR_POOL | FAIL_STOR_POOL_CONFIGURATION_ERROR (990) — byte-identical to upstream LINSTOR's value, so audit-log greppers and the Python CLI's reply classifier both route the entry to the storage-pool error bucket.
  • message mentioning both missing and the pool name so operators can correlate from linstor sp l output alone.
  • cause / correction strings explaining the typical trigger (zpool destroy / vgremove) and the recovery path (linstor ps cdp or linstor sp c).
  • obj_refs[Node] / obj_refs[StorPool] so log queries can filter by either axis.

The mask + sub-code constants live in pkg/api/v1 (next to the APICallRc type) rather than pkg/rest, so the store layer can stamp the entry without pulling a cyclic dependency on pkg/rest.

Tests

  • pkg/store/k8s/storage_pools_internal_test.go::TestCrdToWireStoragePoolFaultyStampsReports — pins the k8s store's CRD→wire converter: Status.PoolMissing=true produces State=="Faulty" AND a non-empty Reports slice with MASK_ERROR + FAIL_STOR_POOL_CONFIGURATION_ERROR + missing + pool name + ObjRefs.
  • pkg/store/k8s/storage_pools_internal_test.go::TestCrdToWireStoragePoolHealthyHasNoReports — pins the inverse: a healthy pool MUST emit an empty Reports slice (stamping a no-op entry would trip get_replies_state into rendering the State column from a misleading first entry).
  • pkg/rest/storage_pools_test.go::TestViewStoragePoolsFaultyStampsReports — end-to-end through the REST view path with the in-memory store.

Test plan

  • go test ./pkg/store/... ./pkg/rest/... -count=1
  • go build ./...
  • E2E confirmation on dev-kvaps stand: zpool destroy blockstor-zfs on a worker → linstor sp l MUST render the affected pool's State column as Faulty / Error / Warn (whatever get_replies_state returns for an ERROR-severity reports entry) rather than Ok.

… (Bug 83)

Python linstor-client's `storpool_cmds.py:472` derives the State column
from `get_replies_state(storpool.reports)` — the structured ApiCallRc
array — not from the bare `state` string field. With an empty reports[],
`get_replies_state` returns ("Ok", green) regardless of `state`, so a
satellite-reported "Faulty" pool (PoolMissing=true after `zpool destroy`)
kept rendering as Ok in `linstor sp l` even though REST emitted
state="Faulty" correctly.

Synthesise an ERROR-severity reports[] entry whenever PoolMissing=true,
in both the k8s store's crdToWireStoragePool and the in-memory store's
withDerivedState. The ret_code mirrors upstream LINSTOR's
FAIL_STOR_POOL_CONFIGURATION_ERROR (990) | MASK_STOR_POOL | MASK_ERROR
so audit-log greppers and the Python CLI's reply classifier both route
the entry correctly. The mask + sub-code constants live in pkg/api/v1
to avoid a cyclic dependency from the store layer on pkg/rest.

Tests pin the wire contract at both layers: store-level
TestCrdToWireStoragePoolFaultyStampsReports /
TestCrdToWireStoragePoolHealthyHasNoReports, and REST-level
TestViewStoragePoolsFaultyStampsReports.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 14, 2026

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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 06ddb75e-1cc1-47c9-b825-52e1371d0a5e

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
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/sp-reports-on-poolmissing

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request addresses Bug 83 by ensuring that storage pools with PoolMissing set to true correctly report a 'Faulty' state to the Python LINSTOR client. This is implemented by synthesizing an error-severity report entry in both the in-memory and Kubernetes store layers using new constants added to the API package. Feedback suggests refactoring the duplicated report generation logic into a shared helper in pkg/api/v1 to ensure consistency and reduce code duplication.

Comment on lines +146 to +165
func poolMissingReport(node, pool string) apiv1.APICallRc {
return apiv1.APICallRc{
RetCode: apiv1.APICallRcMaskError |
apiv1.APICallRcMaskStorPool |
apiv1.APICallRcFailStorPoolConfigurationError,
Message: "pool backing storage missing on node " + node +
": storage pool " + pool + " is not present",
Cause: "The satellite probed the backing volume group / zpool / " +
"directory for this storage pool and found it absent — " +
"typically the result of an operator `zpool destroy`, " +
"`vgremove`, or an unmounted filesystem.",
Correc: "Re-discover the pool via `linstor ps cdp <provider> " +
"<node> <pool>` once the backing storage is restored, or " +
"recreate the pool with `linstor sp c`.",
ObjRefs: map[string]string{
"Node": node,
"StorPool": pool,
},
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The poolMissingReport function is identical to the one added in pkg/store/k8s/storage_pools.go. Since both packages already depend on pkg/api/v1, moving this logic to a shared helper in that package (e.g., NewPoolMissingReport) would eliminate duplication and ensure consistency across store implementations. This is particularly important for maintaining the specific error messages and codes used for this state.

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