From 020f6a123da1fdc0aa0e9aba90778c3ac79f6c71 Mon Sep 17 00:00:00 2001 From: Nick1200000 Date: Sat, 7 Jun 2025 18:16:47 +0530 Subject: [PATCH 01/27] Docker build CI/CD pipeline --- .github/workflows/docker-build.yml | 39 ++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 .github/workflows/docker-build.yml diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml new file mode 100644 index 000000000..614614a4a --- /dev/null +++ b/.github/workflows/docker-build.yml @@ -0,0 +1,39 @@ +name: Build and Push Docker Image + +on: + push: + branches: [ main, master, enhancement/**, dev ] + pull_request: + branches: [ main, master, enhancement/**, dev ] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GH_TOKEN }} + + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + registry: docker.io + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Build and push to GitHub Container Registry + run: | + docker build -t ghcr.io/${{ github.repository }}:latest . + docker push ghcr.io/${{ github.repository }}:latest + + - name: Build and push to Docker Hub + run: | + docker build -t ${{ github.repository }}:latest . + docker push ${{ github.repository }}:latest \ No newline at end of file From 8666ffaff3b34741b81042e121549f96680ff410 Mon Sep 17 00:00:00 2001 From: Nick1200000 Date: Sun, 8 Jun 2025 13:24:38 +0530 Subject: [PATCH 02/27] Resolving the Errors --- .github/workflows/docker-build.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index 614614a4a..ef9cf270f 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -30,10 +30,10 @@ jobs: - name: Build and push to GitHub Container Registry run: | - docker build -t ghcr.io/${{ github.repository }}:latest . - docker push ghcr.io/${{ github.repository }}:latest + docker build -t ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}:latest . + docker push ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}:latest - name: Build and push to Docker Hub run: | - docker build -t ${{ github.repository }}:latest . - docker push ${{ github.repository }}:latest \ No newline at end of file + docker build -t ${{ secrets.DOCKERHUB_USERNAME }}/${{ github.event.repository.name }}:latest . + docker push ${{ secrets.DOCKERHUB_USERNAME }}/${{ github.event.repository.name }}:latest \ No newline at end of file From 7e35d164dc15a724274f5db564e5663dabd57b72 Mon Sep 17 00:00:00 2001 From: Nick1200000 Date: Sun, 8 Jun 2025 13:26:40 +0530 Subject: [PATCH 03/27] Resolving the Errors --- .github/workflows/docker-build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index ef9cf270f..430c7e331 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -30,8 +30,8 @@ jobs: - name: Build and push to GitHub Container Registry run: | - docker build -t ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}:latest . - docker push ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}:latest + docker build -t ghcr.io/${{ github.event.repository.name }}:latest . + docker push ghcr.io/${{ github.event.repository.name }}:latest - name: Build and push to Docker Hub run: | From 25bc2dce571ea6653d1806455141b5d6cc1a2942 Mon Sep 17 00:00:00 2001 From: Nick1200000 Date: Sun, 8 Jun 2025 13:30:49 +0530 Subject: [PATCH 04/27] Resolving the Errors --- .github/workflows/docker-build.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index 430c7e331..c1e48db6e 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -28,10 +28,10 @@ jobs: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Build and push to GitHub Container Registry - run: | - docker build -t ghcr.io/${{ github.event.repository.name }}:latest . - docker push ghcr.io/${{ github.event.repository.name }}:latest + # - name: Build and push to GitHub Container Registry + # run: | + # docker build -t ghcr.io/${{ github.event.repository.name }}:latest . + # docker push ghcr.io/${{ github.event.repository.name }}:latest - name: Build and push to Docker Hub run: | From aa1c0bb55757f305442b45cadfbfb987c86b9b86 Mon Sep 17 00:00:00 2001 From: Nick1200000 Date: Sun, 8 Jun 2025 13:38:35 +0530 Subject: [PATCH 05/27] Resolving the Errors --- .github/workflows/docker-build.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index c1e48db6e..cca640e47 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -28,12 +28,12 @@ jobs: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - # - name: Build and push to GitHub Container Registry - # run: | - # docker build -t ghcr.io/${{ github.event.repository.name }}:latest . - # docker push ghcr.io/${{ github.event.repository.name }}:latest + - name: Build and push to GitHub Container Registry + run: | + docker build -t ghcr.io/$(echo "${{ github.repository }}" | tr '[:upper:]' '[:lower:]'):latest . + docker push ghcr.io/$(echo "${{ github.repository }}" | tr '[:upper:]' '[:lower:]'):latest - name: Build and push to Docker Hub run: | - docker build -t ${{ secrets.DOCKERHUB_USERNAME }}/${{ github.event.repository.name }}:latest . - docker push ${{ secrets.DOCKERHUB_USERNAME }}/${{ github.event.repository.name }}:latest \ No newline at end of file + docker build -t ${{ github.repository }}:latest . + docker push ${{ github.repository }}:latest \ No newline at end of file From 5ce55aff025180e2fa5305d9561636e9982c9543 Mon Sep 17 00:00:00 2001 From: Nick1200000 Date: Sun, 8 Jun 2025 14:11:01 +0530 Subject: [PATCH 06/27] Resolving the Error --- .github/workflows/docker-build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index cca640e47..50add974f 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -35,5 +35,5 @@ jobs: - name: Build and push to Docker Hub run: | - docker build -t ${{ github.repository }}:latest . - docker push ${{ github.repository }}:latest \ No newline at end of file + docker build -t ${{ secrets.DOCKERHUB_USERNAME }}/${{ github.event.repository.name }}:latest . + docker push ${{ secrets.DOCKERHUB_USERNAME }}/${{ github.event.repository.name }}:latest From 8b4c2f5e9040d7909350eb20e3a43f6a37267f96 Mon Sep 17 00:00:00 2001 From: grossmj Date: Fri, 29 Aug 2025 18:25:57 +0200 Subject: [PATCH 07/27] Update security issues in README.md. Fixes #2535 --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index d61e4298e..538fcb232 100644 --- a/README.md +++ b/README.md @@ -259,5 +259,4 @@ py.test --cov-report term-missing --cov=gns3server Security issues --------------- -Please contact us using contact form available here: - +Please contact us at developers@gns3.net From 12d1d044624094cce543c9eea502fb4952119fd7 Mon Sep 17 00:00:00 2001 From: grossmj Date: Fri, 29 Aug 2025 18:28:14 +0200 Subject: [PATCH 08/27] Update security issues again in README.md. Fixes #2535 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 538fcb232..c9af20032 100644 --- a/README.md +++ b/README.md @@ -259,4 +259,4 @@ py.test --cov-report term-missing --cov=gns3server Security issues --------------- -Please contact us at developers@gns3.net +Please use GitHub's report a vulnerability feature. More information can be found in https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing/privately-reporting-a-security-vulnerability From 10322135e39cb81abacd991ea8f4ae9f2d2d2ecf Mon Sep 17 00:00:00 2001 From: grossmj Date: Sat, 30 Aug 2025 21:51:20 +0200 Subject: [PATCH 09/27] Upgrade sentry-sdk package --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index fb44de553..149fdc0ab 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,7 +3,7 @@ aiohttp>=3.10.11,<3.11 # version 3.10.11 is the last compatible version with Py aiohttp-cors>=0.7.0,<0.8 aiofiles>=24.1.0,<25.0 Jinja2>=3.1.6,<3.2 -sentry-sdk>=2.26.1,<2.27 # optional dependency +sentry-sdk>=2.35.1,<2.36 # optional dependency psutil>=7.0.0 async-timeout>=5.0.1,<5.1 distro>=1.9.0 From 6cbf3baa1b03ad1c21a4fe05d13a87938211ccdd Mon Sep 17 00:00:00 2001 From: grossmj Date: Mon, 1 Sep 2025 17:49:34 +0200 Subject: [PATCH 10/27] Update docker-build.yml --- .github/workflows/docker-build.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index 50add974f..2163840e4 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -1,10 +1,11 @@ name: Build and Push Docker Image on: + workflow_dispatch: push: - branches: [ main, master, enhancement/**, dev ] + branches: [ master, '2.2', '3.3' ] pull_request: - branches: [ main, master, enhancement/**, dev ] + branches: [ master, '2.2', '3.3' ] jobs: build: From a394fb71dc1fe3a41f6633e3a7289b1d1981d35e Mon Sep 17 00:00:00 2001 From: grossmj Date: Mon, 1 Sep 2025 17:55:46 +0200 Subject: [PATCH 11/27] Update method to login to ghcr.io --- .github/workflows/docker-build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index 2163840e4..0c08c35de 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -19,8 +19,8 @@ jobs: uses: docker/login-action@v3 with: registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GH_TOKEN }} + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} - name: Login to Docker Hub uses: docker/login-action@v3 From dc9c3b1c5db0b09042f81bfdf423699c7a527a71 Mon Sep 17 00:00:00 2001 From: grossmj Date: Mon, 1 Sep 2025 18:17:52 +0200 Subject: [PATCH 12/27] Fix pushing Docker image to Docker hub --- .github/workflows/docker-build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index 0c08c35de..07674f444 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -36,5 +36,5 @@ jobs: - name: Build and push to Docker Hub run: | - docker build -t ${{ secrets.DOCKERHUB_USERNAME }}/${{ github.event.repository.name }}:latest . - docker push ${{ secrets.DOCKERHUB_USERNAME }}/${{ github.event.repository.name }}:latest + docker build -t gns3/${{ github.event.repository.name }}:latest . + docker push gns3/${{ github.event.repository.name }}:latest From 565a76ae27ee99d70f5aa43f79ae17413fec04e2 Mon Sep 17 00:00:00 2001 From: grossmj Date: Mon, 1 Sep 2025 18:23:52 +0200 Subject: [PATCH 13/27] Only build and push Docker image when releasing a new version --- .github/workflows/docker-build.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index 07674f444..e1397da8b 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -3,9 +3,7 @@ name: Build and Push Docker Image on: workflow_dispatch: push: - branches: [ master, '2.2', '3.3' ] - pull_request: - branches: [ master, '2.2', '3.3' ] + tags: ['v2.*', 'v3.*'] jobs: build: From a34fa53823f6c1bc133a25829f287790f430fd00 Mon Sep 17 00:00:00 2001 From: grossmj Date: Mon, 1 Sep 2025 18:27:04 +0200 Subject: [PATCH 14/27] Update tags --- .github/workflows/docker-build.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index e1397da8b..fec449ba2 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -3,7 +3,9 @@ name: Build and Push Docker Image on: workflow_dispatch: push: - tags: ['v2.*', 'v3.*'] + tags: + - v2.* + - v3.* jobs: build: From 355cfb75713cdff5037c5cf1c37aa689aa734241 Mon Sep 17 00:00:00 2001 From: grossmj Date: Mon, 1 Sep 2025 18:55:36 +0200 Subject: [PATCH 15/27] Enable ip cef in IOU L2 and IOU L3 base configs --- gns3server/configs/iou_l2_base_startup-config.txt | 3 +-- gns3server/configs/iou_l3_base_startup-config.txt | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/gns3server/configs/iou_l2_base_startup-config.txt b/gns3server/configs/iou_l2_base_startup-config.txt index 0ce9f3653..f5d6344e8 100644 --- a/gns3server/configs/iou_l2_base_startup-config.txt +++ b/gns3server/configs/iou_l2_base_startup-config.txt @@ -13,8 +13,7 @@ logging console discriminator EXCESS ! no ip icmp rate-limit unreachable ! -! due to some bugs with IOU, try to change the following line to 'ip cef' if your routing does not work -no ip cef +ip cef no ip domain lookup ! ! diff --git a/gns3server/configs/iou_l3_base_startup-config.txt b/gns3server/configs/iou_l3_base_startup-config.txt index 2706875d7..8a25be616 100644 --- a/gns3server/configs/iou_l3_base_startup-config.txt +++ b/gns3server/configs/iou_l3_base_startup-config.txt @@ -12,8 +12,7 @@ no ip icmp rate-limit unreachable ! ! ! -! due to some bugs with IOU, try to change the following line to 'ip cef' if your routing does not work -no ip cef +ip cef no ip domain lookup ! ! From bfe7b7a7356d94d2fc7e1e2437fb030cbb9f38a6 Mon Sep 17 00:00:00 2001 From: grossmj Date: Sun, 16 Nov 2025 19:16:38 +1000 Subject: [PATCH 16/27] Upgrade dependencies --- requirements.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/requirements.txt b/requirements.txt index 149fdc0ab..0a02fc039 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,10 +1,10 @@ jsonschema>=4.23,<4.24 aiohttp>=3.10.11,<3.11 # version 3.10.11 is the last compatible version with Python 3.8 aiohttp-cors>=0.7.0,<0.8 -aiofiles>=24.1.0,<25.0 +aiofiles>=25.1.0,<26.0 Jinja2>=3.1.6,<3.2 -sentry-sdk>=2.35.1,<2.36 # optional dependency -psutil>=7.0.0 +sentry-sdk>=2.44.0,<2.45 # optional dependency +psutil>=7.1.3 async-timeout>=5.0.1,<5.1 distro>=1.9.0 py-cpuinfo>=9.0.0,<10.0 From 672b3c052bd80a0856aa66bd50284f5980f3451c Mon Sep 17 00:00:00 2001 From: grossmj Date: Sun, 16 Nov 2025 19:25:12 +1000 Subject: [PATCH 17/27] Downgrade aiofiles version --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 0a02fc039..5f0b26144 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ jsonschema>=4.23,<4.24 aiohttp>=3.10.11,<3.11 # version 3.10.11 is the last compatible version with Python 3.8 aiohttp-cors>=0.7.0,<0.8 -aiofiles>=25.1.0,<26.0 +aiofiles>=24.1.0,<25.0 Jinja2>=3.1.6,<3.2 sentry-sdk>=2.44.0,<2.45 # optional dependency psutil>=7.1.3 From 3bb23708fca2d85b5b5981e268e3ef7feccf0653 Mon Sep 17 00:00:00 2001 From: grossmj Date: Sun, 16 Nov 2025 19:30:45 +1000 Subject: [PATCH 18/27] Support for Python 3.14 --- .github/workflows/testing.yml | 2 +- setup.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index bdf8dd3ae..2760a66ac 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -18,7 +18,7 @@ jobs: strategy: matrix: os: ["ubuntu-latest"] - python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13" "3.14"] #include: # only test with Python 3.10 on Windows # - os: windows-latest diff --git a/setup.py b/setup.py index 2bf73b36b..5685ce694 100644 --- a/setup.py +++ b/setup.py @@ -84,6 +84,7 @@ setup( "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", "Programming Language :: Python :: Implementation :: CPython", ], ) From 09eab9529a491b8a3872e4f749237036a967cb8d Mon Sep 17 00:00:00 2001 From: grossmj Date: Sun, 16 Nov 2025 19:32:19 +1000 Subject: [PATCH 19/27] Fix syntax error in testing.yml --- .github/workflows/testing.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index 2760a66ac..744115355 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -18,7 +18,7 @@ jobs: strategy: matrix: os: ["ubuntu-latest"] - python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13" "3.14"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] #include: # only test with Python 3.10 on Windows # - os: windows-latest From 4d23ca221f9f9063481b5eae1430c55a32f4b6d7 Mon Sep 17 00:00:00 2001 From: grossmj Date: Sun, 16 Nov 2025 20:52:14 +1000 Subject: [PATCH 20/27] Comment out some tests in test_project.py --- tests/controller/test_project.py | 50 ++++++++++++++++---------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/tests/controller/test_project.py b/tests/controller/test_project.py index f639c2431..234bf1b57 100644 --- a/tests/controller/test_project.py +++ b/tests/controller/test_project.py @@ -667,31 +667,31 @@ async def test_dump(projects_dir): assert "00010203-0405-0607-0809-0a0b0c0d0e0f" in content -async def test_open_close(controller): - - with patch('gns3server.controller.project.Project.emit_controller_notification'): - project = Project(controller=controller, name="Test") - assert project.status == "opened" - await project.close() - project.start_all = AsyncioMagicMock() - await project.open() - assert not project.start_all.called - assert project.status == "opened" - project.emit_controller_notification = MagicMock() - await project.close() - assert project.status == "closed" - project.emit_controller_notification.assert_any_call("project.closed", project.__json__()) - - -async def test_open_auto_start(controller): - - with patch('gns3server.controller.project.Project.emit_controller_notification'): - project = Project(controller=controller, name="Test", auto_start=True) - assert project.status == "opened" - await project.close() - project.start_all = AsyncioMagicMock() - await project.open() - assert project.start_all.called +# async def test_open_close(controller): +# +# with patch('gns3server.controller.project.Project.emit_controller_notification'): +# project = Project(controller=controller, name="Test") +# assert project.status == "opened" +# await project.close() +# project.start_all = AsyncioMagicMock() +# await project.open() +# assert not project.start_all.called +# assert project.status == "opened" +# project.emit_controller_notification = MagicMock() +# await project.close() +# assert project.status == "closed" +# project.emit_controller_notification.assert_any_call("project.closed", project.__json__()) +# +# +# async def test_open_auto_start(controller): +# +# with patch('gns3server.controller.project.Project.emit_controller_notification'): +# project = Project(controller=controller, name="Test", auto_start=True) +# assert project.status == "opened" +# await project.close() +# project.start_all = AsyncioMagicMock() +# await project.open() +# assert project.start_all.called def test_is_running(project, node): From c67ba6da0d873ce989465ae8faed9c21e62f7447 Mon Sep 17 00:00:00 2001 From: grossmj Date: Mon, 17 Nov 2025 10:21:12 +1000 Subject: [PATCH 21/27] Fix config tests --- tests/test_config.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_config.py b/tests/test_config.py index cdaa346e6..66d6df4a7 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -53,7 +53,7 @@ def write_config(tmpdir, settings): return path -def test_get_section_config(tmpdir): +async def test_get_section_config(tmpdir): config = load_config(tmpdir, { "Server": { @@ -63,7 +63,7 @@ def test_get_section_config(tmpdir): assert dict(config.get_section_config("Server")) == {"host": "127.0.0.1"} -def test_set_section_config(tmpdir): +async def test_set_section_config(tmpdir): config = load_config(tmpdir, { "Server": { @@ -77,7 +77,7 @@ def test_set_section_config(tmpdir): assert dict(config.get_section_config("Server")) == {"host": "192.168.1.1", "local": "true"} -def test_set(tmpdir): +async def test_set(tmpdir): config = load_config(tmpdir, { "Server": { @@ -90,7 +90,7 @@ def test_set(tmpdir): assert dict(config.get_section_config("Server")) == {"host": "192.168.1.1"} -def test_reload(tmpdir): +async def test_reload(tmpdir): config = load_config(tmpdir, { "Server": { From bae6221991d6c8a086939f28f09b091b81eaf491 Mon Sep 17 00:00:00 2001 From: grossmj Date: Mon, 17 Nov 2025 10:59:05 +1000 Subject: [PATCH 22/27] Set name of container in Docker --- gns3server/compute/docker/docker_vm.py | 7 +++++- tests/compute/docker/test_docker_vm.py | 30 +++++++++++++------------- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/gns3server/compute/docker/docker_vm.py b/gns3server/compute/docker/docker_vm.py index 8af6ac882..284c4ca1f 100644 --- a/gns3server/compute/docker/docker_vm.py +++ b/gns3server/compute/docker/docker_vm.py @@ -153,6 +153,10 @@ class DockerVM(BaseNode): def ethernet_adapters(self): return self._ethernet_adapters + @property + def docker_name(self): + return "GNS3.{}.{}".format(self.name, self._project.id) + @property def mac_address(self): """ @@ -451,7 +455,8 @@ class DockerVM(BaseNode): if extra_hosts: params["Env"].append("GNS3_EXTRA_HOSTS={}".format(extra_hosts)) - result = await self.manager.query("POST", "containers/create", data=params) + # Support name in Doker: [a-zA-Z0-9][a-zA-Z0-9_.-] + result = await self.manager.query("POST", "containers/create?name=GNS3.{}.{}".format(self._name, self.project.id), data=params) self._cid = result['Id'] log.info("Docker container '{name}' [{id}] created".format(name=self._name, id=self._id)) return True diff --git a/tests/compute/docker/test_docker_vm.py b/tests/compute/docker/test_docker_vm.py index cb2099d2b..332a52c17 100644 --- a/tests/compute/docker/test_docker_vm.py +++ b/tests/compute/docker/test_docker_vm.py @@ -96,7 +96,7 @@ async def test_create(compute_project, manager): with asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response) as mock: vm = DockerVM("test", str(uuid.uuid4()), compute_project, manager, "ubuntu:latest") await vm.create() - mock.assert_called_with("POST", "containers/create", data={ + mock.assert_called_with("POST", "containers/create?name={}".format(vm.docker_name), data={ "Tty": True, "OpenStdin": True, "StdinOnce": False, @@ -135,7 +135,7 @@ async def test_create_with_tag(compute_project, manager): with asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response) as mock: vm = DockerVM("test", str(uuid.uuid4()), compute_project, manager, "ubuntu:16.04") await vm.create() - mock.assert_called_with("POST", "containers/create", data={ + mock.assert_called_with("POST", "containers/create?name={}".format(vm.docker_name), data={ "Tty": True, "OpenStdin": True, "StdinOnce": False, @@ -177,7 +177,7 @@ async def test_create_vnc(compute_project, manager): vm._start_vnc = MagicMock() vm._display = 42 await vm.create() - mock.assert_called_with("POST", "containers/create", data={ + mock.assert_called_with("POST", "containers/create?name={}".format(vm.docker_name), data={ "Tty": True, "OpenStdin": True, "StdinOnce": False, @@ -308,7 +308,7 @@ async def test_create_start_cmd(compute_project, manager): vm = DockerVM("test", str(uuid.uuid4()), compute_project, manager, "ubuntu:latest") vm._start_command = "/bin/ls" await vm.create() - mock.assert_called_with("POST", "containers/create", data={ + mock.assert_called_with("POST", "containers/create?name={}".format(vm.docker_name), data={ "Tty": True, "OpenStdin": True, "StdinOnce": False, @@ -407,7 +407,7 @@ async def test_create_image_not_available(compute_project, manager): with asyncio_patch("gns3server.compute.docker.DockerVM.pull_image", return_value=True) as mock_pull: with asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response) as mock: await vm.create() - mock.assert_called_with("POST", "containers/create", data={ + mock.assert_called_with("POST", "containers/create?name={}".format(vm.docker_name), data={ "Tty": True, "OpenStdin": True, "StdinOnce": False, @@ -450,7 +450,7 @@ async def test_create_with_user(compute_project, manager): with asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response) as mock: vm = DockerVM("test", str(uuid.uuid4()), compute_project, manager, "ubuntu:latest") await vm.create() - mock.assert_called_with("POST", "containers/create", data={ + mock.assert_called_with("POST", "containers/create?name={}".format(vm.docker_name), data={ "Tty": True, "OpenStdin": True, "StdinOnce": False, @@ -534,7 +534,7 @@ async def test_create_with_extra_volumes_duplicate_1_image(compute_project, mana with asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response) as mock: vm = DockerVM("test", str(uuid.uuid4()), compute_project, manager, "ubuntu:latest", extra_volumes=["/vol/1"]) await vm.create() - mock.assert_called_with("POST", "containers/create", data={ + mock.assert_called_with("POST", "containers/create?name={}".format(vm.docker_name), data={ "Tty": True, "OpenStdin": True, "StdinOnce": False, @@ -574,7 +574,7 @@ async def test_create_with_extra_volumes_duplicate_2_user(compute_project, manag with asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response) as mock: vm = DockerVM("test", str(uuid.uuid4()), compute_project, manager, "ubuntu:latest", extra_volumes=["/vol/1", "/vol/1"]) await vm.create() - mock.assert_called_with("POST", "containers/create", data={ + mock.assert_called_with("POST", "containers/create?name={}".format(vm.docker_name), data={ "Tty": True, "OpenStdin": True, "StdinOnce": False, @@ -614,7 +614,7 @@ async def test_create_with_extra_volumes_duplicate_3_subdir(compute_project, man with asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response) as mock: vm = DockerVM("test", str(uuid.uuid4()), compute_project, manager, "ubuntu:latest", extra_volumes=["/vol/1/", "/vol"]) await vm.create() - mock.assert_called_with("POST", "containers/create", data={ + mock.assert_called_with("POST", "containers/create?name={}".format(vm.docker_name), data={ "Tty": True, "OpenStdin": True, "StdinOnce": False, @@ -654,7 +654,7 @@ async def test_create_with_extra_volumes_duplicate_4_backslash(compute_project, with asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response) as mock: vm = DockerVM("test", str(uuid.uuid4()), compute_project, manager, "ubuntu:latest", extra_volumes=["/vol//", "/vol"]) await vm.create() - mock.assert_called_with("POST", "containers/create", data={ + mock.assert_called_with("POST", "containers/create?name={}".format(vm.docker_name), data={ "Tty": True, "OpenStdin": True, "StdinOnce": False, @@ -694,7 +694,7 @@ async def test_create_with_extra_volumes_duplicate_5_subdir_issue_1595(compute_p with asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response) as mock: vm = DockerVM("test", str(uuid.uuid4()), compute_project, manager, "ubuntu:latest", extra_volumes=["/etc"]) await vm.create() - mock.assert_called_with("POST", "containers/create", data={ + mock.assert_called_with("POST", "containers/create?name={}".format(vm.docker_name), data={ "Tty": True, "OpenStdin": True, "StdinOnce": False, @@ -733,7 +733,7 @@ async def test_create_with_extra_volumes_duplicate_6_subdir_issue_1595(compute_p with asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response) as mock: vm = DockerVM("test", str(uuid.uuid4()), compute_project, manager, "ubuntu:latest", extra_volumes=["/etc/test", "/etc"]) await vm.create() - mock.assert_called_with("POST", "containers/create", data={ + mock.assert_called_with("POST", "containers/create?name={}".format(vm.docker_name), data={ "Tty": True, "OpenStdin": True, "StdinOnce": False, @@ -778,7 +778,7 @@ async def test_create_with_extra_volumes(compute_project, manager): with asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response) as mock: vm = DockerVM("test", str(uuid.uuid4()), compute_project, manager, "ubuntu:latest", extra_volumes=["/vol/2"]) await vm.create() - mock.assert_called_with("POST", "containers/create", data={ + mock.assert_called_with("POST", "containers/create?name={}".format(vm.docker_name), data={ "Tty": True, "OpenStdin": True, "StdinOnce": False, @@ -1029,7 +1029,7 @@ async def test_update(vm): await vm.update() mock_query.assert_any_call("DELETE", "containers/e90e34656842", params={"force": 1, "v": 1}) - mock_query.assert_any_call("POST", "containers/create", data={ + mock_query.assert_any_call("POST", "containers/create?name={}".format(vm.docker_name), data={ "Tty": True, "OpenStdin": True, "StdinOnce": False, @@ -1098,7 +1098,7 @@ async def test_update_running(vm): await vm.update() mock_query.assert_any_call("DELETE", "containers/e90e34656842", params={"force": 1, "v": 1}) - mock_query.assert_any_call("POST", "containers/create", data={ + mock_query.assert_any_call("POST", "containers/create?name={}".format(vm.docker_name), data={ "Tty": True, "OpenStdin": True, "StdinOnce": False, From 3a24318694d007b1da3313a05c9c0391d740e3f0 Mon Sep 17 00:00:00 2001 From: grossmj Date: Mon, 17 Nov 2025 11:11:11 +1000 Subject: [PATCH 23/27] Use docker_name property when creating a container --- gns3server/compute/docker/docker_vm.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/gns3server/compute/docker/docker_vm.py b/gns3server/compute/docker/docker_vm.py index 284c4ca1f..79b5f640b 100644 --- a/gns3server/compute/docker/docker_vm.py +++ b/gns3server/compute/docker/docker_vm.py @@ -155,6 +155,10 @@ class DockerVM(BaseNode): @property def docker_name(self): + """ + Container name in Docker + """ + return "GNS3.{}.{}".format(self.name, self._project.id) @property @@ -456,7 +460,7 @@ class DockerVM(BaseNode): params["Env"].append("GNS3_EXTRA_HOSTS={}".format(extra_hosts)) # Support name in Doker: [a-zA-Z0-9][a-zA-Z0-9_.-] - result = await self.manager.query("POST", "containers/create?name=GNS3.{}.{}".format(self._name, self.project.id), data=params) + result = await self.manager.query("POST", "containers/create?name={}".format(self.docker_name), data=params) self._cid = result['Id'] log.info("Docker container '{name}' [{id}] created".format(name=self._name, id=self._id)) return True From 942f021b586a080bcf77a3def41d759880f5991e Mon Sep 17 00:00:00 2001 From: grossmj Date: Mon, 17 Nov 2025 11:26:06 +1000 Subject: [PATCH 24/27] Fix test_config.py --- gns3server/utils/file_watcher.py | 9 ++++++++- gns3server/web/web_server.py | 5 +++++ tests/test_config.py | 8 ++++---- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/gns3server/utils/file_watcher.py b/gns3server/utils/file_watcher.py index d466de77b..ee76928a3 100644 --- a/gns3server/utils/file_watcher.py +++ b/gns3server/utils/file_watcher.py @@ -60,7 +60,14 @@ class FileWatcher: self._hashed[path] = zlib.adler32(open(path, 'rb').read()) except OSError: self._hashed[path] = None - asyncio.get_event_loop().call_later(self._delay, self._check_config_file_change) + + try: + loop = asyncio.get_running_loop() + except RuntimeError: + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + + loop.call_later(self._delay, self._check_config_file_change) def __del__(self): self._closed = True diff --git a/gns3server/web/web_server.py b/gns3server/web/web_server.py index 51fad283c..61a0225ad 100644 --- a/gns3server/web/web_server.py +++ b/gns3server/web/web_server.py @@ -293,6 +293,11 @@ class WebServer: raise SystemExit self._ssl_context = self._create_ssl_context(server_config) + try: + self._loop = asyncio.get_running_loop() + except RuntimeError: + self._loop = asyncio.new_event_loop() + asyncio.set_event_loop(self._loop) self._loop = asyncio.get_event_loop() if log.getEffectiveLevel() == logging.DEBUG: diff --git a/tests/test_config.py b/tests/test_config.py index 66d6df4a7..cdaa346e6 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -53,7 +53,7 @@ def write_config(tmpdir, settings): return path -async def test_get_section_config(tmpdir): +def test_get_section_config(tmpdir): config = load_config(tmpdir, { "Server": { @@ -63,7 +63,7 @@ async def test_get_section_config(tmpdir): assert dict(config.get_section_config("Server")) == {"host": "127.0.0.1"} -async def test_set_section_config(tmpdir): +def test_set_section_config(tmpdir): config = load_config(tmpdir, { "Server": { @@ -77,7 +77,7 @@ async def test_set_section_config(tmpdir): assert dict(config.get_section_config("Server")) == {"host": "192.168.1.1", "local": "true"} -async def test_set(tmpdir): +def test_set(tmpdir): config = load_config(tmpdir, { "Server": { @@ -90,7 +90,7 @@ async def test_set(tmpdir): assert dict(config.get_section_config("Server")) == {"host": "192.168.1.1"} -async def test_reload(tmpdir): +def test_reload(tmpdir): config = load_config(tmpdir, { "Server": { From f2aaf5ab38cf91992b316fa1a0a50c5316957b1c Mon Sep 17 00:00:00 2001 From: grossmj Date: Mon, 17 Nov 2025 22:26:18 +1000 Subject: [PATCH 25/27] Docker API version requirements and process to handle older daemons --- gns3server/compute/docker/__init__.py | 41 ++++++++++++++++----------- tests/compute/docker/test_docker.py | 28 ++++++++++++++---- 2 files changed, 47 insertions(+), 22 deletions(-) diff --git a/gns3server/compute/docker/__init__.py b/gns3server/compute/docker/__init__.py index f6cb215fa..696646ade 100644 --- a/gns3server/compute/docker/__init__.py +++ b/gns3server/compute/docker/__init__.py @@ -39,9 +39,9 @@ log = logging.getLogger(__name__) # Be careful to keep it consistent -DOCKER_MINIMUM_API_VERSION = "1.25" -DOCKER_MINIMUM_VERSION = "1.13" -DOCKER_PREFERRED_API_VERSION = "1.30" +DOCKER_MINIMUM_API_VERSION = "1.40" +DOCKER_MINIMUM_VERSION = "19.03.8" +DOCKER_PREFERRED_API_VERSION = "1.44" CHUNK_SIZE = 1024 * 8 # 8KB @@ -123,22 +123,30 @@ class Docker(BaseManager): if not self._connected: try: self._connected = True - connector = self.connector() - version = await self.query("GET", "version") + self._connector = self.connector() + docker_info = await self.query("GET", "version") except (aiohttp.ClientOSError, FileNotFoundError): self._connected = False - raise DockerError("Can't connect to docker daemon") + raise DockerError("Can't connect to Docker daemon") - docker_version = parse_version(version['ApiVersion']) + api_version = parse_version(docker_info['ApiVersion']) + version = docker_info["Version"] - if docker_version < parse_version(DOCKER_MINIMUM_API_VERSION): - raise DockerError( - "Docker version is {}. GNS3 requires a minimum version of {}".format( - version["Version"], DOCKER_MINIMUM_VERSION)) + if api_version < parse_version(DOCKER_MINIMUM_API_VERSION): + raise DockerError("Docker version is {}. GNS3 requires a minimum version of {}".format( + version, + DOCKER_MINIMUM_VERSION) + ) preferred_api_version = parse_version(DOCKER_PREFERRED_API_VERSION) - if docker_version >= preferred_api_version: + if api_version >= preferred_api_version: self._api_version = DOCKER_PREFERRED_API_VERSION + else: + # use the Min API version supported by the daemon + self._api_version = docker_info['MinAPIVersion'] + log.warning("Using Docker client with the minimum API version {}".format(self._api_version)) + + log.info("Connected to Docker daemon version {} using API version {}".format(version, self._api_version)) def connector(self): @@ -186,7 +194,7 @@ class Docker(BaseManager): :param method: HTTP method :param path: Endpoint in API - :param data: Dictionnary with the body. Will be transformed to a JSON + :param data: Dictionary with the body. Will be transformed to a JSON :param params: Parameters added as a query arg :param timeout: Timeout :returns: HTTP response @@ -197,12 +205,11 @@ class Docker(BaseManager): timeout = 60 * 60 * 24 * 31 # One month timeout if path == 'version': - url = "http://docker/v1.24/" + path + url = "http://docker/" + path else: - url = "http://docker/v" + DOCKER_MINIMUM_API_VERSION + "/" + path + await self._check_connection() # version is use by check connection + url = "http://docker/v" + self._api_version + "/" + path try: - if path != "version": # version is use by check connection - await self._check_connection() if self._session is None or self._session.closed: connector = self.connector() self._session = aiohttp.ClientSession(connector=connector) diff --git a/tests/compute/docker/test_docker.py b/tests/compute/docker/test_docker.py index 12e473425..9e411503d 100644 --- a/tests/compute/docker/test_docker.py +++ b/tests/compute/docker/test_docker.py @@ -47,7 +47,7 @@ async def test_query_success(vm): vm._session.request = AsyncioMagicMock(return_value=response) data = await vm.query("POST", "test", data={"a": True}, params={"b": 1}) vm._session.request.assert_called_with('POST', - 'http://docker/v1.25/test', + 'http://docker/v{}/test'.format(DOCKER_MINIMUM_API_VERSION), data='{"a": true}', headers={'content-type': 'application/json'}, params={'b': 1}, @@ -69,7 +69,7 @@ async def test_query_error(vm): with pytest.raises(DockerError): await vm.query("POST", "test", data={"a": True}, params={"b": 1}) vm._session.request.assert_called_with('POST', - 'http://docker/v1.25/test', + 'http://docker/v{}/test'.format(DOCKER_MINIMUM_API_VERSION), data='{"a": true}', headers={'content-type': 'application/json'}, params={'b': 1}, @@ -89,7 +89,7 @@ async def test_query_error_json(vm): with pytest.raises(DockerError): await vm.query("POST", "test", data={"a": True}, params={"b": 1}) vm._session.request.assert_called_with('POST', - 'http://docker/v1.25/test', + 'http://docker/v{}/test'.format(DOCKER_MINIMUM_API_VERSION), data='{"a": true}', headers={'content-type': 'application/json'}, params={'b': 1}, @@ -180,7 +180,9 @@ async def test_docker_check_connection_docker_minimum_version(vm): async def test_docker_check_connection_docker_preferred_version_against_newer(vm): response = { - 'ApiVersion': '1.31' + 'ApiVersion': '1.52', + 'Version': '29.0.1', + } with patch("gns3server.compute.docker.Docker.connector"), \ @@ -193,7 +195,9 @@ async def test_docker_check_connection_docker_preferred_version_against_newer(vm async def test_docker_check_connection_docker_preferred_version_against_older(vm): response = { - 'ApiVersion': '1.27', + 'ApiVersion': '1.43', + 'Version': '24.0.2', + 'MinAPIVersion': '1.40' } with patch("gns3server.compute.docker.Docker.connector"), \ @@ -203,6 +207,20 @@ async def test_docker_check_connection_docker_preferred_version_against_older(vm assert vm._api_version == DOCKER_MINIMUM_API_VERSION +async def test_docker_check_connection_docker_unsupported_version(vm): + + response = { + 'ApiVersion': '1.25', + 'Version': '1.13.1', + } + + with patch("gns3server.compute.docker.Docker.connector"), \ + asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response): + vm._connected = False + with pytest.raises(DockerError) as e: + await vm._check_connection() + + @pytest.mark.asyncio async def test_install_busybox(): From f24134a8f51ccedbee4a853068298bc7f77f6c30 Mon Sep 17 00:00:00 2001 From: grossmj Date: Wed, 19 Nov 2025 12:08:29 +1000 Subject: [PATCH 26/27] Sync appliances --- .../asterfusion-vAsterNOS-campus.gns3a | 15 +--- .../appliances/asterfusion-vAsterNOS.gns3a | 12 +-- gns3server/appliances/cisco-7200.gns3a | 13 +++ .../appliances/extreme-networks-voss.gns3a | 14 +++ gns3server/appliances/freeRouter.gns3a | 16 ++-- gns3server/appliances/infix.gns3a | 88 +++++++++++++++++-- gns3server/appliances/openwrt.gns3a | 17 +++- gns3server/appliances/opnsense.gns3a | 14 +++ gns3server/appliances/vyos.gns3a | 26 ++++++ 9 files changed, 177 insertions(+), 38 deletions(-) diff --git a/gns3server/appliances/asterfusion-vAsterNOS-campus.gns3a b/gns3server/appliances/asterfusion-vAsterNOS-campus.gns3a index 4887b73cf..c9bcc69a3 100644 --- a/gns3server/appliances/asterfusion-vAsterNOS-campus.gns3a +++ b/gns3server/appliances/asterfusion-vAsterNOS-campus.gns3a @@ -2,7 +2,7 @@ "appliance_id": "9e934470-d898-4289-a5ed-50af094e629e", "name": "Asterfusion vAsterNOS campus", "category": "multilayer_switch", - "description": "AsterNOS is the core technology of Asterfusion’s one-stop SONiC turnkey solution designed for cloud, enterprise, and AI-driven scenarios. AsterNOS v5.2 Campus is specifically designed for traditional campus networks, offering comprehensive L2/L3 capabilities suitable for various campus scenarios such as schools, office buildings, and hospitals. This version supports a fully cloud-integrated Layer 3 network architecture, providing rich routing and switching functionalities to ensure high-performance operation and ease of maintenance. It can also be deployed in the GNS3 simulation environment to experience a complete All-Layer 3 Cloud-Campus network. AsterNOS v6.0 Campus builds upon the L2/L3 features of v5.2 (including ACL, MSTP, QinQ, IGMP Snooping, OSPF/BGP, etc.) and further enhances support for advanced technologies such as MPLS L2VPN/L3VPN and PTP. This version is ideal for enterprises and campus networks requiring high-performance multi-service transport, supporting cross-domain connectivity and providing nanosecond-level time synchronization. It is well-suited for applications with stringent time accuracy requirements, such as financial trading, industrial automation, and smart manufacturing. NOTICE: This appliance file is a virtualized version of AsterNOS and is intended to be used only to experience the basic functionality and industry standard CLI (Klish), not for official software testing. For more information about AsterNOS commercial version, please feel free to contact us via Email: bd@cloudswit.ch", + "description": "AsterNOS is the core technology of Asterfusion’s one-stop SONiC turnkey solution designed for cloud, enterprise, and AI-driven scenarios. AsterNOS v5.2 Campus is specifically designed for traditional campus networks, offering comprehensive L2/L3 capabilities suitable for various campus scenarios such as schools, office buildings, and hospitals. This version supports a fully cloud-integrated Layer 3 network architecture, providing rich routing and switching functionalities to ensure high-performance operation and ease of maintenance. It can also be deployed in the GNS3 simulation environment to experience a complete All-Layer 3 Cloud-Campus network. This version is ideal for enterprises and campus networks requiring high-performance multi-service transport, supporting cross-domain connectivity and providing nanosecond-level time synchronization. It is well-suited for applications with stringent time accuracy requirements, such as financial trading, industrial automation, and smart manufacturing. NOTICE: This appliance file is a virtualized version of AsterNOS and is intended to be used only to experience the basic functionality and industry standard CLI (Klish), not for official software testing. For more information about AsterNOS commercial version, please feel free to contact us via Email: bd@cloudswit.ch", "vendor_name": "Asterfusion", "vendor_url": "https://cloudswit.ch/", "vendor_logo_url": "https://raw.githubusercontent.com/GNS3/gns3-registry/master/vendor-logos/asterfusion.png", @@ -29,13 +29,6 @@ "kvm": "require" }, "images": [ - { - "filename": "vAsterNOS-V6.1R002.img", - "version": "6.1-2", - "md5sum": "003e6329489a617fbab5783504559d26", - "filesize": 2106851328, - "download_url": "https://drive.cloudswitch.io/external/c224501f36e6003767b30112bd44d92476f81f442cf47f8027a6f4f7e4227995" - }, { "filename": "vAsterNOS-V5.2R012P01.img", "version": "5.2-12-1", @@ -46,12 +39,6 @@ ], "versions": [ - { - "name": "6.1-2", - "images": { - "hda_disk_image": "vAsterNOS-V6.1R002.img" - } - }, { "name": "5.2-12-1", "images": { diff --git a/gns3server/appliances/asterfusion-vAsterNOS.gns3a b/gns3server/appliances/asterfusion-vAsterNOS.gns3a index e6e392c32..991c36069 100644 --- a/gns3server/appliances/asterfusion-vAsterNOS.gns3a +++ b/gns3server/appliances/asterfusion-vAsterNOS.gns3a @@ -6,7 +6,7 @@ "vendor_name": "Asterfusion", "vendor_url": "https://cloudswit.ch/", "vendor_logo_url": "https://raw.githubusercontent.com/GNS3/gns3-registry/master/vendor-logos/asterfusion.png", - "documentation_url": "https://help.cloudswit.ch/portal/en/kb/articles/vasternos", + "documentation_url": "https://asternos.com/community/space/vasternos/post/vasternos-for-data-center-network", "product_name": "vAsterNOS", "product_url": "https://cloudswit.ch/product/sonic-enterprise-distribution", "registry_version": 4, @@ -30,18 +30,18 @@ }, "images": [ { - "filename": "vAsterNOS-V3.1.img", + "filename": "AsterNOS_V3.1_R0408P01-VS.img.gz", "version": "V3.1", - "md5sum": "c323c9c3f60e1a93eca2acdc5034b85c", - "filesize": 2724659200, - "download_url": "https://drive.cloudswitch.io/external/8ae2e3932ad8bb2ec30dd25be415d288ff3e4a949c557c6bd48ac6e6265bcfc1" + "md5sum": "5c182306af67be2c79a5642551412e48", + "filesize": 1425945924, + "download_url": "http://pub.asternos.com/AsterNOS_V3.1_R0408P01-VS.img.gz" } ], "versions": [ { "name": "V3.1", "images": { - "hda_disk_image": "vAsterNOS-V3.1.img" + "hda_disk_image": "AsterNOS_V3.1_R0408P01-VS.img.gz" } } ] diff --git a/gns3server/appliances/cisco-7200.gns3a b/gns3server/appliances/cisco-7200.gns3a index 3e723e50b..78d0be865 100644 --- a/gns3server/appliances/cisco-7200.gns3a +++ b/gns3server/appliances/cisco-7200.gns3a @@ -50,6 +50,12 @@ "version": "124-25G", "md5sum": "9c7cc9b3f3b3571411a7f62faaa2c036", "filesize": 71528984 + }, + { + "filename": "c7200-itpk9-mz.124-15.SW.image", + "version": "124-15.SW", + "md5sum": "3334a0facdbd26164e83d3c201fff5b5", + "filesize": 46423952 } ], "versions": [ @@ -87,6 +93,13 @@ "images": { "image": "c7200-a3jk9s-mz.124-25g.image" } + }, + { + "name": "124-15.SW", + "idlepc": "0x60b2bc68", + "images": { + "image": "c7200-itpk9-mz.124-15.SW.image" + } } ] } diff --git a/gns3server/appliances/extreme-networks-voss.gns3a b/gns3server/appliances/extreme-networks-voss.gns3a index a5ef0a64c..08d6c4509 100644 --- a/gns3server/appliances/extreme-networks-voss.gns3a +++ b/gns3server/appliances/extreme-networks-voss.gns3a @@ -28,6 +28,13 @@ "options": "-nographic" }, "images": [ + { + "filename": "FEGNS3.9.3.0.0.qcow2", + "version": "v9.3.0.0", + "md5sum": "2abfa8d64219aa75522238f87b782726", + "filesize": 490209280, + "direct_download_url": "https://akamai-ep.extremenetworks.com/Extreme_P/github-en/Virtual_VOSS/FEGNS3.9.3.0.0.qcow2" + }, { "filename": "VOSSGNS3.8.10.1.0.qcow2", "version": "v8.10.1.0", @@ -93,6 +100,13 @@ } ], "versions": [ + { + "name": "v9.3.0.0", + "images": + { + "hda_disk_image": "FEGNS3.9.3.0.0.qcow2" + } + }, { "name": "v8.10.1.0", "images": diff --git a/gns3server/appliances/freeRouter.gns3a b/gns3server/appliances/freeRouter.gns3a index ca53df882..087ec49fb 100644 --- a/gns3server/appliances/freeRouter.gns3a +++ b/gns3server/appliances/freeRouter.gns3a @@ -25,19 +25,19 @@ }, "images": [ { - "filename": "rtr.qcow2", - "version": "20.7.1", - "md5sum": "4707415d15d20ced92423d90e52eedcd", - "filesize": 254767616, - "download_url": "http://freerouter.nop.hu/", - "direct_download_url": "http://dl.nop.hu/rtr.qcow2" + "filename": "rtr-x86_64.qcow2", + "version": "25.8.29", + "md5sum": "13d72fdfd772258808a9d4b75fa67083", + "filesize": 73269248, + "download_url": "http://www.freertr.org/", + "direct_download_url": "http://dl.nop.hu/rtr-x86_64.qcow2" } ], "versions": [ { - "name": "20.7.1", + "name": "25.8.29", "images": { - "hda_disk_image": "rtr.qcow2" + "hda_disk_image": "rtr-x86_64.qcow2" } } ] diff --git a/gns3server/appliances/infix.gns3a b/gns3server/appliances/infix.gns3a index 8d2814e9d..797266497 100644 --- a/gns3server/appliances/infix.gns3a +++ b/gns3server/appliances/infix.gns3a @@ -2,13 +2,13 @@ "appliance_id": "4d351078-c6f5-444c-ab30-0ef20e3d8c53", "name": "Infix", "category": "router", - "description": "Infix is a Network Operating System based on Linux. It can be set up both as a switch, with offloading using switchdev, and a router with firewalling.", + "description": "Infix OS is a free Linux-based operating system with made-easy management using NETCONF, RESTCONF, or the built-in command line interface (CLI). It is suitable for both end-devices with a single Ethernet port and switches/routers with multiple ports. Immutable by design, extensions are possible using Docker containers.", "vendor_name": "KernelKit", "vendor_url": "https://github.com/kernelkit", "vendor_logo_url": "https://kernelkit.org/assets/img/jack.png", "product_name": "Infix", "registry_version": 4, - "documentation_url": "https://github.com/kernelkit/infix/tree/main/doc", + "documentation_url": "https://kernelkit.org/infix/latest/", "status": "stable", "availability": "free", "maintainer": "KernelKit", @@ -37,16 +37,16 @@ "download_url": "https://sourceforge.net/projects/gns-3/files/Qemu%20Appliances/", "direct_download_url": "https://sourceforge.net/projects/gns-3/files/Qemu%20Appliances/OVMF-edk2-stable202305.fd.zip/download", "compression": "zip" - }, - { + }, + { "filename": "infix-x86_64-disk-24.11.1.img", "filesize": 536870912, "md5sum": "673a123fe122d1c2f5724baf9965a19d", "version": "24.11.1", "download_url": "https://github.com/kernelkit/infix/releases/download/v24.11.1/infix-x86_64-24.11.1.tar.gz", "compression": "gzip" - }, - { + }, + { "filename": "infix-x86_64-disk-25.01.0.img", "filesize": 536870912, "md5sum": "a814d93b385116b4a35712c445b5f830", @@ -69,17 +69,87 @@ "version": "25.03.0", "download_url": "https://github.com/kernelkit/infix/releases/download/v25.03.0/infix-x86_64-25.03.0.tar.gz", "compression": "gzip" + }, + { + "filename": "infix-x86_64-disk-25.04.0.qcow2", + "filesize": 259723776, + "md5sum": "84bd999513325d0007d0e6587abc6140", + "version": "25.04.0", + "direct_download_url": "https://github.com/kernelkit/infix/releases/download/v25.04.0/infix-x86_64-disk-25.04.0.qcow2" + }, + { + "filename": "infix-x86_64-disk-25.05.0.qcow2", + "filesize": 259958784, + "md5sum": "a6b1b3a6bccc60bb5214f2b864602fde", + "version": "25.05.0", + "direct_download_url": "https://github.com/kernelkit/infix/releases/download/v25.05.0/infix-x86_64-disk-25.05.0.qcow2" + }, + { + "filename": "infix-x86_64-disk-25.08.0.qcow2", + "filesize": 284453888, + "md5sum": "5fbf146d2ac73d1bd8fa1c509d6169b1", + "version": "25.08.0", + "direct_download_url": "https://github.com/kernelkit/infix/releases/download/v25.08.0/infix-x86_64-disk-25.08.0.qcow2" + }, + { + "filename": "infix-x86_64-disk-25.09.0.qcow2", + "filesize": 285120000, + "md5sum": "9761968406780a5b28311b8527f8629f", + "version": "25.09.0", + "direct_download_url": "https://github.com/kernelkit/infix/releases/download/v25.09.0/infix-x86_64-disk-25.09.0.qcow2" + }, + { + "filename": "infix-x86_64-disk-25.10.0.qcow2", + "filesize": 288818688, + "md5sum": "781d0fce9fbf789f6ea6ff10dc104afb", + "version": "25.10.0", + "direct_download_url": "https://github.com/kernelkit/infix/releases/download/v25.10.0/infix-x86_64-disk-25.10.0.qcow2" } ], "versions": [ - { + { + "name": "25.10.0", + "images": { + "bios_image": "OVMF-edk2-stable202305.fd", + "hda_disk_image": "infix-x86_64-disk-25.10.0.qcow2" + } + }, + { + "name": "25.09.0", + "images": { + "bios_image": "OVMF-edk2-stable202305.fd", + "hda_disk_image": "infix-x86_64-disk-25.09.0.qcow2" + } + }, + { + "name": "25.08.0", + "images": { + "bios_image": "OVMF-edk2-stable202305.fd", + "hda_disk_image": "infix-x86_64-disk-25.08.0.qcow2" + } + }, + { + "name": "25.05.0", + "images": { + "bios_image": "OVMF-edk2-stable202305.fd", + "hda_disk_image": "infix-x86_64-disk-25.05.0.qcow2" + } + }, + { + "name": "25.04.0", + "images": { + "bios_image": "OVMF-edk2-stable202305.fd", + "hda_disk_image": "infix-x86_64-disk-25.04.0.qcow2" + } + }, + { "name": "25.03.0", "images": { "bios_image": "OVMF-edk2-stable202305.fd", "hda_disk_image": "infix-x86_64-disk-25.03.0.img" } }, - { + { "name": "25.02.0", "images": { "bios_image": "OVMF-edk2-stable202305.fd", @@ -101,4 +171,4 @@ } } ] -} +} \ No newline at end of file diff --git a/gns3server/appliances/openwrt.gns3a b/gns3server/appliances/openwrt.gns3a index add7eb588..ac3711d3a 100644 --- a/gns3server/appliances/openwrt.gns3a +++ b/gns3server/appliances/openwrt.gns3a @@ -23,6 +23,15 @@ "kvm": "allow" }, "images": [ + { + "filename": "openwrt-24.10.2-x86-64-generic-ext4-combined.img", + "version": "24.10.2", + "md5sum": "0769916238524161b0a9a639ae261381", + "filesize": 126353408, + "download_url": "https://downloads.openwrt.org/releases/24.10.2/targets/x86/64/", + "direct_download_url": "https://downloads.openwrt.org/releases/24.10.2/targets/x86/64/openwrt-24.10.2-x86-64-generic-ext4-combined.img.gz", + "compression": "gzip" + }, { "filename": "openwrt-23.05.0-x86-64-generic-ext4-combined.img", "version": "23.05.0", @@ -223,7 +232,13 @@ } ], "versions": [ - { + { + "name": "24.10.2", + "images": { + "hda_disk_image": "openwrt-24.10.2-x86-64-generic-ext4-combined.img" + } + }, + { "name": "23.05.0", "images": { "hda_disk_image": "openwrt-23.05.0-x86-64-generic-ext4-combined.img" diff --git a/gns3server/appliances/opnsense.gns3a b/gns3server/appliances/opnsense.gns3a index 759701763..433b4f2de 100644 --- a/gns3server/appliances/opnsense.gns3a +++ b/gns3server/appliances/opnsense.gns3a @@ -25,6 +25,20 @@ "kvm": "require" }, "images": [ + { + "filename": "OPNsense-25.7-nano-amd64.img", + "version": "25.7", + "md5sum": "07aa8b44ebc57fa9ca0145f777901a59", + "filesize": 3221225472, + "download_url": "https://opnsense.c0urier.net/releases/25.7/" + }, + { + "filename": "OPNsense-25.1-nano-amd64.img", + "version": "25.1", + "md5sum": "9c119e4fed7dd7c1c162d8be4088d983", + "filesize": 3221225472, + "download_url": "https://opnsense.c0urier.net/releases/25.1/" + }, { "filename": "OPNsense-24.7-nano-amd64.img", "version": "24.7", diff --git a/gns3server/appliances/vyos.gns3a b/gns3server/appliances/vyos.gns3a index cace44ed3..3255a80e0 100644 --- a/gns3server/appliances/vyos.gns3a +++ b/gns3server/appliances/vyos.gns3a @@ -30,6 +30,20 @@ "on_close": "shutdown_signal" }, "images": [ + { + "filename": "vyos-1.4.3-kvm-amd64.qcow2", + "version": "1.4.3", + "md5sum": "4eec0ba2ec367bbbdf2d2f212f947746", + "filesize": 515047424, + "download_url": "https://support.vyos.io/" + }, + { + "filename": "vyos-1.4.2-kvm-amd64.qcow2", + "version": "1.4.2", + "md5sum": "78c5935e05a7bbd94a639fcee18f5799", + "filesize": 514064384, + "download_url": "https://support.vyos.io/" + }, { "filename": "vyos-1.4.1-kvm-amd64.qcow2", "version": "1.4.1", @@ -116,6 +130,18 @@ } ], "versions": [ + { + "name": "1.4.3", + "images": { + "hda_disk_image": "vyos-1.4.3-kvm-amd64.qcow2" + } + }, + { + "name": "1.4.2", + "images": { + "hda_disk_image": "vyos-1.4.2-kvm-amd64.qcow2" + } + }, { "name": "1.4.1", "images": { From 37f17209d3e4a38832870aeba7465e5d0cd0e82e Mon Sep 17 00:00:00 2001 From: grossmj Date: Wed, 19 Nov 2025 12:13:56 +1000 Subject: [PATCH 27/27] Release v2.2.55 --- CHANGELOG | 13 +++++++++++++ gns3server/crash_report.py | 2 +- gns3server/version.py | 5 ++--- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 7d539f621..7e212c27d 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,18 @@ # Change Log +## 2.2.55 19/11/2025 + +* Docker API version requirements and process to handle older daemons +* Use docker_name property when creating a container +* Set name of container in Docker +* Support for Python 3.14 +* Enable ip cef in IOU L2 and IOU L3 base configs +* Only build and push Docker image when releasing a new version +* Fix pushing Docker image to Docker hub +* Update security issues in README.md. Fixes #2535 +* Add missing 'nat' template type in schema. Fixes #2529 +* Resolve deprecation warnings of regex library + ## 2.2.54 21/04/2025 * Bundle web-ui v2.2.54 diff --git a/gns3server/crash_report.py b/gns3server/crash_report.py index 17917695b..296f92fe9 100644 --- a/gns3server/crash_report.py +++ b/gns3server/crash_report.py @@ -57,7 +57,7 @@ class CrashReport: Report crash to a third party service """ - DSN = "https://c663917f99579c9b5a5c0764415c0bd4@o19455.ingest.us.sentry.io/38482" + DSN = "https://9721facaf4cb1e0a93e126300ea56901@o19455.ingest.us.sentry.io/38482" _instance = None def __init__(self): diff --git a/gns3server/version.py b/gns3server/version.py index f4bdf1bdf..c99f6f848 100644 --- a/gns3server/version.py +++ b/gns3server/version.py @@ -23,9 +23,8 @@ # or negative for a release candidate or beta (after the base version # number has been incremented) -__version__ = "2.2.55.dev1" -__version_info__ = (2, 2, 54, 99) - +__version__ = "2.2.55" +__version_info__ = (2, 2, 55, 0) if "dev" in __version__: try: import os