7295 Commits

Author SHA1 Message Date
YueGuobin
ec2a0ffff3
log: lower per-node lifecycle logs in base_node to DEBUG
The per-node created / is-closing / Starting-uBridge / Stopping-uBridge
INFO lines flood the log at 1000+ node scale during project open, close,
start-all and stop-all. Demote them to DEBUG (consistent with the
docker_vm lifecycle-log demotion).
2026-08-11 00:39:49 +08:00
YueGuobin
3aa39a2da3
perf: raise start/stop/suspend/reset-console concurrency from 3
The historic concurrency=3 was far too conservative for 1000+ node
topologies. Raise them based on operation weight:

  stop_all:      3 -> 100  (docker kill + permissions cleanup, light)
  suspend_all:   3 ->  50  (docker pause/unpause, light)
  start_all:     3 ->  20  (ubridge startup + docker start + network, heavy)
  reset_console: 3 ->  20  (terminal reset, light)

Node creation (open) already uses concurrency=100 and approaches the
container-create daemon limit (~20/s); stop/kill is substantially faster
than create, so 100 is safe.
2026-08-11 00:33:49 +08:00
YueGuobin
7dc66d4836
fix: race in concurrent node creation sending duplicate POST /projects
When opening a project, nodes are created with Pool(concurrency=100).
Each _create_node did an unlocked check-then-act: 'if compute not in
_project_created_on_compute' -> await POST /projects -> add(compute).
The await let dozens of concurrent node creations race past the check
before any registered, each firing a redundant POST /projects at the
same compute. The compute-side sync handler then ran in a thread pool
and instantiated the Project N times (the repeated 'Project ... created'
INFO logs, ~16x).

Guard the check+POST+register with a project-level asyncio.Lock. The
first creation holds it for one POST; the rest acquire, see the compute
already registered, and return immediately — negligible serialization.

Also annotate the node/link progress logs with project name+id and add
completion lines, so the log clearly shows which project is loading and
when each phase finishes.
2026-08-11 00:30:33 +08:00
YueGuobin
df3ca6e26b
log: lower per-node docker lifecycle logs to DEBUG
At 1000+ nodes the per-node INFO lines flood the log during open /
start-all / stop-all: MAC changed, adapters changed, created, started,
console listen, fix ownership, stopped, paused, removed, adapter created,
NIO removed, capture start/stop, CPU/memory limits, mount resources.

Demote all of these routine per-node/per-adapter lines to DEBUG. Keep
INFO only for genuinely rare/important events: image pull (missing image)
and stale-container cleanup. Warnings unchanged.
2026-08-11 00:22:26 +08:00
YueGuobin
4c6ef7752a
log: emit progress lines for node and link loading on project open
A large topology takes ~50s to open (dominated by docker daemon node
creation). Without any start marker the user sees no feedback that work
is in progress. Add two INFO lines: 'Loading N nodes...' before the node
pool and 'Creating N links...' before the bulk link prepare/dispatch.
2026-08-11 00:16:32 +08:00
YueGuobin
e2fd922922
cleanup: remove project-open stage timing logs, lower NIO-added log to debug
- Drop the diagnostic stage-timing logs added during link-create perf
  work (nodes / preallocate / prepare / dispatch) now that bottlenecks
  are resolved and verified.
- Lower the per-NIO 'added to adapter' log in docker_vm from INFO to
  DEBUG — at 5000+ NIOs per project open it floods the log at INFO.
2026-08-11 00:14:58 +08:00
YueGuobin
c9a93c0aeb
perf: skip per-link topology dump during project-open prepare
Stage timing showed prepare taking 96s for 1275 links (vs 0.31s for the
batch dispatch itself). Root cause: _prepare_link_from_topology called
add_link (dump=True default) and update_link_style/update_show_filters_icon
(each unconditionally dump the full topology). 1275 links x serialize-
and-write-the-whole-topology = the entire 96s.

- add_link(..., dump=False): the project is dumped once at the end of open
- set link._link_style / _show_filters_icon directly instead of the
  update_* helpers, which also avoids spurious 'link.updated' notifications
  before the link is finalised

The final self.dump() at the end of project.open already persists everything.
2026-08-10 23:54:26 +08:00
YueGuobin
38c49a655c
perf: batch NIO dispatch on project open (one HTTP per compute)
Project open used to create each link by issuing two NIO POSTs from the
controller to the compute — ~5000 HTTP round-trips for a 2500-link
topology, all funnelling through the single shared controller/compute
event loop and capping throughput near 12 links/s.

Replace it with a bulk path:
- UDPLink split into _prepare() (local: ports, peer addrs, link_data)
  and _commit_nios() (dispatch). create() = prepare + commit (interactive).
- Link.add_node gains batch=True: attach both nodes without triggering
  per-link NIO HTTP.
- compute: new POST /projects/{id}/nios/batch endpoint with a unified
  _add_nio_binding dispatch across node types (docker/qemu/iou/vpcs/
  builtin differ in signature).
- project.open: prepare all links locally, group NIO entries by compute,
  send each compute a single /nios/batch, then finalise (wire node/port
  refs, mark created, notify, apply marker defs) in parallel.

Cuts controller->compute HTTP from O(links) to O(computes). Test added
for the batch endpoint.
2026-08-10 23:42:50 +08:00
YueGuobin
96d4d82716
perf: cache compute.host_ip + add UDPLink.create timing log
host_ip resolved socket.gethostbyname on every access with no cache.
get_ip_on_same_subnet touches host_ip 2-4 times per link, so opening a
2500-link project issued thousands of blocking DNS calls inline on the
event loop — freezing all concurrent link coroutines each time. This is
the most likely cause of the 12-link/s throughput (1000x below what
Pool(concurrency=100) should deliver) and the burst+pause pattern.

- compute.host_ip: cache the resolution in _host_ip_cache, invalidate
  on host setter change
- UDPLink.create: timing log splitting get_ip / ports / nio so the next
  project-open confirms where time actually goes
2026-08-10 23:20:40 +08:00
YueGuobin
b155980402
perf: dedicated 500-worker thread pool for ubridge batch I/O
Replace the default asyncio executor (capped at ~32 threads) with a
dedicated ThreadPoolExecutor sized for large-topology parallelism.
When 500 nodes each call _connect_nio, up to 500 OS threads can now
send blocking ubridge commands in parallel — no longer serialised by
either the event loop or a small thread pool.

- ubridge_hypervisor: module-level _ubridge_sync_pool (max_workers=500)
- docker_vm._connect_nio: dispatches to the dedicated pool instead of
  the default executor
2026-08-10 23:11:23 +08:00
YueGuobin
05934fa8e8
perf: offload per-NIO ubridge commands to thread-pool executor
Replace the 3-5 sequential await _ubridge_send calls in _connect_nio
with a single run_in_executor batch.  The batch holds the node-level
asyncio Lock to prevent interleaving with async sends, then uses the
hypervisor's new send_batch_sync method which does blocking socket
sendall/recv inside the thread pool.  Different nodes' batches now
run in true OS-thread parallelism rather than serialising through
the asyncio event loop between every command.

- ubridge_hypervisor.send_batch_sync: blocking batch send using
  the underlying socket from the asyncio transport, protected by
  threading.Lock.
- _connect_nio: builds command list (add_nio_udp, start_capture,
  bridge start, reset_packet_filters, add_packet_filter) and
  dispatches to the default executor.
2026-08-10 23:06:54 +08:00
YueGuobin
506b0b9f4a
perf: enable TCP keep-alive for local compute HTTP requests
When controller and compute share the same process, every HTTP request
to localhost was paying a full TCP handshake (force_close=True forced
connection teardown after each request).  With 2500+ links each sending
two NIO POSTs, that's 5000 SYN->SYN-ACK->ACK cycles even for sub-ms
in-memory handlers.  Switch to keep-alive for loopback compute
(127.0.0.1 / ::1 / localhost) while keeping force_close for remote
computes that may sit behind NAT/firewalls that drop idle connections.
2026-08-10 23:00:59 +08:00
YueGuobin
82fc7f2bd1
debug: add per-command timing to _connect_nio ubridge calls
Temporary diagnostic instrumentation to measure the wall-clock time of
each ubridge command during NIO addition (add_nio_udp, bridge start,
filters, markers).  The logs will reveal whether the 12-NIO/s
throughput stems from ubridge command latency itself or from lock
contention / HTTP overhead outside _connect_nio.
2026-08-10 22:45:32 +08:00
YueGuobin
acc9e670af
perf: parallelize UDP port allocation and NIO creation in UDPLink.create
Previously the two sides' UDP port reservations and NIO tunnel
POSTs were issued sequentially, even though they are independent
once the peer addresses are known — each node talks to its own
compute/uBridge with no shared lock, so the HTTP round-trips can
overlap.

- Port allocation: gather _allocate_port for both computes
- NIO creation: gather both node.post calls together
- Error handling: if either side fails, roll back whichever side
  succeeded (previously only node2-fails→cleanup-node1) before
  re-raising the first error

Batch loading (open/import project) benefits most because links
are created at high concurrency and the per-link wall-clock
time is dominated by the sum of its two sequential NIO POSTs.
2026-08-10 22:36:41 +08:00
Jeremy Grossmann
372874c7d5
Merge pull request #2847 from yueguobin/fix/docker-pid1-signal-chain
fix: SIGKILL docker container on stop instead of 5s grace period
2026-08-10 16:03:22 +02:00
YueGuobin
e7dfe9c7c4
fix: SIGKILL docker container on stop instead of 5s grace period
Docker node stop took ~5s every time. The stop API grace period
(params t=5, unchanged since 2015) was always exhausted: the business
process (often an interactive shell) ignores SIGTERM, and GNS3 doesn't
depend on graceful shutdown — _fix_permissions and /gns3volumes already
persist container state before stop() is called.

Use POST /containers/{id}/kill (SIGKILL, zero delay) instead of stop.
The 409 (container already stopped) replaces the previous 304 handling
for the race where the container exits between the state check and the call.

t=5 traced to commit 33edbefa3 (2015-10-14) "Docker cleanup and
improvements" — introduced with no recorded rationale.
2026-08-10 21:48:26 +08:00
Jeremy Grossmann
cf072c7922
Merge pull request #2752 from Volo6uev/base-configs-3.0
Api endpoints to manage base configuration files for templates
2026-08-08 22:16:36 +02:00
Jeremy Grossmann
909ccf8fcd
Merge branch '3.1' into base-configs-3.0 2026-08-08 22:11:39 +02:00
Jeremy Grossmann
1d6218a3f1
Merge pull request #2846 from GNS3/bugfix/2773
Handle manual project deletion
2026-08-08 22:10:46 +02:00
grossmj
bc8e43dfca
fix: add event handling for deleted projects 2026-08-08 17:27:52 +02:00
grossmj
544d15c87e
fix: tests after merging 2026-08-08 16:50:19 +02:00
grossmj
dff79f65d3
Merge branch '2.2' into 3.1
# Conflicts:
#	gns3server/compute/qemu/qemu_vm.py
#	gns3server/controller/import_project.py
#	gns3server/controller/project.py
#	gns3server/crash_report.py
#	gns3server/version.py
#	tests/compute/docker/test_docker_vm.py
#	tests/controller/test_import_project.py
#	tests/controller/test_project.py
2026-08-08 16:27:14 +02:00
Jeremy Grossmann
955d545cf2
Merge pull request #2844 from yueguobin/marker-enabled-pause-resume
marker: direction, real-time control, serial-link (WAN) support; uBridge AF_UNIX control channel
2026-08-07 22:00:13 +02:00
Guobin Yue
183739e630
Merge branch '3.1' into marker-enabled-pause-resume 2026-08-08 01:18:39 +08:00
YueGuobin
82945cd81e
server: raise the open-files (RLIMIT_NOFILE) limit at startup
Every started node holds ~3 file descriptors in the server's table (pidfd + stdout/stderr pipes per child process), so a few hundred started nodes exhaust the default 1024 soft limit and the uBridge version-check subprocess fails with EMFILE ('Too many open files: /dev/null').

At startup, best-effort raise RLIMIT_NOFILE to 65535 (capped by the hard limit); failures are logged, never fatal. Runs before daemonize() so the daemon inherits the raised limit. Linux-only, matching the platform target.
2026-08-08 00:26:00 +08:00
YueGuobin
ad8bac8328
marker: batch topology dumps in bulk fan-out (per-def on 500+ links was ~1 minute)
Every per-link marker operation (start_marker / stop_marker / update_marker) called Project.dump() -- a full topology serialization + file write. A definition fan-out over 500 links therefore wrote the whole topology 500+ times (each blocking the event loop), which dominated the observed ~1 minute; the NIO round-trips themselves were negligible.

Add a dump: bool = True parameter to the three per-link operations and inherit_marker (matching the existing dump param on Link.add_node). The bulk paths -- definition create fan-out, definition-update sync and re-fan-out, definition-delete cleanup, pause/resume, new-link inheritance -- pass dump=False and their caller dumps once after. apply_defs_to_new_link suppresses per-def dumps too: link create / project open dump once after, so opening a 500-link project with N definitions no longer does 500xN topology writes.
2026-08-08 00:03:30 +08:00
YueGuobin
078cf92aef
marker: concurrent (bounded) definition fan-out
The per-definition fan-out applied markers to links in a serial loop -- one compute round-trip per link. On a 1000-link project that serializes N HTTP round-trips (minutes on remote computes). Fan out with asyncio.gather + Semaphore(32): links are independent (own _markers/_link_data), per-link ControllerError stays isolated, and Project.dump is synchronous + atomic (tmp + rename) so concurrent dumps cannot corrupt the topology file.

Converts the definition-create fan-out, the definition-update sync and re-fan-out loops, and the definition-delete cleanup to the shared _marker_apply_concurrently helper. apply_defs_to_new_link stays serial deliberately: all definitions share one link and each push carries the link's full marker set, so concurrent pushes would race and lose markers.
2026-08-07 23:50:41 +08:00
YueGuobin
0f86dabfa3
marker: forward data_link_type on per-link marker create
The per-link create_marker route (links.py) called start_marker without the data_link_type from the body, so it always defaulted to DLT_EN10MB and a serial encapsulation chosen by the caller was silently dropped. Same oversight the IOU capture and definition routes had; one-arg fix.
2026-08-07 13:38:33 +08:00
YueGuobin
ba318150fa
iou: skip already-installed markers in _ubridge_apply_markers (NIO update idempotency)
The IOU override of _ubridge_apply_markers lacked the incremental guard the generic path has, so on an NIO update it re-added every marker the port already carried. uBridge's add_packet_filter rejects a duplicate filter name (packet_filter.c find_packet_filter), so adding a private marker to an IOU link that already hosted an inherited global-* copy failed with 'Failed to add filter global-...' -- the NIO update re-sends ALL markers on the port (inherited + new private), and the pre-existing one collided.

Add the same (name, link_id) in self._marker_filter_bridges skip as the generic base_node path, so an update only installs markers not already on the port. Mirrors how Dynamips/vpcs/etc. stay idempotent across add + update.
2026-08-07 13:30:12 +08:00
YueGuobin
3322233658
marker: serial-link (WAN) support via data_link_type -> uBridge linktype
Markers now work on serial links (Cisco HDLC / PPP / Frame Relay / ATM), not just Ethernet. A marker carries a data_link_type (default DLT_EN10MB); at the uBridge boundary it becomes the 'mark ... linktype <dlt>' keyword so the BPF compiles and the pcap is written with the matching link-layer.

- MarkerCreate / MarkerDefinitionCreate gain data_link_type (default DLT_EN10MB). Per-link it is create-only; definitions are updatable (a change re-fans-out).
- base_node._marker_linktype() normalizes the GNS3 DLT name (strip DLT_, uppercase, None for EN10MB). Single source is SerialPort.data_link_types, so Cisco PPP -> PPP_SERIAL (50), matching the capture path -- no second mapping table.
- _ubridge_add_marker_filter (generic) and the IOU marker loop append 'linktype <dlt>'.
- Definition fan-out branches on link_type in inherit_marker: Ethernet is always EN10MB; a serial link uses the definition's WAN encapsulation, or is SKIPPED when none was chosen (an EN10MB pcap on serial is undecodable). One definition covers a mixed topology.
- MCP marker_definition exposes data_link_type (None = not forwarded).
- No uBridge rebuild on a data_link_type change -- only that one marker's filter is swapped (delete + re-add), mirroring a BPF change; reset_packet_filters preserves sibling mark filters.

Requires the uBridge build with 'mark ... linktype' support.
2026-08-07 01:53:33 +08:00
Jeremy Grossmann
555c3ecab5
Merge pull request #2845 from yueguobin/fix/iou-serial-capture-linktype
Fix IOU serial-link captures always written as Ethernet (DLT_EN10MB)
2026-08-06 19:07:22 +02:00
YueGuobin
117f580cfd
Merge fix/iou-serial-capture-linktype: IOU serial capture data_link_type 2026-08-07 00:36:25 +08:00
YueGuobin
17020b7f7b
iou: forward data_link_type on capture start so serial pcaps use the correct linktype
The IOU compute capture/start route received data_link_type in the NodeCapture body but dropped it when calling node.start_capture(), so iou_vm.start_capture always defaulted to DLT_EN10MB -- every IOU serial capture (Cisco HDLC / PPP / Frame Relay) was written as an Ethernet pcap. Dynamips, cloud and the L2 switches already forward this value; IOU was the only serial-capable node that omitted it. One-argument fix: the node method already accepts and forwards data_link_type to 'iol_bridge start_capture', so only the route was missing it.

Verified: IOU serial captures now produce the expected linktype (C_HDLC / PPP_SERIAL / FRELAY) instead of Ethernet.
2026-08-07 00:35:39 +08:00
YueGuobin
5f4ac38434
ubridge: default the control transport to unix
Switch ubridge_control_transport from tcp (-H) to unix (-U), the
AF_UNIX + SO_PEERCRED channel recommended on Linux for kernel-level
peer authentication. tcp is retained as an opt-in for backward
compatibility. Existing deployments that set the key explicitly are
unaffected; only fresh installs / unset keys pick up the new default.
2026-08-06 21:35:09 +08:00
YueGuobin
b8ac76f047
ubridge: require version >= 1.2.0
Drop the platform-specific floor (0.9.12 darwin / 0.9.14 others) in
favour of a single 1.2.0 minimum. This server now relies on features
only present in recent uBridge builds: the AF_UNIX control channel
(-U / SO_PEERCRED), the marker (mark) filter, and the brctl-backed
builtin Ethernet Switch. Removes the now-unused sys import left behind
by the dropped darwin branch (target is Linux-only).
2026-08-06 21:35:09 +08:00
grossmj
29c1b04078
fix: add missing HTTPException import 2026-08-05 21:55:23 +02:00
YueGuobin
7ed4eeab29
mcp: drop direction from marker_definition tool
A marker definition fans out to every link and auto-selects its capture node
on each, so tx/rx is relative to a node that varies per link — the controller
already rejects it (409). Exposing direction on the MCP definition tool let an
agent ask for something that could only fail. Remove the parameter and the
handler's direction handling; the docstring now points to encoding direction in
the BPF (e.g. 'icmp and icmp[icmptype]==8'). Per-link link_marker keeps
direction, where the capture node is fixed.
2026-08-05 01:40:22 +08:00
YueGuobin
f7b19dae99
marker: tighten marker name max length from 128 to 32
128 characters was far beyond any realistic marker label (icmp, arp, tcp-syn)
and would have collided with the pcap filename budget once a tag prefix is
added later. Cap the user-facing name at 32 in both MarkerCreate and
MarkerDefinitionCreate; the compute-side name guard now also rejects names
longer than 48, which covers the `global-{def_name}` inherited form (≤ 39).
2026-08-05 00:49:37 +08:00
YueGuobin
75f278228b
marker: drop deleted marker from port NIO cache to stop empty pcap on restart
Deleting a marker while its node was stopped, then starting the node, recreated
an empty pcap. Root cause: delete_marker_capture removed the uBridge filter and
the pcap file but not the marker spec cached on the port NIO (nio.markers) —
the data source _ubridge_apply_markers reads on node start. The stale spec
reinstalled the marker when uBridge came up.

This was a regression from switching stop_marker off update() (which re-sent
the NIO and implicitly refreshed nio.markers) to the fine-grained
node.delete(/markers/{name}) path.

Fix: make the delete port-aware so the compute can locate the NIO. The DELETE
marker route becomes /adapters/{a}/ports/{p}/markers/{name} across all six
node types; the handler resolves the NIO via get_nio and passes it to
delete_marker_capture, which now pops the marker from nio.markers. get_nio
works regardless of uBridge state, so the stopped-node case is covered. The
controller's stop_marker targets the capture side's adapter/port.
2026-08-05 00:08:12 +08:00
YueGuobin
1a0ce51f38
marker: validate def BPF once, skip re-validation on inherited fan-out
A marker definition validated its BPF N times — once per link in the fan-out
(start_marker runs validate_bpf_syntax on every copy), spawning one tcpdump -d
subprocess per link for the same expression. Definitions did not validate BPF
at all; only direction was checked.

Move the single validation point to the definition layer (create/update), and
validate each definition's BPF on project load (dropping any that have gone
invalid, like private markers). The inherited fan-out (start_marker) and def
sync (update_marker) now skip validate_bpf_syntax for inherited copies, since
the BPF comes from an already-validated definition. Private per-link markers
still validate inline as before. uBridge still runs pcap_compile at install, so
an invalid expression can never slip through.

Creating a definition over N links now runs one tcpdump instead of N.
2026-08-04 23:20:58 +08:00
YueGuobin
60e2bbbbbb
marker: point directional defs at BPF, refresh implementation doc
The 409 on a tx/rx definition now recommends encoding direction in the BPF
(e.g. icmp[icmptype]==8) as the primary fix, with per-link markers as the
single-link fallback. Doc updated: per-def rejects tx/rx (why + BPF), the
pause section no longer claims bpf changes reset+reapply (they rebuild one
filter), and a new Capture files section covers pcap cleanup + reset-preserves-mark.
2026-08-04 22:59:12 +08:00
YueGuobin
95824b1861
marker: incremental apply, clear bridge map on uBridge stop
_ubridge_apply_markers now installs only markers not already on the bridge
(uBridge's reset_packet_filters preserves mark filters), so an NIO update no
longer re-adds — and reopens — sibling markers' pcaps. _stop_ubridge clears
_marker_filter_bridges so a node restart re-installs everything (the map would
otherwise keep stale entries pointing at a fresh, empty uBridge).
2026-08-04 22:19:43 +08:00
YueGuobin
caec71aa71
marker: fine-grained filter ops, clean pcap on remove
Deleting or updating a marker no longer triggers a full NIO reapply
(reset_packet_filters + re-add), which closed/reopened every sibling
marker's pcap via uBridge. Instead operate on single filters:

- stop_marker: bridge delete_packet_filter + unlink the pcap (works with
  the node stopped; filter removal is skipped, the file is still deleted).
- update_marker: bpf/tag/direction → rebuild just that filter (delete + add);
  enabled → instant toggle; color/highlight_duration → stored only.
- compute delete_marker_capture / rebuild_marker_filter + per-node routes
  (DELETE /markers/{name}, PUT /markers/{name}/rebuild) + MarkerRebuild schema.

IOU overrides _ubridge_delete_marker_filter for iol_bridge; rebuild reuses
the already-overridden add/delete/set, so IOU needs no rebuild override.
2026-08-04 21:35:31 +08:00
YueGuobin
19815f7a37
marker: restore direction on project load, reject tx/rx on definitions
Restore a private marker's direction (and highlight_duration) when a
project is reopened — _create_link_from_topology_data previously dropped
them, silently reverting rx/tx markers to "both".

Reject tx/rx direction on marker definitions with HTTP 409: a definition
auto-selects its capture node per link and direction is relative to that
node, so a fixed tx/rx has no stable project-wide meaning. Per-link
markers still support tx/rx; only the project-wide definition is restricted
to "both" (the default).
2026-08-04 10:14:02 +08:00
YueGuobin
9323ea4cec
schema: accept direction='both' in marker schemas, normalize to None 2026-08-03 00:31:45 +08:00
YueGuobin
b6959f8214
qemu: demote QEMU monitor connect and set_link logs to DEBUG 2026-08-03 00:17:34 +08:00
YueGuobin
e3ce234a09
docs: add log-interpretation note across node types for marker operations 2026-08-03 00:05:08 +08:00
YueGuobin
ff907da5f6
marker: key _marker_filter_bridges by (name, link_id) so multi-link nodes toggle every copy
The _marker_filter_bridges dict was keyed by marker name alone, so when one
node hosted the same filter name on several links (IUOL-BRIDGE per node with
many bays/units, or a multi-interface router), successive apply calls
overwrote earlier entries. pause_marker_definition then toggled only the last
recorded bridge/location — other copies stayed active and kept emitting.

Key by (name, link_id) so each copy is independent, and iterate all matching
entries in _ubridge_set_marker_filter_state (both generic bridge and IOU
iol_bridge override). Toggle route existence checks also iterate matching
names. Tests updated.
2026-08-02 23:48:24 +08:00
YueGuobin
34b644a548
marker: make per-filter toggle fall back to NIO rebuild when the marker isn't installed
Toggle routes silently no-opped when _marker_filter_bridges lacked the filter
name, so update_marker's enabled-only short-circuit succeeded without toggling
uBridge — the controller-layer enabled was set but the uBridge filter stayed on
and kept emitting signals. Now the toggle routes raise HTTPException 404
(FastAPI handles it directly, no ERROR log); the controller's except catches it
and falls back to self.update() (NIO rebuild, which applies the marker + off).
2026-08-02 23:41:43 +08:00
YueGuobin
0afbb897a5
marker: make per-filter toggle a no-op when the marker isn't installed
_ubridge_set_marker_filter_state raised "Marker X is not installed on this
node" when the capture node wasn't running — the name->bridge map is only
populated during _ubridge_apply_markers, which runs when the node is up. The
error was noise: the controller's enabled-only short-circuit catches it and
falls back, and the controller-layer enabled is authoritative (honoured when
the node starts and applies the marker). Treat a missing entry as a no-op
instead of raising.
2026-08-02 23:23:53 +08:00