489 Commits

Author SHA1 Message Date
Jeremy Grossmann
7f4f9eb2d0
Merge branch '3.1' into mypy-api-routes 2026-09-29 13:25:35 +02:00
Jeremy Grossmann
49f1af0962
Merge branch '3.1' into mypy-api-routes-compute-4 2026-09-29 13:23:50 +02:00
Jeremy Grossmann
1f7048d49d
Merge branch '3.1' into mypy-api-routes-compute-3 2026-09-29 13:23:05 +02:00
Jeremy Grossmann
cbd29ece37
Merge branch '3.1' into mypy-api-routes-compute-2 2026-09-29 13:21:10 +02:00
Jeremy Grossmann
726da8383a
Merge branch '3.1' into mypy-agent-gns3_copilot-plus 2026-09-29 13:20:27 +02:00
Jeremy Grossmann
4efc22b7ab
Merge branch '3.1' into mypy-api-routes-compute-1 2026-09-29 13:15:30 +02:00
Jeremy Grossmann
563b6080f0
Merge pull request #2923 from markparonyan/mypy-api-routes-controller-2
fix(typing): resolve mypy errors in gns3server.api.routes.controller
2026-09-29 13:13:58 +02:00
Jeremy Grossmann
9e9a778f8b
Merge pull request #2912 from markparonyan/mypy-api-controller-routes
fix(typing): resolve mypy errors in api.routes.controller
2026-09-29 12:50:06 +02:00
Jeremy Grossmann
ce3911be71
Merge pull request #2911 from markparonyan/mypy-api-compute-routes
fix(typing): resolve mypy errors in api.routes.compute
2026-09-29 12:48:36 +02:00
Mark Paronyan
ebe6dee916
fix(typing): resolve mypy errors in api.routes.controller.snapshots 2026-09-29 09:23:08 +03:00
Mark Paronyan
c340b18ba2
fix(typing): resolve mypy errors in api.routes.controller.roles 2026-09-29 09:23:07 +03:00
Mark Paronyan
2cf90dfce6
fix(typing): resolve mypy errors in api.routes.controller.privileges 2026-09-29 09:23:07 +03:00
Mark Paronyan
93419fc33a
fix(typing): resolve mypy errors in api.routes.controller.nodes 2026-09-29 09:23:07 +03:00
Mark Paronyan
0872576b46
fix(typing): resolve mypy errors in api.routes.controller.acl 2026-09-29 09:23:06 +03:00
Mark Paronyan
83b80b8c52
fix(typing): resolve mypy errors in api.routes.compute.virtualbox_nodes 2026-09-29 09:23:05 +03:00
Mark Paronyan
a86cae541e
fix(typing): resolve mypy errors in api.routes.compute.vpcs_nodes 2026-09-29 09:23:05 +03:00
Mark Paronyan
e0c4cb8122
fix(typing): resolve mypy errors in api.routes.compute.vmware_nodes 2026-09-29 09:23:05 +03:00
Mark Paronyan
0ff63bfd90
fix(typing): resolve mypy errors in api.routes.compute.nat_nodes 2026-09-29 09:23:04 +03:00
Mark Paronyan
04ca7ffd9b
fix(typing): resolve mypy errors in api.routes.compute.frame_relay_switch_nodes 2026-09-29 09:23:04 +03:00
Mark Paronyan
fed8aab86c
fix(typing): resolve mypy errors in api.routes.compute.projects 2026-09-29 09:23:04 +03:00
Mark Paronyan
9218f3fc72
fix(typing): resolve mypy errors in api.routes.compute.iou_nodes 2026-09-29 09:23:04 +03:00
Mark Paronyan
cacbc18ad1
fix(typing): resolve mypy errors in api.routes.compute.ethernet_hub_nodes 2026-09-29 09:23:03 +03:00
Mark Paronyan
5ed85340af
fix(typing): resolve mypy errors in api.routes.compute.ethernet_switch_nodes 2026-09-29 09:23:03 +03:00
Mark Paronyan
904672559e
fix(typing): resolve mypy errors in api.routes.compute.dynamips_nodes 2026-09-29 09:23:03 +03:00
Mark Paronyan
50a749af36
fix(typing): resolve mypy errors in api.routes.compute.cloud_nodes 2026-09-29 09:23:02 +03:00
Mark Paronyan
bb0f29b8e6
fix(typing): resolve mypy errors in api.routes.compute.docker_nodes 2026-09-29 09:23:02 +03:00
Mark Paronyan
8aa07a8c25
fix(typing): resolve mypy errors in api.routes.compute.atm_switch_nodes 2026-09-29 09:23:02 +03:00
Mark Paronyan
10edb109df
fix(typing): resolve mypy errors in api.routes.compute.dependencies.authentication 2026-09-29 09:23:02 +03:00
Mark Paronyan
bbdd9bf861
fix(typing): resolve mypy errors in api.routes.controller.drawings 2026-09-29 08:48:04 +03:00
Mark Paronyan
5fb2c277a9
fix(typing): resolve mypy errors in api.routes.compute.notifications 2026-09-29 08:48:04 +03:00
Mark Paronyan
6526beeb3d
fix(typing): resolve mypy errors in api.routes.controller.chat 2026-09-29 08:48:04 +03:00
Mark Paronyan
777258ff68
fix(typing): resolve mypy errors in api.routes.controller.api_keys 2026-09-29 08:48:04 +03:00
Mark Paronyan
f94a0cae0d
fix(typing): resolve mypy errors in api.routes.compute.compute 2026-09-29 08:48:03 +03:00
Mark Paronyan
5bb7896242
fix(typing): resolve mypy errors in api.routes.compute.capabilities 2026-09-29 08:48:03 +03:00
Jeremy Grossmann
7864a4e567
Merge pull request #2897 from markparonyan/mypy-api-controller-links
fix(typing): resolve mypy errors in api.routes.controller.links
2026-09-28 23:26:48 +02:00
Jeremy Grossmann
f6ee1ee01a
Merge pull request #2896 from markparonyan/mypy-api-compute-qemu-nodes
fix(typing): resolve mypy errors in api.routes.compute.qemu_nodes
2026-09-28 23:25:55 +02:00
Mark Paronyan
ecff4beb65
fix(typing): resolve mypy errors in api.routes.controller.links 2026-09-28 21:47:46 +03:00
Mark Paronyan
b8a51fdefc
fix(typing): resolve mypy errors in api.routes.compute.qemu_nodes 2026-09-28 21:47:36 +03:00
Mark Paronyan
f858ae23a6
fix(typing): resolve mypy errors in api.routes.index 2026-09-28 04:55:30 +00:00
Mark Paronyan
a0450b6bce
refactor: ruff autofixes 2026-09-27 15:55:10 +03:00
Cristi
da16dc9874 fix(qemu): Fixed missing image replacement for linked-clone nodes 2026-09-18 23:58:49 +03:00
YueGuobin
be62e8c022
feat: keep Docker images on computes consistent with the controller host
Only relying on the image name lets a moved tag (e.g. a newer :latest)
silently serve stale content from a compute that already has an image
under the same name. When creating a Docker node, the controller now
pins the image id (Id from the Docker daemon on the controller host)
into the create payload. A compute holding a different image under the
same tag reports the image as missing, which routes it through the
image sync added by the previous commit and re-aligns the tag.

No new template fields or database changes: the controller host daemon
remains the source of truth and the pin is resolved per creation. When
the image is not available on the controller host the pin is omitted
and behavior is unchanged (the compute pulls from the repository).
2026-09-14 00:55:18 +08:00
YueGuobin
c477812332
feat: sync Docker images from the controller to remote computes
When a Docker node is created on a remote compute whose Docker daemon
does not have the image, the compute now raises ImageMissingError
instead of blindly pulling from the Docker repository. The controller
exports the image from the Docker daemon on its host (docker save
stream) and streams it to the compute which loads it, so locally built
or docker-loaded images work across computes. When the image is not
available on the controller host either, the compute is asked to pull
it from the Docker repository as a fallback.

- add a POST /docker/images/load compute endpoint that streams a
  docker save tar into the Docker daemon
- let Docker.http_query pass raw (non-dict) request bodies through so
  the tar can be streamed to the daemon
- drop the inline pull from DockerVM.create() and the now unused
  DockerVM.pull_image wrapper
2026-09-14 00:36:54 +08:00
YueGuobin
5d91ca0efc
fix: harden sharkd replay sessions and serve the uncapped frame list
Review-driven session/transport fixes (each reproduced live against
sharkd 4.6.7 before fixing):

- raise the RPC stream limit to 16 MB: a full 1000-row frames page
  measures ~190 KB against the 64 KB StreamReader default, which failed
  the request with a 500 and desynchronized the resident session; a
  line-over-limit ValueError is now treated as a transport failure
- verify JSON-RPC reply ids: a timed-out request's late reply was
  served as the next request's answer; timeouts, dead pipes, malformed
  and stale replies now kill the session for good instead
- make check-spawn atomic under one manager lock: concurrent requests
  for the same pcap double-spawned sharkd and leaked the loser (process
  plus /tmp scratch copy) forever
- refcount sessions and evict idle only (LRU, cap raised 8 -> 16): a
  tag with more sources than the cap respawned every source on every
  request, and concurrent requests could get their session killed
  mid-RPC (spurious 502)
- map FilterError to sharkd's filter rejection (-13002) only; other
  engine failures with a filter set are 502, not a client 400
- detail: accept an optional frame_number to disambiguate
  same-microsecond frames (ts is not unique within a pcap); drop the
  -8003 -> 404 mapping (the range is validated locally, engine errors
  are real faults); a failed hex read is a 404 instead of "hex": null
- a pcap deleted mid-request is a 404, not a 500; the pcap-sized
  scratch copy runs off the event loop; server shutdown kills every
  resident session and drops its scratch directory
- pin the packet-list layout through scratch-HOME Wireshark
  preferences: the column indexes are a contract the server owns
  (protocol-level column negotiation is rejected by sharkd 4.6.x)

Range contract change (WebUI moved to an always-flat list): the merged
frame list is returned in full, deliberately uncapped - truncated and
per-second buckets are removed, frame_count always equals
len(frames), and rendering cost is the client's concern (the window
endpoint remains the incremental path).
2026-09-11 00:55:18 +08:00
YueGuobin
ef5d81498a
feat: accept link=<link_id> on the replay range and frames endpoints
Narrows the merged frame stream to one capture source BEFORE counting,
slicing and bucketing (frame_count / frames | buckets all recomputed on
the narrowed set), AND-composing with the display filter. A pure
identity filter applied before any engine work — only the selected
link's pcap gets a sharkd pass, so link+filter is cheaper than filter
alone.

Two boundaries by contract with the Web UI:
- sources[] stays the tag's stable inventory: every capture source
  listed with engine-free TOTAL counts, unaffected by link/filter — a
  source dropdown must not shrink when the view narrows (this also
  settles sources[].count on total counts rather than post-filter
  matches, which no spec ever required)
- an unknown link_id matches nothing: frame_count 0, start null, empty
  frames/buckets — the same shape as a zero-match display filter,
  deliberately not a 404; an empty link param is treated as absent
2026-09-09 00:53:06 +08:00
YueGuobin
790c423c26
feat: drive marker replay with resident sharkd sessions
sharkd (the Wireshark daemon) is now the single decode engine — the
tshark/PDML path is gone, and without sharkd every replay endpoint
returns 501 (no degraded mode: one engine, one rendering shape for the
Web UI).

- Frame entries gain packet-list columns from sharkd's frames RPC:
  src/dst/proto/info plus the Wireshark coloring hints bg/fg
- range and frames accept ?filter=<display filter>, applied before
  counting and slicing; invalid expressions are 400 carrying sharkd's
  original text; filters travel as single argv-style elements, capped
  at 2000 chars; filtered frames keep their original pcap frame numbers
- frame detail returns sharkd's protocol tree with keys renamed into
  the REST contract (element/label/name/filter_expr/pos+size/expert/
  generated/children): a census-verified closed key set, values
  untouched, unknown keys passed through verbatim, Wireshark-internal
  hf ids dropped. filter_expr gives the UI click-to-filter; pos/size
  drives hex highlighting (hex still read straight from the pcap)
- one resident 'sharkd -' session per source pcap: lazy spawn, /tmp
  scratch copy + scratch HOME (hardened profiles), per-request
  (mtime,size) validation with respawn, LRU bound, per-session lock,
  per-RPC timeout, bounded close

Timeline backbone (gate, record-header scan, merge ordering, canonical
ts strings, hex reads) stays plain Python — identity and ordering never
depend on the engine.
2026-09-08 00:04:04 +08:00
YueGuobin
a8c96dd3f7
fix: keep port_number in Docker NIO dispatch of the batch endpoints
The project-open bulk path (_add_nio_binding / _get_existing_nio /
_update_nio_binding in routes/compute/projects.py) dropped port_number
for Docker nodes, unlike the per-node routes and the IOU branch. With
multi-port docker adapters (iol-runner nodes model 4 ports per adapter,
0ca9ccc63) every batched NIO landed on port 0 where Adapter.add_nio()
silently overwrites — the last entry per node won — so reopening a
project clobbered the port-0 links with the port-1 NIOs: links ended up
cross-wired between the wrong node pairs and the real links died
(IOL direct-link ping failures after close/reopen, EXCESSCOLL storms
from the phantom loops).
2026-09-06 00:15:43 +08:00
YueGuobin
8b3aafbbdc
feat: IOU-style startup-config for IOL Docker nodes
Templates reference a config file with the GNS3_IOL_STARTUP_CONFIG
environment knob; the controller materializes the file content into
startup_config_content on node creation (sent once, knob consumed —
the same pattern as the IOU startup_config mapping). The compute builds
the content into the node's nvram_<app id> at the next start using the
IOU nvram_import utility (IOL and IOU share the nvram container format,
verified against iol-xe 17.18.02): valid config at boot, no setup
dialog, %h hostname substitution, hostname rewrite on rename.

Semantics verified against the runner: IOL boots from NVRAM whenever it
holds a config, so a plain stop/start never re-applies the startup
config and 'write memory' survives restarts; an explicit content edit
(PUT) is re-applied on the next start and wins over the saved config,
like IOU.
2026-09-05 21:54:52 +08:00
YueGuobin
a8fa529252
feat: add tag-keyed aggregate replay over paused markers' pcaps
Markers on different links sharing a tag form one distributed capture
session. Once every marker under the tag is paused (409 otherwise), three
read-only endpoints replay it:

- GET .../markers/tags/{tag}/replay/range merges the per-marker pcaps by
  scanning 16-byte record headers only (no tshark) into a timestamp-
  ordered frame list (per-second buckets above a 5000-frame cap)
- GET .../replay/frames?ts=&window_ms= returns frames in [T, T+window];
  an empty window is a normal empty array
- GET .../replay/frame/detail lazily decodes one frame the user opened:
  raw bytes for the hex view read straight from the pcap, protocol tree
  from 'tshark -T pdml' mapped isomorphically to JSON (every attribute
  survives, values stay strings). tshark reads a /tmp scratch copy with a
  scratch HOME — hardened profiles deny it the project directory.

Sort key is (ts, source file, frame number): ts is not unique across a
merge. The ts parameter round-trips as the exact string from the frame
list. Round-trip tests pin the PDML→JSON fidelity (element count and
attribute coverage).
2026-09-05 21:54:52 +08:00
YueGuobin
0ca9ccc637
feat: model IOL adapters as 4-port units like the IOU node type
IOL interfaces come in 4-port units (Ethernet0/0-3, Ethernet1/0-3, ...),
addressed like IOU as (adapter_number, port_number 0-3):

- IOLDockerVM builds EthernetAdapter(interfaces=4) per adapter and asks
  the runner for adapters x 4 interfaces (num-eth).
- DockerVM threads port_number through the NIO/capture API (the compute
  routes parsed it from the URL but dropped it); single-port adapters
  keep the historical bridge{N} names and command sequence, multi-port
  adapters get one bridge per port (bridge{a}_{p}).
- The unix-socket NIO wiring addresses sockets flat across adapters:
  adapter x ports-per-adapter + port, so single-port images keep their
  exact socket layout.
- The controller port list for GNS3_IOL_RUNNER docker nodes is generated
  by StandardPortFactory with the IOU naming (Ethernet{segment0}/{port0},
  segment size 4); plain docker nodes keep eth{N}.
2026-09-05 21:54:51 +08:00