Repository navigation
fix(port-block): drop the FirewallClient fallback and the firewall-port plumbing - #1497
Merged
Merged
Conversation
…ient fallback port_block.set_port sent fences to the legacy firewall webapp (FirewallClient, mgmt_ip:firewall_port) whenever node.spdk_version was empty, and redirected a method-not-found from nvmf_port_block there too. 25.10.x node records never carried spdk_version, and it is only set by a restart that passes --spdk-proxy-image, so a node upgraded to 26.3 kept "" and every fence on it went to a port nothing listens on: the SPDK image no longer ships the webapp or iptables (ultra #218). The fence failed with connection refused. - port_block: set_port / get_blocked_ports_set / is_port_blocked use the SPDK RPCs only; an RPC error, method-not-found included, propagates. - health_controller.check_ports_on_node: drop the per-port legacy branch. - Delete fw_api_client.py; port_block was its only user. - Tests: the ftt2 and dual-FT fixtures patch port_block instead of FirewallClient; the expansion simulator answers nvmf_port_block / nvmf_port_unblock / nvmf_get_blocked_ports instead of stubbing FirewallClient; regression tests in test_port_block_rpc_only.py. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Nothing has read any of this since fencing moved to the SPDK nvmf_port_block RPC and the SPDK image dropped iptables and the firewall webapp (ultra #218): - StorageNode.firewall_port, the spdk_process_start(firewall_port=) param, the field in both node-API SPDKParams, the FW_PORT env on the SPDK container (docker and the k8s template) and constants.FW_PORT_START. - utils.get_next_fw_port: no callers. - The deprecated node-API routes /snode/firewall_set_port and /snode/get_firewall, which only returned False. - StorageNode.spdk_version: only the removed FirewallClient fallback read it. - The jc dependency (it parsed iptables output); uv.lock regenerated with uv 0.12.22, the version docker/Dockerfile pins. Existing FDB records keep their stored firewall_port / spdk_version keys; BaseModel.from_dict only reads declared attributes, so they are ignored. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Hamdy-khader
approved these changes
Oct 16, 2026
With the FirewallClient fallback gone, port_block was a pass-through (review on #1496): - set_port only chose nvmf_port_block or nvmf_port_unblock, and every caller passed block= as a constant: the 13 call sites now call node.rpc_client(timeout=.., retry=..).nvmf_port_block/unblock(port) with the budget each already used. The rationale for the short fence budget moves to RPCClient.nvmf_port_block. - get_blocked_ports_set was the only caller of RPCClient.nvmf_get_blocked_ports: the projection to a set of port numbers moves into that method. - is_port_blocked was a one-line membership test; check_port_on_node does it inline. Tests: they faked fencing by patching port_block.set_port, which also told them which node a fence was for. tests._mocks.route_fences keeps that: it wraps each node's rpc_client so nvmf_port_block/unblock reach the same (node, port, block) callback the tests already used. Source text anchors and "must not fence" assertions now look for the RPC calls, and mocks of nvmf_get_blocked_ports return a set. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
mxsrc
approved these changes
Oct 17, 2026
wmousa
added a commit
that referenced
this pull request
Oct 17, 2026
Nothing has read any of this since fencing moved to the SPDK nvmf_port_block RPC and the SPDK image dropped iptables and the firewall webapp (ultra #218): - StorageNode.firewall_port, the spdk_process_start(firewall_port=) param, the field in both node-API SPDKParams, the FW_PORT env on the SPDK container (docker and the k8s template) and constants.FW_PORT_START. - utils.get_next_fw_port: no callers. - The deprecated node-API routes /snode/firewall_set_port and /snode/get_firewall, which only returned False. - StorageNode.spdk_version: only the removed FirewallClient fallback read it. - The jc dependency (it parsed iptables output), from requirements.txt. Existing FDB records keep their stored firewall_port / spdk_version keys; BaseModel.from_dict only reads declared attributes, so they are ignored. (cherry picked from commit d3459b9, part of #1497) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
wmousa
added a commit
that referenced
this pull request
Oct 17, 2026
With the FirewallClient fallback gone, port_block was a pass-through (review on #1496): - set_port only chose nvmf_port_block or nvmf_port_unblock, and every caller passed block= as a constant: the 13 call sites now call node.rpc_client(timeout=.., retry=..).nvmf_port_block/unblock(port) with the budget each already used. The rationale for the short fence budget moves to RPCClient.nvmf_port_block. - get_blocked_ports_set was the only caller of RPCClient.nvmf_get_blocked_ports: the projection to a set of port numbers moves into that method. - is_port_blocked was a one-line membership test; check_port_on_node does it inline. Tests: they faked fencing by patching port_block.set_port, which also told them which node a fence was for. tests._mocks.route_fences keeps that: it wraps each node's rpc_client so nvmf_port_block/unblock reach the same (node, port, block) callback the tests already used. Source text anchors and "must not fence" assertions now look for the RPC calls, and mocks of nvmf_get_blocked_ports return a set. R26.3 port of 59a238b (part of #1497): the same edits replayed on R26.3, where RPCClient calls _request3 and the two-node and failback-listener tests do not exist. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Merged
2 of 4 tasks
wmousa
added a commit
that referenced
this pull request
Oct 17, 2026
…ient fallback (R26.3) (#1496) * Revert "fix(spdk-start): pass the host's fence ports to the SPDK pod so the entrypoint stops flushing iptables (#1484)" This reverts commit 88a50e2. The SPDK image no longer touches iptables at all (ultra #218): 25.10.x -> 26.3 upgrades are offline, so no 25.10 fence rules need cleaning at SPDK start, and nothing reads FENCE_PORTS. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(port-block): fence only through the SPDK RPC, drop the FirewallClient fallback port_block.set_port sent fences to the legacy firewall webapp (FirewallClient, mgmt_ip:firewall_port) whenever node.spdk_version was empty, and redirected a method-not-found from nvmf_port_block there too. 25.10.x node records never carried spdk_version, and it is only set by a restart that passes --spdk-proxy-image, so a node upgraded to 26.3 kept "" and every fence on it went to a port nothing listens on: the SPDK image no longer ships the webapp or iptables (ultra #218). The fence failed with connection refused. - port_block: set_port / get_blocked_ports_set / is_port_blocked use the SPDK RPCs only; an RPC error, method-not-found included, propagates. - health_controller.check_ports_on_node: drop the per-port legacy branch. - Delete fw_api_client.py; port_block was its only user. - Tests: the ftt2 and dual-FT fixtures patch port_block instead of FirewallClient; the expansion simulator answers nvmf_port_block / nvmf_port_unblock / nvmf_get_blocked_ports instead of stubbing FirewallClient; regression tests in test_port_block_rpc_only.py. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * lint: annotate TTLCache._instances as ClassVar (RUF012) The Linter job installs an unpinned ruff; 0.17.1 flags the class-level WeakSet registry as a mutable class attribute. It is meant to be shared across instances, so ClassVar states that. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore: remove the firewall-port plumbing and the jc dependency Nothing has read any of this since fencing moved to the SPDK nvmf_port_block RPC and the SPDK image dropped iptables and the firewall webapp (ultra #218): - StorageNode.firewall_port, the spdk_process_start(firewall_port=) param, the field in both node-API SPDKParams, the FW_PORT env on the SPDK container (docker and the k8s template) and constants.FW_PORT_START. - utils.get_next_fw_port: no callers. - The deprecated node-API routes /snode/firewall_set_port and /snode/get_firewall, which only returned False. - StorageNode.spdk_version: only the removed FirewallClient fallback read it. - The jc dependency (it parsed iptables output), from requirements.txt. Existing FDB records keep their stored firewall_port / spdk_version keys; BaseModel.from_dict only reads declared attributes, so they are ignored. (cherry picked from commit d3459b9, part of #1497) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor: remove utils.port_block, fence through the RPC client directly With the FirewallClient fallback gone, port_block was a pass-through (review on #1496): - set_port only chose nvmf_port_block or nvmf_port_unblock, and every caller passed block= as a constant: the 13 call sites now call node.rpc_client(timeout=.., retry=..).nvmf_port_block/unblock(port) with the budget each already used. The rationale for the short fence budget moves to RPCClient.nvmf_port_block. - get_blocked_ports_set was the only caller of RPCClient.nvmf_get_blocked_ports: the projection to a set of port numbers moves into that method. - is_port_blocked was a one-line membership test; check_port_on_node does it inline. Tests: they faked fencing by patching port_block.set_port, which also told them which node a fence was for. tests._mocks.route_fences keeps that: it wraps each node's rpc_client so nvmf_port_block/unblock reach the same (node, port, block) callback the tests already used. Source text anchors and "must not fence" assertions now look for the RPC calls, and mocks of nvmf_get_blocked_ports return a set. R26.3 port of 59a238b (part of #1497): the same edits replayed on R26.3, where RPCClient calls _request3 and the two-node and failback-listener tests do not exist. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
BombermaG
added a commit
that referenced
this pull request
Oct 20, 2026
Brings in main up to ceff9f5: two-node HA with the arbiter (#1466) and its campaign fixes, regular-cluster gating of the two-node paths (#1475), activation / ANA failback (#1453), the ANA failover retry (#1480), the lvstore poller on nvmf cores (#1452), port fences through nvmf_port_block / nvmf_port_unblock without FirewallClient (#1497), the fenced bdev_lvol_set_leader budget (#1495), keep-alive 2 s on arbitrated clusters (#1494), the unfenced RPC bound during a removal (#1477) and replica repair (#1463). Sync replication adapted to it: - the leadership hand-off fence (_LvsFence, _fence_and_demote_leader, _grant_lvs_leadership) carries main's port-block RPCs, the removal relaxation of the unfenced bound, the set-leader budget and the two-node unblock guard; an abort releases every blocked peer - the sync ANA failover reports whether a flip was attempted, so a skipped failover is retried by the monitor; a removed node attempts nothing and only an attempt is logged - an active primary shut down by a removal counts as down for the site rule and the deferred promotion of its secondary, like an OFFLINE one - the failback of a returning remote primary's secondary is housekeeping that never aborts the rebuild, as the home primary's - the site-activation hold of migrations is a deferral (defer_unless_operable) - cluster activation keeps main's arbitration flag and fences the sync leader with main's port-block RPCs - the v2 cluster update refuses two-node arbitration on a sync-replication cluster (400) before any change - the consistency-group peer serving-check probes every instance of a sync LVS, both sites' triplets - the sync leadership move remembers the arbitration flag for the fence's unblock guard - the Kubernetes drain steps (#1296: prepare-removal, migrate-devices) wait for a running site activation: a retryable refusal (503) before any change, as the removal task defers - docs: the new refusal and the group fail-back no-op with policy-path async replication Tests: sync fixtures route port fences through the RPC mocks, the two-node consumers are pinned off on a sync cluster, and every adaptation has a regression test.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Removes the firewall/iptables leftovers from the control plane on main. Three commits:
1. Fence only through the SPDK RPC, drop the FirewallClient fallback. This is the main version of #1496, which targets R26.3, so main doesn't need a separate cherry-pick of #1496.
port_blockno longer routes fences to the legacy firewall API (FirewallClient,mgmt_ip:firewall_port) whennode.spdk_versionis empty or the RPC returns method-not-found. Every node upgraded from 25.10 had an emptyspdk_version, and nothing listens on that port any more (ultra Testing K8s e2e run. Adding check for docker logs if it is not k8s #218).fw_api_client.pyis deleted.2. Remove the dead plumbing behind it.
firewall_port→FW_PORT:StorageNode.firewall_portfield;spdk_process_start(firewall_port=)param inSNodeClient;SPDKParams;FW_PORTenv on the SPDK container (docker env and the k8s template);constants.FW_PORT_START.The SPDK image stopped reading
FW_PORTwith ultra Testing K8s e2e run. Adding check for docker logs if it is not k8s #218.utils.get_next_fw_port: nothing called it. Its tests are removed.Deprecated node API routes
/snode/firewall_set_portand/snode/get_firewall: they already returnedFalseand did nothing, but were still routes on the unauthenticated :5000 API.StorageNode.spdk_version: it was only read by the fallback removed in commit 1.The
jcdependency (it parsed iptables output).uv.lockwas regenerated with uv 0.12.22, the versiondocker/Dockerfilepins, which also dropsruamel-yamlandxmltodict. That uv writesrevision = 5, anduv lock --checkpasses.Removing a field from the hand-rolled
BaseModelis safe for existing FDB records:from_dictonly reads the model's declared attributes, so the storedfirewall_port/spdk_versionkeys are ignored.Not changed
--snode-api-port/cluster.snode_api_port: its only reader wasget_next_fw_port, so it no longer has any effect. It's part of the cluster-create CLI and the v1/v2 API, so removing it should be a separate interface change.install_deps.shstill installsiptableson Docker-mode hosts. Docker needs it.Test plan
ruff check: cleanmypy simplyblock_web simplyblock_cli simplyblock_core: only the 2asyncio.open_unix_connectionerrors that also occur on unmodified main when run on Windowstest_spdk_proxy_unit.py, 3 intest_lblk_persistent_names.py) are in files this PR does not touch. They look Windows-specific (the proxy tests use Unix sockets) and should pass on Linux CI.uv lock --checkFW_PORT; fence and unfence work throughnvmf_port_block3. Remove
utils.port_block(Max's review on #1496). With the fallback gone, the module was a pass-through:set_port: its 13 call sites all passedblock=as a constant. Each now callsnode.rpc_client(timeout=…, retry=…).nvmf_port_block(port)or.nvmf_port_unblock(port)directly, with the same budget it used before. The note on why fences need a short budget moved toRPCClient.nvmf_port_block.get_blocked_ports_set: the conversion to a set of port numbers moved intoRPCClient.nvmf_get_blocked_ports, its only caller.is_port_blocked: removed;check_port_on_nodedoes the check inline.port_block.set_port, and that patch also told them which node each fence was for. The newtests._mocks.route_fenceskeeps that: it wraps each node'srpc_clientsonvmf_port_block/nvmf_port_unblockreach the same(node, port, block)callback as before. In addition:nvmf_get_blocked_portsreturn a set.--noconftest. That gives the same result before and after this commit: 302 passed, with the same environment-only failures.ftt2andexpansion_simneed their real fixtures, so CI is the check for those.Found while doing this, not changed: the
l2-fw/m2-fwcases intests/integration/ftt2/test_restart_peer_states.pydisconnect a peer when its mock server sees afirewall_set_portRPC. Fencing never reached the mock server, before or after this PR, and the server has no port RPCs, so those cases never trigger and just repeat their siblings. Making them real means addingnvmf_port_blocktoFTT2MockRpcServerand hooking that instead.🤖 Generated with Claude Code