Repository navigation
0.6.2: firewalld --check in one parallel wave of whole listings (#29) - #30
Merged
Merged
Conversation
tui-firewall --check took about 8 s on a firewalld host, and sibling tools (tui-wireguard, tui-tailscale) call it on every reload. Each firewall-cmd is a Python interpreter plus D-Bus calls that firewalld answers one at a time, each checked with polkit, and Load ran 10 + 2 per policy object of them one after another, with the compat probe's --version before them. Load now starts nine reads together: --state, --list-all-zones and --list-all-policies (runtime and permanent), --get-services, --get-log-denied, --query-panic and --query-lockdown. The default zone and the active zones come from the "(default, active)" header flags of the runtime zone listing, which firewall-cmd computes from the same daemon calls --get-default-zone and --get-active-zones make; a firewalld that prints no flags, or lacks --list-all-policies, falls back to the old reads for that part only. --check also runs the version probe and the backend survey while the backend loads. The parsed model is unchanged: unit tests build it both ways from captured firewall-cmd 2.3.2 and 2.4.4 output and compare, and the smoke test checks the active zones against --get-active-zones and bounds the time --check takes. Version 0.6.2. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MbCfX3T8khyd9hsrN5kMo3
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MbCfX3T8khyd9hsrN5kMo3
The runtime zone listing is answered only by a running daemon, so its success already says what --state would; --state is now asked only when that listing fails for another reason, which saves one Python process per load. A stopped daemon is recognised from the listing's own "FirewallD is not running", because each firewall-cmd waits about 10 s for the daemon on D-Bus and asking --state afterwards would double that wait. The README's --check section now states what --check costs per backend and that callers polling it on every reload may keep the answer for a few seconds. The smoke timing uses EPOCHREALTIME, since the uutils date on newer distributions does not expand %3N. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MbCfX3T8khyd9hsrN5kMo3
The lab's 2-vCPU Fedora 44 guest runs --check in about 1.7 s (was 8 s); the README and the smoke comment now quote that rather than the faster container stand-in. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MbCfX3T8khyd9hsrN5kMo3
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.
tui-firewall --checktook about 8 s on the lab's Fedora 44 guest (firewalld), and tui-wireguard and tui-tailscale read it on every reload.Where the time went
0.6.1 ran
firewall-cmd --version(compat probe) and then, one after another,--state,--get-default-zone,--get-active-zones,--list-all-zones,--permanent --list-all-zones,--get-services,--get-log-denied,--query-panic,--query-lockdown,--get-policiesand two--policy=X --list-allreads per policy object. Fedora 44 ships 7 policy objects out of the box (allow-host-ipv6and sixgateway-*), so that is 25 processes in a row.Per read on the lab's
fedoraguest (Fedora 44 Cloud, firewalld 2.4.4, 2 vCPUs), profiled withsudo -n python3 -m cProfile /usr/bin/firewall-cmd .... Wall time, D-Bus round trips (blocking calls plus proxy introspections) and the time spent waiting on them:--state--get-default-zone--get-active-zones--list-all-zones--permanent --list-all-zones--get-services--get-log-denied--query-panic--query-lockdown--get-policies--list-all-policies--permanent --list-all-policies--policy=allow-host-ipv6 --list-all--permanent --policy=allow-host-ipv6 --list-all--version(profiler overhead included; without it each read is about 300 ms on this guest, and the 0.6.1 sequence sums to 7.5 s.) Around 85% of every read is the Python interpreter starting and importing the firewalld client; D-Bus is 40 to 180 ms of it. The cost is the number of processes in a row, so the fix is fewer processes and no queue.
What changed
Load(the one read path, used by--checkand by the TUI's startup andRreload alike) now starts eight reads together:--list-all-zonesand--list-all-policies, runtime and permanent, plus--get-services,--get-log-denied,--query-panic,--query-lockdown.(default, active)header flags of the runtime zone listing. firewall-cmd computes those flags from the same daemon calls--get-default-zoneand--get-active-zonesmake, and the active zones' interfaces and sources are the section's own. A firewalld that prints no flags falls back to the two queries.--list-all-policiesreplaces--get-policies+ 2 reads per policy (same bound of 32, same name check). A firewalld without it falls back to the per-policy reads, also started together.--stateis asked only when the runtime zone listing fails for a reason other than "FirewallD is not running": a successful runtime listing already proves the daemon is up. With firewalld stopped, each firewall-cmd waits about 10 s for the daemon on D-Bus, so the stopped case is recognised from the listing's own message instead of paying that twice.--checkruns the version probe and the backend survey (systemctl is-active/is-enabled) while the backend loads.firewall-cmdininternal/firewalld/only (check-exec clean). A direct D-Bus read would be faster still but would break the "the command you see is the command that runs" rule the package doc explains, so it was not done.Evidence
Lab (tui-lab guests
fedoraandubuntu26)--checkfrom 0.6.1 (main) and from this branch, back to back, as the lab user withsudo -n.IDENTICALiscmpof the two full JSON outputs:On fedora the median dropped from 8.0 s to 1.67 s, under the 2 s target. Because of that, the extra cut (skipping
--query-lockdownon firewalld 2.2 and later) was not needed and is not in this PR../lab.sh test tui-firewall fedora ubuntu26 --bin <this branch>with this branch'stest/smoke.sh: fedora 21 passed, 0 failed (includingthe active zones are the ones --get-active-zones lists (public)and--check answers in under 3 s (took 1592 ms)); ubuntu26 10 passed, 0 failed. Compat lines: firewalld 2.4.4 on fedora-44 pass, ufw 0.36.2 on ubuntu-26.04 pass.Before the lab was reachable (container stand-in)
Earlier I checked the same things in rootless privileged podman containers that I created and then removed. The firewalld one was Fedora 44 with firewalld 2.4.4, limited to 2 CPUs, and seeded with three zones plus services, ports, a rich rule, a bound source, masquerade, a forward port, a custom policy and entries in only one of the two scopes. The ufw one was Ubuntu 26.04. Results:
--checkwent from 2.7 s to 1.0 s with identical JSON over 5 pairs. The output was also identical for all four--demobackends and with firewalld stopped (12.2 s before, 10.8 s after: every firewall-cmd waits about 10 s for a stopped daemon, and the new code does not wait a second time for--state). The smoke passed 21/21 and 10/10.Unit tests:
load_test.goreplays capturedfirewall-cmdoutput (2.3.2 and 2.4.4 fixtures, plus the new--list-all-policiespair) through both the 0.6.1 read sequence and the new one and asserts the samefirewall.Model. It also covers the fallbacks (no header flags, no--list-all-policies, no policy objects at all, zone listing refused, daemon stopped) and the read count (8, with none of the fallbacks asked).Release notes
--checkand the reload in the UI read the firewall in under 2 s instead of about 8 s. The tool now asks firewalld for whole zone and policy listings and runs those reads in parallel. The output is unchanged.--checksection says what--checkcosts on each backend, and that callers can keep its answer for a few seconds.Version 0.6.2.
Closes #29.
🤖 Generated with Claude Code
https://claude.ai/code/session_01MbCfX3T8khyd9hsrN5kMo3