1837 Commits

Author SHA1 Message Date
YueGuobin
73cbb0387c
feat(mcp): add drawing_create_text tool with Web UI compatible SVG assembly
The Web UI re-parses drawing SVG into its own element model instead of
rendering it raw, which makes hand-assembled SVG error-prone for LLM
callers. The new tool assembles the SVG server-side, mirroring what the
Web UI itself serializes for text drawings:

- XML-escaped text content, validated color (#RGB/#RGBA/#RRGGBB/
  #RRGGBBAA) and font family (attribute-safe charset)
- multi-line support: literal newlines render as stacked lines in the
  Web UI (tspan split), width/height computed from the longest line
- pt-aware sizing: the Web UI renders font-size in pt (1pt = 4/3 px),
  so width uses a 0.8/char factor for monospace (1.0 proportional) and
  line height 1.9 x font_size, instead of the px-based 0.62 estimate
- ET.fromstring pre-check so bad input fails loudly instead of hitting
  the controller's silent drop of unparseable 500+ char SVG
- computed width/height/line_height returned for label layout

Also correct the raw drawing_create docstring: root width/height are
required (the parser reads them for the selection box), x/y on rect are
ignored, <g> is unsupported, <path> is re-interpreted as a freehand
curve, and the old examples lacked the mandatory root dimensions.
2026-10-09 11:12:18 +08:00
YueGuobin
1e0d123f4b
Merge branch '3.1' into fix/duplicate-device-configs-merge
Resolve conflicts against the upstream wave (PRs #2952-#2965, incl. the
typed-response/schema hardening and the drop of Python 3.10):

- controller/node.py: keep upstream's _transitioning/_confirm_status
  lifecycle guard; the kernel-anchor link wiring (node_started) runs
  after the transition completes.
- controller/link.py: keep upstream's add_node rollback (release_ports
  on create failure) and delete via release_ports; release_ports now
  resolves ports live (same staleness reason as _bind_members) and
  discards the node link unconditionally.
- schemas/controller/links.py: extend LinkFilterType with the eight
  kernel-datapath filter types and LinkFilterParameter.type with "str"
  so the newly typed /available_filters response validates; Link.filters
  stays a free-form dict — the vocabulary is capability-gated per link
  and validated by the controller (409), not by a strict request schema.
- schemas/controller/topology.py: drop the type: ignore made redundant
  by the dict filters field.
- tests: route-level filter validation now expects the controller's 409
  (unknown types, out-of-range values); packet_loss correlation is a
  valid kernel parameter (positive test added); OpenAPI assertions match
  the free-form filters property.
2026-10-09 10:03:50 +08:00
Jeremy Grossmann
2dd1652e8a
Merge branch '3.1' into api-issue-07-x7z6we 2026-10-08 19:10:42 +02:00
Jeremy Grossmann
7a3a34d3fc
Merge branch '3.1' into api-issue-06-8s9swu 2026-10-08 19:01:57 +02:00
Jeremy Grossmann
8dd438190d
Merge pull request #2960 from markparonyan/api-issue-12-cu0jkp
feat(api): add discriminators to link iface, appliance version and template settings schemas
2026-10-08 17:17:02 +02:00
Jeremy Grossmann
cea858acc4
Merge branch '3.1' into api-issue-12-cu0jkp 2026-10-08 16:46:37 +02:00
Jeremy Grossmann
029983260c
Merge branch '3.1' into api-issue-15-r12z73 2026-10-08 16:42:57 +02:00
Jeremy Grossmann
ddcf661e11
Merge branch '3.1' into api-issue-07-x7z6we 2026-10-08 15:51:11 +02:00
Jeremy Grossmann
3187828325
Merge branch '3.1' into api-issue-06-8s9swu 2026-10-08 15:02:51 +02:00
Jeremy Grossmann
a32f466183
Merge branch '3.1' into api-issue-04-burx2g 2026-10-08 13:59:00 +02:00
Jeremy Grossmann
48b6257410
Merge pull request #2952 from markparonyan/api-issue-02-rp8mx3
feat(api): return node and add wait option on node lifecycle calls
2026-10-08 11:05:28 +02:00
Jeremy Grossmann
5055664456
Merge pull request #2957 from markparonyan/api-issue-08-67pip0
feat(api): add machine-readable error codes and align OpenAPI error schemas
2026-10-08 09:30:13 +02:00
Jeremy Grossmann
ed8b0c3e20
Merge pull request #2958 from markparonyan/api-issue-09-aef9as
feat(api): return token lifetimes and document API keys as bearer tokens
2026-10-08 04:35:19 +02:00
Jeremy Grossmann
dc57c95f1c
Merge pull request #2963 from markparonyan/api-issue-16-0gmylm
feat(api): add Snapshot.created and type NodeFile timestamps as date-time
2026-10-08 04:15:01 +02:00
Jeremy Grossmann
d797069921
Merge pull request #2964 from markparonyan/api-issue-17-asr039
feat: add optional ETag/If-Match concurrency for projects, nodes and links
2026-10-08 04:14:04 +02:00
Jeremy Grossmann
0a78a2bf12
Merge pull request #2961 from markparonyan/api-issue-13-bfbm9z
feat(api): add Notification schema and document notification streams
2026-10-08 04:11:14 +02:00
Jeremy Grossmann
bcf38d74f8
Merge pull request #2951 from markparonyan/api-issue-11-gqdty1
feat(api): type link filters and available_filters response
2026-10-07 18:25:30 +02:00
Guobin Yue
5238bd3b39
Merge branch '3.1' into fix/duplicate-device-configs-merge 2026-10-07 23:59:30 +08:00
Mark Paronyan
241c2b65e7
feat: add optional ETag/If-Match concurrency for projects, nodes and links 2026-10-07 16:53:11 +03:00
Mark Paronyan
6ed9e44f00
feat(api): generate unique readable OpenAPI operationIds 2026-10-07 15:53:00 +03:00
Mark Paronyan
11a310dd9b
feat(api): type link filters and available_filters response 2026-10-07 15:48:52 +03:00
Mark Paronyan
a9e87df644
fix(links): reserve ports on add, accept port name and auto, add port_in_use code 2026-10-07 15:41:04 +03:00
Mark Paronyan
6a416d1a2a
feat(api): return node and add wait option on node lifecycle calls 2026-10-07 15:35:11 +03:00
Mark Paronyan
ead44787f3
feat(api): add Snapshot.created and type NodeFile timestamps as date-time 2026-10-07 15:25:15 +03:00
Mark Paronyan
8cb92e7685
fix(api): derive OpenAPI version from package, add servers, hide AI stub routes 2026-10-07 15:21:30 +03:00
Mark Paronyan
86a279330d
feat(api): add Notification schema and document notification streams 2026-10-07 15:14:50 +03:00
Jeremy Grossmann
0836e01525
Merge pull request #2950 from markparonyan/api-issue-14-yywwso
fix(api): return 200 instead of 201 for ACL endpoints, IOU license up…
2026-10-07 13:50:53 +02:00
Mark Paronyan
a4f20e0966
feat(api): add discriminators to link iface, appliance version and template settings schemas 2026-10-07 14:22:18 +03:00
Mark Paronyan
fcfa968cc5
feat(api): return token lifetimes and document API keys as bearer tokens 2026-10-07 14:18:18 +03:00
Mark Paronyan
289f9394b6
feat(api): add machine-readable error codes and align OpenAPI error schemas 2026-10-07 14:15:41 +03:00
Mark Paronyan
ba0465f191
fix(api): declare binary and streaming bodies in OpenAPI 2026-10-07 14:09:02 +03:00
Mark Paronyan
97d5480241
fix(api): return 200 instead of 201 for ACL endpoints, IOU license update 2026-10-07 13:56:08 +03:00
Mark Paronyan
852dc5be0f
feat(api): add exact-match name filter to list endpoints 2026-10-07 13:40:34 +03:00
YueGuobin
980254b9e6
Merge branch 'feat/e2e-ipv6-dualstack' into fix/duplicate-device-configs-merge 2026-10-07 13:20:22 +08:00
YueGuobin
6459122113
test(e2e): run every traffic checkpoint dual-stack (IPv6)
The live suite asserted the datapath over IPv4 only. Give every guest a
ULA IPv6 address next to its v4 one and twin every v4 assertion point,
sharing the impairment window (one filter application, both families
asserted) so the runtime grows by minutes, not double.

- harness: configure_ios(eth_ipv6=), ping() dispatches to `ping ipv6`,
  docker_bring_up_ipv6 (the Docker daemon leaves eth0 with
  disable_ipv6=1 on v6-less bridge networks, so the interfaces-file
  route cannot work), family-aware docker_wait_address, pcap_records
  scans every record (a leading DAD/MLD frame must not mask the
  sampled ethertype), and pcap_icmp_rtt measures the wire round trip
  from capture timestamps;
- scenarios: connectivity/delay/suspend/isolation/churn checks in both
  families, captures assert 86dd next to 0800, the libpcap bpf drops
  are asserted family-selective (icmp vs icmp6, each killing its own
  family and leaving the other alive), the eBPF classifier modes on
  both families, marker v6 twins with an echo-specific expression
  (bare icmp6 also matches DAD/MLD and would break the exact counts),
  the v6 sharkd replay, and the link-local matrix gains the 86dd
  all-nodes row; serial (HDLC) legs stay v4-only;
- the dynamips-emulated classic IOS under-reports its `ping ipv6` RTT
  by ~40 % of a delayed round trip (pcap-verified: the wire carries the
  v6 echo exchange in the same ~230 ms as v4 while the console reports
  ~130 ms), so the dynamips-family scenarios assert the v6 delay from
  the capture's own timestamps — recorded in tests/e2e/README.md.

Full suite: 17 passed in 58m50s.
2026-10-07 13:19:38 +08:00
Jeremy Grossmann
49f047d07a
Merge branch '3.1' into fix/duplicate-device-configs-merge 2026-10-06 17:09:32 +02:00
Jeremy Grossmann
f184883aa7
Merge branch '3.1' into images-nested-folders-support 2026-10-06 16:50:40 +02:00
YueGuobin
96be37baa1
test: cover the namespace-failure veth cleanup and BridgeNIO field validation
Both fixes from the code-review pass were carried by other tests'
side effects: the move_to_ns failure path (the branch that used to skip
the delete_veth) and the required-but-nullable bridge field on the NIO
create routes.
2026-10-06 22:42:31 +08:00
Jeremy Grossmann
2d8ddb5ed7
Merge branch '3.1' into fix/duplicate-device-configs-merge 2026-10-06 16:34:48 +02:00
Jeremy Grossmann
e2161fb66c
Merge branch '3.1' into fix-api 2026-10-06 16:12:18 +02:00
Jeremy Grossmann
0170d2c8ce
Merge branch '3.1' into fix/duplicate-device-configs-merge 2026-10-06 16:10:23 +02:00
Mark Paronyan
d9678ce98c
refactor: derive update schemas with PartialUpdateModel 2026-10-06 16:46:22 +03:00
YueGuobin
35cf964882
test: cover the kernel-datapath review fixes
Regression coverage added with the fixes:
- capture-slot semantics: second-port refusal, failed-start rollback, and
  a non-owner port's stop not reaching `capture stop_kernel`;
- QEMU stop/start teardown on a mid-body or pre-launch failure, IOU start
  cleanup and filter guards, Dynamips half-attach undo plus retry;
- switch VLAN reconcile skipping a deferred anchor and rolling the
  ports_mapping back on failure;
- Docker/QEMU occupied-port refusal, probe ctime invalidation, probe
  survival of a failing uBridge start;
- env-marker dialects (mid-line mentions, falsy values) and the agent
  merge rejecting entries without a device_name.

Test-body imports of product modules move to the shared header.
2026-10-06 19:55:31 +08:00
Cristi
a7f0c7174b fix(templates): Fixed template test formatting to pass CI 2026-10-06 00:04:26 +03:00
Mark Paronyan
cbd4f457be
Merge branch '3.1' into fix-api 2026-10-05 21:23:03 +03:00
YueGuobin
5841138cef
docs(e2e): complete the README — requirements, image matrix, host notes
Adds what running the suite on a real machine actually requires (uBridge
with caps, tc for the filter assertions, a Docker daemon, cc -static for
the raw-frame probe), the full image-selection matrix (preferred
filenames, fallback patterns, per-folder layout, the alpine digest
ladder; missing images skip, never false-fail), the NetworkManager host
hazard and its record, timing/load sensitivity notes, the complete
GNS3_E2E_* environment-variable list — and the link-local scenario,
which the scenario list was missing.
2026-10-06 00:21:11 +08:00
YueGuobin
f688e654e1
test(e2e): widen the fast-path RTT bound to 100 ms via harness.FAST_RTT_MS
The 'impairment cleared' asserts used avg < 50 on three-packet IOS pings;
the unimpaired baseline is 20-45 ms with 4 ms quantization, and host
scheduling jitter pushed averages to 54/56 in two full-suite runs
(esw, dynamips) — a margin, not an impairment (the qdisc asserts pass,
success is 100 %). The contrasted delayed regime is >= 150 ms, so a
100 ms bound still separates the two cleanly. Docker's sub-millisecond
veth asserts keep their 50 ms bound (three orders of magnitude of
headroom, never observed to flake).
2026-10-06 00:21:11 +08:00
YueGuobin
f21d7be99c
test(e2e): derive busybox ping loss from replies when the summary is lost
The exec stream can truncate after busybox's 4294967 ms RTT timer-wrap
artifact, dropping the summary line — docker_ping then reported a
phantom 100 % loss and made the corrupt-filter scenario fail spuriously.
Factor the parsing into _busybox_ping_parse: the summary stays
authoritative when present; otherwise the loss is derived from the
seq= replies actually seen (no replies at all still reports 100 %).
2026-10-06 00:21:11 +08:00
YueGuobin
15fbef93fb
test(e2e): unmanage the TAP anchors in the remaining kernel scenarios
Same guard the ethernet-switch scenario already carries: NetworkManager
can release an NM-managed TAP from its bridge shortly after
(re-)enslavement (docs/bugs/networkmanager-tap-release.md). Applied to
the Dynamips, QEMU, IOU, IOL-runner and switch-cascade scenarios after
their nodes have booted (no-op where nmcli/NetworkManager is absent).
This keeps those tests deterministic on un-declared hosts.
2026-10-06 00:21:11 +08:00
YueGuobin
1c85befbab
test(e2e): take the switch anchors out of NetworkManager's reach
The esw kernel fast-path scenario deletes and re-creates a link, which
re-enslaves the peer anchor — and on NetworkManager hosts NM's netdev
state machine can release an NM-managed TAP from its bridge some 60-70 ms
after enslavement, ~50% of re-enslavements, while the tap is in NM's
not-yet-assumed state. The freed port stays administratively up and no
GNS3 process logs anything: the membership assert sees the join, then the
ping measures a silent blackhole (this was the line-277 e2e failure).

Root-caused with a server-free harness (one uBridge, one fresh tap, one
bridge, an addif/delif loop): the same tap frees 20/40 while NM-managed,
0/40 after `nmcli device set <tap> managed no`, 20/40 again when
re-managed. Docker's veth host ends are not NM-managed, which is why only
TAP anchors ever showed it — including the "anchor lost its bridge
membership after a ports_mapping update (TAP peers only)" incident from
the switch's development, whose conclusion is corrected in the docs.

Unmanage the anchors once the routers have booted, before the scenario
re-enslaves them; document the failure signature and the host-side remedy
(per-device nmcli call, or NM unmanaged-devices configuration) in the
L2-anchor spec and the switch feature doc.
2026-10-06 00:21:11 +08:00