87 Commits

Author SHA1 Message Date
YueGuobin
72dc10a669
controller: allow markers and packet filters on Ethernet switch links
The brctl Ethernet switch runs a per-port uBridge relay, so it can host
the mark filter and packet filters like any uBridge-backed node. Add
ethernet_switch to _MARKER_CAPABLE_TYPES and _get_filter_node, narrow the
UDPLink.update() NIO-PUT skip down to the Dynamips-hosted ethernet_hub,
and expose the matching compute endpoints: PUT nio (filter/marker
reapply) plus the per-marker toggle/pause/resume/delete/rebuild routes.

The ethernet_hub keeps its exclusion: its routes still wire into the
Dynamips hub, which has no uBridge of its own.
2026-08-21 21:41:51 +08:00
Guobin Yue
74a51a1f9f
Merge branch '3.1' into code-review-fixes 2026-08-15 22:39:42 +08:00
Cristi
a35df76384 feat: Enhance information displayed on the system status dashboard 2026-08-14 23:36:03 +03:00
YueGuobin
9604c85fda
docker: harden the shm/devices/extra_configs/masking work (code review)
Nine fixes from a review of the docker-shm-devices diff:

* GNS3_STOP_TIMEOUT >300 s aborted at the manager's default HTTP timeout
  before Docker finished the stop — the stop query now gets a timeout
  with a margin over the grace period.
* Overlapping bind targets (GNS3_MASK_UDEV + GNS3_MASK_SYSTEMD on the
  same unit, a unit named twice, an extra_configs target equal to a
  masked unit) made Docker reject the create with 'Duplicate mount
  point' — Mounts are deduplicated by target.
* ExtraConfig.target now carries a pydantic validator (absolute file
  path, no '..'), so bad targets 422 at template-save time instead of
  failing at node-create time after a multi-GB image pull; directory
  forms ('/', '/etc/') are also rejected by the runtime guard instead
  of raising IsADirectoryError (raw 500).
* _check_host_readiness skipped every remaining check when one
  /proc/sys key was unreadable (mid-loop return) — now continues.
* The base-class GNS3_* env parser strips trailing commas like the
  vendor parser, so 'GNS3_MASK_UDEV=1,' composed from a list still
  activates.
* Vendor env knobs are re-parsed on every create(), so a PUT to the
  node's environment takes effect on the next (re)create.
* The graceful SIGTERM stop is now limited to the explicit user stop
  route; delete/update/close/crash-cleanup keep the immediate kill
  (those paths force-delete or recreate the container right after).
* An extra_configs target beneath a persisted volume is shadowed by the
  volume bind — warn at create time.
2026-08-15 00:52:55 +08:00
YueGuobin
08e37a4509
docker: inject config files into containers via extra_configs
Add an `extra_configs` field (list of {target, content}) to the docker
node/template/appliance schemas. For each entry GNS3 writes `content` to a
file in the node working directory and bind-mounts it read-only at `target`
inside the container.

This lets a NOS appliance seed its startup config without rebuilding the
image: XRd points XR_FIRST_BOOT_CONFIG at an injected /firstboot.cfg, FRR at
/etc/frr/frr.conf, etc. The bind is a single-file mount applied at create
time, so it works for both the generic init.sh path and vendor nodes that
skip init.sh (console_type=docker_exec). Entries are only injected when
present, so ordinary nodes are unaffected.

The content can't go through `environment` (it is line-delimited, one var per
line), hence a dedicated field -- the same plumbing shape as extra_volumes.
2026-08-13 23:33:34 +08:00
YueGuobin
72917596ab
fix: suppress redundant console port setter log during Docker node create
create_docker_node() passes console, aux etc. to create_node() via
.get() so those keys remain in node_data.  The setattr fallback loop
then re-applies them — if reserve_tcp_port returned a different port
in __init__, the setter fires an INFO log and performs a wasted
release→reserve round-trip.

Pop the 15 keys already consumed by create_node() before the loop so
it only handles truly extra keys.
2026-08-12 00:10:01 +08:00
YueGuobin
1dd334caff
perf: parallelize batch NIO creation across nodes
create_batch_nios bound NIOs in a serial for-loop, so during project open
every builtin L2 node (ethernet_switch/hub/cloud/nat) started its uBridge
one at a time (~0.5s each for fork + AF_UNIX connect). Group entries by
node and bind in parallel with asyncio.gather — mirroring update_batch_nios
— so independent uBridge processes start concurrently. Within a node,
entries stay serial to respect the per-node uBridge command lock.
2026-08-11 22:11:44 +08:00
YueGuobin
5de902c8b1
cleanup: remove stray debug print in Dynamips node creation
The print(node_data.chassis, platform in DEFAULT_CHASSIS) at
dynamips_nodes.py:67 (added in 8ad7b3f6, 2022) emitted "None False"
to stdout on every Dynamips router creation. Pure debug leftover;
the very next line tests the same condition.
2026-08-11 21:46:38 +08:00
YueGuobin
e4d4282026
perf: parallelise compute-side batch NIO update per node
The update_batch_nios handler looped serially across all entries (5010
for a 2505-link topology). On started nodes each entry does uBridge I/O,
so the serial loop added orders of magnitude to the fan-out wall time.

Group entries by node_id before dispatching.  Different nodes talk to
their own uBridge process (AF_UNIX socket) and are fully independent, so
their updates run in parallel via asyncio.gather.  Per-node entries are
still serial (respecting the per-node uBridge command lock).
2026-08-11 09:02:46 +08:00
YueGuobin
48991f2d29
perf: batch marker-def fan-out to one PUT /nios/batch per compute
When a marker definition is created (or re-applied on data_link_type
change), the fan-out used to call inherit_marker -> update() on every
link, issuing one PUT /nio per link end (5000+ round-trips on a 2500-link
project). On started nodes each round-trip also reconfigured uBridge.

Two-phase fan-out:
- inherit_marker/start_marker gain memory_only: writes the marker into
  link._markers and refreshes _link_data without any HTTP/emit/dump.
- _apply_def_to_all_links applies memory-only to every link, then
  _batch_update_link_nios groups the updated NIO specs by compute and
  sends a single PUT /projects/{id}/nios/batch per compute.

compute: new PUT /projects/{id}/nios/batch endpoint with _get_existing_nio
+ _update_nio_binding dispatch (mirrors create_batch_nios), re-applies
filters+markers to uBridge on started nodes.

Precise per-marker operations (update_marker bpf change, stop_marker on
def delete) are untouched — they deliberately avoid a full reapply to
preserve sibling marker pcaps.
2026-08-11 08:44:57 +08:00
YueGuobin
f9f1a4d4d8
log: demote all per-node lifecycle INFO logs to DEBUG
Extend the docker_vm / base_node demotion to the remaining node types
and supporting layers:

  qemu_vm: MAC, adapters, disk image, RAM, priority, NIO added, created
  iou_vm:  application ID, adapters, serial, image, RAM, NIO added
  dynamips router: created, adapter, RAM, NVRAM, IOS, idle-PC, disk,
    MAC, NIO bound; hypervisor create/start/connect; nio_udp created
  builtin: ethernet_switch/hub, cloud, nat — created, NIO bound
  ubridge: hypervisor start/connect

At multi-node scale these per-node lines flood the log. Only the
project-open progress summary (loaded N nodes / creating N links)
now remains at INFO alongside genuinely exceptional events.
2026-08-11 01:05:53 +08:00
YueGuobin
4e30b6905a
fix: await Dynamips.create_nio (it is async, unlike sync base)
Dynamips.create_nio is async def while BaseManager.create_nio is a sync
def.  The previous fix only added the extra 'node' argument but did not
await the resulting coroutine, causing 'was never awaited' warnings and
passing a coroutine object instead of an NIO instance to the binding
dispatch.  Add 'await' on the Dynamips branch.  Test updated to verify
both the async nature and the parameter count.
2026-08-11 01:00:01 +08:00
YueGuobin
09a9555d02
fix: use bound-method param count for Dynamips create_nio detection
The inspect check tested unbound function signatures (3 params unbound vs
2 unbound) but node.manager.create_nio is a bound method — inspect
excludes 'self'.  Dynamips bound = 2 (node + nio_settings), standard
bound = 1 (nio_settings).  The old '== 3' never matched, so the extra
'node' arg was never passed.  Switch to '>= 2' and rewrite the test to
exercise the actual bound-method scenario.
2026-08-11 00:58:08 +08:00
YueGuobin
ed0f1bb4de
fix: Dynamips create_nio(node, nio_settings) takes extra arg + add tests
Dynamips.create_nio requires the node as first positional argument
(unlike every other manager which takes only nio_settings). The batch
handler now detects this via parameter-count inspection (3 vs 2) and
passes node when needed.

Also add Dynamips to _add_nio_binding dispatch: routers use
slot_add_nio_binding(slot, port, nio), switches/hubs fall back to
add_nio(nio, port_number).

Add tests covering Dynamips router dispatch, switch dispatch, and the
create_nio signature detection to prevent regression.
2026-08-11 00:54:53 +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
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
grossmj
29c1b04078
fix: add missing HTTPException import 2026-08-05 21:55:23 +02: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
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
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
3d06c4e22f
marker: drive uBridge enabled/pause/resume in real time
Wire gns3-server to uBridge's real-time marker controls (contract
../ubridge/doc/gns3server-integration.md §3.2), in three layers:

A. enabled reaches uBridge — _markers_for_node no longer drops disabled
   markers controller-side; it carries `enabled` in the NIO spec. apply
   installs every marker then issues `enable_packet_filter … off` for the
   disabled ones (base_node bridge / iou iol_bridge; old-ubridge errors
   downgrade to a warning so a toggle can't break link create).

B. instant per-filter toggle — apply records name→bridge so a new
   `_ubridge_set_marker_filter_state` can flip a running filter with
   `enable_packet_filter on|off` (iou overrides for iol_bridge + bay/unit).
   Each marker-capable node type gains PUT /markers/{name}; update_marker
   short-circuits to it when only `enabled` changes (no NIO rebuild, no pcap
   flush), falling back to reset+reapply if the route is unavailable.

C. global pause/resume — `_ubridge_marker_pause/resume` send `marker pause`
   / `marker resume` direct to the hypervisor (pause stops signal+pcap,
   resume instant, sink retained). Six node-type routes add POST
   /markers/pause|resume; project.pause_all/resume_all_markers fan out to
   each capture node (deduped, best-effort); REST exposes
   POST /projects/{id}/markers/pause|resume.

Toggling enabled and pause/resume are now both instant — only marker create
or a bpf change still go through reset+reapply.
2026-08-02 16:19:20 +08:00
Cristi
2abc7e09a3 Adds controller and compute API support for explicitly pulling or updating Docker images. 2026-07-24 16:40:20 +03:00
YueGuobin
fd4ac4460d
feat: migrate builtin EthernetSwitch from Dynamips ethsw to ubridge brctl
Replace the builtin EthernetSwitch stub with a Linux kernel bridge backed
by uBridge's brctl module. Each switch node creates one kernel bridge
(gns3br{N}) with VLAN filtering; each port is a persistent TAP enslaved to
the bridge, relayed by a per-port uBridge bridge (nio_tap <-> nio_udp).

- Access/dot1q/qinq port modes translated to brctl vlan primitives
- Compute router repointed from Dynamips to Builtin manager
- Tests updated: 21 router-level tests + 215 surrounding tests pass
- Real-kernel e2e verified: access 100 PVID untagged, dot1q trunk
  VIDs 1-4094 + native 1, qinq 802.1ad proto + 200 PVID

Ethertype 0x9100/0x9200 handling and port-count guard relaxation are
deferred pending resolution.
2026-07-18 00:21:19 +08:00
YueGuobin
27356da62d
fix(marker): set nio.markers on iou/dynamips/cloud nio update
The PUT /nio (update) endpoints for iou, dynamips and cloud copied nio.filters from the request body but never copied nio.markers. So adding a marker (e.g. a project-level global definition) to an already-created link of these node types left nio.markers empty: no mark filter was installed on uBridge and no marker.match signal was emitted. vpcs/qemu/docker already set it; mirror them (getattr for the Union NIO types).
2026-07-16 00:39:52 +08:00
YueGuobin
c62b9b0283
refactor(marker): converge to filter single-path model, remove dual-apply endpoints
Markers now follow exactly the same apply pattern as packet filters:
state lives in Link._markers, application goes through NIO
(update() -> PUT /nio -> _ubridge_apply_markers). The former
immediate-apply REST endpoints (/markers/start, /markers/stop on
the compute side) and the per-node start_marker/stop_marker methods
are removed — they were a legacy of the original capture-inspired
design and have been superseded by the NIO flow.

Changes:
- controller/udp_link: start_marker/stop_marker/update_marker now
  set _markers state + call self.update() (mirrors update_filters).
  Removed _marker_capture_nodes runtime dict and its helpers.
- controller/project: _create_link_from_topology_data restores
  _markers directly from persisted data (with BPF validation,
  like filter reload). No long calls start_marker during load.
- compute: _ubridge_apply_markers swallows BPF compile errors
  (warn+skip), matching _ubridge_apply_filters behaviour so a
  single bad expression cannot break link creation / node restart.
- Removed: /markers/start,stop endpoints (6 handlers across
  vpcs/qemu/docker route files), node start_marker/stop_marker
  methods (3 VM files), _ubridge_delete_marker_filter,
  _marker_capture_nodes, MarkerDelete schema.

Net: ~280 lines of dead code removed; marker and packet filter now
share a single, unified apply path via the NIO.
2026-07-16 00:39:51 +08:00
YueGuobin
7c90fa9e64
fix(marker): ride markers on NIO so they survive node restart and project reload
Mirror the packet-filter lifecycle: marker specs now live on the NIO
(next to filters), ride in link_data from controller to compute on
every NIO create/update, and are reapplied by _ubridge_apply_markers
in add_ubridge_udp_connection (bridge creation / node restart) and
update_ubridge_udp_connection (NIO update — following the preceding
reset_packet_filters so markers survive filter changes).

Changes:
- NIO / NIOUDP: _markers property + asdict
- schemas/compute/nios.py: UDPNIO.markers field
- base_manager.create_nio: nio.markers from settings
- PUT /nio routes (vpcs/qemu/docker): nio.markers update
- base_node: _ubridge_apply_markers(bridge_name, nio) iterates
  nio.markers, computes pcap path, calls _ubridge_add_marker_filter
  + MarkerManager.register; called after _ubridge_apply_filters
- controller udp_link: _get_node_markers + _markers_for_node (route
  by capture_node_id); markers in create() and update() link_data
- /markers/start,stop endpoints: mirror spec onto nio.markers so
  the marker survives a subsequent node stop/start without a PUT
- tests: add markers field to NIO data expectations

This covers:
- Node restart: NIO persists, add_ubridge_udp_connection re-applies
- Filter update: reset wipes markers, _ubridge_apply_markers re-adds
- Project reload: create() carries markers in link_data → create_nio
- Immediate create: endpoint sets nio.markers immediately
2026-07-16 00:39:51 +08:00
YueGuobin
d2e9823e6c
feat: implement traffic insight marker backend (ubridge mark filter)
Add compute-side marker subsystem that receives ubridge UDP MARK signals
and forwards them as project-scoped notifications to the web UI for
real-time traffic coloring. Matched packets are always saved to per-link
pcaps for future replay.

Key components:
- gns3server/compute/marker/: MarkerManager (singleton, UDP listener +
  O(1) registry keyed by (node_id, filter_name)) + MarkerListener
  (DatagramProtocol parsing MARK lines per ubridge integration contract)
- gns3server/compute/base_node.py: marker sink/node config at ubridge
  startup; shared _ubridge_add_marker_filter / _ubridge_delete_marker_filter
- Per-node start_marker/stop_marker: VPCS (VPCS-{id}), QEMU
  (QEMU-{id}-{adapter}), Docker (bridge{adapter})
- Compute REST /markers/start + /markers/stop (vpcs/qemu/docker route files)
- Controller Link._markers state + UDPLink.start_marker/stop_marker/
  update_marker (mirror capture pattern: BPF validation, _choose_capture_side,
  node.post forwarding, topology persistence)
- Controller REST GET/POST/DELETE/PUT /v3/projects/{p}/links/{l}/markers
- Config: marker_listen_host / marker_listen_port in ServerSettings
- Signal routing: creation-time registry O(1) lookup, no node-table scan;
  project-scoped WS stream (not global); event payload always carries
  project_id for frontend scoping

Tests: 14 unit tests (registry, listener parsing, UDP round-trip);
562 existing tests pass with zero regressions.
2026-07-16 00:39:51 +08:00
YueGuobin
59e5f8dd2f
Fix review issues: key_prefix length, count validation, WAL log, timeout comment, pointless temp var 2026-06-16 22:50:22 +08:00
YueGuobin
5d0284e7be
Performance: accelerate project opening with parallel link creation and batch UDP port allocation
- Narrow IOU lock scope to only cover application_id allocation,
  allowing concurrent IOU node creation via Pool(concurrency=5)
- Parallelize link creation during project.open() using Pool(concurrency=5)
  instead of sequential processing
- Add batch UDP port allocation endpoint on compute to allocate N ports
  in a single HTTP call
- Pre-allocate UDP ports per compute before link creation during project
  loading, reducing HTTP round-trips
- UDPLink.create() falls back to individual port allocation if no
  pre-allocated port is available
2026-06-15 12:42:18 +08:00
YueGuobin
8e340f0ce8
Add descriptive detail to 403 errors in compute file endpoints
Both is_safe_path rejection and PermissionError were returning 403
without a detail message, making them indistinguishable in logs.
Add specific detail strings to each:
- is_safe_path: 'Path is outside the project directory'
- PermissionError (write): 'Permission denied writing to ...'
- PermissionError (delete): 'Permission denied deleting ...'
2026-06-09 23:33:35 +08:00
YueGuobin
16a9064eb8
Fix silent file write failure in write_compute_project_file
The inner try-except caught OSError/UnicodeEncodeError with 'pass',
silently swallowing all write failures and returning HTTP 204 as if
the file was written successfully.

Remove the nested try-except and let errors propagate properly:
- OSError → 500 with error detail
- PermissionError → 403 (already handled)
- FileNotFoundError → 404 (already handled)
2026-06-09 23:27:45 +08:00
YueGuobin
cbb21e8e40
feat: Node file streaming, recursive listing, file type detection, and file delete
- Stream file GET/POST through controller without buffering in memory
- Add recursive and subdirectory filtering to node file listing
- Replace file extension with magic-based file type detection
- Add DELETE endpoint for node and project files
- Include directories in listing response
- Add params and stream support to http_query
- Fix lambda closures, streamer exception scope, and delete error codes
2026-06-09 22:52:50 +08:00
YueGuobin
8c1dbdf079
Fix ghost Docker nodes causing 60-second VNC timeout on variable updates
When a Docker node is deleted, the compute node's DELETE endpoint only calls
node.delete() which removes the working directory but does not remove the node
object from the project's self._nodes collection. This causes ghost nodes to
remain in memory.

When project variables are updated, the code iterates through ALL nodes in
memory and calls update() on them. For ghost nodes with VNC configuration, this
triggers VNC startup attempts, resulting in 60-second timeouts waiting for X11
socket files that don't exist.

The fix adds await node.project.remove_node(node) to ensure the node object is
removed from the project's node collection when deleted, matching the behavior
of other node types that use manager.delete_node() which already calls
project.remove_node().

This resolves the issue where updating project variables after deleting a VNC
Docker container would timeout with: 'x11 socket file "/tmp/.X11-unix/X100"
does not exist'

Fixes issue #2755
2026-05-30 22:15:40 +08:00
YueGuobin
51a4c73e21
feat: Add detailed metadata to node files listing
Enhance the node files API to include comprehensive file metadata:
- File size in bytes
- File creation time (ISO 8601 format)
- File modification time (ISO 8601 format)
- File extension

Create new NodeFile schema to support these additional fields
while keeping the existing ProjectFile schema for backward compatibility.

This provides users with better information to manage and identify
files in the Web UI.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 22:57:19 +08:00
YueGuobin
5b75d633aa
feat: Add API endpoint to list node files
Implement issue #2719 - Add API endpoint to list project files

- Add GET /v3/projects/{project_id}/nodes/{node_id}/files endpoint
- Add list_node_files() method to Project class
- Add security checks to prevent path traversal
- Filter out .ghost temporary files
- Return file paths with MD5 checksums
- Require Node.Audit privilege

This allows users to discover dynamically created files
such as QEMU disk images created via the disk image API.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 22:52:17 +08:00
Guobin Yue
df28036a68
fix(deps): resolve 3 security vulnerabilities (CVE-2025-71176, CVE-2026-40347, CVE-2018-25031) (#2671)
* fix(deps): upgrade pytest to 9.0.3 to fix CVE-2025-71176

CVE-2025-71176: pytest 9.0.2 and earlier versions have a local
security vulnerability due to predictable temporary directory naming.

- pytest: 8.4.2 → 9.0.3
- Python 3.10+ is now required

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(deps): upgrade python-multipart to 0.0.26 to fix CVE-2026-40347

CVE-2026-40347: python-multipart < 0.0.26 has a denial of
service vulnerability when parsing multipart data.

- python-multipart: 0.0.22 → 0.0.26

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(deps): upgrade swagger-ui to 4.1.3 to fix CVE-2018-25031

CVE-2018-25031: swagger-ui < 4.1.3 has a spoofing vulnerability
where remote attackers can display remote OpenAPI definitions
via crafted URLs.

- swagger-ui: 3.30.0 → 4.1.3
- Updated swagger-ui-bundle.js and swagger-ui.css

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(api): change FastAPI version from "v3" to "3.0.0" for Swagger UI 4.1.3 compatibility

Swagger UI 4.x enforces stricter version format validation.
The version "v3" is not accepted by the new validator.

Changed from:
- version="v3"

To:
- version="3.0.0"

This affects both controller and compute API definitions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(deps): upgrade swagger-ui to 5.32.4

Fixes "Unable to render this definition" error when loading OpenAPI 3.0
docs. Swagger UI 3.x had incomplete OpenAPI 3.0 support.

- swagger-ui: 3.19.1 → 5.32.4 (latest)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 00:35:15 +08:00
YueGuobin
64d3c02d42 refactor(compute): remove SPICE WebSocket console support
Remove all SPICE WebSocket-related code due to frontend dependency issues
with spice-html5 library (missing RSAKey/BigInteger implementations).

Changes:
- Remove start_spice_websocket_console() from BaseNode
- Remove SPICE WebSocket endpoints from QEMU and Docker compute APIs
- Remove SPICE WebSocket proxy endpoint from controller API
- Remove WebSocket subprotocol handling from authentication layers
- Remove SPICE documentation

The SPICE console type remains functional for direct connections,
but WebSocket proxy support has been removed.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-03-17 01:15:26 +08:00
YueGuobin
c98f802362 feat(compute): add SPICE WebSocket console support
Add WebSocket-based console support for SPICE protocol, enabling
   browser-based graphical console access with enhanced features
   like clipboard sharing, USB redirection, and audio streaming.

   Changes:
   - Add start_spice_websocket_console() method in BaseNode for SPICE
     WebSocket to TCP bridging with bidirectional binary forwarding
   - Add /console/spice WebSocket endpoints in QEMU and Docker compute APIs
   - Add /console/spice WebSocket proxy endpoint in controller API
   - Add comprehensive API documentation in docs/features/

   Supported console types:
   - spice: Basic SPICE protocol support
   - spice+agent: SPICE with spice-vdagent for enhanced features

   Architecture:
   - Browser WebSocket → Controller (JWT + RBAC) → Compute (Basic Auth)
   - No external websockify processes required
   - Consistent with existing VNC WebSocket implementation
2026-03-16 22:21:25 +08:00
YueGuobin
ba92b405da feat: add VNC WebSocket console support for Docker and QEMU nodes
Add VNC console WebSocket endpoints for Docker and QEMU nodes:
- Add /console/vnc WebSocket endpoint to compute API (Docker & QEMU)
- Add /console/vnc WebSocket endpoint to controller API
- Implement start_vnc_websocket_console() in BaseNode
- Forward VNC WebSocket traffic between controller and compute layers

The implementation provides bidirectional WebSocket to TCP forwarding
for VNC protocol (RFB) connections, allowing browser-based VNC console
access to containers and VMs.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-03-16 16:40:00 +08:00
Jeremy Grossmann
632b3daab7
Merge pull request #2635 from yueguobin/fix/iou-suspend-405
fix(iou): return 405 error for unsupported suspend operation
2026-03-15 22:47:42 +08:00
grossmj
6e7738a14f
Return 405 error for unsupported actions for other node types 2026-03-15 22:38:51 +08:00
grossmj
f41cc24383
Fix for duplicating Qemu nodes 2026-03-12 13:24:30 +08:00
YueGuobin
0056bdd19b fix(iou): return 405 error for unsupported suspend operation
Fixed IOU node suspend API to return proper HTTP 405 Method Not Allowed
error instead of misleading 204 No Content response.

Changes:
- Added HTTPException import to iou_nodes.py
- Fixed suspend_iou_node route from /stop to /suspend (bug fix)
- Changed response from 204 No Content to 405 Method Not Allowed
- Added clear error message: "Suspend is not supported for IOU nodes"

This fix ensures clients receive explicit feedback when attempting to
suspend IOU nodes, which do not support suspend functionality.

Related issue: IOU nodes previously returned 404 when suspend was called
due to incorrect route registration.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-03-11 15:14:23 +08:00
Mark Paronyan
8c3aaa78f5
feat(compute): option to disable compute authentication 2026-03-03 00:28:57 +03:00
grossmj
5e3ca29c58
Fix updating packet filters 2026-02-21 21:42:22 +08:00
grossmj
ee9f974362
Fix packet capture for links connected to a cloud node. Fixes #2513 2025-03-16 21:12:33 +07:00
grossmj
035a104957
Fix capture on IOU/IOL links. Fixes #2477 2025-01-11 18:46:55 +07:00
grossmj
a00f53fbaa
Fix error 500 on PUT for cloud, nat, vmware and vpcs nodes. Fixes #2426 2024-11-19 12:21:35 +10:00