Conversation
This was referenced Sep 19, 2026
openwrt-ai
reviewed
Sep 20, 2026
openwrt-ai
left a comment
Collaborator
There was a problem hiding this comment.
Commit checks
- 9629ec5 "luci-app-qosify: one Service section on Overview" — "Both run on the boot clock, so an NTP step does not skew it" holds only for the first read; the cached path advances the value with
Date.now(). Either the caching or the claim needs to change.
Generated by Claude Code
choppyc79
force-pushed
the
luci-app-qosify-service-section
branch
2 times, most recently
from
September 20, 2026 10:50
f218d98 to
446555e
Compare
openwrt-ai
reviewed
Sep 20, 2026
rpc.declare() resolves a ubus error status unless reject is set, so a
status code (6 once the session's ACL no longer covers the object, 4
when the object is gone) stays in result[0] and expect {'': {}}
rewrites it to the same {} a working call with nothing to report
returns. L.resolveDefault() cannot tell the two apart, so an ACL left
over from an older install, a stale session or a stopped rpcd reads as
a stopped, unshaped qosify with no init script.
Declare rc.list, service.list and qosify.status with reject: true and
catch each rejection to null. running, enabled, hasInit, active and
shaped are then null rather than false when the call was not answered,
and the Overview service table shows an amber Unknown badge for each,
with the cause named once on the Running row. The qosify object goes
with the daemon, so an unanswered status call while qosify is stopped
still reads as not shaping.
The autostart button carries the state as its label, so it shows
Unknown and is disabled while the state is unknown. Its click now reads
the state from the last refresh rather than from when the button was
built, so a tick that changes the state also changes the action.
waitForRunning() and waitForStopped() are now one waitForState(want),
which returns null when the last service.list went unanswered instead
of reporting a state it could not read. The start, stop, restart and
reload buttons stay clickable and a failing rc.init call still reports
its own error, but where a button then waits for the state to settle,
null now says rpcd is not answering and the service state is unknown
rather than that qosify did not come up or is still running. Reload
does not wait, so it reports the call's own error alone. applyService()
takes the same line: an unanswered service.list aborts the apply with
that error instead of reading as a stopped qosify and starting the
daemon.
A save no longer warns that qosify is not shaping when the check could
not run; it says shaping could not be checked. The Status tab says rpcd
is not answering rather than that qosify is not running, and says the
interface summary is unavailable when only qosify status went
unanswered.
po/templates/qosify.pot regenerated with build/i18n-scan.pl.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Signed-off-by: Ash Clarke <clarkeaj@hotmail.co.uk>
A rules edit only needs the mapping files re-read, but the only way to get that from the page is Reload, the init script's reload_service(), which pushes the whole config. Add Reload Rules next to it: ubus call qosify reload runs qosify_map_reload(), which re-reads the files in the defaults list and leaves the qdiscs, the interface config and the dynamically added entries alone. Add Check Devices to the Advanced tab: ubus call qosify check_devices runs qosify_iface_check(), which starts a section whose device now exists and stops one whose device has gone, so a device that appears after qosify started is picked up without a restart rebuilding every qdisc. The method arms a 10 ms uloop timer and returns before the pass runs, so the page waits before refreshing the service state. Both methods return an empty reply, so they are declared with reject: true; without it a failed call would report success. Both are in qosify 1501e09 (24.10, 25.12) and master. Grant them in the write ACL group. po/templates/qosify.pot regenerated with build/i18n-scan.pl. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BoEgybb68JiJnqmasx6Sfh Signed-off-by: Ash Clarke <clarkeaj@hotmail.co.uk>
Both pollers pass a fixed 10 s to poll.add(), so the page ignores luci.main.pollinterval. Leave the interval out so poll.add() takes L.env.pollinterval (5 s unless set), which the user can change. On the default setting the Status tab now forks qosify-status every 5 s rather than 10 s. It still does so only while that tab is open, and Poll.step() still holds the next tick until the last one settles, so a fork slower than the interval skips ticks instead of stacking up. A target that cannot afford the fork at that rate raises luci.main.pollinterval, which slows the rest of LuCI's polling with it, instead of the page choosing its own rate. Pausing with the header refresh toggle is unchanged, as it stops the whole poll loop. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BoEgybb68JiJnqmasx6Sfh Signed-off-by: Ash Clarke <clarkeaj@hotmail.co.uk>
The page draws its own badges, key/value tables, reference boxes and muted notes with qos-* classes and inline styles, inside fieldset and legend sections, so it looks the same under every theme and ignores the theme's own table, label and section styles. Use the markup the themes already style instead: - sections are div.cbi-section with an h3 title - status and file validity use .label success, warning and danger - key/value tables are .table with .tr and .td rows - Quick Settings and the Advanced file rows are .cbi-value rows, with hints as .cbi-value-description - notes are cbi-section-descr, <p> or <em>, and code is <code> qosify.css now draws each section as a box with a title bar from the theme's own variables, gives the two editors and the tc output their size, and lays out the Quick Add rows. Bootstrap and OpenWrt 2020 have no .label.danger, so the sheet supplies it from --error-color-high. The editor width moves from the qos-edit class to the textarea ids with box-sizing: border-box, next to the height, and the editors resize in both directions, so the theme's textarea width cannot win and a wrong width stays correctable. The Config Reference class list becomes a .table like the one on the Rules tab, so it now picks up that tab's existing "No classes defined in %s" row when no class or alias is configured, where the plain div rendered nothing. That is the only behaviour change, and no string is added or altered. po/templates/qosify.pot regenerated with build/i18n-scan.pl for the moved source lines. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: Ash Clarke <clarkeaj@hotmail.co.uk>
qosify keeps per-class counters and, on master, per-DNS-pattern and eBPF map statistics, but the page shows none of them. Add a Counters tab, polled like the others and only while it is open. Traffic by Class lists the classes from ubus call qosify get_stats with their dscp, packets, bytes and share, and a total row. Bar length is the cube root of each row against the largest, so one bulk flow does not flatten every other row; the share column stays exact. Rows are grouped and coloured by the CAKE tin their egress codepoint lands in under the configured diffserv mode, and AFx1 to AFx3 sort by drop precedence. The tcp_default and udp_default slots are skipped, as a dscp_default_* naming a class is already counted against that class. get_stats has two shapes. qosify master (beeb87e) returns a classes table plus ebpf_map_entries, last_reload_time, dns_cache, dscp and dns; 1501e09 (24.10, 25.12) returns qosify_map_stats() at the top level, one table per class with packets only. Both are read, and only the fields the reply carries are shown. Traffic by CAKE Tin graphs the qosify-status output tin by tin, egress and ingress summed from the pkts, bytes and drops rows tc prints, with marks on hover. Qdiscs running a different diffserv mode get a table of their own. It needs the existing qosify-status exec grant, so a read-only session does not get it. DNS Entries lists the dns entries from ubus call qosify dump with hits, packets and bytes from the get_stats dns table. It is shown only when get_stats carries that table, so dump is never called on 1501e09. While qosify is stopped the figures are cleared rather than left on screen looking live. get_stats and dump are declared with reject: true, so a failed call is reported rather than read as an empty reply. Tin colours use the theme's error, primary, warn and success colours, with light and dark fallbacks for the extra diffserv8 and precedence tins. Grant get_stats and dump in the read ACL group. po/templates/qosify.pot regenerated with build/i18n-scan.pl. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BoEgybb68JiJnqmasx6Sfh Signed-off-by: Ash Clarke <clarkeaj@hotmail.co.uk>
The service state is split over the Service Status table, a Service Controls section, and the per-interface summary at the top of the Status tab. Merge it into one Service section on Overview: status, uptime, autostart, the number of shaping interfaces, one row per interface or device from ubus call qosify status (active state, resolved device, ingress, egress) and /etc/init.d/qosify. The table follows the configured interfaces, so a tick swaps it whole; it holds no input or focus. Uptime is the qosify process's starttime from /proc/<pid>/stat against /proc/uptime. Both run on the boot clock, so an NTP step does not skew it. The start is cached per pid, so a reload keeps counting, a restart starts again, and ticks read nothing new until the pid changes. Grant read on /proc/uptime and /proc/[0-9]*/stat. The service buttons move to a bar at the bottom of Overview. Buttons that do not apply to the current state are disabled: Start while running, Restart, Reload and Stop while stopped, and Reload Rules while the daemon is down. With the state unknown they stay clickable, so the failing call reports its own error. The two config files are one table with status, section or rule count, size and modification time. The Status tab shows only the qosify-status output and no longer calls qosify status. Counters moves before Advanced in the tab order. po/templates/qosify.pot regenerated with build/i18n-scan.pl. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BoEgybb68JiJnqmasx6Sfh Signed-off-by: Ash Clarke <clarkeaj@hotmail.co.uk>
choppyc79
force-pushed
the
luci-app-qosify-service-section
branch
from
September 20, 2026 18:36
446555e to
32e6820
Compare
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.
Pull request details
Description
Depends on #9046, #9047, #9048, #9050, #9051. Only the last commit is this PR; draft until those are merged.
The service state was split over Service Status, Service Controls and the
top of the Status tab. Overview now has one Service section: status,
uptime, autostart, shaping count, one row per interface/device from ubus
call qosify status, and /etc/init.d/qosify.
cached per pid, so a reload keeps counting and a restart starts again.
the current state are disabled; with the state unknown they stay
clickable and report the call's own error.
ACL: read group gains read on /proc/uptime and /proc/[0-9]*/stat.
Screenshot or video of changes
<Overview before / after>
Maintainer
@choppyc79
Tested on
OpenWrt version: SNAPSHOT r36237-2ed2c210c3
LuCI version: 26.257.37274~f4eebd4
Web browser(s): Google Chrome
Checklist