Commit Graph
260 Commits
Author SHA1 Message Date
YueGuobin 2f70bd6daa revert: drop _connect_nio thread-pool executor, restore async _ubridge_send
The _connect_nio thread-pool optimisation (send_batch_sync) targeted
node-start performance, but start_all already runs at concurrency=3
(by design, to avoid overwhelming the host).  It also introduced a
Python 3.13 incompatibility (trsock.setblocking forbidden) that
prevented docker nodes from starting.  Since node-start is not the
target of this branch (project-open link creation is), revert to the
simple per-command async _ubridge_send.

The project-open batch NIO dispatch (create_batch_nios) is unaffected —
it never called _connect_nio (nodes aren't started during open).
2026-08-11 01:16:13 +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 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 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 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 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
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
Sanjay Santhanam 41e8777609 fix: correct always-true state check in DockerVM.stop()
The condition 'state != "stopped" or state != "exited"' is a tautology,
so the state check was a no-op and a stop request was sent even for a
container that had already exited.

_get_container_state() never returns "stopped" (only "running",
"paused" or "exited"), so the intended negation of the condition used in
_fix_permissions() requires 'and', not 'or' (De Morgan's law).

Added a regression test asserting no stop query is issued for an
already-exited container.
2026-07-25 10:30:16 -07: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 e4be98984c fix(marker): enable marker support for docker nodes
Docker's _connect_nio and adapter_update_nio_binding applied packet
filters but never called _ubridge_apply_markers, so markers silently
did nothing on Docker links despite docker being in the allowlist.
Add the missing calls (same pattern as the IOU fix).

Also narrow _MARKER_CAPABLE_TYPES to the four types that actually
implement marker support — vpcs, qemu, docker, iou — removing
dynamips, virtualbox, vmware, and cloud which have no marker pathway
and would silently fail when selected as the capture side.
2026-07-16 00:39:51 +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 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
grossmj a58fa8602b remove wrongly merged code 2026-07-13 10:05:01 +02:00
YueGuobin d8f8b6ec70 fix(docker): handle container name conflict automatically
When a Docker container with the same name already exists (e.g., from a
   previous crashed GNS3 session), Docker returns a 409 Conflict error
   when trying to create a new container with that name. This causes the
   project open operation to fail.

   This fix adds automatic cleanup of stale containers when encountering
   a name conflict:
   - Added DockerHttp409Error exception class
   - Updated http_query to detect 409 status codes
   - Modified create() to remove conflicting containers and retry

   Fixes the issue where opening a project fails with:
   "Docker has returned an error: 409 Conflict. The container name
   '/GNS3.xxx' is already in use by container 'xxx'"
2026-07-13 09:57:55 +02:00
YueGuobin 066b7076c0 Add comment about rootful Docker permissions at container start
Rootful Docker recreates volume mount points as root on start,
preventing the GNS3 process from writing files into node directories
while the container is running. self._fix_permissions() would resolve
this but is currently only called at container stop time.
2026-06-10 00:46:10 +08:00
YueGuobin 1a307edbca Fix _fix_permissions error handling and list_node_files PermissionError
- _fix_permissions: capture stderr, check returncode, only set
  _permissions_fixed on success instead of silently marking as fixed
- list_node_files: wrap os.scandir in try-except to handle
  PermissionError gracefully
2026-06-10 00:14:40 +08:00
YueGuobin c2dd480edd Fix Docker container variable compatibility with Pydantic models
When updating project variables while Docker containers are running, the
system now properly handles both dictionary-format variables and Pydantic
Variable objects. This prevents AttributeError when containers are recreated
after variable updates.

Changes:
- Modified DockerVM.create() to detect and handle Pydantic Variable objects
- Updated _format_env() method to support both variable formats
- Maintains backward compatibility with existing dictionary format

Fixes error: AttributeError: 'Variable' object has no attribute 'get'
2026-05-30 13:26:36 +08:00
YueGuobinandClaude Sonnet 4.6 822abbe671 Fix: Improve Docker container deletion error logging
When closing a Docker node, if container deletion fails, the error
is silently ignored. This can lead to stale containers remaining on
the system and causing 409 conflicts when reopening projects.

Changes:
- Distinguish between 404 (container already removed, normal) and
  other DockerError (deletion failed, needs attention)
- Log warning when deletion fails with error details
- Add comment explaining stale containers will be cleaned up on
  project open (via automatic 409 conflict resolution)

This improves observability without blocking project close operations.
The root cause of stale containers can now be diagnosed from logs.

Fixes #2708

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-07 12:45:05 +08:00
Jeremy Grossmann c3d202abc9 Merge branch '3.0' into ssh-console-support 2026-05-02 22:10:28 +08:00
YueGuobinandClaude Opus 4.6 ba220780a9 fix: improve hostname validation error messages with allowed characters
When a hostname validation fails, the error message now includes
the allowed character set to help users provide valid names.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-02 13:09:04 +08:00
Cristi cc05eb5850 feat(compute): Fix missing SSH wrapping for auxiliary console 2026-04-30 17:47:54 +03:00
Cristi d64eca0418 feat(compute): add SSH console type support 2026-04-30 15:52:47 +03:00
Jeremy Grossmann b222fba42c Revert "Backport telnetlib3 changes" 2026-04-05 21:58:21 +08:00
Cristi 7684ce60ce Backport: Re-write current Telnet server implementation using telnetlib3 library 2026-04-05 13:29:25 +08:00
Cristi 83558d4d9f Enhancement: Re-write current Telnet server implementation using telnetlib3 library 2026-03-24 13:23:45 +02:00
YueGuobin ce90b2b92c fix(docker): handle container name conflict automatically
When a Docker container with the same name already exists (e.g., from a
   previous crashed GNS3 session), Docker returns a 409 Conflict error
   when trying to create a new container with that name. This causes the
   project open operation to fail.

   This fix adds automatic cleanup of stale containers when encountering
   a name conflict:
   - Added DockerHttp409Error exception class
   - Updated http_query to detect 409 status codes
   - Modified create() to remove conflicting containers and retry

   Fixes the issue where opening a project fails with:
   "Docker has returned an error: 409 Conflict. The container name
   '/GNS3.xxx' is already in use by container 'xxx'"
2026-02-27 23:35:11 +08:00
grossmj c5b9f379fe Merge remote-tracking branch 'origin/2.2' into 3.0
# Conflicts:
#	Dockerfile
#	dev-requirements.txt
#	gns3server/compute/qemu/qemu_vm.py
#	gns3server/version.py
#	requirements.txt
2026-02-20 16:28:53 +08:00
Dale Arbogast 3dd4184193 fix: busybox static link detection on Alpine/musl
On musl-based systems (Alpine), ldd returns exit code 0 for static
binaries, unlike glibc which returns 1. This causes install_busybox()
to reject all busybox binaries as "dynamically linked" on Alpine.

Fix by also accepting binaries whose executable name contains "static"
(i.e. busybox-static, busybox.static), which are the first two
candidates checked by the function. The generic "busybox" fallback
still relies on the ldd return code check.
2026-02-19 16:44:01 -05:00
grossmj 69a695c9e6 Merge branch '2.2' into 3.0
# Conflicts:
#	dev-requirements.txt
#	gns3server/appliances/infix.gns3a
#	gns3server/crash_report.py
#	gns3server/run.py
#	gns3server/utils/asyncio/__init__.py
#	gns3server/version.py
#	requirements.txt
#	setup.py
2026-01-24 19:27:39 +08:00
grossmj 58d1e8f0c5 Set default location of udhcpc in "/etc/network/udhcpc". Fixes #2582 2026-01-17 18:01:42 +08:00
grossmj 60c10e7ecc Merge remote-tracking branch 'origin/2.2' into 3.0
# Conflicts:
#	.github/workflows/testing.yml
#	CHANGELOG
#	README.md
#	gns3server/appliances/infix.gns3a
#	gns3server/compute/docker/__init__.py
#	gns3server/compute/docker/docker_vm.py
#	gns3server/crash_report.py
#	gns3server/version.py
#	gns3server/web/web_server.py
#	setup.py
#	tests/controller/test_project.py
2025-11-29 11:42:02 +10:00
grossmj f2aaf5ab38 Docker API version requirements and process to handle older daemons 2025-11-17 22:26:18 +10:00
grossmj 3a24318694 Use docker_name property when creating a container 2025-11-17 11:11:11 +10:00
grossmj bae6221991 Set name of container in Docker 2025-11-17 10:59:05 +10:00
Thomas Schmidt a7108e1592 use GNS3 node name as vnc Desktop name
Most VNC clients use the Desktop name in their window title.
Currently this defaults to user@host which means all docker vnc
connections have identical window names of e.g.
  SSVNC: gns3@gns3server

This uses the node name instead.
2025-05-22 13:13:47 +02:00
grossmj d9dcc2738d Merge remote-tracking branch 'origin/2.2' into 3.0
# Conflicts:
#	CHANGELOG
#	Dockerfile
#	README.md
#	gns3server/appliances/infix.gns3a
#	gns3server/compute/docker/docker_vm.py
#	gns3server/compute/qemu/qemu_vm.py
#	gns3server/controller/gns3vm/virtualbox_gns3_vm.py
#	gns3server/crash_report.py
#	gns3server/static/web-ui/index.html
#	gns3server/static/web-ui/main.9bcf455e62558dedfd48.js
#	gns3server/version.py
2025-04-21 20:13:31 +07:00
grossmj ffd628902c Fix Docker logs decoding. Ref #2522 2025-04-18 13:58:56 +07:00
grossmj ca34053125 Add delay after starting a Docker container and adding connections in uBridge. Ref #2522 2025-04-18 13:50:20 +07:00
grossmj d06f93e772 Fix TypeError when reading Docker container logs. Ref #2522 2025-04-18 13:47:29 +07:00
grossmj 0e8d969cd2 Merge branch '2.2' into 3.0
# Conflicts:
#	gns3server/compute/docker/__init__.py
#	gns3server/compute/docker/docker_vm.py
#	requirements.txt
2025-04-16 17:53:44 +07:00
grossmj 0e89ff56a8 Replace "Docker hub" by "Docker repository" because it is possible to use different repositories 2025-04-16 17:42:40 +07:00
grossmj 4758431c76 Improvements for built-in disks
* Checksum is updated in the database for updated disks.
* It is not possible to prune them.
2025-01-02 23:10:51 +07:00
grossmj ec9dbd20b8 Merge branch '2.2' into 3.0
# Conflicts:
#	gns3server/compute/base_node.py
#	gns3server/compute/docker/docker_vm.py
#	gns3server/compute/qemu/qemu_vm.py
#	gns3server/crash_report.py
#	gns3server/version.py
2024-11-07 23:18:42 +10:00
grossmj f3749e83ec Catch error when cannot resize Docker container TTY. 2024-11-07 14:52:59 +10:00
grossmj fb06eb3c0c Remove old debug message 2024-10-31 13:07:09 +10:00
grossmj c4fcb43e78 Merge branch '2.2' into 3.0
# Conflicts:
#	.github/workflows/testing.yml
#	CHANGELOG
#	README.md
#	dev-requirements.txt
#	gns3server/compute/docker/docker_vm.py
#	gns3server/controller/export_project.py
#	gns3server/controller/import_project.py
#	gns3server/controller/project.py
#	gns3server/crash_report.py
#	gns3server/static/web-ui/index.html
#	gns3server/static/web-ui/main.df8c319a3da6fb0e3629.js
#	gns3server/version.py
#	requirements.txt
#	setup.py
#	tests/compute/docker/test_docker_vm.py
#	tests/controller/test_import_project.py
2024-10-31 12:54:35 +10:00
grossmj 48b7e6ca50 Fix run Docker containers with user namespaces enabled. Fixes #2414 2024-10-26 19:03:05 +10:00
grossmj 6e66b26b88 Fix errors in init.sh. Fixes #2431 2024-10-22 12:18:28 +10:00
grossmj 8af71ee291 Formatting 2024-10-01 17:35:11 +07:00