211 Commits

Author SHA1 Message Date
YueGuobin
c3c025ab2a
fix: resolve the code-review findings on the kernel datapath
Compute:
- Track the anchor that owns uBridge's single AF_PACKET capture slot: a
  second kernel port's capture is refused with a clear error (its flag
  rolled back), a failed start no longer wedges the port, and only the
  owning port's stop issues the process-wide `capture stop_kernel`
  (docker/qemu/iou/dynamips/switch, via the mixin's slot helpers).
- Unwind failed starts instead of leaking uBridge and the anchor TAPs:
  QEMU's stop body runs under try/finally, QEMU and IOU start the datapath
  inside the launch try, Dynamips undoes a half-attached port (the retry
  used to be short-circuited into a silent black hole), and Docker deletes
  the veth pair when the namespace move fails.
- Restore the relay filter guard on IOU (kernel-only filters raise
  NodeError, an uncompilable BPF line degrades to a warning), validate the
  eBPF/quota/window/bpf filter shapes before touching the bridge (short or
  non-numeric values surfaced as 500s after partial state), and refuse a
  second NIO on an occupied Docker/QEMU port (the guard IOU and Dynamips
  already had).
- Probe fixes: `_probe_bridge_tap` survives a failing uBridge start, and
  the probe caches key on ctime too, so a chmod/setcap remedy re-probes.

Controller:
- Parse GNS3_UNIX_SOCKET_NIO like the compute does (line-anchored, truthy
  value) and mirror the compute's class selection in kernel_anchor_type.
- Read partial capability reports defensively; make BridgeNIO.bridge
  required-but-nullable so an omitted key is a 422 rather than a KeyError.
- Strip the packet_loss correlation slot even when it is zero: the relay
  filter takes exactly one argument and rejected `packet_loss 10 0`.

Ethernet switch: skip ports whose absorbed anchor does not exist yet (the
join is deferred by design) and roll the ports_mapping back when the VLAN
reconcile fails, so a retry still diffs against the old mapping.

Agent: reject device config entries without a device_name instead of
silently dropping them (a malformed batch used to execute partially while
reporting success).

Also split the netem builder to clear C901 and drop the two per-file ruff
ignores the PR had added, keeping the remove-only policy.
2026-10-06 19:55:21 +08:00
YueGuobin
7d04d08dba
fix: repair imports lost in the 3.1 rebase and meet the new lint baseline
The rebase onto upstream 3.1 (mypy + ruff I001/F401 waves) left the tree
short of the newly enforced baselines in three ways:

* contextlib / UbridgeError imports dropped as false duplicates during
  conflict resolution left NameErrors in iou_vm and qemu_vm (F821);
  the Dynamips router grabbed the compute-level NIOTAP instead of the
  hypervisor one, breaking TAP anchoring (test failure).
* unused imports (KERNEL_UNSUPPORTED_FILTERS, UbridgeError, List) and
  unsorted blocks left behind where the stack's later commits moved the
  last usage elsewhere — ruff I001/F401/RUF022 now enforce these.
* mypy, now baseline-free: cache dict annotations in tc_probe, the
  locators candidate list, the resolved-params None branch in the MCP
  runner, and validate_bpf_syntax keeps the upstream Dict[str, Any]
  return (the strict Optional[str] was wrong for the "valid" bool entry).

The e2e raw-frame probe resolves its compiler via shutil.which (S607)
and zips with strict= (B905); the remaining subprocess call is S603 by
nature and gets a per-file ignore like other test files.

ruff check/format clean, mypy at upstream parity (no new errors),
full unit suite 2167 passed.
2026-10-06 00:21:11 +08:00
YueGuobin
e13e5f3804
fix(tools): merge same-device entries before device command execution
A batch carrying the same device twice lost all but the last entry's
commands: the per-device command map kept only the last entry while
result processing iterated every original entry, so each row reported
the last entry's output under its own command list.

c3c78f99a consolidated the merge into the MCP template renderer, but
that only runs when a template is passed — the MCP direct-commands
path and gns3-copilot's direct _run path were left uncovered.
Normalize once in each tool's _run (single point covering all entry
paths, restoring the c42b3a59b coverage) and keep the renderer merge
as boundary normalization. Blocked-command info now also accumulates
across same-device entries instead of last-winning in the per-device
map.
2026-10-06 00:21:11 +08:00
YueGuobin
6633304e97
fix(mcp): let link_list return kernel_datapath
The tool descriptions have always advertised kernel_datapath as a
requestable field, but the handler's whitelist did not contain it: asking
for it was rejected as an invalid field, so the MCP clients could not see
which links run on kernel interfaces at all.

Also record what seeding a netem experiment on IOU costs: IOL emits its own
CDP/keepalive frames and they draw from the netem RNG stream, so a
seeded run only reproduces after `no cdp run` (verified bit-identical pcaps).
2026-10-06 00:21:09 +08:00
YueGuobin
187201a08c
feat(controller): offer QEMU the kernel datapath, gated per compute
Kernel eligibility becomes "both endpoints on one compute, each able to
anchor": Docker always (adapters are born as veth pairs), QEMU when that
compute's uBridge can create the persistent TAPs its adapters anchor on. The
question is asked per compute, because the link needs both ends anchored --
so a mixed or half-upgraded deployment degrades to the relay instead of
failing an attach.

probe_tap_support() creates and deletes one throwaway persistent TAP through a
scratch uBridge, cached by binary identity like the tc report; /capabilities
exposes `ubridge_tap` (absent = unknown, which keeps links on the relay).

The compute route for a QEMU adapter NIO took UDPNIO only, so a kernel link
was rejected with 422: its body and response now accept
Union[UDPNIO, BridgeNIO] exactly like Docker's.

Stale "Docker-to-Docker" wording in the config, link and MCP descriptions now
talks about anchors, and docs/features/qemu-kernel-datapath.md documents the
datapath -- verified end-to-end on a real instance: 32/32 kernel-datapath
checks and 29/29 on the relay variant.
2026-10-06 00:21:08 +08:00
YueGuobin
1db9f171ca
fix(controller): re-arm window_drop and expose kernel_datapath
Three fixes from live-calibrating the kernel impairment filters on an
isolated instance (Docker-to-Docker veth link, per-sequence evidence):

- update_filters skipped an unchanged filters dict as a no-op, so a
  one-shot window_drop outage that had already passed could never be
  re-armed: re-applying the same values produced no datapath command at
  all. A PUT still carrying window_drop now reconciles, restarting the
  schedule from the apply as documented. Measured: identical re-apply
  moves the outage window from seq 50-70 to 70-91 (arm at +8s of the
  new apply).

- gemodel parameter semantics were documented backwards in the
  validation rules, available_filters, the MCP/copilot descriptions and
  the feature docs. Measured against the kernel (per-direction counters,
  offered = sent + dropped): p is the good-to-bad transition chance, r
  the bad-to-good transition, 1-h the loss chance in the bad state (the
  good state loses nothing) - mean one-way loss p/(p+r)*(1-h), e.g.
  [10,30,100] -> 24.8%/25.7% measured vs 25% predicted, [5,45,100] ->
  10.8% both ways vs 10%.

- kernel_datapath was silently stripped from every REST/MCP link payload
  by the response model (LinkBase carried no such field), so clients
  could not tell a kernel link from a relay one.

Also verified on the same setup: the 3-argument window_drop form works
exactly as specified ([3000,5000,100] -> a single seq 31-55 outage,
[20000,6000,100] -> seq 115-144), and both generations of the command
builder emit the parameters unpadded.

Adds an API test for the field and controller tests for the no-op skip
and the window_drop re-arm exemption.
2026-10-06 00:21:08 +08:00
YueGuobin
5ac1521217
feat(mcp): accept project/node names as tool locators
LLM agents transcribe UUIDs unreliably — a 2-character slip invents an
id that never existed and is invisible to the eye. Tool parameters now
accept names wherever the server guarantees uniqueness: project names
among live projects, node names within a project. Links, drawings,
appliances and snapshots have no names and stay UUID-only.

Resolution sits in the single dispatch choke point (_run_handler_sync):
UUID-shaped values pass through untouched, names resolve with one list
call, and an unknown name fails fast listing the known names instead of
a confusing downstream 404. As a backstop for residual UUID typos,
error replies carrying "<UUID> doesn't exist" are matched against the
in-scope ids; a single candidate within edit distance 2 earns a
"Did you mean ...?" hint.
2026-10-06 00:21:08 +08:00
YueGuobin
935c00802f
docs(mcp): refresh filter vocabulary in the MCP/copilot tool descriptions
link_update, link_available_filters and the copilot packet-filter tool
still advertised the pre-kernel-datapath five types. List the full
thirteen with positional parameter formats, ranges and kernel-only
marking, point at link_available_filters as the per-link authority
(topology + compute capability gated), and document window_drop's
apply-relative schedule restart. Also add kernel_datapath to the link
field lists.

Description-only: handlers pass filters through unchanged.
2026-10-06 00:21:08 +08:00
Mark Paronyan
b622ccae7b
refactor: declare explicit public API in package __init__ modules 2026-10-03 19:43:59 +03:00
Mark Paronyan
306d8fbde4
refactor: remove unused imports 2026-10-03 19:43:58 +03:00
Mark Paronyan
61caa20524 style: sort imports in agent and remaining compute modules 2026-10-02 12:43:26 +03:00
Mark Paronyan
06899c92fc
refactor: use iterable unpacking instead of concatenation 2026-10-01 19:57:46 +03:00
Mark Paronyan
2dc7479d11
style: remove f-strings without placeholders 2026-10-01 19:57:46 +03:00
Mark Paronyan
c9416d2569
style: remove quotes from type annotations 2026-10-01 19:57:45 +03:00
Mark Paronyan
396407827e
style: drop redundant open mode arguments 2026-10-01 19:57:44 +03:00
Mark Paronyan
d356a68987
refactor: replace aliased OSError subclasses 2026-10-01 19:57:44 +03:00
Mark Paronyan
0416ad8228
style: sort __all__ declarations 2026-10-01 19:57:42 +03:00
Mark Paronyan
c5b5091f5e
refactor: use explicit conversion in f-strings 2026-10-01 19:57:41 +03:00
Mark Paronyan
9e5ab02147
style: remove unused noqa directives 2026-10-01 19:57:40 +03:00
Jeremy Grossmann
726da8383a
Merge branch '3.1' into mypy-agent-gns3_copilot-plus 2026-09-29 13:20:27 +02:00
Jeremy Grossmann
468ad4e85f
Merge branch '3.1' into mypy-agent 2026-09-29 13:19:30 +02:00
Jeremy Grossmann
4bcaf596df
Merge pull request #2910 from markparonyan/mypy-agent-copilot-utils
fix(typing): resolve mypy errors in agent.gns3_copilot.utils
2026-09-29 12:48:03 +02:00
Jeremy Grossmann
b73128d791
Merge branch '3.1' into mypy-agent-copilot-tools-analysis 2026-09-29 12:45:07 +02:00
Jeremy Grossmann
77a323e1e8
Merge branch '3.1' into mypy-agent-copilot-tools-nornir 2026-09-29 12:38:40 +02:00
Jeremy Grossmann
7b40064658
Merge branch '3.1' into mypy-agent-copilot-skills 2026-09-29 12:30:40 +02:00
Jeremy Grossmann
eb9a9ae19a
Merge pull request #2904 from markparonyan/mypy-agent-web-wireshark
fix(typing): resolve mypy errors in agent.web_wireshark
2026-09-29 12:27:28 +02:00
Jeremy Grossmann
6aac6c8e34
Merge pull request #2906 from markparonyan/mypy-agent-copilot-client
fix(typing): resolve mypy errors in agent.gns3_copilot.gns3_client
2026-09-29 12:21:33 +02:00
Mark Paronyan
780b7f2ab6
fix(typing): resolve mypy errors in agent node lifecycle tools 2026-09-29 09:46:32 +03:00
Mark Paronyan
bc27c4814f
fix(typing): resolve mypy errors in agent.gns3_copilot.gns3_client.api_handlers 2026-09-29 09:46:31 +03:00
Mark Paronyan
9882eba088
fix(typing): resolve mypy errors in agent.gns3_copilot.chat_sessions_repository 2026-09-29 09:46:31 +03:00
Mark Paronyan
07560b0026
fix(typing): resolve mypy errors in agent 2026-09-29 09:46:31 +03:00
Mark Paronyan
c154d1c41e
fix(typing): resolve mypy errors in agent.web_wireshark.docker_client 2026-09-29 09:23:01 +03:00
Mark Paronyan
d25cb5cdca
fix(typing): resolve mypy errors in agent.mcp.appliances 2026-09-29 09:23:01 +03:00
Mark Paronyan
2d712e0a90
fix(typing): resolve mypy errors in agent.gns3_copilot.utils.custom_netmiko.scripts.list_netmiko_telnet_devices 2026-09-29 09:23:01 +03:00
Mark Paronyan
4ae6b5338d
fix(typing): resolve mypy errors in agent.gns3_copilot.utils.parse_tool_content 2026-09-29 08:52:38 +03:00
Mark Paronyan
5cb1b8e3d2
fix(typing): resolve mypy errors in agent.gns3_copilot.utils.message_converters 2026-09-29 08:52:37 +03:00
Mark Paronyan
cd7f5ab8e9
fix(typing): resolve mypy errors in agent.gns3_copilot.agent.model_factory 2026-09-29 08:52:33 +03:00
Mark Paronyan
a251ca73ac
fix(typing): resolve mypy errors in agent.gns3_copilot.agent.context_manager 2026-09-29 08:52:33 +03:00
Mark Paronyan
f8144494f6
fix(typing): resolve mypy errors in agent.gns3_copilot.tools_v2.vpcs_tools_netmiko 2026-09-29 08:48:02 +03:00
Mark Paronyan
d2fd238383
fix(typing): resolve mypy errors in agent.gns3_copilot.tools_v2.gns3_packet_filter 2026-09-29 08:48:02 +03:00
Mark Paronyan
67f68c9676
fix(typing): resolve mypy errors in agent.gns3_copilot.tools_v2.packet_analysis_tool 2026-09-29 08:48:02 +03:00
Mark Paronyan
d6ae597f36
fix(typing): resolve mypy errors in agent.gns3_copilot.skills.manager 2026-09-29 08:48:01 +03:00
Mark Paronyan
35b360ca75
fix(typing): resolve mypy errors in agent.gns3_copilot.tools_v2.display_tools_nornir 2026-09-29 08:48:01 +03:00
Mark Paronyan
c7c7e3b583
fix(typing): resolve mypy errors in agent.gns3_copilot.skills.loader 2026-09-29 08:48:01 +03:00
Mark Paronyan
e8bff79370
fix(typing): resolve mypy errors in agent.gns3_copilot.tools_v2.config_tools_nornir 2026-09-29 08:48:01 +03:00
Mark Paronyan
97f71269d3
fix(typing): resolve mypy errors in agent.gns3_copilot.gns3_client.project_inventory 2026-09-29 08:48:00 +03:00
Mark Paronyan
890093de01
fix(typing): resolve mypy errors in agent.gns3_copilot.gns3_client.connector_factory 2026-09-29 08:48:00 +03:00
Mark Paronyan
7f2d1bc789
fix(typing): resolve mypy errors in agent.web_wireshark.manager 2026-09-29 08:41:57 +03:00
Mark Paronyan
81d77bb689
fix(typing): resolve mypy errors in agent.web_wireshark.stats 2026-09-29 08:41:57 +03:00
Mark Paronyan
755556c146
fix(typing): resolve mypy errors in agent.web_wireshark.manage_wireshark 2026-09-29 08:41:57 +03:00