Compare commits

..

74 Commits

Author SHA1 Message Date
Jeremy Grossmann
a3e85b7041
Merge pull request #2853 from hjicks/master
add support basic support for OpenBSD.
2026-08-18 18:01:15 +02:00
chara
80da7da515 add support basic support for OpenBSD.
there remains some work on ubridge, but this seems to work so far.
2026-08-13 22:24:58 +03:30
Jeremy Grossmann
d2f73cae28
Merge pull request #2842 from GNS3/release/v2.2.61
Release v2.2.61
2026-07-30 19:15:25 +02:00
grossmj
da2942c139
Release v2.2.61 2026-07-30 12:53:08 +02:00
grossmj
5485eef3ad
Sync appliances 2026-07-29 18:52:23 +02:00
Jeremy Grossmann
ccf4b3f9db
Merge pull request #2840 from GNS3/fix-unvalidated-symlink
Fix unvalidated symlink creation in import_project
2026-07-29 18:51:21 +02:00
grossmj
b797981a65
test: add tests for unvalidated symlink creation in import_project 2026-07-29 18:48:31 +02:00
grossmj
a225622a6e
fix(import): unvalidated symlink creation in import_project 2026-07-29 18:10:52 +02:00
Jeremy Grossmann
3f19ede8d2
Merge pull request #2839 from GNS3/bugfix/2838
Fix addition of QEMU RNG device in 2.2.60 causes interface names to change
2026-07-29 18:00:16 +02:00
Jeremy Grossmann
668169d1cc
Merge branch '2.2' into bugfix/2838 2026-07-29 17:55:53 +02:00
grossmj
66b39f0616
Merge remote-tracking branch 'origin/master' into 2.2 2026-07-29 17:54:18 +02:00
Jeremy Grossmann
7efef28d4f
Merge branch '2.2' into bugfix/2838 2026-07-29 17:50:01 +02:00
grossmj
d9140d2f8c
fix(qemu): move the bios options after the network ones 2026-07-29 17:47:03 +02:00
grossmj
e2657febf0
fix(qemu): fix addition of QEMU RNG device causes interface names to change 2026-07-29 17:23:28 +02:00
Jeremy Grossmann
d161f1997d
Merge pull request #2837 from my-code-is-grey/ghcr_lowercase
fix: lowercase GHCR image name in docker-build workflow
2026-07-28 17:28:41 +02:00
my-code-is-grey
37490f2c74 bugfix: docker workflow needs lowercase name 2026-07-27 22:27:48 -04:00
Jeremy Grossmann
9b1d7d44bf
Merge pull request #2836 from iskanred/bugfix/2835
fix(qemu): remove trailing space from RNG object argument
2026-07-27 20:42:29 +02:00
Iskander Nafikov
9562c9d828 fix(qemu): remove trailing space from RNG object argument 2026-07-28 00:27:55 +08:00
Jeremy Grossmann
6c2a980afc
Merge pull request #2833 from Sanjays2402/fix/delete-project-does-not-start-nodes
Do not start nodes when deleting a project
2026-07-26 09:23:12 +02:00
Sanjay Santhanam
00ac2c19bd Do not start nodes when deleting a project
Project.delete() calls open() to rebuild the internal data structures
needed for cleanup. open() schedules start_all() when the project has
auto_start enabled, so deleting an auto-start project actually launched
every node process, allocating ports and consuming resources, only for
close() to kill them moments later.

open() now takes an auto_start argument (default True, so normal opens
are unchanged) and delete() passes auto_start=False.

Fixes #2784
2026-07-25 20:00:43 -07:00
Jeremy Grossmann
6742d09a70
Merge pull request #2831 from Sanjays2402/fix/docker-stop-state-check
fix: correct always-true state check in DockerVM.stop()
2026-07-25 19:59:28 +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
Jeremy Grossmann
20868aa233
Merge pull request #2830 from rafael-33/master
docs: add structured bug-report template
2026-07-24 22:55:36 +02:00
rafael-33
27c6535ad0
add bug report issue template 2026-07-24 22:51:44 +02:00
grossmj
c75e1ba0be
Development on 2.2.61.dev1 2026-07-15 20:45:33 +02:00
Jeremy Grossmann
c1e845cb22
Merge pull request #2819 from GNS3/release/v2.2.60
Release v2.2.60
2026-07-15 20:44:04 +02:00
grossmj
803cc17410
Release v2.2.60 2026-07-15 18:36:26 +02:00
grossmj
f088c55cfe
Sync appliances 2026-07-15 18:32:12 +02:00
Jeremy Grossmann
8a8864c90b
Merge pull request #2814 from GNS3/bugfix/2783
fix(import): project import does not move images symlinks into place
2026-07-15 09:53:15 +02:00
grossmj
1986bce538
Only set IOU images to be executable when importing images 2026-07-15 09:43:34 +02:00
grossmj
c528d2cd95
fix(import): project import does not move images symlinks into place 2026-07-13 18:19:52 +02:00
Jeremy Grossmann
7529ee4168
Merge pull request #2813 from GNS3/uefi-improvements
UEFI improvements for Qemu VMs
2026-07-13 12:05:07 +02:00
grossmj
61c7f4469d
Search for system UEFI that is compatible with Python < 3.12 2026-07-13 11:34:29 +02:00
grossmj
896b4fc56a
UEFI improvements for Qemu VMs
* Add OVMF firmware directory configuration
* Automatically add a Random Number Generator (RNG) device when using uefi option is enabled
2026-07-13 11:08:17 +02: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
Jeremy Grossmann
b256d5a5f6
Merge pull request #2812 from GNS3/bugfix/2788
fix: gns3-server crashes on startup if "Open this project in the background" is active but there is a problem with that project
2026-07-12 13:50:11 +02:00
grossmj
ea09cc2724
fix: gns3-server crashes on startup if "Open this project in the background" is active but there is a problem with that project 2026-07-12 13:46:12 +02:00
grossmj
e3e0a511b6
Handle HTTPNotFound exception when retrieving compute status 2026-07-12 11:08:13 +02:00
grossmj
0ecaab4da0
Replace ControllerForbiddenError with aiohttp.web.HTTPForbidden for project deletion error handling 2026-07-12 11:05:15 +02:00
YueGuobin
40644f33e6
Fix: Check compute connectivity before open() during project deletion
Previously the disconnected compute check ran after open(), which would
block for 120s trying to connect to unreachable remote computes before
rejecting the deletion. Now reads the topology file directly to extract
compute IDs and checks connectivity before calling open(), enabling
immediate rejection of deletions involving offline computes.

Also removes the redundant post-open() check since the early check
covers both opened and closed project states.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-07-12 10:57:56 +02:00
grossmj
809d4cc609
Merge branch 'master' into 2.2
# Conflicts:
#	.github/workflows/docker-build.yml
2026-07-08 10:30:53 +02:00
Jeremy Grossmann
89e4e836c9
Merge pull request #2806 from my-code-is-grey/2798-dockerhub
#2798 - DockerHub adjustments
2026-07-08 05:17:04 +02:00
my-code-is-grey
69ddcf553f .dockerignore ignore .dockerignore 2026-07-07 22:37:24 -04:00
my-code-is-grey
1e1ec5aae8 typo made me reconsider and move out to env 2026-07-07 21:58:49 -04:00
my-code-is-grey
e30e6435ca defaulted DOCKERHUB_ORG repository variable 2026-07-07 21:48:13 -04:00
my-code-is-grey
77700f361c 2798 2026-07-07 01:30:58 -04:00
Jeremy Grossmann
ad4aa1d49b
Merge pull request #2803 from GNS3/update-ga-workflows
Update GitHub Actions workflows
2026-07-06 18:10:17 +02:00
grossmj
8499ed3d65
Update GitHub Actions workflows 2026-07-06 18:05:28 +02:00
Jeremy Grossmann
db605120fa
Merge pull request #2741 from Volo6uev/issue-2735/api-base-config
api endpoint implemented
2026-05-31 19:10:55 +02:00
Volobue
9106ac4522 api endpoint implemented 2026-05-18 21:49:01 +03:00
Jeremy Grossmann
713dbb7f45
Merge pull request #2718 from GNS3/2.2
Release v2.2.59
2026-05-08 22:03:17 +08:00
grossmj
c3d23d7cae
Development on 2.2.60.dev1 2026-05-08 20:03:31 +08:00
grossmj
a1edf74fcf
Merge branch 'master' into 2.2 2026-05-08 19:49:53 +08:00
grossmj
59a95edd3d
Release v2.2.59 2026-05-08 19:06:04 +08:00
grossmj
e34a7afe79
Sync appliances 2026-05-08 19:03:14 +08:00
Jeremy Grossmann
2dcbe0f1c2
Merge pull request #2717 from GNS3/bugfix/2709
Fix snapshot restore does not work after export/import of project
2026-05-08 18:59:37 +08:00
grossmj
b1c8ce8b58
Remove unneeded code 2026-05-08 18:56:56 +08:00
grossmj
b304511b5e
Fix snapshot restore does not work after export/import of project 2026-05-08 18:47:47 +08:00
grossmj
163d4e0534
Update requirements.txt & python_requires 2026-05-08 17:01:10 +08:00
grossmj
72f64ee60b
Warn to use 64-bit IOU images. Fixes #2716
32-bit IOU image support has been removed from the GNS3 VM.
2026-05-08 16:37:06 +08:00
Jeremy Grossmann
cd03abb7ed
Merge pull request #2669 from arl1984/fix/telnet-proxy-reader-cleanup-2344
Fix telnet console silent-proxy hang on non-ConnectionError exit (#2344)
2026-05-08 15:42:51 +08:00
grossmj
6551bf56f8
Tweak and merge changes from 3.0 branch 2026-05-08 15:36:40 +08:00
arl1984
de0d050689 Fix telnet console silent-proxy hang on non-ConnectionError exit (#2344)
The run() cleanup block was guarded by `except (ConnectionError, OSError):`,
so exits via asyncio.CancelledError or any other exception type skipped
cleanup. Result: `_reader_process` stays pinned to the dead reader and
`_get_reader()` returns None for every subsequent client — the silent-proxy
symptom described in #2344.

Convert the except block to try/finally so cleanup always runs, regardless
of how `_process()` exits. Also:
- catch asyncio.CancelledError + generic Exception (with log.exception) so
  unexpected failures don't swallow the cleanup
- reset `_current_read = None` after cancellation
- use `dict.pop(..., None)` instead of `del` to avoid KeyError races if
  the broadcast loop's timeout handler already removed the entry

Triggering pattern observed in practice: a diagnostic tool opens a console,
sends a few commands, and closes abruptly (e.g. from a test harness or
orchestration script that cancels its Task). If the `_process()` task was
awaiting on one of the `network_read` / `reader_read` futures at the time
of cancellation, the CancelledError propagates up through `run()` and
bypasses the ConnectionError-only except clause. The proxy accepts future
connections (the listen socket is still alive) but never forwards any data
because `_reader_process` never got reset.

Validated against gns3/gns3-server:latest (2.2.56.1) running a 10-scenario
sequential regression batch that previously hung reliably on the 4th
sp_v1 / L3VPN scenario and now completes cleanly across all 10.
2026-04-16 14:46:49 +00:00
Jeremy Grossmann
d59709071a
Merge pull request #2668 from GNS3/release/v2.2.58.1
Release v2.2.58.1
2026-04-12 21:18:23 +08:00
grossmj
ae3ae46151
Development on 2.2.59.dev2 2026-04-12 21:17:17 +08:00
grossmj
58fc6a8478
Release v2.2.58.1 2026-04-12 20:59:20 +08:00
grossmj
d9b8d0d1ff
Sync appliances 2026-04-12 20:49:56 +08:00
grossmj
2b0b31b402
Merge branch 'master' into 2.2 2026-04-12 20:47:14 +08:00
grossmj
4a47151526
Development on 2.2.59.dev1 2026-04-11 18:05:54 +08:00
Jeremy Grossmann
adeec10188
Merge pull request #2667 from GNS3/release/v2.2.58
Release v2.2.58
2026-04-11 17:42:38 +08:00
Jeremy Grossmann
e747deae88
Merge branch 'master' into release/v2.2.58 2026-04-11 17:37:31 +08:00
Jeremy Grossmann
264fffbc6c
Potential fix for pull request finding 'CodeQL / Information exposure through an exception'
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2026-04-11 17:34:53 +08:00
Jeremy Grossmann
4701712a86
Merge pull request #2644 from GNS3/release/v2.2.57
Release v2.2.57
2026-03-24 10:10:22 +08:00
67 changed files with 1441 additions and 839 deletions

57
.dockerignore Normal file
View File

@ -0,0 +1,57 @@
# Version control
.git
.gitignore
.gitattributes
# CI / GitHub / Docker
.github
.whitesource
.dockerignore
# Editor / IDE
.idea
.vscode
.settings
.project
.pydevproject
.mr.developer.cfg
# Claude
.claude
# Python build artifacts
__pycache__
*.py[cod]
*.so
*.egg
*.egg-info
build/
dist/
eggs/
parts/
var/
sdist/
develop-eggs/
.installed.cfg
lib/
lib64/
.ropeproject
# Test & coverage
tests/
pytest.ini
.coveragerc
.coverage
.coverage*
.tox
.cache
.pytest_cache
nosetests.xml
# Virtualenv
env/
venv/
.venv/
# Editor backup files
*~

40
.github/ISSUE_TEMPLATE/bug_report.yml vendored Normal file
View File

@ -0,0 +1,40 @@
name: Bug report
description: Report a bug so we can fix it.
title: "[Bug]: "
labels: ["bug"]
body:
- type: textarea
id: what-happened
attributes:
label: What happened?
description: A clear description of the bug.
validations:
required: true
- type: textarea
id: reproduce
attributes:
label: Steps to reproduce
description: How can we reproduce this? Numbered steps if possible.
validations:
required: true
- type: textarea
id: expected
attributes:
label: Expected behavior
description: What did you expect to happen instead?
- type: input
id: version
attributes:
label: Version / commit
description: Which version or commit hash are you on?
- type: textarea
id: environment
attributes:
label: Environment
description: OS, runtime version, anything else that might be relevant.
- type: textarea
id: logs
attributes:
label: Relevant logs
description: Paste any relevant log output. This is automatically rendered as code.
render: shell

View File

@ -10,7 +10,7 @@ jobs:
name: Add issue to project
runs-on: ubuntu-latest
steps:
- uses: actions/add-to-project@v1.0.1
- uses: actions/add-to-project@v2
with:
project-url: https://github.com/orgs/GNS3/projects/3
github-token: ${{ secrets.ADD_NEW_ISSUES_TO_PROJECT }}

View File

@ -56,11 +56,11 @@ jobs:
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v7
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
uses: github/codeql-action/init@v4
with:
languages: ${{ matrix.language }}
build-mode: ${{ matrix.build-mode }}
@ -88,6 +88,6 @@ jobs:
exit 1
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
uses: github/codeql-action/analyze@v4
with:
category: "/language:${{matrix.language}}"

View File

@ -10,31 +10,59 @@ on:
jobs:
build:
runs-on: ubuntu-latest
env:
DOCKERHUB_ORG: ${{ vars.DOCKERHUB_ORG || 'gns3' }}
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v7
- name: Check for stable release
id: ver
run: |
TAG="${GITHUB_REF_NAME#v}"
echo "tag=$TAG" >> $GITHUB_OUTPUT
if echo "$TAG" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+$'; then
echo "stable=true" >> $GITHUB_OUTPUT
else
echo "stable=false" >> $GITHUB_OUTPUT
fi
- name: Set lowercase image name vars
if: steps.ver.outputs.stable == 'true'
id: names
run: |
echo "owner=$(echo '${{ github.repository_owner }}' | tr '[:upper:]' '[:lower:]')" >> "$GITHUB_OUTPUT"
echo "repo=$(echo '${{ github.event.repository.name }}' | tr '[:upper:]' '[:lower:]')" >> "$GITHUB_OUTPUT"
- name: Set up Docker Buildx
if: steps.ver.outputs.stable == 'true'
uses: docker/setup-buildx-action@v3
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
if: steps.ver.outputs.stable == 'true'
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Login to Docker Hub
uses: docker/login-action@v3
if: steps.ver.outputs.stable == 'true'
uses: docker/login-action@v4
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/$(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 gns3/${{ github.event.repository.name }}:latest .
docker push gns3/${{ github.event.repository.name }}:latest
- name: Build and push
if: steps.ver.outputs.stable == 'true'
uses: docker/build-push-action@v6
with:
context: .
push: true
tags: |
${{ env.DOCKERHUB_ORG }}/${{ github.event.repository.name }}:${{ steps.ver.outputs.tag }}
${{ env.DOCKERHUB_ORG }}/${{ github.event.repository.name }}:latest
ghcr.io/${{ steps.names.outputs.owner }}/${{ steps.names.outputs.repo }}:${{ steps.ver.outputs.tag }}
ghcr.io/${{ steps.names.outputs.owner }}/${{ steps.names.outputs.repo }}:latest

View File

@ -12,11 +12,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
with:
fetch-depth: 0
ref: "gh-pages"
- uses: actions/setup-python@v5
- uses: actions/setup-python@v6
with:
python-version: 3.8
- name: Merge changes from 3.0 branch

View File

@ -21,9 +21,9 @@ jobs:
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies

View File

@ -1,5 +1,39 @@
# Change Log
## 2.2.61 30/07/2026
* Sync appliances
* fix(import): unvalidated symlink creation in import_project
* fix(qemu): fix addition of QEMU RNG device causes interface names to change
* fix(qemu): remove trailing space from RNG object argument
* fix: do not start nodes when deleting a project
* fix: correct always-true state check in DockerVM.stop()
## 2.2.60 15/07/2026
* Sync appliances
* Only set IOU images to be executable when importing images
* fix(import): project import does not move images symlinks into place
* Search for system UEFI that is compatible with Python < 3.12
* Add OVMF firmware directory configuration
* Automatically add a Random Number Generator (RNG) device when using uefi option is enabled
* fix(docker): handle container name conflict automatically
* fix: gns3-server crashes on startup if "Open this project in the background" is active but there is a problem with that project
* Handle HTTPNotFound exception when retrieving compute status
* Fix: Check compute connectivity before open() during project deletion
* API endpoints to manage base configuration files for templates
## 2.2.59 08/05/2026
* Sync appliances
* Fix snapshot restore does not work after export/import of project
* Warn to use 64-bit IOU images. Fixes #2716 32-bit IOU image support has been removed from the GNS3 VM.
* Fix telnet console silent-proxy hang on non-ConnectionError exit (#2344)
## 2.2.58.1 12/04/2026
* Sync appliances
## 2.2.58 10/04/2026
* Bundle web-ui v2.2.58

View File

@ -103,6 +103,8 @@ enable_hardware_acceleration = True
require_hardware_acceleration = False
; Allow unsafe additional command line options
allow_unsafe_options = False
; Path to the OVMF firmware directory
ovmf_firmware_dir = "/usr/share/OVMF"
[VMware]
; First vmnet interface of the range that can be managed by the GNS3 server

View File

@ -0,0 +1,17 @@
curl -i -X 'http://localhost:3080/v2/templates/19021f99-e36f-394d-b4a1-8aaa902ab9cc/base-config/vpcs_base_config.txt'
GET /v2/templates/19021f99-e36f-394d-b4a1-8aaa902ab9cc/base-config/vpcs_base_config.txt HTTP/1.1
HTTP/1.1 200 OK
Connection: close
X-Route: /v2/templates/{template_id}/base-config/{filename}
Server: Python/3.14 GNS3/2.2.60.dev1+713dbb7f
Content-Type: application/json
Content-Length: 144
Date: Mon, 18 May 2026 18:34:10 GMT
{
"content": "# test config\n\ndhcp\n",
"filename": "vpcs_base_config.txt",
"template_id": "19021f99-e36f-394d-b4a1-8aaa902ab9cc"
}

View File

@ -0,0 +1,44 @@
curl -i -X 'http://localhost:3080/v2/templates/base-configs'
GET /v2/templates/base-configs HTTP/1.1
HTTP/1.1 200 OK
Connection: close
X-Route: /v2/templates/base-configs
Server: Python/3.14 GNS3/2.2.60.dev1+713dbb7f
Content-Type: application/json
Content-Length: 576
Date: Mon, 18 May 2026 18:28:53 GMT
[
{
"filename": "config1.txt"
},
{
"filename": "config2.txt"
},
{
"filename": "ios_base_startup-config.txt"
},
{
"filename": "ios_etherswitch_startup-config.txt"
},
{
"filename": "iou_l2_base_startup-config.txt"
},
{
"filename": "iou_l3_base_startup-config.txt"
},
{
"filename": "test_config.txt"
},
{
"filename": "update_test.txt"
},
{
"filename": "vpcs_base_config.txt"
},
{
"filename": "vpcs_base_config2.txt"
}
]

View File

@ -0,0 +1,17 @@
curl -i -X 'http://localhost:3080/v2/templates/19021f99-e36f-394d-b4a1-8aaa902ab9cc/base-config/vpcs_base_config.txt'
PUT /v2/templates/19021f99-e36f-394d-b4a1-8aaa902ab9cc/base-config/vpcs_base_config.txt HTTP/1.1
HTTP/1.1 200 OK
Connection: close
X-Route: /v2/templates/{template_id}/base-config/{filename}
Server: Python/3.14 GNS3/2.2.60.dev1+713dbb7f
Content-Type: application/json
Content-Length: 144
Date: Mon, 18 May 2026 18:37:57 GMT
{
"content": "# test config\n\ndhcp\n",
"filename": "vpcs_base_config.txt",
"template_id": "19021f99-e36f-394d-b4a1-8aaa902ab9cc"
}

View File

@ -0,0 +1,38 @@
GET /v2/templates/base-configs
------------------------------------------------------------------------------------------------------------------------------------------
.. contents::
GET /v2/templates/base-configs
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
List all available base configuration files
Response status codes
**********************
- **200**: List of base configuration files returned
Output
*******
.. raw:: html
<table>
<tr>
<th>Name</th>
<th>Type</th>
<th>Description</th>
</tr>
<tr>
<td>filename</td>
<td>string</td>
<td>Name of the base configuration file</td>
</tr>
</table>
Sample session
***************
.. literalinclude:: ../../../examples/controller_get_templatesbaseconfigs.txt

View File

@ -0,0 +1,90 @@
/v2/templates/{template_id}/base-config/{filename}
------------------------------------------------------------------------------------------------------------------------------------------
.. contents::
GET /v2/templates/**{template_id}**/base-configs/**{filename}**
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
List all available base configuration files
Response status codes
**********************
- **200**: List of base configuration files returned
Output
*******
.. raw:: html
<table>
<tr>
<th>Name</th>
<th>Type</th>
<th>Description</th>
</tr>
<tr>
<td>filename</td>
<td>string</td>
<td>Name of the base configuration file</td>
</tr>
</table>
Sample session
***************
.. literalinclude:: ../../../examples/controller_get_templateidbaseconfig.txt
PUT /v2/templates/**{template_id}**/base-config/**{filename}**
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Update base configuration file content
Response status codes
**********************
- **200**: File updated
- **404**: File not found
Input
*******
.. raw:: html
<table>
<tr>
<th>Name</th>
<th>Mandatory</th>
<th>Type</th>
<th>Description</th>
</tr>
<tr><td>content</td><td>&#10004;</td><td>string</td><td>New file content</td></tr>
</table>
Output
*******
.. raw:: html
<table>
<tr>
<th>Name</th>
<th>Mandatory</th>
<th>Type</th>
<th>Description</th>
</tr>
<tr><td>template_id</td><td>&#10004;</td><td>string</td><td>Template UUID</td></tr>
<tr><td>filename</td><td>&#10004;</td><td>string</td><td>Base configuration filename</td></tr>
<tr><td>content</td><td>&#10004;</td><td>string</td><td>Updated file content</td></tr>
</table>
Sample session
***************
.. literalinclude:: ../../../examples/controller_put_templateidbaseconfig.txt

View File

@ -25,6 +25,30 @@
"options": "-cpu host -nographic"
},
"images": [
{
"filename": "AlmaLinux-10-GenericCloud-10.0-20250528.0.x86_64.qcow2",
"version": "10.0",
"md5sum": "e5099e33f16014b6cc851482d865720d",
"filesize": 460062720,
"download_url": "https://vault.almalinux.org/10.0/cloud/x86_64/images/",
"direct_download_url": "https://vault.almalinux.org/10.0/cloud/x86_64/images/AlmaLinux-10-GenericCloud-10.0-20250528.0.x86_64.qcow2"
},
{
"filename": "AlmaLinux-9-GenericCloud-9.6-20250522.x86_64.qcow2",
"version": "9.6",
"md5sum": "ae02074389961caf08bbe44439603061",
"filesize": 530776064,
"download_url": "https://vault.almalinux.org/9.6/cloud/x86_64/images/",
"direct_download_url": "https://vault.almalinux.org/9.6/cloud/x86_64/images/AlmaLinux-9-GenericCloud-9.6-20250522.x86_64.qcow2"
},
{
"filename": "AlmaLinux-9-GenericCloud-9.5-20241120.x86_64.qcow2",
"version": "9.5",
"md5sum": "cb48bc1c609a1d3d62f48397acdd038e",
"filesize": 490995712,
"download_url": "https://vault.almalinux.org/9.5/cloud/x86_64/images/",
"direct_download_url": "https://vault.almalinux.org/9.5/cloud/x86_64/images/AlmaLinux-9-GenericCloud-9.5-20241120.x86_64"
},
{
"filename": "AlmaLinux-9-GenericCloud-9.4-20240805.x86_64.qcow2",
"version": "9.4",
@ -75,6 +99,27 @@
}
],
"versions": [
{
"name": "10.0",
"images": {
"hda_disk_image": "AlmaLinux-10-GenericCloud-10.0-20250528.0.x86_64.qcow2",
"cdrom_image": "almalinux-cloud-init-data.iso"
}
},
{
"name": "9.6",
"images": {
"hda_disk_image": "AlmaLinux-9-GenericCloud-9.6-20250522.x86_64.qcow2",
"cdrom_image": "almalinux-cloud-init-data.iso"
}
},
{
"name": "9.5",
"images": {
"hda_disk_image": "AlmaLinux-9-GenericCloud-9.5-20241120.x86_64.qcow2",
"cdrom_image": "almalinux-cloud-init-data.iso"
}
},
{
"name": "9.4",
"images": {

View File

@ -0,0 +1,59 @@
{
"appliance_id": "b3b90fde-143a-4129-8031-ccbba73c5e02",
"name": "armbian",
"category": "guest",
"description": "A highly optimized base operating system specialized for single board computers (SBCs) and its extensive build framework.",
"vendor_name": "The Armbian team",
"vendor_url": "https://armbian.com/",
"documentation_url": "https://docs.armbian.com/",
"product_name": "Armbian UEFI x86",
"product_url": "https://armbian.com/boards/uefi-x86",
"registry_version": 4,
"status": "stable",
"maintainer": "GNS3 Team",
"maintainer_email": "developers@gns3.net",
"usage": "By first login you create root password and new sudo user.\n\nBoot disk from UEFI shell, type: FS0:EFI\\BOOT\\BOOTX64 and press <Enter>",
"port_name_format": "Ethernet{0}",
"qemu": {
"adapter_type": "virtio-net-pci",
"adapters": 2,
"ram": 256,
"hda_disk_interface": "virtio",
"arch": "x86_64",
"console_type": "spice+agent",
"uefi": false,
"boot_priority": "c",
"kvm": "require",
"options": "-nographic"
},
"images": [
{
"filename": "OVMF-edk2-stable202305.fd",
"version": "stable202305",
"md5sum": "6c4cf1519fec4a4b95525d9ae562963a",
"filesize": 4194304,
"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": "Armbian_26.5.1_Uefi-x86_trixie_cloud_6.18.32_minimal.img.qcow2",
"version": "Armbian 26.5.1 Minimal (CLI)",
"md5sum": "7f4c915668718d6135406de5a6c4fc30",
"filesize": 877920512,
"download_url": "https://armbian.com/boards/uefi-x86",
"direct_download_url": "https://armbian.atomonetworks.com/dl/uefi-x86/archive/Armbian_26.5.1_Uefi-x86_trixie_cloud_6.18.32_minimal.img.qcow2.xz",
"compression": "xz"
}
],
"versions": [
{
"name": "Armbian 26.5.1 Minimal (CLI)",
"images": {
"bios_image": "OVMF-edk2-stable202305.fd",
"hda_disk_image": "Armbian_26.5.1_Uefi-x86_trixie_cloud_6.18.32_minimal.img.qcow2"
}
}
]
}

View File

@ -1,41 +1,56 @@
{
"appliance_id": "088df570-f637-46f5-8a68-85acde538e5e",
"appliance_id": "8fecbf89-5cd1-4aea-b735-5f36cf0efbb7",
"name": "BIRD",
"category": "router",
"description": "The BIRD project aims to develop a fully functional dynamic IP routing daemon primarily targeted on (but not limited to) Linux, FreeBSD and other UNIX-like systems and distributed under the GNU General Public License.",
"vendor_name": "CZ.NIC Labs",
"vendor_url": "http://bird.network.cz/",
"documentation_url": "http://bird.network.cz/?get_doc&f=bird.html",
"vendor_url": "https://bird.network.cz",
"documentation_url": "https://bird.network.cz/?get_doc&f=bird.html&v=20",
"product_name": "BIRD internet routing daemon",
"registry_version": 4,
"status": "stable",
"maintainer": "GNS3 Team",
"maintainer_email": "developers@gns3.net",
"usage": "\n*** BIRD v1 is end-of-life ***\nPlease use the BIRD2 appliance.\n\nConfigure interfaces in /opt/bootlocal.sh, BIRD configuration is done in /usr/local/etc/bird",
"maintainer": "Bernhard Ehlers",
"maintainer_email": "dev-ehlers@mailbox.org",
"usage": "Username:\tgns3\nPassword:\tgns3\nTo become root, use \"sudo -s\".\n\nNetwork configuration:\nsudo nano /etc/network/interfaces\nsudo systemctl restart networking\n\nBIRD:\nRestart: sudo systemctl restart bird\nReconfigure: birdc configure",
"port_name_format": "eth{0}",
"qemu": {
"adapter_type": "e1000",
"adapter_type": "virtio-net-pci",
"adapters": 4,
"ram": 128,
"hda_disk_interface": "ide",
"ram": 512,
"hda_disk_interface": "scsi",
"arch": "x86_64",
"console_type": "telnet",
"kvm": "allow"
},
"images": [
{
"filename": "bird-tinycore64-1.5.0.img",
"version": "1.5.0",
"md5sum": "08d50ba2b1b262e2e03e4babf90abf69",
"filesize": 22413312,
"filename": "bird2-debian-2.14.qcow2",
"version": "2.14",
"md5sum": "029cf1756201ee79497c169502b08b88",
"filesize": 303717376,
"download_url": "https://sourceforge.net/projects/gns-3/files/Qemu%20Appliances/",
"direct_download_url": "http://downloads.sourceforge.net/project/gns-3/Qemu%20Appliances/bird-tinycore64-1.5.0.img"
"direct_download_url": "https://downloads.sourceforge.net/project/gns-3/Qemu%20Appliances/bird2-debian-2.14.qcow2"
},
{
"filename": "bird2-debian-2.0.12.qcow2",
"version": "2.0.12",
"md5sum": "435218a2e90cba921cc7fde1d64a9419",
"filesize": 287965184,
"download_url": "https://sourceforge.net/projects/gns-3/files/Qemu%20Appliances/",
"direct_download_url": "https://downloads.sourceforge.net/project/gns-3/Qemu%20Appliances/bird2-debian-2.0.12.qcow2"
}
],
"versions": [
{
"name": "1.5.0",
"name": "2.14",
"images": {
"hda_disk_image": "bird-tinycore64-1.5.0.img"
"hda_disk_image": "bird2-debian-2.14.qcow2"
}
},
{
"name": "2.0.12",
"images": {
"hda_disk_image": "bird2-debian-2.0.12.qcow2"
}
}
]

View File

@ -1,57 +0,0 @@
{
"appliance_id": "8fecbf89-5cd1-4aea-b735-5f36cf0efbb7",
"name": "BIRD2",
"category": "router",
"description": "The BIRD project aims to develop a fully functional dynamic IP routing daemon primarily targeted on (but not limited to) Linux, FreeBSD and other UNIX-like systems and distributed under the GNU General Public License.",
"vendor_name": "CZ.NIC Labs",
"vendor_url": "https://bird.network.cz",
"documentation_url": "https://bird.network.cz/?get_doc&f=bird.html&v=20",
"product_name": "BIRD internet routing daemon",
"registry_version": 4,
"status": "stable",
"maintainer": "Bernhard Ehlers",
"maintainer_email": "dev-ehlers@mailbox.org",
"usage": "Username:\tgns3\nPassword:\tgns3\nTo become root, use \"sudo -s\".\n\nNetwork configuration:\nsudo nano /etc/network/interfaces\nsudo systemctl restart networking\n\nBIRD:\nRestart: sudo systemctl restart bird\nReconfigure: birdc configure",
"port_name_format": "eth{0}",
"qemu": {
"adapter_type": "virtio-net-pci",
"adapters": 4,
"ram": 512,
"hda_disk_interface": "scsi",
"arch": "x86_64",
"console_type": "telnet",
"kvm": "allow"
},
"images": [
{
"filename": "bird2-debian-2.14.qcow2",
"version": "2.14",
"md5sum": "029cf1756201ee79497c169502b08b88",
"filesize": 303717376,
"download_url": "https://sourceforge.net/projects/gns-3/files/Qemu%20Appliances/",
"direct_download_url": "https://downloads.sourceforge.net/project/gns-3/Qemu%20Appliances/bird2-debian-2.14.qcow2"
},
{
"filename": "bird2-debian-2.0.12.qcow2",
"version": "2.0.12",
"md5sum": "435218a2e90cba921cc7fde1d64a9419",
"filesize": 287965184,
"download_url": "https://sourceforge.net/projects/gns-3/files/Qemu%20Appliances/",
"direct_download_url": "https://downloads.sourceforge.net/project/gns-3/Qemu%20Appliances/bird2-debian-2.0.12.qcow2"
}
],
"versions": [
{
"name": "2.14",
"images": {
"hda_disk_image": "bird2-debian-2.14.qcow2"
}
},
{
"name": "2.0.12",
"images": {
"hda_disk_image": "bird2-debian-2.0.12.qcow2"
}
}
]
}

View File

@ -27,20 +27,20 @@
},
"images": [
{
"filename": "CentOS-Stream-GenericCloud-x86_64-10-20250331.0.x86_64.qcow2",
"version": "Stream-10 (20250331.0)",
"md5sum": "776033371ca346001dd6390f0cbaf0d0",
"filesize": 952041472,
"filename": "CentOS-Stream-GenericCloud-10-20260504.0.x86_64.qcow2",
"version": "Stream-10 (20260504.0)",
"md5sum": "932b0bcc7e5c7404f91432979dc95314",
"filesize": 2173501440,
"download_url": "https://cloud.centos.org/centos/10-stream/x86_64/images",
"direct_download_url": "https://cloud.centos.org/centos/10-stream/x86_64/images/CentOS-Stream-GenericCloud-x86_64-10-20250331.0.x86_64.qcow2"
"direct_download_url": "https://cloud.centos.org/centos/10-stream/x86_64/images/CentOS-Stream-GenericCloud-10-20260504.0.x86_64.qcow2"
},
{
"filename": "CentOS-Stream-GenericCloud-9-20250331.0.x86_64.qcow2",
"version": "Stream-9 (20250331.0)",
"md5sum": "4aaeddc6ca497065522c75a7471f9bfd",
"filesize": 1250625536,
"filename": "CentOS-Stream-GenericCloud-9-20260504.0.x86_64.qcow2",
"version": "Stream-9 (20260504.0)",
"md5sum": "ebfbbd23ba7b85bbaf83c6c0626c2b70",
"filesize": 1877251072,
"download_url": "https://cloud.centos.org/centos/9-stream/x86_64/images",
"direct_download_url": "https://cloud.centos.org/centos/9-stream/x86_64/images/CentOS-Stream-GenericCloud-9-20250331.0.x86_64.qcow2"
"direct_download_url": "https://cloud.centos.org/centos/9-stream/x86_64/images/CentOS-Stream-GenericCloud-9-20260504.0.x86_64.qcow2"
},
{
"filename": "CentOS-Stream-GenericCloud-8-20240603.0.x86_64.qcow2",
@ -61,16 +61,16 @@
],
"versions": [
{
"name": "Stream-10 (20250331.0)",
"name": "Stream-10 (20260504.0)",
"images": {
"hda_disk_image": "CentOS-Stream-GenericCloud-x86_64-10-20250331.0.x86_64.qcow2",
"hda_disk_image": "CentOS-Stream-GenericCloud-10-20260504.0.x86_64.qcow2",
"cdrom_image": "centos-cloud-init-data.iso"
}
},
{
"name": "Stream-9 (20250331.0)",
"name": "Stream-9 (20260504.0)",
"images": {
"hda_disk_image": "CentOS-Stream-GenericCloud-9-20250331.0.x86_64.qcow2",
"hda_disk_image": "CentOS-Stream-GenericCloud-9-20260504.0.x86_64.qcow2",
"cdrom_image": "centos-cloud-init-data.iso"
}
},

View File

@ -1,5 +1,6 @@
{
"appliance_id": "cc2b1802-3520-4963-8ff7-c19b1f6418c5",
"tags": ["device_type:cisco_ios_telnet"],
"name": "Cisco 1700",
"category": "router",
"description": "Cisco 1700 Router",

View File

@ -1,5 +1,6 @@
{
"appliance_id": "ed474fea-cd3b-4e2e-be84-a855a133060a",
"tags": ["device_type:cisco_ios_telnet"],
"name": "Cisco 2600",
"category": "router",
"description": "Cisco 2600 Router",

View File

@ -1,5 +1,6 @@
{
"appliance_id": "ed0f079e-a506-4cb4-b46c-714a80bbe2d3",
"tags": ["device_type:cisco_ios_telnet"],
"name": "Cisco 2691",
"category": "router",
"description": "Cisco 2691 Router",

View File

@ -1,5 +1,6 @@
{
"appliance_id": "7ada6336-7280-4306-9f32-6b1e242ae989",
"tags": ["device_type:cisco_ios_telnet"],
"name": "Cisco 3620",
"category": "router",
"description": "Cisco 3620 Router",

View File

@ -1,5 +1,6 @@
{
"appliance_id": "ef119e49-19fe-4239-9c6b-16ea12f6ec01",
"tags": ["device_type:cisco_ios_telnet"],
"name": "Cisco 3640",
"category": "router",
"description": "Cisco 3640 Router",

View File

@ -1,5 +1,6 @@
{
"appliance_id": "ac460a9c-9274-4ccf-a056-af50b699925f",
"tags": ["device_type:cisco_ios_telnet"],
"name": "Cisco 3660",
"category": "router",
"description": "Cisco 3660 Router",

View File

@ -1,5 +1,6 @@
{
"appliance_id": "5b9c5293-a5a7-47a3-9df4-6cf658f2f378",
"tags": ["device_type:cisco_ios_telnet"],
"name": "Cisco 3725",
"category": "router",
"description": "Cisco 3725 Router",

View File

@ -1,5 +1,6 @@
{
"appliance_id": "18737edb-e43f-4fb0-8a0f-88982cff58b1",
"tags": ["device_type:cisco_ios_telnet"],
"name": "Cisco 3745",
"category": "router",
"description": "Cisco 3745 Multiservice Access Router",

View File

@ -1,5 +1,6 @@
{
"appliance_id": "38aa3135-f169-4131-9b64-73605787b5ef",
"tags": ["device_type:cisco_ios_telnet"],
"name": "Cisco 7200",
"category": "router",
"description": "Cisco 7200 Router",

View File

@ -9,7 +9,7 @@
"product_name": "Alpine Linux Virt",
"product_url": "https://developer.cisco.com/modeling-labs/",
"registry_version": 4,
"status": "beta",
"status": "experimental",
"maintainer": "GNS3 Team",
"maintainer_email": "developers@gns3.net",
"usage": "By default, a \"cisco\" account with password \"cisco\" is created.",

View File

@ -26,6 +26,13 @@
"kvm": "require"
},
"images": [
{
"filename": "asav9-23-1.qcow2",
"version": "9.23.1 CML",
"md5sum": "30454794b46fe07d1cd6a3ee72b511e1",
"filesize": 400687104,
"download_url": "https://u.cisco.com/my-learning/my-account"
},
{
"filename": "asav9-22-1-1.qcow2",
"version": "9.22.1.1 CML",
@ -133,6 +140,12 @@
}
],
"versions": [
{
"name": "9.23.1 CML",
"images": {
"hda_disk_image": "asav9-23-1.qcow2"
}
},
{
"name": "9.22.1.1 CML",
"images": {

View File

@ -17,7 +17,7 @@
"qemu": {
"adapter_type": "vmxnet3",
"adapters": 4,
"ram": 3072,
"ram": 4096,
"hda_disk_interface": "ide",
"arch": "x86_64",
"console_type": "telnet",

View File

@ -1,11 +1,12 @@
{
"appliance_id": "3bf492b6-5717-4257-9bfd-b34617c6f133",
"tags": ["device_type:cisco_ios_telnet"],
"name": "Cisco IOSv",
"category": "router",
"description": "Cisco Virtual IOS allows user to run IOS on a standard computer.",
"vendor_name": "Cisco",
"vendor_url": "http://www.cisco.com/",
"documentation_url": "https://developer.cisco.com/docs/modeling-labs/iol/",
"documentation_url": "https://developer.cisco.com/docs/modeling-labs/iosv/",
"product_name": "IOSv",
"product_url": "https://developer.cisco.com/modeling-labs/",
"registry_version": 4,
@ -25,6 +26,13 @@
"kvm": "require"
},
"images": [
{
"filename": "vios-adventerprisek9-m.spa.159-3.m10.qcow2",
"version": "15.9(3)M10",
"md5sum": "960ff630ce3fc030f6155a8e77d64105",
"filesize": 57323520,
"download_url": "https://u.cisco.com/my-learning/my-account"
},
{
"filename": "vios-adventerprisek9-m.spa.159-3.m9.qcow2",
"version": "15.9(3)M9",
@ -112,6 +120,13 @@
}
],
"versions": [
{
"name": "15.9(3)M10",
"images": {
"hda_disk_image": "vios-adventerprisek9-m.spa.159-3.m10.qcow2",
"hdb_disk_image": "IOSv_startup_config.img"
}
},
{
"name": "15.9(3)M9",
"images": {

View File

@ -1,5 +1,6 @@
{
"appliance_id": "4368f802-ddec-4863-adbd-a36a6d83cd4c",
"tags": ["device_type:cisco_ios_telnet"],
"name": "Cisco IOSvL2",
"category": "multilayer_switch",
"description": "Cisco Virtual IOS L2 allows user to run a IOS switching image on a standard computer.",

View File

@ -19,13 +19,21 @@
"adapter_type": "virtio-net-pci",
"adapters": 7,
"ram": 16384,
"cpus": 4,
"hda_disk_interface": "ide",
"arch": "x86_64",
"console_type": "telnet",
"kvm": "require",
"options": "-smp 4 -cpu host"
"options": "-cpu host"
},
"images": [
{
"filename": "xrv9k-fullk9-x-25.1.1.qcow2",
"version": "25.1.1",
"md5sum": "554d795d495badf90b6c75a2a342b409",
"filesize": 2002911232,
"download_url": "https://u.cisco.com/my-learning/my-account"
},
{
"filename": "xrv9k-fullk9-x-24.3.1.qcow2",
"version": "24.3.1",
@ -49,6 +57,12 @@
}
],
"versions": [
{
"name": "25.1.1",
"images": {
"hda_disk_image": "xrv9k-fullk9-x-25.1.1.qcow2"
}
},
{
"name": "24.3.1",
"images": {

View File

@ -1,5 +1,6 @@
{
"appliance_id": "9593db48-558a-4914-bb78-a20605f39c65",
"tags": ["device_type:cisco_ios_telnet"],
"name": "Cisco IOU L2",
"category": "multilayer_switch",
"description": "Cisco IOS on UNIX Layer 2 image.",
@ -37,18 +38,6 @@
"version": "17.12.1",
"md5sum": "2b5055e4cef8fd257416d74a94adb626",
"filesize": 240355720
},
{
"filename": "i86bi_linux_l2-adventerprisek9-ms.SSA.high_iron_20190423.bin",
"version": "15.2(20190423)",
"md5sum": "ffe884f8e762b762d7886c51d8ee015c",
"filesize": 126249700
},
{
"filename": "i86bi_LinuxL2-AdvEnterpriseK9-M_152_May_2018.bin",
"version": "15.2(20180510)",
"md5sum": "d704b68c5f4c4f92e56b754b49b92012",
"filesize": 126226692
}
],
"versions": [
@ -69,18 +58,6 @@
"images": {
"image": "x86_64_crb_linux_l2-adventerprisek9-ms"
}
},
{
"name": "15.2(20190423)",
"images": {
"image": "i86bi_linux_l2-adventerprisek9-ms.SSA.high_iron_20190423.bin"
}
},
{
"name": "15.2(20180510)",
"images": {
"image": "i86bi_LinuxL2-AdvEnterpriseK9-M_152_May_2018.bin"
}
}
]
}

View File

@ -1,5 +1,6 @@
{
"appliance_id": "87bf6f58-1e5a-4ee2-afe7-715dabffcf18",
"tags": ["device_type:cisco_ios_telnet"],
"name": "Cisco IOU L3",
"category": "router",
"description": "Cisco IOS on UNIX Layer 3 image.",
@ -37,12 +38,6 @@
"version": "17.12.1",
"md5sum": "4a2fce8de21d1831fbceffd155e41ae7",
"filesize": 288947184
},
{
"filename": "i86bi_LinuxL3-AdvEnterpriseK9-M2_157_3_May_2018.bin",
"version": "15.7(3)M2",
"md5sum": "d6874260c3daeeb96d10fc844ae0b93b",
"filesize": 184759244
}
],
"versions": [
@ -63,12 +58,6 @@
"images": {
"image": "x86_64_crb_linux-adventerprisek9-ms"
}
},
{
"name": "15.7(3)M2",
"images": {
"image": "i86bi_LinuxL3-AdvEnterpriseK9-M2_157_3_May_2018.bin"
}
}
]
}

View File

@ -0,0 +1,69 @@
{
"appliance_id": "4c7fd5da-be86-4168-8484-7071dc9c71fb",
"name": "Cisco NX-OSv 9000 Lite",
"category": "multilayer_switch",
"description": "The NX-OSv 9000 is a virtual platform that is designed to simulate the control plane aspects of a network element running Cisco Nexus 9000 software. The NX-OSv 9000 shares the same software image running on Cisco Nexus 9000 hardware platform although no specific hardware emulation is implemented. When the software runs as a virtual machine, line card (LC) ASIC provisioning or any interaction from the control plane to hardware ASIC is handled by the NX-OSv 9000 software data plane.\nThe NX-OSv 9000 for the Cisco Nexus 9000 Series provides a useful tool to enable the devops model and rapidly test changes to the infrastructure or to infrastructure automation tools. This enables network simulations in large scale for customers to validate configuration changes on a simulated network prior to applying them on a production network. Some users have also expressed interest in using the simulation system for feature test ,verification, and automation tooling development and test simualtion prior to deployment. NX-OSv 9000 can be used as a programmability vehicle to validate software defined networks (SDNs) and Network Function Virtualization (NFV) based solutions.\nThe lite version has a reduced memory footprint, resulting in a much smaller image size than the earlier images for Cisco Nexus 9300v and 9500v.",
"vendor_name": "Cisco",
"vendor_url": "http://www.cisco.com/",
"documentation_url": "https://developer.cisco.com/docs/modeling-labs/nx-os-9000/",
"product_name": "NX-OSv 9000",
"product_url": "https://developer.cisco.com/modeling-labs/",
"registry_version": 4,
"status": "stable",
"availability": "service-contract",
"maintainer": "GNS3 Team",
"maintainer_email": "developers@gns3.net",
"first_port_name": "mgmt0",
"port_name_format": "Ethernet1/{port1}",
"qemu": {
"adapter_type": "e1000",
"adapters": 10,
"ram": 6144,
"cpus": 2,
"hda_disk_interface": "sata",
"arch": "x86_64",
"console_type": "telnet",
"kvm": "require"
},
"images": [
{
"filename": "nexus9500v64-lite.10.5.5.M.qcow2",
"version": "9500v lite 10.5.5.M",
"md5sum": "a9aafd88f36a7f44a5a2ef3322cd9bde",
"filesize": 2617638912,
"download_url": "https://software.cisco.com/download/home/286312239/type/282088129/release/10.5(5)"
},
{
"filename": "nexus9300v64-lite.10.5.5.M.qcow2",
"version": "9300v lite 10.5.5.M",
"md5sum": "78610953005faab9505252baf4c86b78",
"filesize": 2617573376,
"download_url": "https://software.cisco.com/download/home/286312239/type/282088129/release/10.5(5)"
},
{
"filename": "OVMF-edk2-stable202305.fd",
"version": "stable202305",
"md5sum": "6c4cf1519fec4a4b95525d9ae562963a",
"filesize": 4194304,
"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"
}
],
"versions": [
{
"name": "9500v lite 10.5.5.M",
"images": {
"bios_image": "OVMF-edk2-stable202305.fd",
"hda_disk_image": "nexus9500v64-lite.10.5.5.M.qcow2"
}
},
{
"name": "9300v lite 10.5.5.M",
"images": {
"bios_image": "OVMF-edk2-stable202305.fd",
"hda_disk_image": "nexus9300v64-lite.10.5.5.M.qcow2"
}
}
]
}

View File

@ -27,6 +27,20 @@
"kvm": "require"
},
"images": [
{
"filename": "nexus9500v64.10.5.5.M.qcow2",
"version": "9500v 10.5.5.M",
"md5sum": "c1d34bb3331d958d3219a56b6885150e",
"filesize": 2919170048,
"download_url": "https://software.cisco.com/download/home/286312239/type/282088129/release/10.5(5)"
},
{
"filename": "nexus9300v64.10.5.5.M.qcow2",
"version": "9300v 10.5.5.M",
"md5sum": "f1cc2342b920320654d232b68551ccfd",
"filesize": 2919104512,
"download_url": "https://software.cisco.com/download/home/286312239/type/282088129/release/10.5(5)"
},
{
"filename": "nexus9500v64.10.5.1.F.qcow2",
"version": "9500v 10.5.1.F",
@ -80,6 +94,20 @@
}
],
"versions": [
{
"name": "9500v 10.5.5.M",
"images": {
"bios_image": "OVMF-edk2-stable202305.fd",
"hda_disk_image": "nexus9500v64.10.5.5.M.qcow2"
}
},
{
"name": "9300v 10.5.5.M",
"images": {
"bios_image": "OVMF-edk2-stable202305.fd",
"hda_disk_image": "nexus9300v64.10.5.5.M.qcow2"
}
},
{
"name": "9500v 10.5.1.F",
"images": {

View File

@ -26,6 +26,30 @@
"options": "-nographic"
},
"images": [
{
"filename": "Fedora-Cloud-Base-Generic-44-1.7.x86_64.qcow2",
"version": "44-1.7",
"md5sum": "49784011eb752291bfead24d3be8163f",
"filesize": 583729152,
"download_url": "https://download.fedoraproject.org/pub/fedora/linux/releases/44/Cloud/x86_64/images",
"direct_download_url": "https://fedora.mirrorservice.org/fedora/linux/releases/44/Cloud/x86_64/images/Fedora-Cloud-Base-Generic-44-1.7.x86_64.qcow2"
},
{
"filename": "Fedora-Cloud-Base-Generic-43-1.6.x86_64.qcow2",
"version": "43-1.6",
"md5sum": "f2f671db53fae58b79bb2e2259fa3622",
"filesize": 583335936,
"download_url": "https://download.fedoraproject.org/pub/fedora/linux/releases/43/Cloud/x86_64/images",
"direct_download_url": "https://fedora.mirrorservice.org/fedora/linux/releases/43/Cloud/x86_64/images/Fedora-Cloud-Base-Generic-43-1.6.x86_64.qcow2"
},
{
"filename": "Fedora-Cloud-Base-Generic-42-1.1.x86_64.qcow2",
"version": "42-1.1",
"md5sum": "2d660d2adc20b1fe793df5cb66f1d868",
"filesize": 532217856,
"download_url": "https://download.fedoraproject.org/pub/fedora/linux/releases/42/Cloud/x86_64/images",
"direct_download_url": "https://fedora.mirrorservice.org/fedora/linux/releases/42/Cloud/x86_64/images/Fedora-Cloud-Base-Generic-42-1.1.x86_64.qcow2"
},
{
"filename": "Fedora-Cloud-Base-Generic-41-1.4.x86_64.qcow2",
"version": "41-1.4",
@ -34,30 +58,6 @@
"download_url": "https://download.fedoraproject.org/pub/fedora/linux/releases/41/Cloud/x86_64/images",
"direct_download_url": "https://fedora.mirrorservice.org/fedora/linux/releases/41/Cloud/x86_64/images/Fedora-Cloud-Base-Generic-41-1.4.x86_64.qcow2"
},
{
"filename": "Fedora-Cloud-Base-Generic.x86_64-40-1.14.qcow2",
"version": "40-1.14",
"md5sum": "3eed4b1a9de35208ed30d9bb72c1522d",
"filesize": 397475840,
"download_url": "https://download.fedoraproject.org/pub/fedora/linux/releases/40/Cloud/x86_64/images",
"direct_download_url": "https://fedora.mirrorservice.org/fedora/linux/releases/40/Cloud/x86_64/images/Fedora-Cloud-Base-Generic.x86_64-40-1.14.qcow2"
},
{
"filename": "Fedora-Cloud-Base-39-1.5.x86_64.qcow2",
"version": "39-1.5",
"md5sum": "800a10df2d369891ed65900bcacacd47",
"filesize": 544604160,
"download_url": "https://download.fedoraproject.org/pub/fedora/linux/releases/39/Cloud/x86_64/images",
"direct_download_url": "https://download.fedoraproject.org/pub/fedora/linux/releases/39/Cloud/x86_64/images/Fedora-Cloud-Base-39-1.5.x86_64.qcow2"
},
{
"filename": "Fedora-Cloud-Base-38-1.6.x86_64.qcow2",
"version": "38-1.6",
"md5sum": "53ddfe7b28666d5ddc55e93ff06abad2",
"filesize": 497287168,
"download_url": "https://download.fedoraproject.org/pub/fedora/linux/releases/38/Cloud/x86_64/images",
"direct_download_url": "https://download.fedoraproject.org/pub/fedora/linux/releases/38/Cloud/x86_64/images/Fedora-Cloud-Base-38-1.6.x86_64.qcow2"
},
{
"filename": "fedora-cloud-init-data.iso",
"version": "1.0",
@ -68,33 +68,33 @@
}
],
"versions": [
{
"name": "44-1.7",
"images": {
"hda_disk_image": "Fedora-Cloud-Base-Generic-44-1.7.x86_64.qcow2",
"cdrom_image": "fedora-cloud-init-data.iso"
}
},
{
"name": "43-1.6",
"images": {
"hda_disk_image": "Fedora-Cloud-Base-Generic-43-1.6.x86_64.qcow2",
"cdrom_image": "fedora-cloud-init-data.iso"
}
},
{
"name": "42-1.1",
"images": {
"hda_disk_image": "Fedora-Cloud-Base-Generic-42-1.1.x86_64.qcow2",
"cdrom_image": "fedora-cloud-init-data.iso"
}
},
{
"name": "41-1.4",
"images": {
"hda_disk_image": "Fedora-Cloud-Base-Generic-41-1.4.x86_64.qcow2",
"cdrom_image": "fedora-cloud-init-data.iso"
}
},
{
"name": "40-1.14",
"images": {
"hda_disk_image": "Fedora-Cloud-Base-Generic.x86_64-40-1.14.qcow2",
"cdrom_image": "fedora-cloud-init-data.iso"
}
},
{
"name": "39-1.5",
"images": {
"hda_disk_image": "Fedora-Cloud-Base-39-1.5.x86_64.qcow2",
"cdrom_image": "fedora-cloud-init-data.iso"
}
},
{
"name": "38-1.6",
"images": {
"hda_disk_image": "Fedora-Cloud-Base-38-1.6.x86_64.qcow2",
"cdrom_image": "fedora-cloud-init-data.iso"
}
}
]
}

View File

@ -51,250 +51,12 @@
"download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx"
},
{
"filename": "FMG_VM64_KVM-v7.4.3-build2487-FORTINET.out.kvm.qcow2",
"version": "7.4.3",
"md5sum": "b01d9f86aa27c538407d518df1326863",
"filesize": 346107904,
"download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx"
},
{
"filename": "FMG_VM64_KVM-v7.4.2-build2397-FORTINET.out.kvm.qcow2",
"version": "7.4.2",
"md5sum": "36371fbf06210ded57c00b2ff290f2c5",
"filesize": 322514944,
"download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx"
},
{
"filename": "FMG_VM64_KVM-v7.4.1-build2308-FORTINET.out.kvm.qcow2",
"version": "7.4.1",
"md5sum": "e542cc8f2d8f46e9c32b783bf31bef39",
"filesize": 309387264,
"download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx"
},
{
"filename": "FMG_VM64_KVM-v7.2.5-build1574-FORTINET.out.kvm.qcow2",
"version": "7.2.5",
"md5sum": "754326845096afd909ec45d98f8d5a83",
"filesize": 278401024,
"download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx"
},
{
"filename": "FMG_VM64_KVM-v7.2.4-build1460-FORTINET.out.kvm.qcow2",
"version": "7.2.4",
"md5sum": "98fa9830d9ecb5911a703d03b80026b6",
"filesize": 261992448,
"download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx"
},
{
"filename": "FMG_VM64_KVM-v7.2.2-build1334-FORTINET.out.kvm.qcow2",
"version": "7.2.2",
"md5sum": "2ff1298257321cd485d2cad91d6ce510",
"filesize": 246083584,
"download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx"
},
{
"filename": "FMG_VM64_KVM-v7.2.1-build1215-FORTINET.out.kvm.qcow2",
"version": "7.2.1",
"md5sum": "1a3eeff1204fa8f4243773f7521e12b5",
"filesize": 242814976,
"download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx"
},
{
"filename": "FMG_VM64_KVM-v7.0.12-build0623-FORTINET.out.kvm.qcow2",
"version": "7.0.12",
"md5sum": "5b6f6a2b8bc00e56337aa7023a9025cf",
"filesize": 249520128,
"download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx"
},
{
"filename": "FMG_VM64_KVM-v7.0.11-build0595-FORTINET.out.kvm.qcow2",
"version": "7.0.11",
"md5sum": "7b166222136e26190159f37cccbaab6e",
"filesize": 249360384,
"download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx"
},
{
"filename": "FMG_VM64_KVM-v7.0.9-build0489-FORTINET.out.kvm.qcow2",
"version": "7.0.9",
"md5sum": "dbeb6a79b6e421000573dbbbdb50b8b5",
"filesize": 247955456,
"download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx"
},
{
"filename": "FMG_VM64_KVM-v7.0.6-build0372-FORTINET.out.kvm.qcow2",
"version": "7.0.6",
"md5sum": "dfa4df9e976ed87e73cb9601a8a70323",
"filesize": 239190016,
"download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx"
},
{
"filename": "FMG_VM64_KVM-v7.0.5-build0365-FORTINET.out.kvm.qcow2",
"version": "7.0.5",
"md5sum": "e8b9c992784cea766b52a427a5fe0279",
"filesize": 237535232,
"download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx"
},
{
"filename": "FMG_VM64_KVM-v6.4.14-build2660-FORTINET.out.kvm.qcow2",
"version": "6.4.14",
"md5sum": "0fe56e363b166c07b710bde795e36049",
"filesize": 219430912,
"download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx"
},
{
"filename": "FMG_VM64_KVM-v6.4.12-build2610-FORTINET.out.kvm.qcow2",
"version": "6.4.12",
"md5sum": "36c0dc531d921e5f1e1e09b030f7c813",
"filesize": 219455488,
"download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx"
},
{
"filename": "FMG_VM64_KVM-v6-build2288-FORTINET.out.kvm.qcow2",
"version": "6.4.5",
"md5sum": "bd2791984b03f55a6825297e83c6576a",
"filesize": 117014528,
"download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx"
},
{
"filename": "FMG_VM64_KVM-v6-build2253-FORTINET.out.kvm.qcow2",
"version": "6.4.4",
"md5sum": "3554a47fde2dc91d17eec16fd0dc10a3",
"filesize": 116621312,
"download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx"
},
{
"filename": "FMG_VM64_KVM-v6-build1183-FORTINET.out.kvm.qcow2",
"version": "6.2.2",
"md5sum": "f5051a8fe49d916bb554b9bae32a1eb4",
"filesize": 139145216,
"download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx"
},
{
"filename": "FMG_VM64_KVM-v6-build1050-FORTINET.out.kvm.qcow2",
"version": "6.2.0",
"md5sum": "c19d2527f91ad1bbafbde5bf08487867",
"filesize": 126894080,
"download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx"
},
{
"filename": "FMG_VM64_KVM-v6-build0349-FORTINET.out.kvm.qcow2",
"version": "6.0.6",
"md5sum": "d03f024c948ba6e2bb9e66c11ca8f34c",
"filesize": 112553984,
"download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx"
},
{
"filename": "FMG_VM64_KVM-v6-build0255-FORTINET.out.kvm.qcow2",
"version": "6.0.3",
"md5sum": "5f34d52d9289b0be2a4c04943446ea39",
"filesize": 115703808,
"download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx"
},
{
"filename": "FMG_VM64_KVM-v6-build0205-FORTINET.out.kvm.qcow2",
"version": "6.0.2",
"md5sum": "8f748649c537d9b5466b24c5b4e62017",
"filesize": 116981760,
"download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx"
},
{
"filename": "FMG_VM64_KVM-v6-build0092-FORTINET.out.kvm.qcow2",
"version": "6.0.0",
"md5sum": "73bfe1bc70124521a524d857646b9c2e",
"filesize": 119066624,
"download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx"
},
{
"filename": "FMG_VM64_KVM-v5-build1631-FORTINET.out.kvm.qcow2",
"version": "5.6.2",
"md5sum": "c81cc247e8eb03249b475fe0e847653e",
"filesize": 106946560,
"download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx"
},
{
"filename": "FMG_VM64_KVM-v5-build1619-FORTINET.out.kvm.qcow2",
"version": "5.6.1",
"md5sum": "8cc553842564d232af295d6a0c784c1f",
"filesize": 106831872,
"download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx"
},
{
"filename": "FMG_VM64_KVM-v5-build1557-FORTINET.out.kvm.qcow2",
"version": "5.6.0",
"md5sum": "f8bd600796f894f4ca1ea2d6b4066d3d",
"filesize": 108363776,
"download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx"
},
{
"filename": "FMG_VM64_KVM-v5-build1225-FORTINET.out.kvm.qcow2",
"version": "5.4.4",
"md5sum": "53bc6e320fe7bde5d2b636bde95a910c",
"filesize": 89911296,
"download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx"
},
{
"filename": "FMG_VM64_KVM-v5-build1187-FORTINET.out.kvm.qcow2",
"version": "5.4.3",
"md5sum": "53602c776d215d98e32163a10804fc49",
"filesize": 87425024,
"download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx"
},
{
"filename": "FMG_VM64_KVM-v5-build1151-FORTINET.out.kvm.qcow2",
"version": "5.4.2",
"md5sum": "8e131ad40009c740f3efdee6dc3a0ac3",
"filesize": 86437888,
"download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx"
},
{
"filename": "FMG_VM64_KVM-v5-build1082-FORTINET.out.kvm.qcow2",
"version": "5.4.1",
"md5sum": "fc1815410f3f0536e2e3a9c1c5c07f41",
"filesize": 83124224,
"download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx"
},
{
"filename": "FMG_VM64_KVM-v5-build1019-FORTINET.out.kvm.qcow2",
"version": "5.4.0",
"md5sum": "1cfb22671cb372d8bf3e47b9c3c55ded",
"filesize": 77541376,
"download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx"
},
{
"filename": "FMG_VM64_KVM-v5-build0786-FORTINET.out.kvm.qcow2",
"version": "5.2.10",
"md5sum": "377fe38bf07bc2435608e5b65f780f07",
"filesize": 64962560,
"download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx"
},
{
"filename": "FMG_VM64_KVM-v5-build0780-FORTINET.out.kvm.qcow2",
"version": "5.2.9",
"md5sum": "04268e779d3d5e6c928c6fd638423c52",
"filesize": 65007616,
"download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx"
},
{
"filename": "FMG_VM64_KVM-v5-build0777-FORTINET.out.kvm.qcow2",
"version": "5.2.8",
"md5sum": "6dbf148ace9bf309ad383757afd75fad",
"filesize": 65011712,
"download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx"
},
{
"filename": "FMG_VM64_KVM-v5-build0757-FORTINET.out.kvm.qcow2",
"version": "5.2.7",
"md5sum": "d37dbaa49d7522324681eeba19f7699b",
"filesize": 65056768,
"download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx"
},
{
"filename": "empty30G.qcow2",
"filename": "empty500G.qcow2",
"version": "1.0",
"md5sum": "3411a599e822f2ac6be560a26405821a",
"filesize": 197120,
"md5sum": "658c825441b9b3080ba00f9eec002eaa",
"filesize": 204608,
"download_url": "https://sourceforge.net/projects/gns-3/files/Empty%20Qemu%20disk/",
"direct_download_url": "https://sourceforge.net/projects/gns-3/files/Empty%20Qemu%20disk/empty30G.qcow2/download"
"direct_download_url": "https://sourceforge.net/projects/gns-3/files/Empty%20Qemu%20disk/empty500G.qcow2/download"
}
],
"versions": [
@ -302,259 +64,21 @@
"name": "7.4.6",
"images": {
"hda_disk_image": "FMG_VM64_KVM-v7.4.6.M-build2588-FORTINET.out.kvm.qcow2",
"hdb_disk_image": "empty30G.qcow2"
"hdb_disk_image": "empty500G.qcow2"
}
},
{
"name": "7.4.5",
"images": {
"hda_disk_image": "FMG_VM64_KVM-v7.4.5.M-build2553-FORTINET.out.kvm.qcow2",
"hdb_disk_image": "empty30G.qcow2"
"hdb_disk_image": "empty500G.qcow2"
}
},
{
"name": "7.4.4",
"images": {
"hda_disk_image": "FMG_VM64_KVM-v7.4.4.F-build2550-FORTINET.out.kvm.qcow2",
"hdb_disk_image": "empty30G.qcow2"
}
},
{
"name": "7.4.3",
"images": {
"hda_disk_image": "FMG_VM64_KVM-v7.4.3-build2487-FORTINET.out.kvm.qcow2",
"hdb_disk_image": "empty30G.qcow2"
}
},
{
"name": "7.4.2",
"images": {
"hda_disk_image": "FMG_VM64_KVM-v7.4.2-build2397-FORTINET.out.kvm.qcow2",
"hdb_disk_image": "empty30G.qcow2"
}
},
{
"name": "7.4.1",
"images": {
"hda_disk_image": "FMG_VM64_KVM-v7.4.1-build2308-FORTINET.out.kvm.qcow2",
"hdb_disk_image": "empty30G.qcow2"
}
},
{
"name": "7.2.5",
"images": {
"hda_disk_image": "FMG_VM64_KVM-v7.2.5-build1574-FORTINET.out.kvm.qcow2",
"hdb_disk_image": "empty30G.qcow2"
}
},
{
"name": "7.2.4",
"images": {
"hda_disk_image": "FMG_VM64_KVM-v7.2.4-build1460-FORTINET.out.kvm.qcow2",
"hdb_disk_image": "empty30G.qcow2"
}
},
{
"name": "7.2.2",
"images": {
"hda_disk_image": "FMG_VM64_KVM-v7.2.2-build1334-FORTINET.out.kvm.qcow2",
"hdb_disk_image": "empty30G.qcow2"
}
},
{
"name": "7.2.1",
"images": {
"hda_disk_image": "FMG_VM64_KVM-v7.2.1-build1215-FORTINET.out.kvm.qcow2",
"hdb_disk_image": "empty30G.qcow2"
}
},
{
"name": "7.0.12",
"images": {
"hda_disk_image": "FMG_VM64_KVM-v7.0.12-build0623-FORTINET.out.kvm.qcow2",
"hdb_disk_image": "empty30G.qcow2"
}
},
{
"name": "7.0.11",
"images": {
"hda_disk_image": "FMG_VM64_KVM-v7.0.11-build0595-FORTINET.out.kvm.qcow2",
"hdb_disk_image": "empty30G.qcow2"
}
},
{
"name": "7.0.9",
"images": {
"hda_disk_image": "FMG_VM64_KVM-v7.0.9-build0489-FORTINET.out.kvm.qcow2",
"hdb_disk_image": "empty30G.qcow2"
}
},
{
"name": "7.0.6",
"images": {
"hda_disk_image": "FMG_VM64_KVM-v7.0.6-build0372-FORTINET.out.kvm.qcow2",
"hdb_disk_image": "empty30G.qcow2"
}
},
{
"name": "7.0.5",
"images": {
"hda_disk_image": "FMG_VM64_KVM-v7.0.5-build0365-FORTINET.out.kvm.qcow2",
"hdb_disk_image": "empty30G.qcow2"
}
},
{
"name": "6.4.14",
"images": {
"hda_disk_image": "FMG_VM64_KVM-v6.4.14-build2660-FORTINET.out.kvm.qcow2",
"hdb_disk_image": "empty30G.qcow2"
}
},
{
"name": "6.4.12",
"images": {
"hda_disk_image": "FMG_VM64_KVM-v6.4.12-build2610-FORTINET.out.kvm.qcow2",
"hdb_disk_image": "empty30G.qcow2"
}
},
{
"name": "6.4.5",
"images": {
"hda_disk_image": "FMG_VM64_KVM-v6-build2288-FORTINET.out.kvm.qcow2",
"hdb_disk_image": "empty30G.qcow2"
}
},
{
"name": "6.4.4",
"images": {
"hda_disk_image": "FMG_VM64_KVM-v6-build2253-FORTINET.out.kvm.qcow2",
"hdb_disk_image": "empty30G.qcow2"
}
},
{
"name": "6.2.2",
"images": {
"hda_disk_image": "FMG_VM64_KVM-v6-build1183-FORTINET.out.kvm.qcow2",
"hdb_disk_image": "empty30G.qcow2"
}
},
{
"name": "6.2.0",
"images": {
"hda_disk_image": "FMG_VM64_KVM-v6-build1050-FORTINET.out.kvm.qcow2",
"hdb_disk_image": "empty30G.qcow2"
}
},
{
"name": "6.0.6",
"images": {
"hda_disk_image": "FMG_VM64_KVM-v6-build0349-FORTINET.out.kvm.qcow2",
"hdb_disk_image": "empty30G.qcow2"
}
},
{
"name": "6.0.3",
"images": {
"hda_disk_image": "FMG_VM64_KVM-v6-build0255-FORTINET.out.kvm.qcow2",
"hdb_disk_image": "empty30G.qcow2"
}
},
{
"name": "6.0.2",
"images": {
"hda_disk_image": "FMG_VM64_KVM-v6-build0205-FORTINET.out.kvm.qcow2",
"hdb_disk_image": "empty30G.qcow2"
}
},
{
"name": "6.0.0",
"images": {
"hda_disk_image": "FMG_VM64_KVM-v6-build0092-FORTINET.out.kvm.qcow2",
"hdb_disk_image": "empty30G.qcow2"
}
},
{
"name": "5.6.2",
"images": {
"hda_disk_image": "FMG_VM64_KVM-v5-build1631-FORTINET.out.kvm.qcow2",
"hdb_disk_image": "empty30G.qcow2"
}
},
{
"name": "5.6.1",
"images": {
"hda_disk_image": "FMG_VM64_KVM-v5-build1619-FORTINET.out.kvm.qcow2",
"hdb_disk_image": "empty30G.qcow2"
}
},
{
"name": "5.6.0",
"images": {
"hda_disk_image": "FMG_VM64_KVM-v5-build1557-FORTINET.out.kvm.qcow2",
"hdb_disk_image": "empty30G.qcow2"
}
},
{
"name": "5.4.4",
"images": {
"hda_disk_image": "FMG_VM64_KVM-v5-build1225-FORTINET.out.kvm.qcow2",
"hdb_disk_image": "empty30G.qcow2"
}
},
{
"name": "5.4.3",
"images": {
"hda_disk_image": "FMG_VM64_KVM-v5-build1187-FORTINET.out.kvm.qcow2",
"hdb_disk_image": "empty30G.qcow2"
}
},
{
"name": "5.4.2",
"images": {
"hda_disk_image": "FMG_VM64_KVM-v5-build1151-FORTINET.out.kvm.qcow2",
"hdb_disk_image": "empty30G.qcow2"
}
},
{
"name": "5.4.1",
"images": {
"hda_disk_image": "FMG_VM64_KVM-v5-build1082-FORTINET.out.kvm.qcow2",
"hdb_disk_image": "empty30G.qcow2"
}
},
{
"name": "5.4.0",
"images": {
"hda_disk_image": "FMG_VM64_KVM-v5-build1019-FORTINET.out.kvm.qcow2",
"hdb_disk_image": "empty30G.qcow2"
}
},
{
"name": "5.2.10",
"images": {
"hda_disk_image": "FMG_VM64_KVM-v5-build0786-FORTINET.out.kvm.qcow2",
"hdb_disk_image": "empty30G.qcow2"
}
},
{
"name": "5.2.9",
"images": {
"hda_disk_image": "FMG_VM64_KVM-v5-build0780-FORTINET.out.kvm.qcow2",
"hdb_disk_image": "empty30G.qcow2"
}
},
{
"name": "5.2.8",
"images": {
"hda_disk_image": "FMG_VM64_KVM-v5-build0777-FORTINET.out.kvm.qcow2",
"hdb_disk_image": "empty30G.qcow2"
}
},
{
"name": "5.2.7",
"images": {
"hda_disk_image": "FMG_VM64_KVM-v5-build0757-FORTINET.out.kvm.qcow2",
"hdb_disk_image": "empty30G.qcow2"
"hdb_disk_image": "empty500G.qcow2"
}
}
]

View File

@ -1,5 +1,6 @@
{
"appliance_id": "bd86792e-9870-4b42-8f16-cb2776f1d5d5",
"tags": ["device_type:gns3_huawei_telnet_ce"],
"name": "HuaWei CE12800",
"category": "multilayer_switch",
"description": "CE12800 series switches are high-performance core switches designed for data center networks and high-end campus networks. The switches provide stable, reliable, secure, and high-performance Layer 2/Layer 3 switching services, to help build an elastic, virtualized, agile, and high-quality network.",

View File

@ -132,9 +132,37 @@
"md5sum": "24cd1006734993dab338e5c75f80b875",
"version": "26.03.0",
"direct_download_url": "https://github.com/kernelkit/infix/releases/download/v26.03.0/infix-x86_64-v26.03.0.qcow2"
},
{
"filename": "infix-x86_64-v26.05.0.qcow2",
"filesize": 330039296,
"md5sum": "60f7c36c389c33ab108acc021f41ccd5",
"version": "26.05.0",
"direct_download_url": "https://github.com/kernelkit/infix/releases/download/v26.05.0/infix-x86_64-v26.05.0.qcow2"
},
{
"filename": "infix-x86_64-v26.06.0.qcow2",
"filesize": 363593728,
"md5sum": "79ca8bd8534bbaa1af0ab874a49d6f4c",
"version": "26.06.0",
"direct_download_url": "https://github.com/kernelkit/infix/releases/download/v26.06.0/infix-x86_64-v26.06.0.qcow2"
}
],
"versions": [
{
"name": "26.06.0",
"images": {
"bios_image": "OVMF-edk2-stable202305.fd",
"hda_disk_image": "infix-x86_64-v26.06.0.qcow2"
}
},
{
"name": "26.05.0",
"images": {
"bios_image": "OVMF-edk2-stable202305.fd",
"hda_disk_image": "infix-x86_64-v26.05.0.qcow2"
}
},
{
"name": "26.03.0",
"images": {

View File

@ -17,7 +17,7 @@
"qemu": {
"adapter_type": "virtio-net-pci",
"adapters": 1,
"ram": 1024,
"ram": 1536,
"hda_disk_interface": "virtio",
"arch": "x86_64",
"console_type": "telnet",
@ -27,60 +27,36 @@
},
"images": [
{
"filename": "OL9U5_x86_64-kvm-b259.qcow2",
"version": "9.5",
"md5sum": "05e9b62c408ab49a02d6833fc683d1ad",
"filesize": 652935168,
"filename": "OL10U1_x86_64-kvm-b270.qcow2",
"version": "10.1",
"md5sum": "c3ed48965110e0cbbd75734d3c23a2ce",
"filesize": 1044119552,
"download_url": "https://yum.oracle.com/oracle-linux-templates.html",
"direct_download_url": "https://yum.oracle.com/templates/OracleLinux/OL9/u5/x86_64/OL9U5_x86_64-kvm-b259.qcow2"
"direct_download_url": "https://yum.oracle.com/templates/OracleLinux/OL10/u1/x86_64/OL10U1_x86_64-kvm-b270.qcow2"
},
{
"filename": "OL9U2_x86_64-kvm-b197.qcow",
"version": "9.2",
"md5sum": "2ff3d0bc8a243ad89c96215f303f1c73",
"filesize": 560791552,
"filename": "OL9U7_x86_64-kvm-b269.qcow2",
"version": "9.7",
"md5sum": "fe0c555620d2001bb4d4d95a53b43738",
"filesize": 792985600,
"download_url": "https://yum.oracle.com/oracle-linux-templates.html",
"direct_download_url": "https://yum.oracle.com/templates/OracleLinux/OL9/u2/x86_64/OL9U2_x86_64-kvm-b197.qcow"
"direct_download_url": "https://yum.oracle.com/templates/OracleLinux/OL9/u7/x86_64/OL9U7_x86_64-kvm-b269.qcow2"
},
{
"filename": "OL9U1_x86_64-kvm-b158.qcow",
"version": "9.1",
"md5sum": "9f32851b96fc38191892197fa11f7435",
"filesize": 539033600,
"download_url": "https://yum.oracle.com/oracle-linux-templates.html",
"direct_download_url": "https://yum.oracle.com/templates/OracleLinux/OL9/u1/x86_64/OL9U1_x86_64-kvm-b158.qcow"
},
{
"filename": "OL8U10_x86_64-kvm-b258.qcow2",
"filename": "OL8U10_x86_64-kvm-b271.qcow2",
"version": "8.10",
"md5sum": "bb07581af5122515b6822595ded5deef",
"filesize": 1251672064,
"md5sum": "f3184e536af1c2aba6b6f499b1a7085a",
"filesize": 1585643520,
"download_url": "https://yum.oracle.com/oracle-linux-templates.html",
"direct_download_url": "https://yum.oracle.com/templates/OracleLinux/OL8/u10/x86_64/OL8U10_x86_64-kvm-b258.qcow2"
"direct_download_url": "https://yum.oracle.com/templates/OracleLinux/OL8/u10/x86_64/OL8U10_x86_64-kvm-b271.qcow2"
},
{
"filename": "OL8U8_x86_64-kvm-b198.qcow",
"version": "8.8",
"md5sum": "717622f373d77349cc102a3a325efbd3",
"filesize": 934215680,
"download_url": "https://yum.oracle.com/oracle-linux-templates.html",
"direct_download_url": "https://yum.oracle.com/templates/OracleLinux/OL8/u8/x86_64/OL8U8_x86_64-kvm-b198.qcow"
},
{
"filename": "OL8U7_x86_64-kvm-b148.qcow",
"version": "8.7",
"md5sum": "962cdde7e810888b9914e937222f687f",
"filesize": 913965056,
"download_url": "https://yum.oracle.com/oracle-linux-templates.html",
"direct_download_url": "https://yum.oracle.com/templates/OracleLinux/OL8/u7/x86_64/OL8U7_x86_64-kvm-b148.qcow"
},
{
"filename": "OL7U9_x86_64-kvm-b145.qcow",
"filename": "OL7U9_x86_64-kvm-b257.qcow2",
"version": "7.9",
"md5sum": "e60d4145a69b34026db6121109ca9131",
"filesize": 725221376,
"md5sum": "d3af7cf0b8f44d298a67ce9ebe184494",
"filesize": 998244352,
"download_url": "https://yum.oracle.com/oracle-linux-templates.html",
"direct_download_url": "https://yum.oracle.com/templates/OracleLinux/OL7/u9/x86_64/OL7U9_x86_64-kvm-b145.qcow"
"direct_download_url": "https://yum.oracle.com/templates/OracleLinux/OL7/u9/x86_64/OL7U9_x86_64-kvm-b257.qcow2"
},
{
"filename": "oracle-cloud-init-data.iso",
@ -93,51 +69,30 @@
],
"versions": [
{
"name": "9.5",
"name": "10.1",
"images": {
"hda_disk_image": "OL9U5_x86_64-kvm-b259.qcow2",
"hda_disk_image": "OL10U1_x86_64-kvm-b270.qcow2",
"cdrom_image": "oracle-cloud-init-data.iso"
}
},
{
"name": "9.2",
"name": "9.7",
"images": {
"hda_disk_image": "OL9U2_x86_64-kvm-b197.qcow",
"cdrom_image": "oracle-cloud-init-data.iso"
}
},
{
"name": "9.1",
"images": {
"hda_disk_image": "OL9U1_x86_64-kvm-b158.qcow",
"hda_disk_image": "OL9U7_x86_64-kvm-b269.qcow2",
"cdrom_image": "oracle-cloud-init-data.iso"
}
},
{
"name": "8.10",
"images": {
"hda_disk_image": "OL8U10_x86_64-kvm-b258.qcow2",
"cdrom_image": "oracle-cloud-init-data.iso"
}
},
{
"name": "8.8",
"images": {
"hda_disk_image": "OL8U8_x86_64-kvm-b198.qcow",
"cdrom_image": "oracle-cloud-init-data.iso"
}
},
{
"name": "8.7",
"images": {
"hda_disk_image": "OL8U7_x86_64-kvm-b148.qcow",
"hda_disk_image": "OL8U10_x86_64-kvm-b271.qcow2",
"cdrom_image": "oracle-cloud-init-data.iso"
}
},
{
"name": "7.9",
"images": {
"hda_disk_image": "OL7U9_x86_64-kvm-b145.qcow",
"hda_disk_image": "OL7U9_x86_64-kvm-b257.qcow2",
"cdrom_image": "oracle-cloud-init-data.iso"
}
}

View File

@ -17,7 +17,7 @@
"qemu": {
"adapter_type": "virtio-net-pci",
"adapters": 1,
"ram": 1024,
"ram": 1536,
"hda_disk_interface": "virtio",
"arch": "x86_64",
"console_type": "telnet",
@ -26,6 +26,22 @@
"options": "-nographic -cpu host"
},
"images": [
{
"filename": "Rocky-10-GenericCloud-Base-10.1-20251116.0.x86_64.qcow2",
"version": "10.1",
"md5sum": "729d7235716bd41a0a15e07d2b44c679",
"filesize": 575406080,
"download_url": "https://download.rockylinux.org/pub/rocky/9/images/x86_64/",
"direct_download_url": "https://download.rockylinux.org/pub/rocky/10/images/x86_64/Rocky-10-GenericCloud-Base-10.1-20251116.0.x86_64.qcow2"
},
{
"filename": "Rocky-9-GenericCloud-Base-9.7-20251123.2.x86_64.qcow2",
"version": "9.7",
"md5sum": "4a0cbfb45b97f09b690044429aaca1c8",
"filesize": 648806400,
"download_url": "https://download.rockylinux.org/pub/rocky/9/images/x86_64/",
"direct_download_url": "https://download.rockylinux.org/pub/rocky/9/images/x86_64/Rocky-9-GenericCloud-Base-9.7-20251123.2.x86_64.qcow2"
},
{
"filename": "Rocky-9-GenericCloud-Base-9.5-20241118.0.x86_64.qcow2",
"version": "9.5",
@ -76,6 +92,20 @@
}
],
"versions": [
{
"name": "10.1",
"images": {
"hda_disk_image": "Rocky-10-GenericCloud-Base-10.1-20251116.0.x86_64.qcow2",
"cdrom_image": "rocky-cloud-init-data.iso"
}
},
{
"name": "9.7",
"images": {
"hda_disk_image": "Rocky-9-GenericCloud-Base-9.7-20251123.2.x86_64.qcow2",
"cdrom_image": "rocky-cloud-init-data.iso"
}
},
{
"name": "9.5",
"images": {

View File

@ -14,7 +14,7 @@
"symbol": "linux_guest.svg",
"docker": {
"adapters": 1,
"image": "gns3/ubuntu:noble",
"image": "gns3/ubuntu:resolute",
"console_type": "telnet"
}
}

View File

@ -0,0 +1,86 @@
{
"appliance_id": "90afc191-6195-4ff2-905b-5470573a882b",
"name": "Westermo WeOS",
"category": "router",
"description": "Westermo Operating System (WeOS) is a leading-edge industrial network operating system that is being continuously developed whilst being subjected to exhaustive quality testing. It powers the most reliable switches and routers on the market, designed for harsh industrial environments. WeOS has extensive support for network and security protocols that have been made simple to configure and maintain. WeOS along with Westermos robust hardware provides the worlds most reliable network solutions.",
"vendor_name": "Westermo",
"vendor_url": "https://www.westermo.com/",
"vendor_logo_url": "https://api-0831ddf0-b06c-4a4e-bcf7-afba78872de1.digizuite.app/DigizuiteCore/CollaborationService/api/share/asset/4C90E538E869D47D2782840A06F726F6D7548D864857388E38218F",
"documentation_url": "https://docs.westermo.com/weos/weos-5/",
"product_name": "WeOS",
"product_url": "https://www.westermo.com/products/software/weos",
"registry_version": 4,
"status": "stable",
"availability": "free",
"maintainer": "Westermo",
"maintainer_email": "info@westermo.com",
"usage": "\nFactory Configuration\n=====================\nAll ports are in VLAN1 untagged. The system will acquire a link-local\nIPv4 address (169.254.0.0/16) and will also request an address using\nDHCP. mDNS is enabled and the device is reachable under these names\n(where XX-XX-XX corresponds to the last three octets in the device's\nMAC address): zero-XX-XX-XX.local, zero.local, and weos.local.\n\nManagement\n==========\nAccessible via console, ssh, and http/https.\n\nUsername: admin\nPassword: admin\n\nUpgrade\n=======\nTo upgrade a device, simply download the new version and replace the\nHDA (Primary Master) image under the HDD tab in the configuration pane\nof the device.\n",
"symbol": ":/symbols/classic/router.svg",
"port_name_format": "eth{0}",
"linked_clone": true,
"qemu": {
"adapter_type": "virtio-net-pci",
"adapters": 8,
"ram": 512,
"cpus": 1,
"hda_disk_interface": "virtio",
"hdb_disk_interface": "virtio",
"arch": "x86_64",
"console_type": "telnet",
"kvm": "allow",
"options": "-nographic --machine pc-i440fx-4.2"
},
"images": [
{
"filename": "Config-zero-1.0.0.disk",
"version": "1.0.0",
"md5sum": "afe7b74683971fec48802997f6470ca5",
"filesize": 17825792,
"direct_download_url": "https://dropzone.westermo.com/file.aspx?id=e6a7676d-85a7-4374-8961-c68aacb74921"
},
{
"filename": "WeOS-zero-5.29.0.disk",
"version": "5.29.0",
"filesize": 81788928,
"md5sum": "f464de7b2b424f4a8ad7c650108fee3d",
"direct_download_url": "https://dropzone.westermo.com/file.aspx?id=52655074-0fab-4119-ba01-b68200a733ab"
},
{
"filename": "WeOS-zero-5.28.0.disk",
"version": "5.28.0",
"md5sum": "7b4e29761091c12ec2cd482f7c1a0bea",
"filesize": 75497472,
"direct_download_url": "https://dropzone.westermo.com/file.aspx?id=4926adbe-0cd6-4851-b70f-e9d60b3b3f06"
},
{
"filename": "WeOS-zero-5.27.0.disk",
"version": "5.27.0",
"md5sum": "8bd4944b60eaeca391bb5d1dc0defb8b",
"filesize": 78643200,
"direct_download_url": "https://dropzone.westermo.com/file.aspx?id=1a7bf389-a4b1-4dae-b121-6fb3d6781542"
}
],
"versions": [
{
"name": "5.29.0",
"images": {
"hda_disk_image": "WeOS-zero-5.29.0.disk",
"hdb_disk_image": "Config-zero-1.0.0.disk"
}
},
{
"name": "5.28.0",
"images": {
"hda_disk_image": "WeOS-zero-5.28.0.disk",
"hdb_disk_image": "Config-zero-1.0.0.disk"
}
},
{
"name": "5.27.0",
"images": {
"hda_disk_image": "WeOS-zero-5.27.0.disk",
"hdb_disk_image": "Config-zero-1.0.0.disk"
}
}
]
}

View File

@ -311,7 +311,7 @@ class Cloud(BaseNode):
if not port_info["interface"] in network_interfaces:
raise NodeError("Interface '{}' could not be found on this system, please update '{}'".format(port_info["interface"], self.name))
if sys.platform.startswith("linux"):
if sys.platform.startswith("linux") or sys.platform.startswith("openbsd"):
await self._add_linux_ethernet(port_info, bridge_name)
elif sys.platform.startswith("darwin"):
await self._add_osx_ethernet(port_info, bridge_name)

View File

@ -33,7 +33,7 @@ from gns3server.config import Config
from gns3server.utils.asyncio import locking
from gns3server.compute.base_manager import BaseManager
from gns3server.compute.docker.docker_vm import DockerVM
from gns3server.compute.docker.docker_error import DockerError, DockerHttp304Error, DockerHttp404Error
from gns3server.compute.docker.docker_error import DockerError, DockerHttp304Error, DockerHttp404Error, DockerHttp409Error
log = logging.getLogger(__name__)
@ -236,6 +236,8 @@ class Docker(BaseManager):
raise DockerHttp304Error("Docker has returned an error: {} {}".format(response.status, body))
elif response.status == 404:
raise DockerHttp404Error("Docker has returned an error: {} {}".format(response.status, body))
elif response.status == 409:
raise DockerHttp409Error("Docker has returned an error: {} {}".format(response.status, body))
else:
raise DockerError("Docker has returned an error: {} {}".format(response.status, body))
return response

View File

@ -32,3 +32,7 @@ class DockerHttp304Error(DockerError):
class DockerHttp404Error(DockerError):
pass
class DockerHttp409Error(DockerError):
pass

View File

@ -43,7 +43,8 @@ from ..nios.nio_udp import NIOUDP
from .docker_error import (
DockerError,
DockerHttp304Error,
DockerHttp404Error
DockerHttp404Error,
DockerHttp409Error
)
import logging
@ -459,10 +460,31 @@ class DockerVM(BaseNode):
if extra_hosts:
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={}".format(self.docker_name), data=params)
self._cid = result['Id']
log.info("Docker container '{name}' [{id}] created".format(name=self._name, id=self._id))
try:
# Supported names in Docker: [a-zA-Z0-9][a-zA-Z0-9_.-]
result = await self.manager.query("POST", f"containers/create?name={self.docker_name}", data=params)
except DockerHttp409Error:
# Container name already exists. This can happen when the server crashes
# and leaves containers behind. Try to remove the conflicting container.
log.warning(f"Container name '{self.docker_name}' is already in use, attempting to clean up the stale container...")
try:
# Try to get and remove the conflicting container
try:
container_info = await self.manager.query("GET", f"containers/{self.docker_name}/json")
container_id = container_info["Id"]
# Force remove the container
await self.manager.query("DELETE", f"containers/{container_id}", params={"force": 1, "v": 1})
log.info(f"Removed stale container '{self.docker_name}' ({container_id})")
except DockerHttp404Error:
# Container doesn't exist anymore, race condition - just continue
pass
# Retry creating the container
result = await self.manager.query("POST", f"containers/create?name={self.docker_name}", data=params)
except DockerError as e:
log.error(f"Failed to clean up conflicting container '{self.docker_name}': {e}")
raise
self._cid = result["Id"]
log.info(f"Docker container '{self._name}' [{self._id}] created")
return True
def _format_env(self, variables, env):
@ -847,7 +869,7 @@ class DockerVM(BaseNode):
await self._fix_permissions()
state = await self._get_container_state()
if state != "stopped" or state != "exited":
if state != "stopped" and state != "exited":
# t=5 number of seconds to wait before killing the container
try:
await self.manager.query("POST", "containers/{}/stop".format(self._cid), params={"t": 5})

View File

@ -578,7 +578,7 @@ class IOUVM(BaseNode):
callback = functools.partial(self._termination_callback, "IOU")
gns3server.utils.asyncio.monitor_process(self._iou_process, callback)
except FileNotFoundError as e:
raise IOUError("Could not start IOU: {}: 32-bit binary support is probably not installed".format(e))
raise IOUError("Could not start IOU: {}: 32-bit binary support is probably not installed, it is recommended to use a 64-bit image instead".format(e))
except (OSError, subprocess.SubprocessError) as e:
iou_stdout = self.read_iou_stdout()
log.error("Could not start IOU {}: {}\n{}".format(self._path, e, iou_stdout))

View File

@ -35,6 +35,7 @@ import time
import json
import psutil
from pathlib import Path
from gns3server.utils import parse_version, shlex_quote
from gns3server.utils.asyncio import subprocess_check_output, cancellable_wait_run_in_executor
from .qemu_error import QemuError
@ -2057,15 +2058,23 @@ class QemuVM(BaseNode):
options.extend(["-bios", self._bios_image.replace(",", ",,")])
elif self._uefi:
ovmf_firmware_dir = self._manager.config.get_section_config("Qemu").get("ovmf_firmware_dir", "/usr/share/OVMF")
system_ovmf_firmware_dir = Path(ovmf_firmware_dir)
log.info("Using OVMF firmware directory: {}".format(system_ovmf_firmware_dir))
old_ovmf_vars_path = os.path.join(self.working_dir, "OVMF_VARS.fd")
if os.path.exists(old_ovmf_vars_path):
# the node has its own UEFI variables store already, we must also use the old UEFI firmware
ovmf_firmware_path = self.manager.get_abs_image_path("OVMF_CODE.fd")
else:
system_ovmf_firmware_path = "/usr/share/OVMF/OVMF_CODE_4M.fd"
if os.path.exists(system_ovmf_firmware_path):
ovmf_firmware_path = system_ovmf_firmware_path
# Use a manual case-insensitive search instead
try:
system_ovmf_firmware_path = next((f for f in system_ovmf_firmware_dir.glob("*.fd")
if f.name.lower() == "ovmf_code_4m.fd"), None)
except (FileNotFoundError, StopIteration):
system_ovmf_firmware_path = None
if system_ovmf_firmware_path:
ovmf_firmware_path = str(system_ovmf_firmware_path)
else:
# otherwise, get the UEFI firmware from the images directory
ovmf_firmware_path = self.manager.get_abs_image_path("OVMF_CODE_4M.fd")
@ -2074,9 +2083,13 @@ class QemuVM(BaseNode):
options.extend(["-drive", "if=pflash,format=raw,readonly,file={}".format(ovmf_firmware_path)])
# try to use the UEFI variables store from the system first
system_ovmf_vars_path = "/usr/share/OVMF/OVMF_VARS_4M.fd"
if os.path.exists(system_ovmf_vars_path):
ovmf_vars_path = system_ovmf_vars_path
try:
system_ovmf_vars_path = next((f for f in system_ovmf_firmware_dir.glob("*.fd")
if f.name.lower() == "ovmf_vars_4m.fd"), None)
except (FileNotFoundError, StopIteration):
system_ovmf_vars_path = None
if system_ovmf_vars_path:
ovmf_vars_path = str(system_ovmf_vars_path)
else:
# otherwise, get the UEFI variables store from the images directory
ovmf_vars_path = self.manager.get_abs_image_path("OVMF_VARS_4M.fd")
@ -2092,6 +2105,10 @@ class QemuVM(BaseNode):
except OSError as e:
raise QemuError("Cannot copy OVMF_VARS_4M.fd file to the node working directory: {}".format(e))
options.extend(["-drive", "if=pflash,format=raw,file={}".format(ovmf_vars_node_path)])
# edk2 firmware requires a Random Number Generator (RNG) device in order to turn network adapters on
options.extend(["-object", "rng-random,filename=/dev/urandom,id=rng0"])
options.extend(["-device", "virtio-rng-pci,rng=rng0"])
return options
def _linux_boot_options(self):
@ -2463,7 +2480,6 @@ class QemuVM(BaseNode):
elif sys.platform.startswith("win") or sys.platform.startswith("darwin"):
command.extend(["-enable-hax"])
command.extend(["-boot", "order={}".format(self._boot_priority)])
command.extend(self._bios_option())
command.extend(self._cdrom_option())
command.extend((await self._disk_options()))
command.extend(self._linux_boot_options())
@ -2481,6 +2497,8 @@ class QemuVM(BaseNode):
raise QemuError("Console type {} is unknown".format(self._console_type))
command.extend(self._monitor_options())
command.extend((await self._network_options()))
# bios options must be last to have predictable NIC numbering, see https://github.com/GNS3/gns3-server/issues/2838
command.extend(self._bios_option())
if self.on_close != "save_vm_state":
await self._clear_save_vm_stated()
else:

View File

@ -562,7 +562,15 @@ class Controller:
for project in self._projects.values():
if project.auto_open:
await project.open()
try:
await project.open()
except aiohttp.web.HTTPClientError as e:
details = e.text or e.reason or str(e)
log.warning(
"Failed to auto-open project '%s': %s",
project.name,
details,
)
def get_free_project_name(self, base_name):
"""

View File

@ -166,24 +166,52 @@ async def import_project(
project = await controller.load_project(dot_gns3_path, load=False)
return project
def _create_symbolic_links(zip_file, path):
"""
Manually create symbolic links (if any) because ZipFile does not support it.
Refuse any target that escapes `path`.
:param zip_file: ZipFile instance
:param path: project location
"""
path_root = os.path.realpath(path) + os.sep
for zip_info in zip_file.infolist():
if stat.S_ISLNK(zip_info.external_attr >> 16):
symlink_target = zip_file.read(zip_info.filename).decode()
symlink_path = os.path.join(path, zip_info.filename)
try:
# remove the regular file and replace it by a symbolic link
os.remove(symlink_path)
os.symlink(symlink_target, symlink_path)
except OSError as e:
raise aiohttp.web.HTTPConflict(text=f"Cannot create symbolic link: {e}")
if not stat.S_ISLNK(zip_info.external_attr >> 16):
continue
symlink_target = zip_file.read(zip_info.filename).decode()
symlink_path = os.path.join(path, zip_info.filename)
# 1. Reject absolute targets outright.
if os.path.isabs(symlink_target):
raise aiohttp.web.HTTPConflict(
text=f"Symlink {zip_info.filename!r} has absolute target {symlink_target!r}, refusing"
)
# 2. Reject paths where the entry name itself escapes (defence in depth;
# extractall normally would already have caught this).
member_abs = os.path.realpath(symlink_path)
if not (member_abs + os.sep).startswith(path_root) and member_abs + os.sep != path_root:
raise aiohttp.web.HTTPConflict(
text=f"Symlink entry {zip_info.filename!r} escapes project dir, refusing"
)
# 3. Resolve the symlink target relative to the entry's own parent
# directory and verify the resolved real path stays inside `path`.
link_dir = os.path.realpath(os.path.dirname(symlink_path))
resolved_target = os.path.realpath(os.path.join(link_dir, symlink_target))
if not (resolved_target + os.sep).startswith(path_root) and resolved_target + os.sep != path_root:
raise aiohttp.web.HTTPConflict(
text=f"Symlink {zip_info.filename!r} -> {symlink_target!r} escapes project dir, refusing"
)
try:
os.remove(symlink_path)
os.symlink(symlink_target, symlink_path)
except OSError as e:
raise aiohttp.web.HTTPConflict(text=f"Cannot create symbolic link: {e}")
def regenerate_topology_ids(topology, new_project_path, reset_mac_addresses=False):
"""
@ -271,7 +299,7 @@ async def _upload_file(compute, project_id, file_path, path):
async def _import_images(controller, images_path):
"""
Copy images to the images directory or delete them if they already exists.
Copy images to the images directory or delete them if they already exist.
"""
image_dir = controller.images_path()
@ -279,12 +307,19 @@ async def _import_images(controller, images_path):
for (dirpath, dirnames, filenames) in os.walk(root, followlinks=False):
for filename in filenames:
path = os.path.join(dirpath, filename)
if os.path.islink(path):
continue
dst = os.path.join(image_dir, os.path.relpath(path, root))
os.makedirs(os.path.dirname(dst), exist_ok=True)
await wait_run_in_executor(shutil.move, path, dst)
if not os.path.exists(dst):
await wait_run_in_executor(shutil.move, path, dst)
try:
with open(dst, "rb") as f:
# read the first 7 bytes of the file.
elf_header_start = f.read(7)
# IOU images must start with the ELF magic number, be 32-bit or 64-bit, little endian and have an ELF version of 1
if elf_header_start == b'\x7fELF\x01\x01\x01' or elf_header_start == b'\x7fELF\x02\x01\x01':
os.chmod(dst, stat.S_IWRITE | stat.S_IREAD | stat.S_IEXEC)
except OSError as e:
continue
async def update_snapshots(snapshots_dir, project_path, project_name, project_id, reset_mac_addresses=True):
"""
@ -316,7 +351,7 @@ async def update_snapshots(snapshots_dir, project_path, project_name, project_id
topology = json.load(f)
topology["name"] = project_name
topology["project_id"] = project_id
regenerate_topology_ids(topology, project_path, reset_mac_addresses)
regenerate_topology_ids(topology, tmpdir, reset_mac_addresses)
with open(topology_file_path, "w+", encoding="utf-8") as f:
json.dump(topology, f, indent=4, sort_keys=True)
except OSError as e:

View File

@ -779,8 +779,9 @@ class Project:
try:
with open(self._snapshot_conf_path, encoding="utf-8") as f:
self._snapshot_conf = json.load(f)
except (OSError, UnicodeDecodeError, ValueError) as e:
raise aiohttp.web.HTTPConflict(text="Could not read snapshot config {}: {}".format(self._snapshot_conf_path, str(e)))
except (OSError, UnicodeDecodeError, ValueError):
log.exception("Could not read snapshot config %s", self._snapshot_conf_path)
raise aiohttp.web.HTTPConflict(text="Could not read snapshot configuration file")
# Load all legacy snapshots (.gns3project files) to create an initial snapshot config if it doesn't exist
if os.path.exists(snapshot_dir) and not self._snapshot_conf:
@ -904,12 +905,23 @@ class Project:
async def delete(self):
# Check compute connectivity before open() to avoid 120s timeout
# when remote computes are unreachable
disconnected = self._get_disconnected_computes()
if disconnected:
compute_names = ", ".join([f"'{c.name}'" for c in disconnected])
raise aiohttp.web.HTTPForbidden(
f"Cannot delete project '{self.name}': {len(disconnected)} compute(s) are disconnected: {compute_names}. "
f"Please fix the connection or delete the project manually on those computes."
)
if self._status != "opened":
try:
await self.open()
await self.open(auto_start=False)
except aiohttp.web.HTTPConflict as e:
# ignore missing images or other conflicts when deleting a project
log.warning("Conflict while deleting project: {}".format(e.text))
log.warning(f"Conflict while deleting project: {e}")
await self.delete_on_computes()
await self.close()
try:
@ -921,6 +933,42 @@ class Project:
raise aiohttp.web.HTTPConflict(text="Cannot delete project directory {}: {}".format(self.path, str(e)))
self.emit_controller_notification("project.deleted", self.__json__())
def _get_disconnected_computes(self):
"""
Check compute connectivity by reading the topology file directly,
without opening the project (which would try to connect to computes).
Returns a list of disconnected Compute objects.
"""
if self._status == "opened":
# Project is already open, use the already-loaded _computes list
compute_ids = self._computes
else:
# Read compute IDs from topology file without connecting
path = self._topology_file()
if not os.path.exists(path):
return []
try:
project_data = load_topology(path)
except (ValueError, OSError) as e:
log.warning(f"Could not read topology file for project '{self._name}': {e}")
return []
topology = project_data.get("topology", {})
compute_ids = set()
for node in topology.get("nodes", []):
compute_id = node.get("compute_id")
if compute_id:
compute_ids.add(compute_id)
disconnected = []
for compute_id in compute_ids:
try:
compute = self._controller.get_compute(compute_id)
if not compute.connected:
disconnected.append(compute)
except aiohttp.web.HTTPNotFound:
log.warning(f"Compute '{compute_id}' not found in controller")
return disconnected
async def delete_on_computes(self):
"""
Delete the project on computes but not on controller
@ -950,9 +998,12 @@ class Project:
return os.path.join(self.path, self._filename)
@locking
async def open(self):
async def open(self, auto_start=True):
"""
Load topology elements
:param auto_start: whether the nodes may be started when the project
has auto start enabled
"""
if self._closing:
@ -1066,7 +1117,7 @@ class Project:
self._loading = False
self.emit_controller_notification("project.opened", self.__json__())
# Should we start the nodes when project is open
if self._auto_start:
if self._auto_start and auto_start:
# Start all in the background without waiting for completion
# we ignore errors because we want to let the user open
# their project and fix it

View File

@ -139,7 +139,7 @@ class Snapshot:
# delete the current project files
project_files_path = os.path.join(self._project.path, "project-files")
if os.path.exists(project_files_path):
await wait_run_in_executor(shutil.rmtree, project_files_path)
await wait_run_in_executor(shutil.rmtree, project_files_path, ignore_errors=True)
with open(self._path, "rb") as f:
project = await import_project(
self._project.controller,

View File

@ -57,7 +57,7 @@ class CrashReport:
Report crash to a third party service
"""
DSN = "https://74851857a3f05aecd909415e2a4cbd60@o19455.ingest.us.sentry.io/38482"
DSN = "https://89ba0bf8d773c2c95518bfb696d6d86f@o19455.ingest.us.sentry.io/38482"
_instance = None
def __init__(self):

View File

@ -22,6 +22,7 @@ from gns3server.schemas.template import TEMPLATE_USAGE_SCHEMA
import hashlib
import json
import os
from gns3server.schemas.template import (
TEMPLATE_OBJECT_SCHEMA,
@ -173,3 +174,131 @@ class TemplateHandler:
compute_id=request.json.get("compute_id"))
response.set_status(201)
response.json(node)
@Route.get(
r"/templates/{template_id}/base-config/{filename}",
description="Get base configuration file content",
)
def get_base_config(request, response):
controller = Controller.instance()
template_id = request.match_info["template_id"]
template = controller.template_manager.get_template(template_id)
filename = os.path.basename(request.match_info["filename"])
path = os.path.join(controller.configs_path(), filename)
try:
if not os.path.exists(path):
response.set_status(404)
response.json({"message": "File not found"})
return
with open(path, encoding="utf-8", errors="ignore") as f:
content = f.read()
except Exception as e:
response.set_status(500)
response.json({"message": str(e)})
return
response.set_status(200)
response.json({
"template_id": template.id,
"filename": filename,
"content": content
})
@Route.put(
r"/templates/{template_id}/base-config/{filename}",
description="Update base configuration file content",
parameters={
"template_id": "Template UUID",
"filename": "Base config filename"
},
status_codes={
200: "File updated",
400: "Invalid request",
404: "Template or file not found"
},
)
def update_base_config(request, response):
controller = Controller.instance()
template_id = request.match_info["template_id"]
filename = os.path.basename(request.match_info["filename"])
try:
template = controller.template_manager.get_template(template_id)
except Exception:
response.set_status(404)
response.json({"message": "Template not found"})
return
path = os.path.join(controller.configs_path(), filename)
body = request.json
if not body or "content" not in body:
response.set_status(400)
response.json({"message": "Missing 'content' field"})
return
content = body["content"]
try:
os.makedirs(controller.configs_path(), exist_ok=True)
with open(path, "w", encoding="utf-8") as f:
f.write(content)
except OSError as e:
response.set_status(500)
response.json({"message": str(e)})
return
response.set_status(200)
response.json({
"template_id": template.id,
"filename": filename,
"content": content
})
@Route.get(
r"/templates/base-configs",
description="List all available base configuration files",
status_codes={
200: "List of base configuration files returned"
}
)
def list_base_configs(request, response):
controller = Controller.instance()
configs_path = controller.configs_path()
try:
files = []
if os.path.exists(configs_path):
for filename in os.listdir(configs_path):
path = os.path.join(configs_path, filename)
if os.path.isfile(path):
files.append({"filename": filename})
except OSError as e:
response.set_status(500)
response.json({
"message": str(e)
})
return
files = sorted(files, key=lambda x: x["filename"])
response.set_status(200)
response.json(files)

View File

@ -215,18 +215,32 @@ class AsyncioTelnetServer:
await self._write_intro(network_writer, echo=self._echo, binary=self._binary, naws=self._naws)
await connection.connected()
await self._process(network_reader, network_writer, connection)
except (ConnectionError, OSError):
except (ConnectionError, OSError, asyncio.CancelledError):
pass
except Exception:
# Catch any unexpected exception so the cleanup below still runs.
# Without the try/finally, an uncaught exception here would leave
# _reader_process pinned to a dead reader, and subsequent client
# connections would see _get_reader() return None and never
# receive node output -- the "silent proxy" hang (issue #2344).
log.exception("Unexpected error in telnet proxy; cleaning up client connection...")
finally:
async with self._lock:
network_writer.close()
# await network_writer.wait_closed() # this doesn't work in Python 3.6
if self._reader_process == network_reader:
self._reader_process = None
# Cancel current read from this reader
if self._current_read is not None:
self._current_read.cancel()
await connection.disconnected()
del self._connections[network_writer]
try:
network_writer.close()
finally:
if self._reader_process == network_reader:
self._reader_process = None
# Cancel current read from this reader
if self._current_read is not None:
self._current_read.cancel()
self._current_read = None
try:
await connection.disconnected()
finally:
# Use pop() to avoid KeyError if connection was already removed
# elsewhere (e.g. by the broadcast loop's timeout handler)
self._connections.pop(network_writer, None)
async def close(self):
for writer, connection in self._connections.items():
@ -318,7 +332,7 @@ class AsyncioTelnetServer:
except (OSError, ConnectionError, asyncio.TimeoutError) as e:
log.debug(f"Error sending data to client {client_info}: {e}, closing and removing from connection table.")
connection.close()
del self._connections[connection_key]
self._connections.pop(connection_key, None)
async def _read(self, cmd, buffer, location, reader):
""" Reads next op from the buffer or reader"""

View File

@ -147,7 +147,7 @@ def is_interface_up(interface):
:returns: boolean
"""
if sys.platform.startswith("linux"):
if sys.platform.startswith("linux") or sys.platform.startswith("openbsd"):
if interface not in psutil.net_if_addrs():
return False
@ -234,7 +234,7 @@ def interfaces():
result["special"] = False
for special_interface in ("lo", "vmnet", "vboxnet", "docker", "lxcbr",
"virbr", "ovs-system", "veth", "fw", "p2p",
"bridge", "vmware", "virtualbox", "gns3"):
"bridge", "vmware", "virtualbox", "gns3","veb"):
if result["name"].lower().startswith(special_interface):
result["special"] = True
for special_interface in ("-nic"):

View File

@ -23,8 +23,8 @@
# or negative for a release candidate or beta (after the base version
# number has been incremented)
__version__ = "2.2.58"
__version_info__ = (2, 2, 58, 0)
__version__ = "2.2.61"
__version_info__ = (2, 2, 61, 0)
if "dev" in __version__:
try:

View File

@ -1,10 +1,11 @@
jsonschema>=4.25.1,<4.26 # version 4.25.1 is the last to support Python 3.9
aiohttp>=3.13.3,<3.14
jsonschema==4.25.1; python_version == '3.9' # version 4.25.1 is the last to support Python 3.9
jsonschema>=4.26.0,<4.27; python_version >= '3.10'
aiohttp>=3.13.5,<3.14
aiohttp-cors>=0.8.1,<0.9
aiofiles>=25.1.0,<26.0
Jinja2>=3.1.6,<3.2
sentry-sdk>=2.50.0,<3 # optional dependency
psutil>=7.2.1
sentry-sdk>=2.59.0,<3 # optional dependency
psutil>=7.2.2
async-timeout>=5.0.1,<5.1 # this library has effectively been upstreamed into Python 3.11+
distro>=1.9.0
py-cpuinfo>=9.0.0,<10.0

View File

@ -64,7 +64,7 @@ setup(
include_package_data=True,
zip_safe=False,
platforms="any",
python_requires=">=3.8",
python_requires=">=3.9",
setup_requires=["setuptools>=45.2"],
classifiers=[
"Development Status :: 5 - Production/Stable",

View File

@ -1503,3 +1503,19 @@ async def test_read_console_output_with_binary_mode(vm):
with asyncio_patch('gns3server.compute.docker.docker_vm.DockerVM.stop'):
await vm._read_console_output(input_stream, output_stream)
output_stream.feed_data.assert_called_once_with(b"test")
async def test_stop_exited_container_no_stop_query(vm):
vm._ubridge_hypervisor = None
vm._fix_permissions = MagicMock()
with asyncio_patch("gns3server.compute.docker.DockerVM._get_container_state", return_value="exited"):
with asyncio_patch("gns3server.compute.docker.Docker.query") as mock_query:
vm._permissions_fixed = False
await vm.stop()
assert not any(
call.args[:2] == ("POST", "containers/e90e34656842/stop")
for call in mock_query.mock_calls
)
assert vm.status == "stopped"

View File

@ -19,6 +19,8 @@ import os
import uuid
import json
import zipfile
import pytest
import aiohttp
from tests.utils import asyncio_patch, AsyncioMagicMock
from unittest.mock import patch, MagicMock
@ -117,36 +119,48 @@ async def write_file(path, z):
f.write(chunk)
async def test_import_project_containing_symlink(tmpdir, controller):
@pytest.fixture
def export_project_with_symlink(tmpdir, controller):
async def _export(symlink_target):
project = Project(controller=controller, name="test")
project.dump = MagicMock()
project = Project(controller=controller, name="test")
project.dump = MagicMock()
path = project.path
topology = {
"project_id": str(uuid.uuid4()),
"name": "test",
"auto_open": True,
"auto_start": True,
"topology": {
},
"version": "2.0.0"
}
with open(os.path.join(project.path, "project.gns3"), 'w+') as f:
json.dump(topology, f)
os.makedirs(os.path.join(project.path, "vm1", "dynamips"))
symlink_path = os.path.join(project.path, "vm1", "dynamips", "symlink")
os.symlink(symlink_target, symlink_path)
zip_path = str(tmpdir / "project.zip")
with aiozipstream.ZipFile() as z:
with patch("gns3server.compute.Dynamips.get_images_directory", return_value=str(tmpdir / "IOS"),):
await export_project(z, project, str(tmpdir), include_images=False)
await write_file(zip_path, z)
return zip_path
return _export
async def test_import_project_containing_symlink(controller, export_project_with_symlink):
"""
Test importing a project containing a valid symlink (target inside the project directory).
"""
project_id = str(uuid.uuid4())
topology = {
"project_id": str(uuid.uuid4()),
"name": "test",
"auto_open": True,
"auto_start": True,
"topology": {
},
"version": "2.0.0"
}
with open(os.path.join(path, "project.gns3"), 'w+') as f:
json.dump(topology, f)
os.makedirs(os.path.join(path, "vm1", "dynamips"))
symlink_path = os.path.join(project.path, "vm1", "dynamips", "symlink")
symlink_target = "/tmp/anywhere"
os.symlink(symlink_target, symlink_path)
zip_path = str(tmpdir / "project.zip")
with aiozipstream.ZipFile() as z:
with patch("gns3server.compute.Dynamips.get_images_directory", return_value=str(tmpdir / "IOS"),):
await export_project(z, project, str(tmpdir), include_images=False)
await write_file(zip_path, z)
symlink_target = "../symlink_target"
zip_path = await export_project_with_symlink(symlink_target)
with open(zip_path, "rb") as f:
project = await import_project(controller, project_id, f)
@ -158,6 +172,36 @@ async def test_import_project_containing_symlink(tmpdir, controller):
assert os.readlink(symlink_path) == symlink_target
async def test_import_project_containing_absolute_symlink(controller, export_project_with_symlink):
"""
Test importing a project containing an absolute symlink.
This should fail because absolute symlinks are not allowed for security reasons.
"""
project_id = str(uuid.uuid4())
symlink_target = "/tmp/anywhere"
zip_path = await export_project_with_symlink(symlink_target)
with pytest.raises(aiohttp.web.HTTPConflict):
with open(zip_path, "rb") as f:
await import_project(controller, project_id, f)
async def test_import_project_containing_escaping_symlink(controller, export_project_with_symlink):
"""
Test importing a project containing a symlink that escapes the project directory.
This should fail because symlinks that escape the project directory are not allowed for security reasons.
"""
project_id = str(uuid.uuid4())
symlink_target = "../../../../symlink_target"
zip_path = await export_project_with_symlink(symlink_target)
with pytest.raises(aiohttp.web.HTTPConflict):
with open(zip_path, "rb") as f:
await import_project(controller, project_id, f)
async def test_import_upgrade(tmpdir, controller):
"""
Topology made for previous GNS3 version are upgraded during the process

View File

@ -655,6 +655,19 @@ async def test_delete(project):
assert not os.path.exists(project.path)
async def test_delete_does_not_start_nodes(project):
"""
Deleting a project must not start its nodes, even when auto_start is enabled.
"""
project.auto_start = True
project.dump()
await project.close()
project.start_all = AsyncioMagicMock()
await project.delete()
assert not project.start_all.called
async def test_dump(projects_dir):
directory = projects_dir

View File

@ -14,8 +14,7 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
import uuid
from tests.utils import asyncio_patch
@ -955,3 +954,81 @@ async def test_create_node_from_template(controller_api, controller, project):
mock.assert_called_with(id, x=42, y=12, name=None, compute_id=None)
assert response.route == "/projects/{project_id}/templates/{template_id}"
assert response.status == 201
async def test_get_base_config(controller_api, controller):
template_id = str(uuid.uuid4())
controller.template_manager._templates[template_id] = Template(template_id, {
"template_type": "vpcs",
"category": 0,
"name": "test",
"symbol": "guest.svg",
"default_name_format": "{name}-{0}",
"compute_id": "local"
})
config_path = os.path.join(controller.configs_path(), "test_config.txt")
with open(config_path, "w") as f:
f.write("hello config")
response = await controller_api.get(
f"/templates/{template_id}/base-config/test_config.txt"
)
assert response.status == 200
assert response.json["filename"] == "test_config.txt"
assert response.json["content"] == "hello config"
async def test_update_base_config(controller_api, controller):
template_id = str(uuid.uuid4())
controller.template_manager._templates[template_id] = Template(template_id, {
"template_type": "vpcs",
"category": 0,
"name": "test",
"symbol": "guest.svg",
"default_name_format": "{name}-{0}",
"compute_id": "local"
})
config_path = os.path.join(controller.configs_path(), "update_test.txt")
with open(config_path, "w") as f:
f.write("old content")
response = await controller_api.put(
f"/templates/{template_id}/base-config/update_test.txt",
{
"content": "new updated content"
}
)
assert response.status == 200
assert response.json["content"] == "new updated content"
with open(config_path) as f:
assert f.read() == "new updated content"
async def test_list_base_configs(controller_api, controller):
config1 = os.path.join(controller.configs_path(), "config1.txt")
config2 = os.path.join(controller.configs_path(), "config2.txt")
with open(config1, "w") as f:
f.write("test1")
with open(config2, "w") as f:
f.write("test2")
response = await controller_api.get("/templates/base-configs")
assert response.status == 200
filenames = [item["filename"] for item in response.json]
assert "config1.txt" in filenames
assert "config2.txt" in filenames