Skip to main content

Cozystack Linstor

@magistr/cozystack-linstorv2026.08.19.1· 3d agoMODELS
01README

Linstor distributed storage management for Cozystack — ZFS pool creation, storage classes, node and pool status

02Models1
@magistr/cozystack-linstorv2026.08.19.1extensions/models/cozystack_linstor.ts
fn getLinstorControllerStatus()
Check if linstor-controller deployment is ready
fn listNodes()
List Linstor cluster nodes
fn listStoragePools()
List Linstor storage pools across all nodes
fn createZfsPool(node: string, device: string, poolName: string)
Create a ZFS storage pool on a node. Idempotent: skips if pool already exists. Verify-before-destructive: reads LINSTOR's live physical-storage inventory to confirm the requested device is available before creating, refusing instead of wiping a device already in use or silently discarding a mismatched request.
ArgumentTypeDescription
nodestringNode name to create the pool on
devicestringBlock device path (e.g. /dev/vdb)
poolNamestringZFS pool name
fn setZfsFailmode(node: string, poolName: string)
Set ZFS failmode=continue on a node. Idempotent (re-setting is a no-op).
ArgumentTypeDescription
nodestringNode name
poolNamestringZFS pool name
fn applyStorageClasses()
Apply a storage class YAML manifest. Idempotent (uses kubectl apply).

Resources

node(infinite)— Linstor cluster node
storagePool(infinite)— Linstor storage pool
result(infinite)— Operation result
03Previous Versions5
2026.08.01.1

2026.08.01.1

Fixes the HIGH device-reuse fail-open guard in createZfsPool (cozystack-linstor-fail-open-guards, local model). The idempotency decision was previously (node, storagePool NAME) only and never considered device:

  • Direction A (device wipe): reusing an already-provisioned device under a DIFFERENT storage-pool name fell through to the destructive physical-storage create-device-pool call — a device wipe. Now REFUSED (success:false).
  • Direction B (silent no-op): the SAME storage-pool name with a DIFFERENT requested device was silently suppressed as "already exists" (success:true) without ever provisioning the requested device. Now REFUSED (success:false) instead of silently discarding the request.

Fix is verify-before-destructive (CLAUDE.md Rule 5): createZfsPool now reads LINSTOR's live physical-storage list inventory to establish device-availability BEFORE any create decision, and folds it together with the existing (node, storagePool) name match into a 4-way branch:

  • nameMatch && !deviceFree -> idempotent no-op (unchanged, success:true)
  • nameMatch && deviceFree -> Direction B, REFUSED (success:false)
  • !nameMatch && !deviceFree -> Direction A, REFUSED (success:false)
  • !nameMatch && deviceFree -> legit create (unchanged, create-device-pool argv stays byte-identical)

A device absent from the live inventory is conservatively treated as "in use" (fail-closed), per the documented conservative bias in the plan.

  • extensions/models/cozystack_linstor.ts: added the physical-storage inventory read + 4-way branch in createZfsPool.execute; model.version bumped to 2026.08.01.1.
  • fixtures/physical-storage-list.json: new synthetic fixture (doc-derived from LINBIT's linstor-api-py PhysicalDevice/NodeStorageEntry response classes, no live capture) plus a fixtures/PROVENANCE.md entry.
  • cozystack_linstor_adversarial_test.ts: the two fail-open characterization pins are flipped to assert refusal (no create-device-pool call, success:false); fixtures-secret-scan FIXTURES map extended with the new fixture.
  • cozystack_linstor_methods_test.ts / cozystack_linstor_property_test.ts / cozystack_linstor_coverage_test.ts: harness updates for the inserted physical-storage-list read and shifted invocation indices; the legit-create and genuine-idempotent-no-op paths stay green.

Also included in this release (previously unreleased): the test backfill to the STANDARD.md five-suite quality bar (wave-1 child of the extension-quality backfill program, ext-quality-test-backfill) — no behavior change of its own, the model version stayed 2026.07.16.2 until this release's guard fix above.

  • Added extensions/models/cozystack_linstor_test.ts (contract-fixture), cozystack_linstor_methods_test.ts (methods), cozystack_linstor_adversarial_test.ts (adversarial), cozystack_linstor_coverage_test.ts (coverage), cozystack_linstor_property_test.ts (property-invariant-flow) — 0 tests before this change, 87 after.
  • Added fixtures/ — pure doc-derived, synthetic LINSTOR machine-readable (-m) and Kubernetes Deployment wire-shape fixtures (node-list, storage-pool-list, deploy-ready, deploy-notready) plus PROVENANCE.md. No live call was made against the real Cozystack cluster or its kubeconfig; every value is synthetic (worker-0..2, /dev/vdb, RFC 5737 addresses).
  • Boundary is Deno.Command (not fetch) — a reassignable fake stub captures every {command, args} invocation and returns a queued {success, stdout, stderr}, with @std/testing FakeTime collapsing the model's one-at-a-time 5s retry delays for the retry-exhaustion tests.
  • The two live pre-flight checks (cluster-reachable, linstor-controller-ready) are exercised directly via model.checks[<name>].execute(), not just the 6 methods — closing a round-1 review HIGH finding that the destructive-op guards themselves were untested.
  • createZfsPool's idempotency-key guard (node + storagePool NAME only, never device) was pinned in both fail-open directions at the time of this test backfill: a different storagePool name on the same device still issued the destructive create-device-pool call (wipe), and — per a review fold-in — the same storagePool name with a different requested device was silently suppressed as "already exists" without ever provisioning the requested device. Both directions are FIXED by this same release (see above, cozystack-linstor-fail-open-guards).
  • The linstor-controller-ready check's parseInt(stdout) < 1 comparison is still pinned as fail-open on a non-numeric stdout (parseInt("abc") is NaN, and NaN < 1 is false) — a review fold-in, tracked separately and NOT fixed by this release (out of scope for `cozystack-linstor-fa
2026.07.16.2

Release 2026.07.16.2 — align model versions with manifests

Maintenance release across the @magistr extensions. For most packages this carries no functional change: the only edit is the model's version: field, brought back in line with its manifest version so the published model type version and the package version no longer drift.

Functional changes in this release are limited to:

  • anime-cron: normalizeTitle now strips a ": subtitle" suffix and a trailing parenthesized year before comparison, fixing dedup false-misses where the torrent title carries a subtitle or year that the AniList romaji does not.

  • arckit: first publish. Standalone ArcKit port — a 12-phase architecture governance state machine with 65 bundled templates, driven by a bundled skill.

Also tracks three extensions (kaiten, observability-agent, music-library) that previously existed only as untracked working-tree directories, recovered from stashes.

2026.07.16.1

Maintenance version bump. No functional changes.

Added 1, removed 1 models

2026.05.25.1

Merge pull request #4 from umag/extensions/magistr-grade-a-workspace

extensions: stage 15 @magistr extensions as Grade A workspace dirs + wire CI

2026.03.13.1
04Stats
A
100 / 100
Downloads
1
Archive size
8.8 KB
  • Has README or module doc2/2earned
  • README has a code example1/1earned
  • README is substantive1/1earned
  • Most symbols documented1/1earned
  • No slow types (deprecated)1/1earned
  • Dependencies pass trust audit2/2earned
  • Has description1/1earned
  • Platform support declared (or universal)2/2earned
  • License declared1/1earned
  • Verified public repository2/2earned
05Platforms
06Labels