Releases: simplyblock/sbcli
Release list
26.3.0.8
What's Changed
Full Changelog: 26.3.0.7...26.3.0.8
26.3.0.7
What's Changed
- fix(monitoring): node-exporter rootfs path and args reconcile on update (R26.3 backport) by @boddumanohar in #1329
- fix: preserve original source lvolID through failback cutover by swap… by @geoffrey1330 in #1276
- R26.3 fix fdb backup by @Hamdy-khader in #1332
Full Changelog: 26.3.0.6...26.3.0.7
26.3.0.6
Hotfix on top of 26.3.0.5.
fix(lvol-delete): self-heal delete status 3 (async done, leadership moved) (138719a)
bdev_lvol_get_lvol_delete_status returns 3 when the async (data-plane) delete
finished but LVS leadership then moved, so the follow-up per-node sync deletes
are blocked. The data clusters are already freed; only the per-node record /
subsystem / bdev cleanup remains, and that does not depend on who holds
leadership now.
The ret == 3 branch in lvol_monitor only logged and fell through — the sole
delete status that took no action (ret == 4 and ret == -35, both also
leadership situations, recover). So after leadership left the async node,
delete_lvol_from_node hit check_non_leader_for_operation and skipped/queued
the leader-side sync delete forever: the blob + bdev survived and the record
was never removed, while retried client DELETEs short-circuited on in_deletion
and returned success in ms.
Production run n_plus_k_failover_multi_client_ha_all_nodes-20260908-174343: a
supported 2-host outage moved LVS_1's primary+secondary together, the clone's
async delete completed on the tertiary, leadership moved back to the primary
44s later, and the monitor logged "error code: 3" 2250x for one lvol, still
going 14 min after the test gave up.
Fix: ret == 3 now completes the delete leader-independently — sync deletes in
the fixed sequence (async node first, then primary → secondary → tertiary,
online-only, skipping already-done), forcing the async-node sync delete past
the leadership gate. The normal path (ret 0/2) is unchanged.
🤖 Generated with Claude Code
fix(restart): block non-leader ports before the leader, demote only once all blocked (#1327)
On a primary restart the leader path blocked the leader's LVS port first,
suspended its replication, then blocked the non-leaders — leaving a window in
which a non-leader (typically the tertiary) kept serving client IO and
redirected it through the hublvol to a leader mid-transition, producing
writer_conflict on the journal. Section 3 is reordered so non-leader ports are
blocked FIRST, then the leader's, and the demote (replication suspend →
leadership drop) starts only once every port is blocked.
26.3.0.5
Hotfix on top of 26.3.0.4.
fix(jm): pin JM RAID geometry so upgrades never rebuild journals under a new layout (#1322)
The per-node JM device is a RAID created with superblock=False, so its
geometry is not recorded on disk. Older releases built it as one N-way RAID1
mirror across every JM partition; commit 41dea94 ("RAID 0+1 journal layout")
changed the planner to a mirror of two RAID0 legs, which stripes each leg 4 KiB
across its drives.
On upgrade of a cluster created under the old code, the new planner rebuilt each
JM raid under RAID 0+1. Because the same on-disk bytes are then read through a
different geometry, the alceml PBA header / journal / distrib output come back
scrambled and the blobstore superblock on raid0_<lvs> fails to parse
("unsupported version on super block") — sn restart and cluster activate
fail on every LVS. No data is written (a fresh RAID1 create with both legs
present neither resyncs nor writes; the alceml opens pba_init_mode=1 and the JM
appends nothing), so it is recoverable by reproducing the original geometry.
Fix: Cluster.jm_raid_layout pins the geometry once and reproduces it forever —
fresh clusters are pinned raid01 at create; an upgrade pins it from the
pre-restart JMDevice records before restarting any node; _create_jm_stack_on_raid
builds the pinned geometry (legacy => one N-way RAID1 across all partitions).
An existing cluster can never have its journals rebuilt under a different
geometry than they were written with.
Recovery runbook for an already-broken cluster:
docs/runbooks/jm-raid-geometry-recovery.md.
Reproducible on any cluster with >= 3 devices per node (1-2 devices produce
byte-identical geometries under both layouts).
Also included since 26.3.0.4:
- fix(nvme): detect an SPDK running without the control plane's nvme options (e6082b0)
🤖 Generated with Claude Code
26.3.0.4
What's Changed
- fix: adjust log level to INFO for swarm services (#1318) by @Hamdy-khader in #1319
- feat(monitoring): alert when the control plane API is slow by @boddumanohar in #1320
Full Changelog: 26.3.0.2...26.3.0.4
26.3.0.2
Hotfix on top of 26.3.0.1.
fix(capacity): snapshot admission charges the source's utilized size, at pool AND cluster level (9c152f6)
A snapshot immediately inherits the source volume's utilized bytes, so
creating one is now blocked when
provisioned-capacity limit
- sum of provisioned volume sizes
- actual utilization by existing snapshots
< utilized size of the source volume
- Pool level: pool_max_size admission now charges the snapshot at the
source's UTILIZED size; the former extra check at the source's full
provisioned size (which made large thin volumes un-snapshottable
regardless of utilization) is removed. - Cluster level (new): the same rule against prov_cap_crit percent of
effective cluster capacity — snapshot creation previously had no
cluster-level admission at all. - Applies to all snapshot sources (user, backup policies, internal
replication snapshots). A missing stats record falls back to the
provisioned size; unlimited pools/clusters skip the scans.
fix: hublvol redirect invariant + lift stale SPDK port fences (e87a088)
A non-leader peer left with a single hublvol path loses its redirect outright
when the primary dies, promotes itself on the next write, and produces a writer
conflict whose fence is then never lifted. Two changes:
- The tertiary's hublvol failover path is repaired on the
repair_paths
cadence rather thanauto_fixalone (a controller holding one of its two
paths PASSES the coarse check, so the branch was unreachable in exactly the
state it repairs), and is no longer gated on the primary beingready— the
path targets the SECONDARY, and the primary dying is what makes it
load-bearing. storage_node_monitornow lifts a client port that SPDK fenced and nothing
released, afterSTALE_PORT_BLOCK_SEC = 25s, gated on the node being ONLINE,
no restart owning that LVS, and the hublvol being healthy and connected.
Unblocking a peer with no redirect would only reopen the loop.
feat(device): bounded, differential self-repair of an unavailable device (6872469)
An unavailable verdict is reached by CONSENSUS — more than half the nodes
failing to reach the device over NVMe-oF — and is then persisted, so a
transient cause left the device unavailable indefinitely with nothing trying to
bring it back. Nothing repaired it, for two independent reasons: a
consensus-unavailable device carries io_error == False and the auto-restart
branch required it, and that branch sat in an elif under
cluster.status == ACTIVE so it never ran on a healthy cluster.
device_repair() probes the stack bottom-up (one bounded RPC per layer) and
rebuilds only what is missing via the additive _def_create_device_stack() —
deliberately not the destructive restart_device(). Five attempts on
[0, 10, 60, 180, 600] seconds; an attempt is counted only when there was
something local to rebuild, and reaching ONLINE by any route clears the budget.
feat(device): self-repair a removed device too, unless the operator removed it (d996595)
STATUS_REMOVED means SPDK unregistered the bdev and the control plane tore
the stack down after it — not proof the hardware is gone. It was previously a
dead end: device_monitor's loop filters to online/unavailable/readonly/
cannot-allocate, so a removed device hit continue before any repair could see
it, leaving only "Device status is not recognised, status: removed" every 10s.
Unsolicited SPDK removals now join STATUS_UNAVAILABLE in
DEVICE_REPAIRABLE_STATES, on the same bounded backoff. The one removal never
undone is the one a human asked for: CAUSE_ADMIN_REMOVE is passed explicitly
by the CLI sn remove-device and the v1/v2 API remove endpoints, recorded on
the device as admin_removed atomically before teardown, and honoured in
both device_repair_due() and device_repair() — not overridable by force.
It clears when the device next reaches ONLINE, alongside deleted.
fix(proxy): stop the stats thread before interpreter shutdown (f017b98)
The integration tier aborted with _enter_buffered_busy: could not acquire lock for <_io.BufferedWriter name='<stderr>'> after all 1213 tests had passed —
pytest exit -6, tox 250, so a green run reported as failure. print_stats() is
a daemon thread Python never joins; once logging's stream closed, each emit
raised ValueError and the handler logged that to the same dead stream and
looped. Now an atexit-set threading.Event drives the sleep, and
ValueError/OSError return instead of being logged.
26.3.0.1
Hotfix on top of 26.3.0.
fix(upgrade): heal legacy lvstore_stack_secondary/_tertiary values on load (07447ec)
Clusters created before 2026-04 carry the pre-refactor list value in these
node-record fields; 26.3.0's str annotation coerced it into the truthy string
"[]" at load time, aborting the first post-upgrade storage-node restart with
KeyError: 'StorageNode [] not found' during LVS recreation.
- StorageNode.from_dict now normalizes legacy list / "[]" values to "" on
every read — stored records heal on load, no migration step required (also
repairs records already re-persisted with the poison string by a 26.3.0
control plane). - The JM-connect path degrades a dangling primary reference to a warning +
skip instead of aborting the restart. - Annotation audit R26.2-PRE → 26.3.0 confirmed no other field carries a
load-semantics-changing type switch.
Upgrade path for affected clusters: update the control plane to 26.3.0.1,
then re-run the rolling storage-node restarts with the R26.3 SPDK image.
🤖 Generated with Claude Code
26.3.0
What's Changed
- Remove remote object from node when receiving distrib events by @Hamdy-khader in #743
- Add req id to RPC logs for spdk_proxy by @Hamdy-khader in #752
- Main lvol sync delete (#734) by @Hamdy-khader in #757
- R25.10 hotfix sfam 2471 by @Hamdy-khader in #758
- fix linter and type checker issues by @Hamdy-khader in #759
- Prometheus hostpath by @geoffrey1330 in #761
- Format nvme devices when run sbcli sn configure with --force by @wmousa in #760
- Nsocket env by @geoffrey1330 in #764
- Fix fw connection error handling not to set node status down by @Hamdy-khader in #765
- Fix sfam-2473 by @Hamdy-khader in #766
- Do not install pip package on cluster update by @Hamdy-khader in #749
- Fix calculate total_mem for multi sn nodes on same numa by @wmousa in #767
- use max_size instead as hugepage memory when set by @geoffrey1330 in #754
- refactor node add task runner by @Hamdy-khader in #768
- Fix sfam-2483 by @Hamdy-khader in #773
- fix logger by @Hamdy-khader in #776
- fix SFAM-2476 by @Hamdy-khader in #775
- Fix SFAM-2482 by @Hamdy-khader in #769
- use emptyDir memory medium as socket directory by @geoffrey1330 in #783
- addd endpoint bind_device_to_nvme in kubernetes by @geoffrey1330 in #784
- Create partitions and alcemls on node add in parallel by @Hamdy-khader in #763
- Remove stats from fdb and get it from Prometheus by @Hamdy-khader in #762
- R25.10 hotfix isolate by @wmousa in #792
- Fix sfam-2515 by @Hamdy-khader in #810
- R25.10 hotfix spdk proxy stats by @Hamdy-khader in #812
- Add --cores-percentage to sbctl sn configure and support oracle OS fo… by @wmousa in #813
- Use diff fw port per node by @Hamdy-khader in #817
- Return back max number of distribs to be 12 distribs by @wmousa in #827
- Fix sfam-2555 by @Hamdy-khader in #831
- Fix sfam-2556 by @Hamdy-khader in #835
- Fix node auto restart by @Hamdy-khader in #836
- Adds failed device back to cluster as a new one by @Hamdy-khader in #841
- Fix remove device function by @Hamdy-khader in #840
- fix sfam-2557 by @Hamdy-khader in #843
- Fix sfam-2564 by @Hamdy-khader in #838
- Fix node restart jm raid config by @Hamdy-khader in #851
- Fix sfam-2578 by @Hamdy-khader in #852
- Fix sfam-2579 by @Hamdy-khader in #850
- Adds sbctl -v/--version by @Hamdy-khader in #853
- R25.10 hotfix cluster id from main by @Hamdy-khader in #854
- Add --nvme-names to sn configure and --format-4k to sn add-node by @wmousa in #859
- Improve sync delete handling for lvol bdevs in lvol_monitor.py by @Hamdy-khader in #863
- R25.10 hotfix client data nic by @Hamdy-khader in #874
- Enhance graceful-startup and graceful-shutdown by @wmousa in #876
- R25.10 hotfix dump tree by @Hamdy-khader in #879
- fix-description of max-size by @wmousa in #881
- Add --calculate-hp-only to calculate the minimum required huge pages by @wmousa in #884
- Fix sfam-2620 by @Hamdy-khader in #886
- Adds link detection when pinging data nic by @Hamdy-khader in #897
- Add static numa tomplogy for openshift depoyment by @wmousa in #830
- Add LVol migration feature and related improvements by @schmidt-scaled in #915
- Stress run scheduler fix by @RaunakJalan in #914
- Adding dump validation by @RaunakJalan in #916
- Checking basic placement dump by @RaunakJalan in #917
- Fixing validation for placement dump by @RaunakJalan in #918
- Enable conditional inclusion of ndcs and npcs in lvol_create parameters by @Hamdy-khader in #919
- Test: Fixing fio timeout and merging auto restart fix by @RaunakJalan in #922
- delete fluent pod when removing spdk pod by @geoffrey1330 in #923
- Adding new test case for fault tolerance 2 by @RaunakJalan in #924
- Adding rdma multi outage scenario by @RaunakJalan in #925
- added nodeselector and tolerations by @geoffrey1330 in #928
- updated metrics labels and made prometheus url configurable by @geoffrey1330 in #930
- added prometheus_url and port env across all services by @geoffrey1330 in #931
- added helm dependency to chart by @geoffrey1330 in #932
- feat: add spdk_proxy_image support across cluster and storage node co… by @Hamdy-khader in #933
- R25.10 hotfix lvol clone (#934) by @Hamdy-khader in #941
- Security enabled lvol restore fix by @RaunakJalan in #943
- Checking concurrency param by @RaunakJalan in #926
- feat: add log collection script for Graylog/OpenSearch by @schmidt-scaled in #945
- added functionality to get k8s based logs by @geoffrey1330 in #947
- support foundationdb client arm64 on k8s by @geoffrey1330 in #948
- fix 500 error on cluster delete by @boddumanohar in #952
- Main snapshot replication by @Hamdy-khader in #938
- refactor: update backup controller to use original lvol crypto settings by @Hamdy-khader in #953
- fix lint and generate cli by @boddumanohar in #954
- New flag --enable-journal-device by @noctarius in #955
- Support for backup as a part of cluster create API by @boddumanohar in #946
- Cleaning up of the help texts for command, parameters, attributes by @noctarius in #949
- update openapi.json by @boddumanohar in #958
- raise duplicate cluster name as 409 conflict by @boddumanohar in #959
- Replace IPTables with NFTables in the Firewall wep app by @Hamdy-khader in #950
- Update storage_node.py by @geoffrey1330 in #963
- Adding snapshot backup, dhchap, k8s native cases by @RaunakJalan in #964
- Update init.py by @geoffrey1330 in #966
- Update init.py by @geoffrey1330 in #967
- Update init.py by @geoffrey1330 in #968
- Fixing yaml, sbcli cmd input, k8s runs by @RaunakJalan in #965
- K8s native cases by @RaunakJalan in #969
- Update DTOs with additional fields by @mxsrc in #971
- FTT2 restart refactor, remote-device reconciliation, collect_logs.py, FTT2 test suite by @schmidt-scaled in #956
- Small fixes for the documentation by @noctarius in #960
- fix(openshift): use cluster-scoped MCP with multi-node hostname selec… by @wmousa in #976
- Added list type which implements a comma-separated list by @noctarius in #977
- user ClusterFirstWithHostNet for spdk pod by @boddumanohar in #972
- wait for numa-align device plugin to become ready before exiting cpu… by @geoffrey1330 in #978
- adde...
26.2.12-PRE
26.2.11-PRE
Add performance improvement in pool capacity reporting.
Full Changelog: 26.2.10-PRE...26.2.11-PRE