Compare commits

...

183 Commits

Author SHA1 Message Date
github-actions 447bf637bb Publish API documentation
1 month ago
github-actions 27fdebcf28 Merge remote-tracking branch 'origin/3.0' into gh-pages
1 month ago
grossmj 84f7337cb1
Release v3.0.0b2
1 month ago
grossmj e11621467b
Remove old files
1 month ago
grossmj 123123d767
Bundle web-ui v3.0.0b2
1 month ago
grossmj b3bdeb611e
Merge branch '2.2' into 3.0
1 month ago
grossmj 7a229e33f9
Sync appliances
1 month ago
grossmj 5b1ba5a57c
Fix cannot stop Docker VM while console connection is still active.
1 month ago
grossmj bc1a8c99d1
Fix cannot stop Docker VM while console connection is still active.
1 month ago
Jeremy Grossmann ae64f15386
Merge pull request #2365 from GNS3/feature/custom-qemu-path
1 month ago
grossmj 123b5d0800
Support for custom Qemu path in templates and nodes
1 month ago
Jeremy Grossmann e1d3ee12b9
Merge pull request #2364 from GNS3/bugfix/3572
1 month ago
grossmj 998898a471
Fix CPU fractional values for Docker VMs.
1 month ago
grossmj 9b66d939d2
Support for cooperative signal handling of uvicorn v0.29.0
1 month ago
grossmj fd60bda2c2
Downgrade pytest-asyncio to v0.21.1
1 month ago
grossmj c5f4e25756
Upgrade uvicorn, fastapi and sqlalchemy packages
1 month ago
grossmj 16bad6e0b5
Downgrade httpx_ws to v0.4.2
2 months ago
grossmj 8e661df981
Upgrade dev packages
2 months ago
grossmj 31c7fe88f1
Remove websocket.close()
2 months ago
Jeremy Grossmann 03b491f7b1
Merge pull request #2362 from GNS3/feature/use-bcrypt
2 months ago
grossmj fa41d9ba75
Use bcrypt directly instead of passlib
2 months ago
grossmj a80aed5420
Development on 2.2.47.dev1
2 months ago
grossmj e9827653ae
Merge branch '2.2' into 3.0
2 months ago
grossmj 9da8d1ec0f
Upgrade uvicorn, fastapi, python-multipart, sqlalchemy, aiosqlite and email-validator dependencies
2 months ago
grossmj a53fd2e8b1
Upgrade sentry-sdk to version 1.40.6
2 months ago
grossmj e8a7e80456
Release v2.2.46
3 months ago
grossmj ab05784c33
Sync appliances
3 months ago
grossmj 6983e686ca
Bundle web-ui v2.2.46
3 months ago
grossmj 3631202ce5
Merge remote-tracking branch 'origin/2.2' into 2.2
3 months ago
grossmj 8d8a3247c4
Bundle web-ui v2.2.46
3 months ago
Jeremy Grossmann d100a132d6
Merge pull request #2360 from GNS3/bugfix/3555
3 months ago
Jeremy Grossmann 52638a9a8d
Merge branch '2.2' into bugfix/3555
3 months ago
grossmj 9a5af82a28
Save empty directories when exporting a project
3 months ago
Jeremy Grossmann 14a618766d
Merge pull request #2359 from GNS3/bugfix/2357
3 months ago
grossmj 0137688ba7
Test busybox is installed
3 months ago
grossmj 84db3b9996
Fix not all Docker resources are copied to a writable location
3 months ago
grossmj 800920e3df
Merge branch '2.2' into 3.0
3 months ago
grossmj 14917580ac
Downgrade to alembic v1.12.1
3 months ago
grossmj 4c65426e97
Revert "Downgrade platformdirs to v4.0.0"
3 months ago
grossmj fd85e49220
Downgrade platformdirs to v4.0.0
3 months ago
Jeremy Grossmann 0a08031d85
Merge pull request #2355 from GNS3/refactor/busybox
3 months ago
grossmj 1a53c9aabf
Backport from v3: install Docker resources in a writable location at runtime.
3 months ago
grossmj 1f5085608c
Use Docker API v1.24 to get version.
3 months ago
Jeremy Grossmann 16f72b4d3d
Merge pull request #2347 from SpikefishSolutions/DeadTelnetConsoleFix
3 months ago
grossmj 3ced41633f
Upgrade dependencies
3 months ago
Jeremy Grossmann 4fa10be5aa
Merge pull request #2353 from GNS3/feature/drop-python-3.6
3 months ago
grossmj f050fc7e00
Change runtime checks for Python version
3 months ago
grossmj c93aafc9af
Fix aiohttp dependency for Python 3.7
3 months ago
grossmj 93520b4d6c
Do not test with Python 3.6
3 months ago
grossmj 1fb0260ae6
Drop support for Python 3.6
3 months ago
grossmj 91fd543321
Upgrade dependencies
3 months ago
John Fleming 763ef24108 Address the telnet console bug. Add wait_for for drain() call. If we're stuck on drain then the buffer isn't getting emptied. 5 seconds after drain() blocks, exception will be thrown and client will be removed from connection table and will no longer be a problem.
3 months ago
grossmj 17aabd6cda
Merge branch 'master' into 2.2
3 months ago
grossmj d94adf4c8f
Merge remote-tracking branch 'origin/master'
3 months ago
Jeremy Grossmann 4dd9cf2da2
Merge pull request #2340 from spikefishjohn/patch-3
3 months ago
grossmj d6f8069739
Remove files from 2.2 doc
3 months ago
grossmj 0037f31553
Merge branch '2.2' into 3.0
3 months ago
grossmj e2a3d391d8
Merge remote-tracking branch 'origin/2.2' into 2.2
3 months ago
John Fleming 6c5f54fe57
Update telnet_server.py
3 months ago
John Fleming 082fbee1bd
Update telnet_server.py
3 months ago
Xatrekak 6d97feaced
Fixed updating system and GNS3.
3 months ago
Dustin 8d35089661
Update welcome.py
3 months ago
Dustin 6455f62447
Update remote-install.sh
3 months ago
grossmj 2fb3b1ebab
Use Python 3.8 to publish API doc
3 months ago
grossmj 58399a9fa8
Upgrade sentry-sdk, psutil and distro dependencies
3 months ago
grossmj 907b305ecf
Development on 2.2.46.dev1
3 months ago
grossmj 57f92db124
Release v2.2.45
3 months ago
grossmj 0f6f943a83
Bundle web-ui v2.2.45
3 months ago
grossmj 2cb76b2274
Remove old web-ui files
3 months ago
grossmj 2d2db52a8b
Bundle web-ui v2.2.45
3 months ago
grossmj a8d0818e07
Sync appliances
3 months ago
grossmj f7eb2492d9
Fix mouse offset issues with VNC in Qemu. Fixes #2335
3 months ago
grossmj 218522b08c
Fix issues when generating docs
3 months ago
grossmj 470f13f448
Update readthedocs.yml to use Python 3.12
3 months ago
grossmj 317aa669ac
Update readthedocs.yml
3 months ago
grossmj 8d160ad5ed
Update documentation
3 months ago
grossmj e3493870b2
Add project.created, project.opened and project.deleted controller notification stream.
3 months ago
grossmj d466c85385
Do not stop searching for Qemu binaries if one binary cannot be executed. Ref #2306
3 months ago
grossmj 35d4391fc0
Fix Ethernet switch and Ethernet hub port validations. Fixes #2334
3 months ago
grossmj eea0ab69bd
Bundle dev version of the web-ui
3 months ago
grossmj 22ade94118
Update CORS policy
3 months ago
grossmj 08ee40548f
Add custom executable paths on Windows
3 months ago
grossmj 7f05a06766
Upgrade sentry-sdk and aiohttp
3 months ago
grossmj 9fd2f58ef7
Development in 2.2.45.dev3
3 months ago
grossmj b9b802ebab
Release v2.2.44.1
3 months ago
grossmj 1d86e322e9
Do not compute checksums on macOS
3 months ago
grossmj 8eb5f10971
Add multiprocessing.set_start_method()
3 months ago
grossmj cc4783ab98
Bump version to v2.2.45.dev2
3 months ago
grossmj 19792f328d
Have freeze support for macOS as well
3 months ago
grossmj 596d1274a3
Catch exceptions when computing image checksums. Ref https://github.com/GNS3/gns3-server/issues/2228
3 months ago
grossmj 2f765747b8
Add freeze_support() for multiprocessing
3 months ago
grossmj 4fe57b6a15
Development on 2.2.45.dev1
3 months ago
grossmj 1177626a53
Release v2.2.44
3 months ago
grossmj c4cc346864
Sync appliances
3 months ago
grossmj 8915dfffa5
Bundle web-ui v2.2.44
3 months ago
grossmj ff027ebd17
Upgrade sentry-sdk
3 months ago
grossmj 27d5ac537f
Non-blocking checksums computation when server starts. Fixes #2228
3 months ago
grossmj fe246cd413
Fix timeout issue when creating Qemu disk image. Fixes https://github.com/GNS3/gns3-server/issues/2313
3 months ago
grossmj 2bbb560b8e
Support for web socket console over HTTPS
3 months ago
grossmj 1624c7d6ad
Add back script create_cert.sh
3 months ago
grossmj 2a3bb81076
Bundle web-ui
3 months ago
grossmj e0a0dd83d7
Use Python 3.8 in appveyor.yml
3 months ago
grossmj 16ea395618
Upgrade sentry-sdk, psutil and distro dependencies
4 months ago
Jeremy Grossmann 9dd8da4f6e
Merge pull request #2340 from spikefishjohn/patch-3
4 months ago
John Fleming 54abf85523
Update telnet_server.py
4 months ago
Jeremy Grossmann 385fffec56
Merge pull request #2339 from spikefishjohn/patch-2
4 months ago
John Fleming ac86717bc0
Update telnet_server.py
4 months ago
grossmj 7b7af33920
Development on 2.2.46.dev1
4 months ago
Jeremy Grossmann 85fd3ef4a4
Merge pull request #2338 from GNS3/2.2
4 months ago
grossmj fbd8f6ce28
Release v2.2.45
4 months ago
grossmj 195da24910
Bundle web-ui v2.2.45
4 months ago
grossmj b4c7609aeb
Remove old web-ui files
4 months ago
grossmj cd9c4c84b6
Bundle web-ui v2.2.45
4 months ago
grossmj 2566de0f5a
Sync appliances
4 months ago
grossmj 6a069d0af6
Fix mouse offset issues with VNC in Qemu. Fixes #2335
4 months ago
grossmj 2789b3802a
Fix issues when generating docs
4 months ago
grossmj 9e1a1337d4
Update readthedocs.yml to use Python 3.12
4 months ago
Jeremy Grossmann 466eb8128e
Merge pull request #2336 from GNS3/fix/2319
4 months ago
grossmj e41227b5bd
Update readthedocs.yml
4 months ago
grossmj 78a3b8e824
Update documentation
4 months ago
grossmj 4c149e1614
Add project.created, project.opened and project.deleted controller notification stream.
4 months ago
grossmj 473fa98eda
Do not stop searching for Qemu binaries if one binary cannot be executed. Ref #2306
4 months ago
grossmj 13d9afd8bc
Fix Ethernet switch and Ethernet hub port validations. Fixes #2334
4 months ago
grossmj b4edbbbaa7 Bundle dev version of the web-ui
5 months ago
grossmj 02e19800fb Update CORS policy
5 months ago
grossmj bc1016474e Merge branch '2.2' into 3.0
5 months ago
grossmj 24bb0ca419 Add custom executable paths on Windows
5 months ago
grossmj 42f668ffc4 Development on 3.0.0.dev12
6 months ago
grossmj 646f0fd837 Release v3.0.0b1
6 months ago
grossmj 3d383b25f0 Bundle web-ui v3.0.0b1
6 months ago
grossmj 8d28178e56 Upgrade sentry-sdk to v1.37.1
6 months ago
grossmj 0d63bf5128 Upgrade aiohttp to v3.9.1
6 months ago
grossmj 37f720ac73 Fix bug when listing endpoints for opened project
6 months ago
grossmj 9509d3a713 Make images executable after importing a project
6 months ago
grossmj 061ee1491d Disable IOS hostname check for Dynamips ghost instances
6 months ago
grossmj c8245e81be Upgrade sentry-sdk
6 months ago
grossmj ff7e10fa7f Merge branch '2.2' into 3.0
6 months ago
grossmj 6c5f75bf43 Upgrade sentry-sdk and aiohttp
6 months ago
grossmj 24d958d9ec Release v3.0.0a6
6 months ago
grossmj 48dab4af61 Bundle web-ui v3.0.0a6
6 months ago
grossmj 67be4259ff Fix tests
6 months ago
grossmj 4e9bc190a4 Upgrade to aiohttp v3.9.0rc0
6 months ago
grossmj 261dd1d6d5 Upgrade dependencies
6 months ago
Jeremy Grossmann e80e80a080
Merge pull request #2326 from GNS3/fix/2325
6 months ago
grossmj 09ff807055 Install Docker resources in writable location
6 months ago
grossmj 303cbf3642 Fix tests
6 months ago
grossmj e367b3a148 Default compute username is "gns3"
6 months ago
grossmj 7afdcbb3d3 Development in 2.2.45.dev3
6 months ago
Jeremy Grossmann de27a57a00
Merge pull request #2322 from GNS3/release-v2.2.44.1
6 months ago
grossmj c88f76b740 Release v2.2.44.1
6 months ago
grossmj b81cc14cef Do not compute checksums on macOS
6 months ago
grossmj aa133c3b8a Add multiprocessing.set_start_method()
6 months ago
grossmj 7449064ea2 Bump version to v2.2.45.dev2
6 months ago
grossmj db315e3c3c Have freeze support for macOS as well
6 months ago
grossmj c437482e85 Catch exceptions when computing image checksums. Ref https://github.com/GNS3/gns3-server/issues/2228
6 months ago
Jeremy Grossmann 46d9ada6d8
Merge pull request #2321 from Xatrekak/master
6 months ago
grossmj aac6fbfc31 Add freeze_support() for multiprocessing
6 months ago
Xatrekak 76bd5921c5 Fixed updating system and GNS3.
6 months ago
grossmj 8a208dbf04 Merge branch '2.2' into 3.0
6 months ago
Jeremy Grossmann af3a618306
Merge pull request #2316 from GNS3/release-v2.2.44
6 months ago
grossmj 3c4b5db1ca Development on 2.2.45.dev1
6 months ago
grossmj 5c8abdc6fd Release v2.2.44
6 months ago
grossmj f80f41b5b3 Sync appliances
6 months ago
grossmj 503373a6e9 Bundle web-ui v2.2.44
6 months ago
grossmj 8236c85b5f Upgrade sentry-sdk
6 months ago
Jeremy Grossmann 531499bbfb
Merge pull request #2314 from Xatrekak/master
6 months ago
grossmj ae825b0080 Non-blocking checksums computation when server starts. Fixes #2228
6 months ago
Dustin 7ad3afbdef
Update welcome.py
6 months ago
grossmj 191feed669 Fix timeout issue when creating Qemu disk image. Fixes https://github.com/GNS3/gns3-server/issues/2313
6 months ago
grossmj cd785e0d17 Fix broken link to Web UI in 3.0 branch. Fixes #2312
6 months ago
grossmj e1bd4638c8 Fix sample config: VMware section declared twice. Fixes #2311
6 months ago
grossmj 61dede72f9 Fix ws console and packet capture over SSL
6 months ago
grossmj 13630179b2 Merge branch '2.2' into 3.0
6 months ago
grossmj 3b22bcfe96 Support for web socket console over HTTPS
6 months ago
grossmj 0f07b9e019 Add back script create_cert.sh
6 months ago
grossmj 8676a3dccf Bundle web-ui
6 months ago
grossmj b0dbf59ac4 Use Python 3.8 in appveyor.yml
6 months ago
Jeremy Grossmann 0e18762b36
Merge pull request #2308 from Xatrekak/master
6 months ago
Dustin cbc7e59d3f
Update remote-install.sh
6 months ago
Jeremy Grossmann 14fd165f2a
Merge pull request #2307 from GNS3/fix/3521
6 months ago
grossmj 7c49a9160c Allow disabling hardware virtualization check
6 months ago
Jeremy Grossmann 5a6e945db2
Merge pull request #2304 from GNS3/upgrade-python-api-doc
7 months ago
grossmj 86c0b90951 Use Python 3.8 to publish API doc
7 months ago

@ -18,7 +18,7 @@ jobs:
ref: "gh-pages"
- uses: actions/setup-python@v3
with:
python-version: 3.7
python-version: 3.8
- name: Merge changes from 3.0 branch
run: |
git config user.name github-actions

@ -1,5 +1,84 @@
# Change Log
## 3.0.0b2 07/04/2024
* Bundle web-ui v3.0.0b2
* Fix cannot stop Docker VM while console connection is still active.
* Support for custom Qemu path in templates and nodes
* Fix CPU fractional values for Docker VMs.
* Use bcrypt directly instead of passlib
* Update CORS policy
* Do not stop searching for Qemu binaries if one binary cannot be executed. Ref #2306
* Fix Ethernet switch and Ethernet hub port validations. Fixes #2334
* Update CORS policy
## 2.2.46 26/02/2024
* Bundle web-ui v2.2.46
* Save empty directories when exporting a project
* Backport from v3: install Docker resources in a writable location at runtime.
* Use Docker API v1.24 to get version.
* Drop support for Python 3.6
* Address the telnet console bug.
* Update welcome.py
* Update remote-install.sh
* Use Python 3.8 to publish API doc
* Upgrade sentry-sdk, psutil and distro dependencies
## 2.2.45 12/01/2024
* Bundle web-ui v2.2.45
* Fix mouse offset issues with VNC in Qemu. Fixes #2335
* Add project.created, project.opened and project.deleted controller notification stream. Move project.updated and project.closed from project notification to controller notification stream.
* Do not stop searching for Qemu binaries if one binary cannot be executed. Ref #2306
* Fix Ethernet switch and Ethernet hub port validations. Fixes #2334
* Update CORS policy
* Add custom executable paths on Windows
* Upgrade sentry-sdk and aiohttp
## 3.0.0b1 27/11/2023
* Bundle web-ui v3.0.0b1
* Upgrade sentry-sdk to v1.37.1
* Upgrade aiohttp to v3.9.1
* Fix bug when listing endpoints for opened project
* Make images executable after importing a project
* Disable IOS hostname check for Dynamips ghost instances
## 3.0.0a6 15/11/2023
* Bundle web-ui v3.0.0a6
* Upgrade to aiohttp v3.9.0rc0
* Install Docker resources in writable location
* Default compute username is "gns3"
* Non-blocking checksums computation when server starts. Fixes #2228
* Fix timeout issue when creating Qemu disk image. Fixes https://github.com/GNS3/gns3-server/issues/2313
* Fix broken link to Web UI in 3.0 branch. Fixes #2312
* Fix sample config: VMware section declared twice. Fixes #2311
* Fix ws console and packet capture over SSL
* Support for web socket console over HTTPS
* Allow disabling hardware virtualization check
## 2.2.44.1 07/11/2023
* Catch exceptions when computing image checksums. Ref https://github.com/GNS3/gns3-server/issues/2228
* Add freeze_support() for multiprocessing
## 2.2.44 06/11/2023
* Bundle web-ui v2.2.44
* Non-blocking checksums computation when server starts. Fixes #2228
* Fix timeout issue when creating Qemu disk image. Fixes https://github.com/GNS3/gns3-server/issues/2313
* Support for web socket console over HTTPS
* Add back script create_cert.sh
* Allow disabling hardware virtualization check
* Fix L2IOU "failed code signing checks" when IOU base file name is >= 63 characters
* Change "ip cef" to "no ip cef" in IOU default configs. Fixes #2298
* Add Qemu IGB network device
* Add Python 3.12 support.
* Fix issue with importlib.resources.files() and Python 3.9
## 3.0.0a5 27/10/2023
* Bundle web-ui v3.0.0a5

@ -6,7 +6,7 @@
[![Snyk scanning](https://snyk.io/test/github/GNS3/gns3-server/badge.svg)](https://snyk.io/test/github/GNS3/gns3-server)
The GNS3 server manages emulators and other virtualization software such as Dynamips, Qemu/KVM, Docker, VPCS, VirtualBox and VMware Workstation.
Clients like the [GNS3 GUI](https://github.com/GNS3/gns3-gui/) and the [GNS3 Web UI](https://github.com/GNS3/gns3-web-ui>) control the server using a HTTP REST API.
Clients like the [GNS3 GUI](https://github.com/GNS3/gns3-gui/) and the [GNS3 Web UI](https://github.com/GNS3/gns3-web-ui/) control the server using a HTTP REST API.
## Installation

@ -1,6 +1,6 @@
pytest==7.4.2
flake8==6.1.0
pytest-timeout==2.2.0
pytest==8.1.1
flake8==7.0.0
pytest-timeout==2.3.1
pytest-asyncio==0.21.1
requests==2.31.0
httpx==0.24.1 # version 0.24.1 is required by httpx_ws

@ -2,14 +2,14 @@
<!DOCTYPE html>
<html>
<head>
<link type="text/css" rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui.css">
<link type="text/css" rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swagger-ui-dist@5.9.0/swagger-ui.css">
<link rel="shortcut icon" href="https://fastapi.tiangolo.com/img/favicon.png">
<title>GNS3 controller API - Swagger UI</title>
</head>
<body>
<div id="swagger-ui">
</div>
<script src="https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui-bundle.js"></script>
<script src="https://cdn.jsdelivr.net/npm/swagger-ui-dist@5.9.0/swagger-ui-bundle.js"></script>
<!-- `SwaggerUIBundle` is now available on the page -->
<script>
const ui = SwaggerUIBundle({

@ -50,11 +50,7 @@ async def project_ws_notifications(websocket: Union[None, WebSocket] = Depends(w
log.info(f"Client {websocket.client.host}:{websocket.client.port} has disconnected from compute WebSocket")
except WebSocketException as e:
log.warning(f"Error while sending to controller event to WebSocket client: {e}")
finally:
try:
await websocket.close()
except OSError:
pass # ignore OSError: [Errno 107] Transport endpoint is not connected
if __name__ == "__main__":

@ -99,8 +99,12 @@ async def endpoints(
for link in links:
node_id_1 = link["nodes"][0]["node_id"]
node_id_2 = link["nodes"][1]["node_id"]
node_name_1 = project.nodes[node_id_1]["name"]
node_name_2 = project.nodes[node_id_2]["name"]
node_name_1 = node_name_2 = "N/A"
for node in nodes:
if node["node_id"] == node_id_1:
node_name_1 = node["name"]
if node["node_id"] == node_id_2:
node_name_2 = node["name"]
add_to_endpoints(
f"/projects/{project.id}/links/{link['link_id']}",
f'Link from "{node_name_1}" to "{node_name_2}" in project "{project.name}"',

@ -221,11 +221,6 @@ async def controller_ws_notifications(
log.info(f"Client {websocket.client.host}:{websocket.client.port} has disconnected from controller WebSocket")
except WebSocketException as e:
log.warning(f"Error while sending to controller event to WebSocket client: {e}")
finally:
try:
await websocket.close()
except OSError:
pass # ignore OSError: [Errno 107] Transport endpoint is not connected
# @Route.post(

@ -267,11 +267,13 @@ async def stream_pcap(request: Request, link: Link = Depends(dep_link)) -> Strea
async def compute_pcap_stream():
try:
ssl_context = Controller.instance().ssl_context()
async with HTTPClient.request(
request.method,
pcap_streaming_url,
user=compute.user,
password=compute.password,
ssl_context=ssl_context,
timeout=None,
data=body
) as response:

@ -541,7 +541,7 @@ async def ws_console(
pass
ws_console_compute_url = (
f"ws://{compute_host}:{compute.port}/v3/compute/projects/"
f"{websocket.url.scheme}://{compute_host}:{compute.port}/v3/compute/projects/"
f"{node.project.id}/{node.node_type}/nodes/{node.id}/console/ws"
)
@ -575,7 +575,8 @@ async def ws_console(
auth = aiohttp.BasicAuth(user, password.get_secret_value(), "utf-8")
else:
auth = aiohttp.BasicAuth(user, "")
async with HTTPClient.get_client().ws_connect(ws_console_compute_url, auth=auth) as ws:
ssl_context = Controller.instance().ssl_context()
async with HTTPClient.get_client().ws_connect(ws_console_compute_url, auth=auth, ssl_context=ssl_context) as ws:
asyncio.ensure_future(ws_receive(ws))
async for msg in ws:
if msg.type == aiohttp.WSMsgType.TEXT:

@ -305,10 +305,6 @@ async def project_ws_notifications(
except WebSocketException as e:
log.warning(f"Error while sending to project event to WebSocket client: {e}")
finally:
try:
await websocket.close()
except OSError:
pass # ignore OSError: [Errno 107] Transport endpoint is not connected
if project.auto_close:
# To avoid trouble with client connecting disconnecting we sleep few seconds before checking
# if someone else is not connected

@ -0,0 +1,49 @@
{
"appliance_id": "e8001e2b-8ef3-44eb-ace5-79f68f3773e8",
"name": "Asterfusion vAsterNOS",
"category": "multilayer_switch",
"description": "AsterNOS is the core technology of Asterfusion's one-stop turnkey SONiC solution for cloud, enterprise and AI. As an enterprise ready SONiC distribution, AsterNOS features rich functionality enhancement such as MC-LAG, VXLAN, BGP EVPN-Multihoming, RoCEv2(Easy RoCE), and more, making it powerful and easy-to-use in a variety of production scenarios. Currently, AsterNOS is compatible with top commercial switching chips (e.g. Marvell Teralynx, Prestera Falcon/Aldrin/Alleycat, Broadcom Tomahawk/Trident, Intel Tofino and some of NVIDIA's chips.)Through AsterNOS's rich L2/L3 features and enhancements in virtualization and management, cloud architecture built with Asterfusion switching families from 1G-800G (or other standard whitebox switches) can scale to tens of thousands of compute and storage nodes, working smoothly both in underlay nework and overlay cloud fabric with OpenStack integrated, supporting ultra low latency lossless RoCE network in AIGC, distributed storage , or building easily managed access clusters for campus networks.NOTICE: This appliance file is a virtualized version of AsterNOS and is intended to be used only to experience the basic functionality and industry standard CLI (Klish), not for official software testing. For more information about AsterNOS commercial version, please feel free to contact us via Email: bd@cloudswit.ch",
"vendor_name": "Asterfusion",
"vendor_url": "https://cloudswit.ch/",
"vendor_logo_url": "https://raw.githubusercontent.com/GNS3/gns3-registry/master/vendor-logos/asterfusion.png",
"documentation_url": "https://help.cloudswit.ch/portal/en/home",
"product_name": "vAsterNOS",
"product_url": "https://cloudswit.ch/product/sonic-enterprise-distribution",
"registry_version": 4,
"status": "experimental",
"maintainer": "Asterfusion",
"maintainer_email": "bd@cloudswit.ch",
"usage": "The login is admin, passwd asteros",
"symbol": "asterfusion-vAsterNOS.svg",
"first_port_name": "eth0",
"port_name_format": "Ethernet{0}",
"qemu": {
"adapter_type": "e1000",
"adapters": 10,
"ram": 4096,
"cpus": 2,
"hda_disk_interface": "virtio",
"arch": "x86_64",
"console_type": "telnet",
"boot_priority": "d",
"kvm": "require"
},
"images": [
{
"filename": "vAsterNOS-V3.1.img",
"version": "V3.1",
"md5sum": "c323c9c3f60e1a93eca2acdc5034b85c",
"filesize": 2724659200,
"download_url": "https://cloudswit.ch/product/sonic-enterprise-distribution/#vAsterNOS"
}
],
"versions": [
{
"name": "V3.1",
"images": {
"hda_disk_image": "vAsterNOS-V3.1.img"
}
}
]
}

@ -11,7 +11,7 @@
"status": "stable",
"maintainer": "GNS3 Team",
"maintainer_email": "developers@gns3.net",
"usage": "Configure interfaces in /opt/bootlocal.sh, BIRD configuration is done in /usr/local/etc/bird",
"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",
"qemu": {
"adapter_type": "e1000",
"adapters": 4,

@ -23,6 +23,14 @@
"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",
@ -33,6 +41,12 @@
}
],
"versions": [
{
"name": "2.14",
"images": {
"hda_disk_image": "bird2-debian-2.14.qcow2"
}
},
{
"name": "2.0.12",
"images": {

@ -26,6 +26,13 @@
"kvm": "require"
},
"images": [
{
"filename": "asav9-18-2.qcow2",
"version": "9.18.2 CML",
"md5sum": "6f10fe106edfad9163625770a47a6b73",
"filesize": 340262912,
"download_url": "https://learningnetworkstore.cisco.com/cisco-modeling-labs-personal/cisco-modeling-labs-personal/CML-PERSONAL.html"
},
{
"filename": "asav9-16-2.qcow2",
"version": "9.16.2 CML",
@ -119,6 +126,12 @@
}
],
"versions": [
{
"name": "9.18.2 CML",
"images": {
"hda_disk_image": "asav9-18-2.qcow2"
}
},
{
"name": "9.16.2 CML",
"images": {

@ -24,6 +24,13 @@
"kvm": "require"
},
"images": [
{
"filename": "c8000v-universalk9_8G_serial.17.09.01a.qcow2",
"version": "17.09.01a 8G",
"md5sum": "a10ae2c4d71f4eb611bc4d83ad7709f0",
"filesize": 1856634880,
"download_url": "https://software.cisco.com/download/home/286327102/type/282046477/release/Bengaluru-17.6.5"
},
{
"filename": "c8000v-universalk9_8G_serial.17.06.05.qcow2",
"version": "17.06.05 8G",
@ -54,6 +61,12 @@
}
],
"versions": [
{
"name": "17.09.01a 8G",
"images": {
"hda_disk_image": "c8000v-universalk9_8G_serial.17.09.01a.qcow2"
}
},
{
"name": "17.06.05 8G",
"images": {

@ -12,20 +12,27 @@
"status": "experimental",
"maintainer": "GNS3 Team",
"maintainer_email": "developers@gns3.net",
"usage": "There is no default configuration present. Virtual Switch and Interfaces may take several minutes to be usable after appliance boot.",
"usage": "There is no default configuration present. Virtual Switch and Interfaces may take several minutes to be usable after appliance boot.\n\nOnly has basic Layer 2 switching features. Will need to enable advance features and reboot to gain access to things like BGP, etc... \n\nconfigure terminal \nlicense boot level network-advantage addon dna-advantage \nend \nwrite memory \nreload",
"first_port_name": "GigabitEthernet0/0",
"port_name_format": "GigabitEthernet1/0/{port1}",
"qemu": {
"adapter_type": "virtio-net-pci",
"adapters": 9,
"ram": 16384,
"cpus": 2,
"ram": 24576,
"cpus": 4,
"hda_disk_interface": "virtio",
"arch": "x86_64",
"console_type": "telnet",
"kvm": "require"
},
"images": [
{
"filename": "cat9kv-prd-17.12.01prd9.qcow2",
"version": "17.12(1)",
"md5sum": "e587e92186f42bdf69d7fa27f34425f7",
"filesize": 2739404800,
"download_url": "https://learningnetworkstore.cisco.com/myaccount"
},
{
"filename": "cat9kv-prd-17.10.01prd7.qcow2",
"version": "17.10(1)",
@ -35,6 +42,12 @@
}
],
"versions": [
{
"name": "17.12(1)",
"images": {
"hda_disk_image": "cat9kv-prd-17.12.01prd9.qcow2"
}
},
{
"name": "17.10(1)",
"images": {

@ -24,6 +24,13 @@
"kvm": "require"
},
"images": [
{
"filename": "csr1000v-universalk9.17.03.06-serial.qcow2",
"version": "17.03.06",
"md5sum": "086ab9bef6e66de847af0da3910c60e8",
"filesize": 1422000128,
"download_url": "https://software.cisco.com/download/home/284364978/type/282046477/release/Gibraltar-16.12.3"
},
{
"filename": "csr1000v-universalk9.16.12.03-serial.qcow2",
"version": "16.12.3",
@ -159,6 +166,12 @@
}
],
"versions": [
{
"name": "17.03.06",
"images": {
"hda_disk_image": "csr1000v-universalk9.17.03.06-serial.qcow2"
}
},
{
"name": "16.12.3",
"images": {

@ -32,6 +32,13 @@
"download_url": "https://sourceforge.net/projects/gns-3/files",
"direct_download_url": "https://sourceforge.net/projects/gns-3/files/Qemu%20Appliances/IOSv_startup_config.img/download"
},
{
"filename": "vios-adventerprisek9-m.spa.159-3.m8.qcow2",
"version": "15.9(3)M8",
"md5sum": "8d93a185c2fa778178a933f20b02150a",
"filesize": 57319424,
"download_url": "https://learningnetworkstore.cisco.com/myaccount"
},
{
"filename": "vios-adventerprisek9-m.spa.159-3.m6.qcow2",
"version": "15.9(3)M6",
@ -97,6 +104,13 @@
}
],
"versions": [
{
"name": "15.9(3)M8",
"images": {
"hda_disk_image": "vios-adventerprisek9-m.spa.159-3.m8.qcow2",
"hdb_disk_image": "IOSv_startup_config.img"
}
},
{
"name": "15.9(3)M6",
"images": {

@ -26,6 +26,13 @@
"options": "-smp 4 -cpu host"
},
"images": [
{
"filename": "xrv9k-fullk9-x-7.7.1.qcow2",
"version": "7.7.1",
"md5sum": "682fff40d2ff373d8da3342906553b54",
"filesize": 1643905024,
"download_url": "https://software.cisco.com/download/home/286288939/type/280805694/release/7.1.1"
},
{
"filename": "xrv9k-fullk9-x-7.1.1.qcow2",
"version": "7.1.1",
@ -105,6 +112,12 @@
}
],
"versions": [
{
"name": "7.7.1",
"images": {
"hda_disk_image": "xrv9k-fullk9-x-7.7.1.qcow2"
}
},
{
"name": "7.1.1",
"images": {

@ -18,6 +18,12 @@
"startup_config": "iou_l2_base_startup-config.txt"
},
"images": [
{
"filename": "x86_64_crb_linux_l2-adventerprisek9-ms",
"version": "17.12.1",
"md5sum": "2b5055e4cef8fd257416d74a94adb626",
"filesize": 240355720
},
{
"filename": "i86bi-linux-l2-ipbasek9-15.1g.bin",
"version": "15.1g",
@ -38,6 +44,12 @@
}
],
"versions": [
{
"name": "17.12.1",
"images": {
"image": "x86_64_crb_linux_l2-adventerprisek9-ms"
}
},
{
"name": "15.1g",
"images": {

@ -18,6 +18,12 @@
"startup_config": "iou_l3_base_startup-config.txt"
},
"images": [
{
"filename": "x86_64_crb_linux-adventerprisek9-ms",
"version": "17.12.1",
"md5sum": "4a2fce8de21d1831fbceffd155e41ae7",
"filesize": 288947184
},
{
"filename": "i86bi_LinuxL3-AdvEnterpriseK9-M2_157_3_May_2018.bin",
"version": "15.7(3)M2",
@ -38,6 +44,12 @@
}
],
"versions": [
{
"name": "17.12.1",
"images": {
"image": "x86_64_crb_linux-adventerprisek9-ms"
}
},
{
"name": "15.7(3)M2",
"images": {

@ -26,6 +26,13 @@
"kvm": "require"
},
"images": [
{
"filename": "nexus9300v64.10.3.1.F.qcow2",
"version": "9300v 10.3.1.F",
"md5sum": "a6ffd2501a5791c11cee319943b912da",
"filesize": 2097086464,
"download_url": "https://software.cisco.com/download/home/286312239/type/282088129/release/10.1(1)"
},
{
"filename": "nexus9500v64.10.1.1.qcow2",
"version": "9500v 10.1.1",
@ -40,6 +47,34 @@
"filesize": 1990983680,
"download_url": "https://software.cisco.com/download/home/286312239/type/282088129/release/10.1(1)"
},
{
"filename": "nexus9500v.9.3.13.qcow2",
"version": "9500v 9.3.13",
"md5sum": "bacf0f664ee34625c85a9f278b2466a2",
"filesize": 2248409088,
"download_url": "https://software.cisco.com/download/home/286312239/type/282088129/release/9.3(13)"
},
{
"filename": "nexus9300v.9.3.13.qcow2",
"version": "9300v 9.3.13",
"md5sum": "d8ce30cb762df02d77ec27786a2435ad",
"filesize": 2248343552,
"download_url": "https://software.cisco.com/download/home/286312239/type/282088129/release/9.3(13)"
},
{
"filename": "nexus9500v.9.3.12.qcow2",
"version": "9500v 9.3.12",
"md5sum": "452e5cb2a7a25feaa3ba0624a82ff9ca",
"filesize": 1997996032,
"download_url": "https://software.cisco.com/download/home/286312239/type/282088129/release/9.3(12)"
},
{
"filename": "nexus9300v.9.3.12.qcow2",
"version": "9300v 9.3.12",
"md5sum": "7b6b5dad1001e11d6ebb54662616e9f2",
"filesize": 1997930496,
"download_url": "https://software.cisco.com/download/home/286312239/type/282088129/release/9.3(12)"
},
{
"filename": "nexus9500v.9.3.9.qcow2",
"version": "9500v 9.3.9",
@ -198,6 +233,13 @@
}
],
"versions": [
{
"name": "9300v 10.3.1.F",
"images": {
"bios_image": "OVMF-edk2-stable202305.fd",
"hda_disk_image": "nexus9300v64.10.3.1.F.qcow2"
}
},
{
"name": "9500v 10.1.1",
"images": {
@ -212,6 +254,34 @@
"hda_disk_image": "nexus9300v.10.1.1.qcow2"
}
},
{
"name": "9500v 9.3.13",
"images": {
"bios_image": "OVMF-edk2-stable202305.fd",
"hda_disk_image": "nexus9500v.9.3.13.qcow2"
}
},
{
"name": "9300v 9.3.13",
"images": {
"bios_image": "OVMF-edk2-stable202305.fd",
"hda_disk_image": "nexus9300v.9.3.13.qcow2"
}
},
{
"name": "9500v 9.3.12",
"images": {
"bios_image": "OVMF-edk2-stable202305.fd",
"hda_disk_image": "nexus9500v.9.3.12.qcow2"
}
},
{
"name": "9300v 9.3.12",
"images": {
"bios_image": "OVMF-edk2-stable202305.fd",
"hda_disk_image": "nexus9300v.9.3.12.qcow2"
}
},
{
"name": "9500v 9.3.9",
"images": {

@ -5,7 +5,7 @@
"description": "Clavister NetShield (cOS Stream) Virtual Appliance offers the same functionality as the Clavister NetShield physical NGappliances FWs in a virtual environment.",
"vendor_name": "Clavister",
"vendor_url": "https://www.clavister.com/",
"documentation_url": "https://kb.clavister.com",
"documentation_url": "https://docs.clavister.com",
"product_name": "NetShield",
"product_url": "https://www.clavister.com/products/netshield/",
"registry_version": 4,
@ -13,7 +13,7 @@
"availability": "free-to-try",
"maintainer": "Mattias Nordlund",
"maintainer_email": "mattias.nordlund@clavister.com",
"usage": "No configuration by default, oen console to set IPs and activate configuration.",
"usage": "No configuration by default, use console to set IPs and activate configuration.",
"port_name_format": "if{0}",
"qemu": {
"adapter_type": "virtio-net-pci",
@ -27,6 +27,13 @@
"options": "-cpu Nehalem"
},
"images": [
{
"filename": "clavister-cos-stream-4.00.00.34-virtual-x64-generic.qcow2",
"version": "cOS Stream 4.00.00",
"md5sum": "02b480621d9bf1a9a4e116cd47bef006",
"filesize": 132841472,
"download_url": "https://my.clavister.com/download/86399b21-44c7-ee11-a434-005056bdfeb0"
},
{
"filename": "clavister-cos-stream-3.80.09.01-virtual-x64-generic.qcow2",
"version": "cOS Stream 3.80.09",
@ -36,6 +43,12 @@
}
],
"versions": [
{
"name": "cOS Stream 4.00.00",
"images": {
"hda_disk_image": "clavister-cos-stream-4.00.00.34-virtual-x64-generic.qcow2"
}
},
{
"name": "cOS Stream 3.80.09",
"images": {

@ -5,7 +5,7 @@
"description": "Clavister NetWall (cOS Core) Virtual Appliance offers the same functionality as the Clavister NetWall physical NGFWs in a virtual environment.",
"vendor_name": "Clavister",
"vendor_url": "https://www.clavister.com/",
"documentation_url": "https://kb.clavister.com",
"documentation_url": "https://docs.clavister.com",
"product_name": "NetWall",
"product_url": "https://www.clavister.com/products/ngfw/",
"registry_version": 4,
@ -13,7 +13,7 @@
"availability": "free-to-try",
"maintainer": "Mattias Nordlund",
"maintainer_email": "mattias.nordlund@clavister.com",
"usage": "DHCP enabled on all interfaces by default, WebUI/SSH access enabled on the local network connected to If1.",
"usage": "DHCP & cloud-init enabled on all interfaces by default, WebUI/SSH access enabled on the local network connected to If1.",
"port_name_format": "If{0}",
"qemu": {
"adapter_type": "e1000",
@ -26,6 +26,13 @@
"kvm": "allow"
},
"images": [
{
"filename": "clavister-cos-core-14.00.13.08-kvm-en.img",
"version": "cOS Core 14.00.13 (x86)",
"md5sum": "d36797077378accb2b8fc72d09d07ee1",
"filesize": 536870912,
"download_url": "https://my.clavister.com/download/f3f494a3-9ee7-ee11-a434-005056bdfeb0"
},
{
"filename": "clavister-cos-core-14.00.01.13-kvm-en.img",
"version": "cOS Core 14.00.01 (x86)",
@ -42,6 +49,12 @@
}
],
"versions": [
{
"name": "cOS Core 14.00.13 (x86)",
"images": {
"hda_disk_image": "clavister-cos-core-14.00.13.08-kvm-en.img"
}
},
{
"name": "cOS Core 14.00.01 (x86)",
"images": {

@ -24,12 +24,12 @@
},
"images": [
{
"filename": "debian-12.2.qcow2",
"version": "12.2",
"md5sum": "adf7716ec4a4e4e9e5ccfc7a1d7bd103",
"filesize": 286654464,
"filename": "debian-12.4.qcow2",
"version": "12.4",
"md5sum": "adcf7fdc25e10b3d2d9e2ef91168bffd",
"filesize": 286179840,
"download_url": "https://sourceforge.net/projects/gns-3/files/Qemu%20Appliances/",
"direct_download_url": "https://downloads.sourceforge.net/project/gns-3/Qemu%20Appliances/debian-12.2.qcow2"
"direct_download_url": "https://downloads.sourceforge.net/project/gns-3/Qemu%20Appliances/debian-12.4.qcow2"
},
{
"filename": "debian-11.8.qcow2",
@ -42,9 +42,9 @@
],
"versions": [
{
"name": "12.2",
"name": "12.4",
"images": {
"hda_disk_image": "debian-12.2.qcow2"
"hda_disk_image": "debian-12.4.qcow2"
}
},
{

@ -11,26 +11,36 @@
"status": "experimental",
"maintainer": "GNS3 Team",
"maintainer_email": "developers@gns3.net",
"usage": "Make sure the Boot priority of the configuration template is HDD or CD.\n\nAbort the BCM process and format the flash after first boot by entering these commands:\nen\nformat flash:\n\nSometimes the flash device is not available after boot.",
"usage": "Make sure the Boot priority of the configuration template is HDD or CD.\n\nFor FTOS 9.8, switch to vnc console, start the device, abort the BMP process and format the flash after first boot by entering these commands:\nen\nformat flash:\n\nSometimes the flash device is not available after boot.",
"first_port_name": "Management0/0",
"port_name_format": "fortyGigE0/{0}",
"qemu": {
"adapter_type": "e1000",
"adapters": 6,
"ram": 512,
"ram": 1024,
"hda_disk_interface": "ide",
"arch": "i386",
"console_type": "vnc",
"console_type": "telnet",
"boot_priority": "cd",
"kvm": "require"
},
"images": [
{
"filename": "FTOS-SI-9.13.0.0.iso",
"version": "9.13.0",
"md5sum": "8418049e451c76e7b85e36eca0a0a730",
"filesize": 131278848,
"download_url": "https://www.dell.com/support/kbdoc/en-us/000184062/ftos-for-s-series-os-emulator-current-release-evaluation-version",
"direct_download_url": "https://downloads.dell.com/translatedpdf/force10/os9/FTOS-SI-9.13.0.0.zip",
"compression": "zip"
},
{
"filename": "FTOS-SI-9.8.0.0.iso",
"version": "9.8.0",
"md5sum": "b9b50eda0a73407dc381792ff7975e24",
"filesize": 108115968,
"download_url": "https://www.force10networks.com/CSPortal20/Software/SSeriesDownloads.aspx",
"download_url": "https://www.dell.com/support/kbdoc/en-us/000184062/ftos-for-s-series-os-emulator-current-release-evaluation-version",
"direct_download_url": "https://downloads.dell.com/translatedpdf/force10/os9/FTOS-SI-9.8.0.0.zip",
"compression": "zip"
},
{
@ -43,6 +53,13 @@
}
],
"versions": [
{
"name": "9.13.0",
"images": {
"hda_disk_image": "empty30G.qcow2",
"cdrom_image": "FTOS-SI-9.13.0.0.iso"
}
},
{
"name": "9.8.0",
"images": {

@ -26,163 +26,25 @@
"kvm": "allow",
"options": "-cpu core2duo"
},
"images": [
{
"filename": "EXOS-VM_v32.1.1.6.qcow2",
"version": "32.1.1.6",
"md5sum": "48868bbcb4255d6365049b5941dd2af7",
"filesize": 231211008,
"direct_download_url": "https://akamai-ep.extremenetworks.com/Extreme_P/github-en/Virtual_EXOS/EXOS-VM_v32.1.1.6.qcow2"
},
{
"filename": "EXOS-VM_v31.7.1.4.qcow2",
"version": "31.7.1.4",
"md5sum": "a70e4fa3bc361434237ad12937aaf0fb",
"filesize": 458227712,
"direct_download_url": "https://akamai-ep.extremenetworks.com/Extreme_P/github-en/Virtual_EXOS/EXOS-VM_v31.7.1.4.qcow2"
},
{
"filename": "EXOS-VM_v31.1.1.3.qcow2",
"version": "31.1.1.3",
"md5sum": "e4936ad94a5304bfeeca8dfc6f285cc0",
"filesize": 561512448,
"direct_download_url": "https://akamai-ep.extremenetworks.com/Extreme_P/github-en/Virtual_EXOS/EXOS-VM_v31.1.1.3.qcow2"
},
{
"filename": "EXOS-VM_v30.7.1.1.qcow2",
"version": "30.7.1.1",
"md5sum": "c6b67023945102f984b47aa67c67fe33",
"filesize": 485687296,
"direct_download_url": "https://akamai-ep.extremenetworks.com/Extreme_P/github-en/Virtual_EXOS/EXOS-VM_v30.7.1.1.qcow2"
},
{
"filename": "EXOS-VM_v30.6.1.11.qcow2",
"version": "30.6.1.11",
"md5sum": "bf3e13570417d7e3464165b344c196eb",
"filesize": 475332608,
"direct_download_url": "https://akamai-ep.extremenetworks.com/Extreme_P/github-en/Virtual_EXOS/EXOS-VM_v30.6.1.11.qcow2"
},
{
"filename": "EXOS-VM_v30.5.1.15.qcow2",
"version": "30.5.1.15",
"md5sum": "380d0c7bb8f0aa272ceb84b85f367a63",
"filesize": 498663424,
"direct_download_url": "https://akamai-ep.extremenetworks.com/Extreme_P/github-en/Virtual_EXOS/EXOS-VM_v30.5.1.15.qcow2"
},
{
"filename": "EXOS-VM_v30.4.1.2.qcow2",
"version": "30.4.1.2",
"md5sum": "133fa38bf80daec9e389729c96e692c0",
"filesize": 454557696,
"direct_download_url": "https://akamai-ep.extremenetworks.com/Extreme_P/github-en/Virtual_EXOS/EXOS-VM_v30.4.1.2.qcow2"
},
{
"filename": "EXOS-VM_v30.3.1.6.qcow2",
"version": "30.3.1.6",
"md5sum": "edb86b406efe99434c6d5366d9bfa97f",
"filesize": 448266240,
"direct_download_url": "https://akamai-ep.extremenetworks.com/Extreme_P/github-en/Virtual_EXOS/EXOS-VM_v30.3.1.6.qcow2"
},
{
"filename": "EXOS-VM_v30.2.1.8.qcow2",
"version": "30.2.1.8",
"md5sum": "4bdbf3ddff7a030e19c6bb71270b56d2",
"filesize": 355205120,
"direct_download_url": "https://akamai-ep.extremenetworks.com/Extreme_P/github-en/Virtual_EXOS/EXOS-VM_v30.2.1.8.qcow2"
},
{
"filename": "EXOS-VM_v30.1.1.4.qcow2",
"version": "30.1.1.4",
"md5sum": "92d3f9b13d750f7bfa804823fa545772",
"filesize": 383385600,
"direct_download_url": "https://akamai-ep.extremenetworks.com/Extreme_P/github-en/Virtual_EXOS/EXOS-VM_v30.1.1.4.qcow2"
},
{
"filename": "EXOS-VM_v22.7.1.2.qcow2",
"version": "22.7.1.2",
"md5sum": "a13e839b3fa05e8a5b0fb31f7e3dda86",
"filesize": 180420608,
"direct_download_url": "https://akamai-ep.extremenetworks.com/Extreme_P/github-en/Virtual_EXOS/EXOS-VM_v22.7.1.2.qcow2"
},
{
"filename": "EXOS-VM_v21.1.1.4-disk1.qcow2",
"version": "21.1.1.4",
"md5sum": "654606809b6fd3bca400377483eb4a79",
"filesize": 117560832,
"direct_download_url": "https://akamai-ep.extremenetworks.com/Extreme_P/github-en/Virtual_EXOS/EXOS-VM_v21.1.1.4-disk1.qcow2"
{
"filename": "EXOS-VM_v32.6.3.126.qcow2",
"version": "32.6.3.126",
"md5sum": "5856b6c427bd605fe1c7adb6ee6b2659",
"filesize": 236716032,
"direct_download_url": "https://akamai-ep.extremenetworks.com/Extreme_P/github-en/Virtual_EXOS/EXOS-VM_v32.6.3.126.qcow2"
}
],
"versions": [
{
"name": "32.1.1.6",
"images": {
"hda_disk_image": "EXOS-VM_v32.1.1.6.qcow2"
}
},
{
"name": "31.7.1.4",
"images": {
"hda_disk_image": "EXOS-VM_v31.7.1.4.qcow2"
}
},
{
"name": "31.1.1.3",
"images": {
"hda_disk_image": "EXOS-VM_v31.1.1.3.qcow2"
}
},
{
"name": "30.7.1.1",
"images": {
"hda_disk_image": "EXOS-VM_v30.7.1.1.qcow2"
}
},
{
"name": "30.6.1.11",
"images": {
"hda_disk_image": "EXOS-VM_v30.6.1.11.qcow2"
}
},
{
"name": "30.5.1.15",
"images": {
"hda_disk_image": "EXOS-VM_v30.5.1.15.qcow2"
}
},
{
"name": "30.4.1.2",
"images": {
"hda_disk_image": "EXOS-VM_v30.4.1.2.qcow2"
}
},
{
"name": "30.3.1.6",
"images": {
"hda_disk_image": "EXOS-VM_v30.3.1.6.qcow2"
}
},
{
"name": "30.2.1.8",
"images": {
"hda_disk_image": "EXOS-VM_v30.2.1.8.qcow2"
}
},
{
"name": "30.1.1.4",
"images": {
"hda_disk_image": "EXOS-VM_v30.1.1.4.qcow2"
}
},
{
"name": "22.7.1.2",
"images": {
"hda_disk_image": "EXOS-VM_v22.7.1.2.qcow2"
}
},
{
"name": "21.1.1.4",
"name": "32.6.3.126",
"images": {
"hda_disk_image": "EXOS-VM_v21.1.1.4-disk1.qcow2"
"hda_disk_image": "EXOS-VM_v32.6.3.126.qcow2"
}
}
]

@ -26,6 +26,14 @@
"options": "-nographic"
},
"images": [
{
"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",
@ -34,30 +42,6 @@
"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-Base-37-1.7.x86_64.qcow2",
"version": "37-1.7",
"md5sum": "36f7b464b6ab46ad97c001b539495e90",
"filesize": 492830720,
"download_url": "https://download.fedoraproject.org/pub/fedora/linux/releases/37/Cloud/x86_64/images",
"direct_download_url": "https://download.fedoraproject.org/pub/fedora/linux/releases/37/Cloud/x86_64/images/Fedora-Cloud-Base-37-1.7.x86_64.qcow2"
},
{
"filename": "Fedora-Cloud-Base-36-1.5.x86_64.qcow2",
"version": "36-1.5",
"md5sum": "7f7cdad25b77f232078bf454c39529d3",
"filesize": 448266240,
"download_url": "https://download.fedoraproject.org/pub/fedora/linux/releases/36/Cloud/x86_64/images",
"direct_download_url": "https://download.fedoraproject.org/pub/fedora/linux/releases/36/Cloud/x86_64/images/Fedora-Cloud-Base-36-1.5.x86_64.qcow2"
},
{
"filename": "Fedora-Cloud-Base-35-1.2.x86_64.qcow2",
"version": "35-1.2",
"md5sum": "cfa9cdcfb946e5f4cf9dd4d7906008d0",
"filesize": 376897536,
"download_url": "https://download.fedoraproject.org/pub/fedora/linux/releases/35/Cloud/x86_64/images",
"direct_download_url": "https://download.fedoraproject.org/pub/fedora/linux/releases/35/Cloud/x86_64/images/Fedora-Cloud-Base-35-1.2.x86_64.qcow2"
},
{
"filename": "fedora-cloud-init-data.iso",
"version": "1.0",
@ -69,30 +53,16 @@
],
"versions": [
{
"name": "38-1.6",
"images": {
"hda_disk_image": "Fedora-Cloud-Base-38-1.6.x86_64.qcow2",
"cdrom_image": "fedora-cloud-init-data.iso"
}
},
{
"name": "37-1.7",
"name": "39-1.5",
"images": {
"hda_disk_image": "Fedora-Cloud-Base-37-1.7.x86_64.qcow2",
"hda_disk_image": "Fedora-Cloud-Base-39-1.5.x86_64.qcow2",
"cdrom_image": "fedora-cloud-init-data.iso"
}
},
{
"name": "36-1.5",
"images": {
"hda_disk_image": "Fedora-Cloud-Base-36-1.5.x86_64.qcow2",
"cdrom_image": "fedora-cloud-init-data.iso"
}
},
{
"name": "35-1.2",
"name": "38-1.6",
"images": {
"hda_disk_image": "Fedora-Cloud-Base-35-1.2.x86_64.qcow2",
"hda_disk_image": "Fedora-Cloud-Base-38-1.6.x86_64.qcow2",
"cdrom_image": "fedora-cloud-init-data.iso"
}
}

@ -29,6 +29,13 @@
"kvm": "allow"
},
"images": [
{
"filename": "FAZ_VM64_KVM-v7.4.2-build2397-FORTINET.out.kvm.qcow2",
"version": "7.4.2",
"md5sum": "33b2938e19a6cb61d4d64e6998a54d23",
"filesize": 480096256,
"download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx"
},
{
"filename": "FAZ_VM64_KVM-v7.4.1-build2308-FORTINET.out.kvm.qcow2",
"version": "7.4.1",
@ -57,6 +64,13 @@
"filesize": 340631552,
"download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx"
},
{
"filename": "FAZ_VM64_KVM-v7.0.11-build0595-FORTINET.out.kvm.qcow2",
"version": "7.0.11",
"md5sum": "ae7ff37d00d0b518e9982a0785665fc2",
"filesize": 349257728,
"download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx"
},
{
"filename": "FAZ_VM64_KVM-v7.0.9-build0489-FORTINET.out.kvm.qcow2",
"version": "7.0.9",
@ -78,6 +92,13 @@
"filesize": 334184448,
"download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx"
},
{
"filename": "FAZ_VM64_KVM-v6.4.14-build2660-FORTINET.out.kvm.qcow2",
"version": "6.4.14",
"md5sum": "1629d76776c9d625faee0304b5840c02",
"filesize": 300683264,
"download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx"
},
{
"filename": "FAZ_VM64_KVM-v6.4.12-build2610-FORTINET.out.kvm.qcow2",
"version": "6.4.12",
@ -235,6 +256,13 @@
}
],
"versions": [
{
"name": "7.4.2",
"images": {
"hda_disk_image": "FAZ_VM64_KVM-v7.4.2-build2397-FORTINET.out.kvm.qcow2",
"hdb_disk_image": "empty30G.qcow2"
}
},
{
"name": "7.4.1",
"images": {
@ -263,6 +291,13 @@
"hdb_disk_image": "empty30G.qcow2"
}
},
{
"name": "7.0.11",
"images": {
"hda_disk_image": "FAZ_VM64_KVM-v7.0.11-build0595-FORTINET.out.kvm.qcow2",
"hdb_disk_image": "empty30G.qcow2"
}
},
{
"name": "7.0.9",
"images": {
@ -284,6 +319,13 @@
"hdb_disk_image": "empty30G.qcow2"
}
},
{
"name": "6.4.14",
"images": {
"hda_disk_image": "FAZ_VM64_KVM-v6.4.14-build2660-FORTINET.out.kvm.qcow2",
"hdb_disk_image": "empty30G.qcow2"
}
},
{
"name": "6.4.12",
"images": {

@ -28,6 +28,13 @@
"kvm": "allow"
},
"images": [
{
"filename": "FGT_VM64_KVM-v7.4.3.F-build2573-FORTINET.out.kvm.qcow2",
"version": "7.4.3",
"md5sum": "e7517095c91dce1a76a9bcf114b5fa15",
"filesize": 100728832,
"download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx"
},
{
"filename": "FGT_VM64_KVM-v7.4.1.F-build2463-FORTINET.out.kvm.qcow2",
"version": "7.4.1",
@ -35,6 +42,13 @@
"filesize": 116064256,
"download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx"
},
{
"filename": "FGT_VM64_KVM-v7.2.7.M-build1577-FORTINET.out.kvm.qcow2",
"version": "7.2.7",
"md5sum": "752b56844e464b0b135e57f72681c288",
"filesize": 102760448,
"download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx"
},
{
"filename": "FGT_VM64_KVM-v7.2.6.F-build1575-FORTINET.out.kvm.qcow2",
"version": "7.2.6",
@ -63,6 +77,20 @@
"filesize": 86704128,
"download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx"
},
{
"filename": "FGT_VM64_KVM-v7.0.14.M-build0601-FORTINET.out.kvm.qcow2",
"version": "7.0.14",
"md5sum": "20c855889e1117902bcf448b30746d30",
"filesize": 77398016,
"download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx"
},
{
"filename": "FGT_VM64_KVM-v7.0.13.M-build0566-FORTINET.out.kvm.qcow2",
"version": "7.0.13",
"md5sum": "47a4bab29407153a635c54b64d6a226e",
"filesize": 89325568,
"download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx"
},
{
"filename": "FGT_VM64_KVM-v7.0.12.M-build0523-FORTINET.out.kvm.qcow2",
"version": "7.0.12",
@ -84,6 +112,13 @@
"filesize": 77135872,
"download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx"
},
{
"filename": "FGT_VM64_KVM-v6.4.15.M-build2095-FORTINET.out.kvm.qcow2",
"version": "6.4.15",
"md5sum": "a0306a3905877de654dab7e1bb67089e",
"filesize": 81592320,
"download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx"
},
{
"filename": "FGT_VM64_KVM-v6.4.14.M-build2093-FORTINET.out.kvm.qcow2",
"version": "6.4.14",
@ -332,6 +367,13 @@
}
],
"versions": [
{
"name": "7.4.3",
"images": {
"hda_disk_image": "FGT_VM64_KVM-v7.4.3.F-build2573-FORTINET.out.kvm.qcow2",
"hdb_disk_image": "empty30G.qcow2"
}
},
{
"name": "7.4.1",
"images": {
@ -339,6 +381,13 @@
"hdb_disk_image": "empty30G.qcow2"
}
},
{
"name": "7.2.7",
"images": {
"hda_disk_image": "FGT_VM64_KVM-v7.2.7.M-build1577-FORTINET.out.kvm.qcow2",
"hdb_disk_image": "empty30G.qcow2"
}
},
{
"name": "7.2.6",
"images": {
@ -367,6 +416,20 @@
"hdb_disk_image": "empty30G.qcow2"
}
},
{
"name": "7.0.14",
"images": {
"hda_disk_image": "FGT_VM64_KVM-v7.0.14.M-build0601-FORTINET.out.kvm.qcow2",
"hdb_disk_image": "empty30G.qcow2"
}
},
{
"name": "7.0.13",
"images": {
"hda_disk_image": "FGT_VM64_KVM-v7.0.13.M-build0566-FORTINET.out.kvm.qcow2",
"hdb_disk_image": "empty30G.qcow2"
}
},
{
"name": "7.0.12",
"images": {
@ -388,6 +451,13 @@
"hdb_disk_image": "empty30G.qcow2"
}
},
{
"name": "6.4.15",
"images": {
"hda_disk_image": "FGT_VM64_KVM-v6.4.15.M-build2095-FORTINET.out.kvm.qcow2",
"hdb_disk_image": "empty30G.qcow2"
}
},
{
"name": "6.4.14",
"images": {

@ -13,7 +13,7 @@
"status": "stable",
"maintainer": "Ean Towne",
"maintainer_email": "ean.fortinet@gmail.com",
"usage": "Default username is admin, no password is set.\n\n- Versions lower than 7.0.x require less CPU/RAM",
"usage": "Default username is admin, no password is set.\n\n- Versions lower than 7.0.x require less CPU/RAM\n7.4.x may require 16GB RAM.",
"symbol": "fortinet.svg",
"port_name_format": "Port{port1}",
"qemu": {
@ -29,6 +29,13 @@
"kvm": "allow"
},
"images": [
{
"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",
@ -57,6 +64,13 @@
"filesize": 242814976,
"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",
@ -78,6 +92,13 @@
"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",
@ -235,6 +256,13 @@
}
],
"versions": [
{
"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": {
@ -263,6 +291,13 @@
"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": {
@ -284,6 +319,13 @@
"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": {

@ -24,13 +24,22 @@
"kvm": "require"
},
"images": [
{
"filename": "FreeBSD-14.0-RELEASE-amd64.qcow2",
"version": "14.0",
"md5sum": "87b988eaa4a8aaabea1c10649c98b3f0",
"filesize": 3506569216,
"download_url": "https://www.freebsd.org/where.html",
"direct_download_url": "https://download.freebsd.org/releases/VM-IMAGES/14.0-RELEASE/amd64/Latest/FreeBSD-14.0-RELEASE-amd64.qcow2.xz",
"compression": "xz"
},
{
"filename": "FreeBSD-13.0-RELEASE-amd64.qcow2",
"version": "13.0",
"md5sum": "e8e598959da456c03260421b5f9890de",
"filesize": 3466854400,
"download_url": "https://www.freebsd.org/where.html",
"direct_download_url": "https://download.freebsd.org/ftp/releases/VM-IMAGES/13.0-RELEASE/amd64/Latest/FreeBSD-13.0-RELEASE-amd64.qcow2.xz",
"direct_download_url": "http://ftp-archive.freebsd.org/pub/FreeBSD-Archive/old-releases/VM-IMAGES/13.0-RELEASE/amd64/Latest/FreeBSD-13.0-RELEASE-amd64.qcow2.xz",
"compression": "xz"
},
{
@ -39,11 +48,17 @@
"md5sum": "3d7d5396f3d89ed30c2bfa2ee2e6b013",
"filesize": 3412000768,
"download_url": "https://www.freebsd.org/where.html",
"direct_download_url": "https://download.freebsd.org/ftp/releases/VM-IMAGES/12.3-RELEASE/amd64/Latest/FreeBSD-12.3-RELEASE-amd64.qcow2.xz",
"direct_download_url": "http://ftp-archive.freebsd.org/pub/FreeBSD-Archive/old-releases/VM-IMAGES/12.3-RELEASE/amd64/Latest/FreeBSD-12.3-RELEASE-amd64.qcow2.xz",
"compression": "xz"
}
],
"versions": [
{
"name": "14.0",
"images": {
"hda_disk_image": "FreeBSD-14.0-RELEASE-amd64.qcow2"
}
},
{
"name": "13.0",
"images": {

@ -7,16 +7,65 @@
"vendor_url": "https://www.huawei.com",
"product_name": "HuaWei NE40E",
"product_url": "https://e.huawei.com/en/products/enterprise-networking/routers/ne/ne40e",
"registry_version": 4,
"registry_version": 6,
"status": "experimental",
"availability": "service-contract",
"maintainer": "none",
"maintainer_email": "",
"first_port_name": "eth0",
"port_name_format": "Ethernet1/0/{0}",
"qemu": {
"adapter_type": "e1000",
"adapters": 12,
"custom_adapters": [
{
"adapter_number": 0,
"port_name": "Gi0/0/0"
},
{
"adapter_number": 1,
"port_name": "Mgmt0/0"
},
{
"adapter_number": 2,
"port_name": "Ethernet1/0/0"
},
{
"adapter_number": 3,
"port_name": "Ethernet1/0/1"
},
{
"adapter_number": 4,
"port_name": "Ethernet1/0/2"
},
{
"adapter_number": 5,
"port_name": "Ethernet1/0/3"
},
{
"adapter_number": 6,
"port_name": "Ethernet1/0/4"
},
{
"adapter_number": 7,
"port_name": "Ethernet1/0/5"
},
{
"adapter_number": 8,
"port_name": "Ethernet1/0/6"
},
{
"adapter_number": 9,
"port_name": "Ethernet1/0/7"
},
{
"adapter_number": 10,
"port_name": "Ethernet1/0/8"
},
{
"adapter_number": 11,
"port_name": "Ethernet1/0/9"
}
],
"ram": 2048,
"cpus": 2,
"hda_disk_interface": "ide",

@ -27,6 +27,15 @@
"options": "-nographic"
},
"images": [
{
"filename": "chr-7.14.2.img",
"version": "7.14.2",
"md5sum": "531901dac85b67b23011e946a62abc7b",
"filesize": 134217728,
"download_url": "http://www.mikrotik.com/download",
"direct_download_url": "https://download.mikrotik.com/routeros/7.14.2/chr-7.14.2.img.zip",
"compression": "zip"
},
{
"filename": "chr-7.11.2.img",
"version": "7.11.2",

@ -8,7 +8,7 @@
"documentation_url": "http://wiki.mikrotik.com/wiki/Manual:CHR",
"product_name": "MikroTik RouterBOARD 1100AHx4 Dude Edition",
"product_url": "http://www.mikrotik.com/download",
"registry_version": 5,
"registry_version": 4,
"status": "stable",
"maintainer": "Azorian Solutions",
"maintainer_email": "help@azorian.solutions",

@ -8,7 +8,7 @@
"documentation_url": "http://wiki.mikrotik.com/wiki/Manual:CHR",
"product_name": "MikroTik RouterBOARD 450G",
"product_url": "http://www.mikrotik.com/download",
"registry_version": 5,
"registry_version": 4,
"status": "stable",
"maintainer": "Azorian Solutions",
"maintainer_email": "help@azorian.solutions",

@ -8,7 +8,7 @@
"documentation_url": "http://wiki.mikrotik.com/wiki/Manual:CHR",
"product_name": "MikroTik RouterBOARD 450Gx4",
"product_url": "http://www.mikrotik.com/download",
"registry_version": 5,
"registry_version": 4,
"status": "stable",
"maintainer": "Azorian Solutions",
"maintainer_email": "help@azorian.solutions",

@ -26,6 +26,14 @@
"kvm": "require"
},
"images": [
{
"filename": "openmediavault_7.0.32-amd64.iso",
"version": "7.0.32",
"md5sum": "36d1fda7de0c8dd6806a65145845d362",
"filesize": 981467136,
"download_url": "https://www.openmediavault.org/download.html",
"direct_download_url": "https://sourceforge.net/projects/openmediavault/files/iso/7.0-32/openmediavault_7.0-32-amd64.iso"
},
{
"filename": "openmediavault_6.5.0-amd64.iso",
"version": "6.5.0",
@ -68,6 +76,14 @@
}
],
"versions": [
{
"name": "7.0.32",
"images": {
"hda_disk_image": "empty30G.qcow2",
"hdb_disk_image": "empty30G.qcow2",
"cdrom_image": "openmediavault_7.0.32-amd64.iso"
}
},
{
"name": "6.5.0",
"images": {

@ -25,6 +25,13 @@
"kvm": "require"
},
"images": [
{
"filename": "OPNsense-24.1-OpenSSL-nano-amd64.img",
"version": "24.1",
"md5sum": "ea8472df2c272419b7834cddaf68048d",
"filesize": 3221225472,
"download_url": "https://opnsense.c0urier.net/releases/24.1/"
},
{
"filename": "OPNsense-23.1-OpenSSL-nano-amd64.img",
"version": "23.1",
@ -62,6 +69,12 @@
}
],
"versions": [
{
"name": "24.1",
"images": {
"hda_disk_image": "OPNsense-24.1-OpenSSL-nano-amd64.img"
}
},
{
"name": "23.1",
"images": {

@ -30,6 +30,13 @@
"options": "-vga std -usbdevice tablet"
},
"images": [
{
"version": "1.3.0",
"filename": "ostinatostd-1.3.0-1.qcow2",
"filesize": 173735936,
"md5sum": "ff25fed989c43aeac84bf0d542ad43ba",
"download_url": "https://ostinato.org/pricing/gns3"
},
{
"filename": "ostinatostd-1.2.0-1.qcow2",
"version": "1.2.0",
@ -46,6 +53,12 @@
}
],
"versions": [
{
"name": "1.3.0",
"images": {
"hda_disk_image": "ostinatostd-1.3.0-1.qcow2"
}
},
{
"name": "1.2.0",
"images": {

@ -11,9 +11,9 @@
"registry_version": 4,
"status": "stable",
"availability": "service-contract",
"maintainer": "Neyder Achahuanco",
"maintainer_email": "neyder@neyder.net",
"usage": "You should download Red Hat Enterprise Linux KVM Guest Image from https://access.redhat.com/downloads/content/479/ver=/rhel---9/9.2/x86_64/product-software attach/customize cloud-init.iso and start.\nusername: cloud-user\npassword: redhat",
"maintainer": "Da-Geek",
"maintainer_email": "dageek@dageeks-geeks.gg",
"usage": "You should download Red Hat Enterprise Linux KVM Guest Image from https://access.redhat.com/downloads/content/479/ver=/rhel---9/9.3/x86_64/product-software attach/customize rhel-cloud-init.iso and start.\nusername: cloud-user\npassword: redhat",
"qemu": {
"adapter_type": "virtio-net-pci",
"adapters": 1,
@ -26,6 +26,13 @@
"options": "-cpu host -nographic"
},
"images": [
{
"filename": "rhel-9.3-x86_64-kvm.qcow2",
"version": "9.3",
"md5sum": "409d8d15f5177db2617b0e3e02139b5c",
"filesize": 858193920,
"download_url": "https://access.redhat.com/downloads/content/479/ver=/rhel---9/9.3/x86_64/product-software"
},
{
"filename": "rhel-9.2-x86_64-kvm.qcow2",
"version": "9.2",
@ -112,6 +119,13 @@
}
],
"versions": [
{
"name": "9.3",
"images": {
"hda_disk_image": "rhel-9.3-x86_64-kvm.qcow2",
"cdrom_image": "rhel-cloud-init.iso"
}
},
{
"name": "9.2",
"images": {

@ -26,6 +26,14 @@
"options": "-nographic -cpu host"
},
"images": [
{
"filename": "Rocky-9-GenericCloud-Base-9.3-20231113.0.x86_64.qcow2",
"version": "9.3",
"md5sum": "48cdeb033364af5909e15ee48d0e144d",
"filesize": 1083965440,
"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.3-20231113.0.x86_64.qcow2"
},
{
"filename": "Rocky-9-GenericCloud-Base-9.2-20230513.0.x86_64.qcow2",
"version": "9.2",
@ -34,6 +42,14 @@
"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.2-20230513.0.x86_64.qcow2"
},
{
"filename": "Rocky-8-GenericCloud-Base-8.9-20231119.0.x86_64.qcow2",
"version": "8.9",
"md5sum": "50c44d8d9c4df43694372c13768f114c",
"filesize": 1971978240,
"download_url": "https://download.rockylinux.org/pub/rocky/8/images/x86_64/",
"direct_download_url": "https://download.rockylinux.org/pub/rocky/8/images/x86_64/Rocky-8-GenericCloud-Base-8.9-20231119.0.x86_64.qcow2"
},
{
"filename": "Rocky-8-GenericCloud-Base-8.8-20230518.0.x86_64.qcow2",
"version": "8.8",
@ -52,6 +68,13 @@
}
],
"versions": [
{
"name": "9.3",
"images": {
"hda_disk_image": "Rocky-9-GenericCloud-Base-9.3-20231113.0.x86_64.qcow2",
"cdrom_image": "rocky-cloud-init-data.iso"
}
},
{
"name": "9.2",
"images": {
@ -59,6 +82,13 @@
"cdrom_image": "rocky-cloud-init-data.iso"
}
},
{
"name": "8.9",
"images": {
"hda_disk_image": "Rocky-8-GenericCloud-Base-8.9-20231119.0.x86_64.qcow2",
"cdrom_image": "rocky-cloud-init-data.iso"
}
},
{
"name": "8.8",
"images": {

@ -28,12 +28,12 @@
},
"images": [
{
"filename": "ubuntu-23.04-server-cloudimg-arm64.img",
"filename": "ubuntu-23.04-server-cloudimg-amd64.img",
"version": "Ubuntu 23.04 (Lunar Lobster)",
"md5sum": "35fa3b31b65717af6f0520a769aac8c0",
"filesize": 786432000,
"md5sum": "369e3b1f68416c39245a8014172406dd",
"filesize": 756678656,
"download_url": "https://cloud-images.ubuntu.com/releases/23.04/release/",
"direct_download_url": "https://cloud-images.ubuntu.com/releases/23.04/release/ubuntu-23.04-server-cloudimg-arm64.img"
"direct_download_url": "https://cloud-images.ubuntu.com/releases/23.04/release/ubuntu-23.04-server-cloudimg-amd64.img"
},
{
"filename": "ubuntu-22.04-server-cloudimg-amd64.img",
@ -72,7 +72,7 @@
{
"name": "Ubuntu 23.04 (Lunar Lobster)",
"images": {
"hda_disk_image": "ubuntu-23.04-server-cloudimg-arm64.img",
"hda_disk_image": "ubuntu-23.04-server-cloudimg-amd64.img",
"cdrom_image": "ubuntu-cloud-init-data.iso"
}
},

@ -25,7 +25,7 @@ import asyncio
import logging
import aiohttp
import shutil
import subprocess
import platformdirs
from gns3server.utils import parse_version
from gns3server.utils.asyncio import locking
@ -59,11 +59,9 @@ class Docker(BaseManager):
self._api_version = DOCKER_MINIMUM_API_VERSION
@staticmethod
async def install_busybox():
async def install_busybox(dst_dir):
if not sys.platform.startswith("linux"):
return
dst_busybox = os.path.join(os.path.dirname(os.path.abspath(__file__)), "resources", "bin", "busybox")
dst_busybox = os.path.join(dst_dir, "bin", "busybox")
if os.path.isfile(dst_busybox):
return
for busybox_exec in ("busybox-static", "busybox.static", "busybox"):
@ -91,6 +89,31 @@ class Docker(BaseManager):
raise DockerError(f"Could not install busybox: {e}")
raise DockerError("No busybox executable could be found")
@staticmethod
def resources_path():
"""
Get the Docker resources storage directory
"""
appname = vendor = "GNS3"
docker_resources_dir = os.path.join(platformdirs.user_data_dir(appname, vendor, roaming=True), "docker", "resources")
os.makedirs(docker_resources_dir, exist_ok=True)
return docker_resources_dir
async def install_resources(self):
"""
Copy the necessary resources to a writable location and install busybox
"""
try:
dst_path = self.resources_path()
log.info(f"Installing Docker resources in '{dst_path}'")
from gns3server.controller import Controller
Controller.instance().install_resource_files(dst_path, "compute/docker/resources")
await self.install_busybox(dst_path)
except OSError as e:
raise DockerError(f"Could not install Docker resources to {dst_path}: {e}")
async def _check_connection(self):
if not self._connected:
@ -170,8 +193,8 @@ class Docker(BaseManager):
if timeout is None:
timeout = 60 * 60 * 24 * 31 # One month timeout
if path == "version":
url = "http://docker/v1.12/" + path # API of docker v1.0
if path == 'version':
url = "http://docker/v1.24/" + path
else:
url = "http://docker/v" + DOCKER_MINIMUM_API_VERSION + "/" + path
try:

@ -299,12 +299,15 @@ class DockerVM(BaseNode):
:returns: Return the path that we need to map to local folders
"""
resources = get_resource("compute/docker/resources")
if not os.path.exists(resources):
raise DockerError(f"{resources} is missing, can't start Docker container")
try:
resources_path = self.manager.resources_path()
except OSError as e:
raise DockerError(f"Cannot access resources: {e}")
log.info(f'Mount resources from "{resources_path}"')
binds = [{
"Type": "bind",
"Source": resources,
"Source": resources_path,
"Target": "/gns3",
"ReadOnly": True
}]
@ -394,6 +397,8 @@ class DockerVM(BaseNode):
if ":" in os.path.splitdrive(self.working_dir)[1]:
raise DockerError("Cannot create a Docker container with a project directory containing a colon character (':')")
#await self.manager.install_resources()
try:
image_infos = await self._get_image_information()
except DockerHttp404Error:
@ -501,6 +506,10 @@ class DockerVM(BaseNode):
result = await self.manager.query("POST", "containers/create", data=params)
self._cid = result["Id"]
log.info(f"Docker container '{self._name}' [{self._id}] created")
if self._cpus > 0:
log.info(f"CPU limit set to {self._cpus} CPUs")
if self._memory > 0:
log.info(f"Memory limit set to {self._memory} MB")
return True
def _format_env(self, variables, env):
@ -545,8 +554,7 @@ class DockerVM(BaseNode):
Starts this Docker container.
"""
# make sure busybox is installed
await self.manager.install_busybox()
await self.manager.install_resources()
try:
state = await self._get_container_state()
@ -567,6 +575,9 @@ class DockerVM(BaseNode):
await self._start_vnc_process(restart=True)
monitor_process(self._vnc_process, self._vnc_callback)
if self._console_websocket:
await self._console_websocket.close()
self._console_websocket = None
await self._clean_servers()
await self.manager.query("POST", f"containers/{self._cid}/start")
@ -829,9 +840,7 @@ class DockerVM(BaseNode):
f"containers/{self._cid}/attach/ws?stream=1&stdin=1&stdout=1&stderr=1"
)
input_stream.ws = self._console_websocket
output_stream.feed_data(self.name.encode() + b" console is now available... Press RETURN to get started.\r\n")
asyncio.ensure_future(self._read_console_output(self._console_websocket, output_stream))
async def _read_console_output(self, ws, out):
@ -854,13 +863,14 @@ class DockerVM(BaseNode):
out.feed_eof()
await ws.close()
break
await self.stop()
async def reset_console(self):
"""
Reset the console.
"""
if self._console_websocket:
await self._console_websocket.close()
await self._clean_servers()
await self._start_console()
@ -904,6 +914,9 @@ class DockerVM(BaseNode):
"""
try:
if self._console_websocket:
await self._console_websocket.close()
self._console_websocket = None
await self._clean_servers()
await self._stop_ubridge()

@ -252,6 +252,9 @@ class Dynamips(BaseManager):
# look for Dynamips
dynamips_path = self.config.settings.Dynamips.dynamips_path
if not os.path.isabs(dynamips_path):
if sys.platform.startswith("win") and hasattr(sys, "frozen"):
dynamips_dir = os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(sys.executable)), "dynamips"))
os.environ["PATH"] = os.pathsep.join(dynamips_dir) + os.pathsep + os.environ.get("PATH", "")
dynamips_path = shutil.which(dynamips_path)
if not dynamips_path:

@ -76,7 +76,7 @@ class Router(BaseNode):
ghost_flag=False,
):
if not is_ios_hostname_valid(name):
if not ghost_flag and not is_ios_hostname_valid(name):
raise DynamipsError(f"{name} is an invalid name to create a Dynamips node")
super().__init__(

@ -149,13 +149,20 @@ class Qemu(BaseManager):
for arch in archs:
if f.endswith(arch) or f.endswith(f"{arch}.exe") or f.endswith(f"{arch}w.exe"):
qemu_path = os.path.join(path, f)
version = await Qemu.get_qemu_version(qemu_path)
try:
version = await Qemu.get_qemu_version(qemu_path)
except QemuError as e:
log.warning(str(e))
continue
qemus.append({"path": qemu_path, "version": version})
else:
qemu_path = os.path.join(path, f)
version = await Qemu.get_qemu_version(qemu_path)
try:
version = await Qemu.get_qemu_version(qemu_path)
except QemuError as e:
log.warning(str(e))
continue
qemus.append({"path": qemu_path, "version": version})
except OSError:
continue

@ -124,6 +124,8 @@ class QemuVM(BaseNode):
except QemuError:
# If the binary is not found for topologies 1.4 and later
# search via the platform otherwise use the binary name
log.warning(f"Could not find the QEMU binary {qemu_path} on this system, "
f"trying to find one using platform {platform}")
if platform:
self.platform = platform
else:
@ -242,7 +244,7 @@ class QemuVM(BaseNode):
if qemu_path and os.pathsep not in qemu_path:
new_qemu_path = shutil.which(qemu_path, path=os.pathsep.join(self._manager.paths_list()))
if new_qemu_path is None:
raise QemuError(f"QEMU binary path {qemu_path} is not found in the path")
raise QemuError(f"QEMU binary '{qemu_path}' cannot be found on the system")
qemu_path = new_qemu_path
self._check_qemu_path(qemu_path)
@ -289,6 +291,7 @@ class QemuVM(BaseNode):
def platform(self, platform):
self._platform = platform
log.info(f"QEMU VM '{self._name}' [{self._id}] has set the platform {platform}")
self.qemu_path = f"qemu-system-{platform}"
def _disk_setter(self, variable, value):
@ -2640,6 +2643,9 @@ class QemuVM(BaseNode):
command.extend(shlex.split(additional_options))
except ValueError as e:
raise QemuError(f"Invalid additional options: {additional_options} error {e}")
# avoiding mouse offset (see https://github.com/GNS3/gns3-server/issues/2335)
if self._console_type == "vnc":
command.extend(['-machine', 'usb=on', '-device', 'usb-tablet'])
return command
def asdict(self):

@ -142,6 +142,9 @@ class VPCSVM(BaseNode):
vpcs_path = self._manager.config.settings.VPCS.vpcs_path
if not os.path.isabs(vpcs_path):
if sys.platform.startswith("win") and hasattr(sys, "frozen"):
vpcs_dir = os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(sys.executable)), "vpcs"))
os.environ["PATH"] = os.pathsep.join(vpcs_dir) + os.pathsep + os.environ.get("PATH", "")
vpcs_path = shutil.which(vpcs_path)
return vpcs_path

@ -86,9 +86,9 @@ udp_end_port_range = 30000
; uBridge executable location, default: search in PATH
;ubridge_path = ubridge
; Username for compute HTTP authentication.
; Username for compute HTTP authentication, "gns3" is the default if not specified
compute_username = gns3
; Password for compute HTTP authentication.
; Password for compute HTTP authentication, a randomly generated password is used if not specified
compute_password = gns3
; Only allow these interfaces to be used by GNS3, for the Cloud node for example (Linux/OSX only)
@ -102,6 +102,9 @@ default_nat_interface = vmnet10
; Enable the built-in templates
enable_builtin_templates = True
; check if hardware virtualization is used by other emulators (KVM, VMware or VirtualBox)
hardware_virtualization_check = True
[VPCS]
; VPCS executable location, default: search in PATH
;vpcs_path = vpcs
@ -126,10 +129,13 @@ license_check = True
vboxmanage_path = vboxmanage
[VMware]
; Path to the vmrun binary used to manage VMware
; Path to vmrun binary used to manage VMware
vmrun_path = vmrun
; First vmnet interface of the range that can be managed by the GNS3 server
vmnet_start_range = 2
; Last vmnet interface of the range that can be managed by the GNS3 server. It must be maximum 19 on Windows.
vmnet_end_range = 255
; block network traffic originating from the host OS
block_host_traffic = False
[Qemu]
@ -142,9 +148,3 @@ monitor_host = 127.0.0.1
enable_hardware_acceleration = True
; Require hardware acceleration in order to start VMs
require_hardware_acceleration = False
[VMware]
; First vmnet interface of the range that can be managed by the GNS3 server
vmnet_start_range = 2
; Last vmnet interface of the range that can be managed by the GNS3 server. It must be maximum 19 on Windows.
vmnet_end_range = 255

@ -320,6 +320,9 @@ class Controller:
if entry.is_file() and not os.path.exists(full_path):
log.debug(f'Installing {resource_name} resource file "{entry.name}" to "{full_path}"')
shutil.copy(str(entry), os.path.join(dst_path, entry.name))
elif entry.is_dir():
os.makedirs(full_path, exist_ok=True)
Controller.install_resource_files(full_path, os.path.join(resource_name, entry.name))
def _install_base_configs(self):
"""

@ -102,6 +102,11 @@ async def export_project(
continue
_patch_mtime(path)
zstream.write(path, os.path.relpath(path, project._path))
# save empty directories
for directory in dirs:
path = os.path.join(root, directory)
if not os.listdir(path):
zstream.write(path, os.path.relpath(path, project._path))
except FileNotFoundError as e:
log.warning(f"Cannot export local file: {e}")
continue

@ -23,6 +23,7 @@ import shutil
import aiofiles
import itertools
import tempfile
import stat
import gns3server.utils.zipfile_zstd as zipfile_zstd
from .controller_error import ControllerError
@ -235,7 +236,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()
@ -247,7 +248,9 @@ async def _import_images(controller, images_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)
os.chmod(dst, stat.S_IWRITE | stat.S_IREAD | stat.S_IEXEC)
async def _import_snapshots(snapshots_path, project_name, project_id):

@ -152,16 +152,27 @@ class Project:
self._iou_id_lock = asyncio.Lock()
log.debug(f'Project "{self.name}" [{self._id}] loaded')
self.emit_controller_notification("project.created", self.asdict())
def emit_notification(self, action, event):
"""
Emit a notification to all clients using this project.
Emit a project notification to all clients using this project.
:param action: Action name
:param event: Event to send
"""
self.controller.notification.project_emit(action, event, project_id=self.id)
self._controller.notification.project_emit(action, event, project_id=self.id)
def emit_controller_notification(self, action, event):
"""
Emit a controller notification, all clients will see it.
:param action: Action name
:param event: Event to send
"""
self._controller.notification.controller_emit(action, event)
async def update(self, **kwargs):
"""
@ -176,7 +187,7 @@ class Project:
# We send notif only if object has changed
if old_json != self.asdict():
self.emit_notification("project.updated", self.asdict())
self.emit_controller_notification("project.updated", self.asdict())
self.dump()
# update on computes
@ -812,7 +823,8 @@ class Project:
self._clean_pictures()
self._status = "closed"
if not ignore_notification:
self.emit_notification("project.closed", self.asdict())
self.emit_controller_notification("project.closed", self.asdict())
self.reset()
self._closing = False
@ -868,6 +880,7 @@ class Project:
shutil.rmtree(self.path)
except OSError as e:
raise ControllerError(f"Cannot delete project directory {self.path}: {str(e)}")
self.emit_controller_notification("project.deleted", self.asdict())
async def delete_on_computes(self):
"""
@ -1001,7 +1014,7 @@ class Project:
await self.add_drawing(dump=False, **drawing_data)
self.dump()
# We catch all error to be able to rollback the .gns3 to the previous state
# We catch all error to be able to roll back the .gns3 to the previous state
except Exception as e:
for compute in list(self._project_created_on_compute):
try:
@ -1026,6 +1039,7 @@ class Project:
pass
self._loading = False
self.emit_controller_notification("project.opened", self.asdict())
# Should we start the nodes when project is open
if self._auto_start:
# Start all in the background without waiting for completion

@ -72,7 +72,7 @@ class Symbols:
return None
symbol_path = theme.get(symbol)
if symbol_path not in self._symbols_path:
log.warning(f"Default symbol {symbol} was not found")
log.debug(f"Default symbol {symbol} was not found")
return None
return symbol_path
@ -153,6 +153,9 @@ class Symbols:
else:
# return the default computer symbol
log.warning(f"Could not retrieve symbol '{symbol_id}', returning default symbol...")
symbol = self.get_default_symbol("computer", self._current_theme)
if symbol and symbol in self._symbols_path:
return self._symbols_path[symbol]
return self._symbols_path[":/symbols/classic/computer.svg"]
def get_size(self, symbol_id):

@ -58,7 +58,7 @@ class CrashReport:
Report crash to a third party service
"""
DSN = "https://803d7abaf0e865096421affb70ee9368@o19455.ingest.sentry.io/38482"
DSN = "https://395af26fb5b2245d4c9810095aa11da9@o19455.ingest.us.sentry.io/38482"
_instance = None
def __init__(self):

@ -16,7 +16,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from sqlalchemy import Boolean, Column, String, Integer, ForeignKey, PickleType
from sqlalchemy import Boolean, Column, String, Integer, Float, ForeignKey, PickleType
from sqlalchemy.orm import relationship
from .base import BaseTable, generate_uuid, GUID
@ -77,7 +77,7 @@ class DockerTemplate(Template):
extra_hosts = Column(String)
extra_volumes = Column(PickleType)
memory = Column(Integer)
cpus = Column(Integer)
cpus = Column(Float)
custom_adapters = Column(PickleType)
__mapper_args__ = {"polymorphic_identity": "docker", "polymorphic_load": "selectin"}

@ -1,4 +1,5 @@
Generic single-database configuration with an async dbapi.
# Command to generate a revision
alembic upgrade head
alembic revision --autogenerate -m "add fields in table"

@ -29,6 +29,7 @@ import gns3server.utils.get_resource
import os
import sys
import asyncio
def daemonize():
@ -70,7 +71,10 @@ def main():
daemonize()
from gns3server.server import Server
Server().run()
try:
asyncio.run(Server().run())
except KeyboardInterrupt:
pass
if __name__ == "__main__":

@ -43,7 +43,7 @@ class DockerBase(BaseModel):
extra_hosts: Optional[str] = Field(None, description="Docker extra hosts (added to /etc/hosts)")
extra_volumes: Optional[List[str]] = Field(None, description="Additional directories to make persistent")
memory: Optional[int] = Field(None, description="Maximum amount of memory the container can use in MB")
cpus: Optional[int] = Field(None, description="Maximum amount of CPU resources the container can use")
cpus: Optional[float] = Field(None, description="Maximum amount of CPU resources the container can use")
custom_adapters: Optional[List[CustomAdapter]] = Field(None, description="Custom adapters")

@ -138,7 +138,7 @@ class ServerSettings(BaseModel):
udp_start_port_range: int = Field(10000, gt=0, le=65535)
udp_end_port_range: int = Field(30000, gt=0, le=65535)
ubridge_path: str = "ubridge"
compute_username: str = "admin"
compute_username: str = "gns3"
compute_password: SecretStr = SecretStr("")
allowed_interfaces: List[str] = Field(default_factory=list)
default_nat_interface: str = None

@ -49,7 +49,7 @@ class DockerTemplate(TemplateBase):
extra_hosts: Optional[str] = Field("", description="Docker extra hosts (added to /etc/hosts)")
extra_volumes: Optional[List] = Field([], description="Additional directories to make persistent")
memory: Optional[int] = Field(0, description="Maximum amount of memory the container can use in MB")
cpus: Optional[int] = Field(0, description="Maximum amount of CPU resources the container can use")
cpus: Optional[float] = Field(0, description="Maximum amount of CPU resources the container can use")
custom_adapters: Optional[List[CustomAdapter]] = Field(default_factory=list, description="Custom adapters")

@ -182,7 +182,7 @@ class Server:
asyncio.ensure_future(Controller.instance().reload())
else:
log.info(f"Server has got signal {signame}, exiting...")
# send SIGTERM to the server PID so uvicorn can shutdown the process
# send SIGTERM to the server PID so uvicorn can shut down the process
os.kill(os.getpid(), signal.SIGTERM)
except asyncio.CancelledError:
pass
@ -239,7 +239,7 @@ class Server:
log.critical("Can't write pid file %s: %s", path, str(e))
sys.exit(1)
def run(self):
async def run(self):
args = self._parse_arguments(sys.argv[1:])
@ -333,8 +333,7 @@ class Server:
uvicorn_logger.propagate = False
server = uvicorn.Server(config)
loop = asyncio.get_event_loop()
loop.run_until_complete(server.serve())
await server.serve()
except Exception as e:
log.critical(f"Critical error while running the server: {e}", exc_info=1)

@ -17,7 +17,7 @@
from jose import JWTError, jwt
from datetime import datetime, timedelta
from passlib.context import CryptContext
import bcrypt
from typing import Optional
from fastapi import HTTPException, status
@ -29,8 +29,6 @@ import logging
log = logging.getLogger(__name__)
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
DEFAULT_JWT_SECRET_KEY = "efd08eccec3bd0a1be2e086670e5efa90969c68d07e072d7354a76cea5e33d4e"
@ -38,11 +36,13 @@ class AuthService:
def hash_password(self, password: str) -> str:
return pwd_context.hash(password)
salt = bcrypt.gensalt()
hashed_password = bcrypt.hashpw(password=password.encode('utf-8'), salt=salt)
return hashed_password.decode('utf-8')
def verify_password(self, password, hashed_password) -> bool:
return pwd_context.verify(password, hashed_password)
return bcrypt.checkpw(password=password.encode('utf-8'), hashed_password=hashed_password.encode('utf-8'))
def create_access_token(self, username, secret_key: str = None, expires_in: int = 0) -> str:

@ -1493,6 +1493,29 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
ipaddr.js
MIT
Copyright (C) 2011-2017 whitequark <whitequark@whitequark.org>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
marked
MIT
# License information

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -0,0 +1 @@
!function(){"use strict";var e,v={},g={};function n(e){var u=g[e];if(void 0!==u)return u.exports;var t=g[e]={id:e,loaded:!1,exports:{}};return v[e].call(t.exports,t,t.exports,n),t.loaded=!0,t.exports}n.m=v,e=[],n.O=function(u,t,o,a){if(!t){var r=1/0;for(i=0;i<e.length;i++){t=e[i][0],o=e[i][1],a=e[i][2];for(var l=!0,f=0;f<t.length;f++)(!1&a||r>=a)&&Object.keys(n.O).every(function(b){return n.O[b](t[f])})?t.splice(f--,1):(l=!1,a<r&&(r=a));if(l){e.splice(i--,1);var s=o();void 0!==s&&(u=s)}}return u}a=a||0;for(var i=e.length;i>0&&e[i-1][2]>a;i--)e[i]=e[i-1];e[i]=[t,o,a]},n.n=function(e){var u=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(u,{a:u}),u},n.d=function(e,u){for(var t in u)n.o(u,t)&&!n.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:u[t]})},n.f={},n.e=function(e){return Promise.all(Object.keys(n.f).reduce(function(u,t){return n.f[t](e,u),u},[]))},n.u=function(e){return e+".92c7ab880f2504d3.js"},n.miniCssF=function(e){},n.hmd=function(e){return(e=Object.create(e)).children||(e.children=[]),Object.defineProperty(e,"exports",{enumerable:!0,set:function(){throw new Error("ES Modules may not assign module.exports or exports.*, Use ESM export syntax, instead: "+e.id)}}),e},n.o=function(e,u){return Object.prototype.hasOwnProperty.call(e,u)},function(){var e={},u="gns3-web-ui:";n.l=function(t,o,a,i){if(e[t])e[t].push(o);else{var r,l;if(void 0!==a)for(var f=document.getElementsByTagName("script"),s=0;s<f.length;s++){var c=f[s];if(c.getAttribute("src")==t||c.getAttribute("data-webpack")==u+a){r=c;break}}r||(l=!0,(r=document.createElement("script")).type="module",r.charset="utf-8",r.timeout=120,n.nc&&r.setAttribute("nonce",n.nc),r.setAttribute("data-webpack",u+a),r.src=n.tu(t)),e[t]=[o];var d=function(h,b){r.onerror=r.onload=null,clearTimeout(p);var _=e[t];if(delete e[t],r.parentNode&&r.parentNode.removeChild(r),_&&_.forEach(function(m){return m(b)}),h)return h(b)},p=setTimeout(d.bind(null,void 0,{type:"timeout",target:r}),12e4);r.onerror=d.bind(null,r.onerror),r.onload=d.bind(null,r.onload),l&&document.head.appendChild(r)}}}(),n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},function(){var e;n.tt=function(){return void 0===e&&(e={createScriptURL:function(u){return u}},"undefined"!=typeof trustedTypes&&trustedTypes.createPolicy&&(e=trustedTypes.createPolicy("angular#bundler",e))),e}}(),n.tu=function(e){return n.tt().createScriptURL(e)},n.p="",function(){var e={666:0};n.f.j=function(o,a){var i=n.o(e,o)?e[o]:void 0;if(0!==i)if(i)a.push(i[2]);else if(666!=o){var r=new Promise(function(c,d){i=e[o]=[c,d]});a.push(i[2]=r);var l=n.p+n.u(o),f=new Error;n.l(l,function(c){if(n.o(e,o)&&(0!==(i=e[o])&&(e[o]=void 0),i)){var d=c&&("load"===c.type?"missing":c.type),p=c&&c.target&&c.target.src;f.message="Loading chunk "+o+" failed.\n("+d+": "+p+")",f.name="ChunkLoadError",f.type=d,f.request=p,i[1](f)}},"chunk-"+o,o)}else e[o]=0},n.O.j=function(o){return 0===e[o]};var u=function(o,a){var f,s,i=a[0],r=a[1],l=a[2],c=0;if(i.some(function(p){return 0!==e[p]})){for(f in r)n.o(r,f)&&(n.m[f]=r[f]);if(l)var d=l(n)}for(o&&o(a);c<i.length;c++)s=i[c],n.o(e,s)&&e[s]&&e[s][0](),e[s]=0;return n.O(d)},t=self.webpackChunkgns3_web_ui=self.webpackChunkgns3_web_ui||[];t.forEach(u.bind(null,0)),t.push=u.bind(null,t.push.bind(t))}()}();

@ -1 +0,0 @@
!function(){"use strict";var e,v={},g={};function n(e){var u=g[e];if(void 0!==u)return u.exports;var t=g[e]={id:e,loaded:!1,exports:{}};return v[e](t,t.exports,n),t.loaded=!0,t.exports}n.m=v,e=[],n.O=function(u,t,o,a){if(!t){var r=1/0;for(i=0;i<e.length;i++){t=e[i][0],o=e[i][1],a=e[i][2];for(var d=!0,f=0;f<t.length;f++)(!1&a||r>=a)&&Object.keys(n.O).every(function(b){return n.O[b](t[f])})?t.splice(f--,1):(d=!1,a<r&&(r=a));if(d){e.splice(i--,1);var s=o();void 0!==s&&(u=s)}}return u}a=a||0;for(var i=e.length;i>0&&e[i-1][2]>a;i--)e[i]=e[i-1];e[i]=[t,o,a]},n.n=function(e){var u=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(u,{a:u}),u},n.d=function(e,u){for(var t in u)n.o(u,t)&&!n.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:u[t]})},n.f={},n.e=function(e){return Promise.all(Object.keys(n.f).reduce(function(u,t){return n.f[t](e,u),u},[]))},n.u=function(e){return e+".92c7ab880f2504d3.js"},n.miniCssF=function(e){},n.hmd=function(e){return(e=Object.create(e)).children||(e.children=[]),Object.defineProperty(e,"exports",{enumerable:!0,set:function(){throw new Error("ES Modules may not assign module.exports or exports.*, Use ESM export syntax, instead: "+e.id)}}),e},n.o=function(e,u){return Object.prototype.hasOwnProperty.call(e,u)},function(){var e={},u="gns3-web-ui:";n.l=function(t,o,a,i){if(e[t])e[t].push(o);else{var r,d;if(void 0!==a)for(var f=document.getElementsByTagName("script"),s=0;s<f.length;s++){var c=f[s];if(c.getAttribute("src")==t||c.getAttribute("data-webpack")==u+a){r=c;break}}r||(d=!0,(r=document.createElement("script")).type="module",r.charset="utf-8",r.timeout=120,n.nc&&r.setAttribute("nonce",n.nc),r.setAttribute("data-webpack",u+a),r.src=n.tu(t)),e[t]=[o];var l=function(h,b){r.onerror=r.onload=null,clearTimeout(p);var _=e[t];if(delete e[t],r.parentNode&&r.parentNode.removeChild(r),_&&_.forEach(function(m){return m(b)}),h)return h(b)},p=setTimeout(l.bind(null,void 0,{type:"timeout",target:r}),12e4);r.onerror=l.bind(null,r.onerror),r.onload=l.bind(null,r.onload),d&&document.head.appendChild(r)}}}(),n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},function(){var e;n.tt=function(){return void 0===e&&(e={createScriptURL:function(u){return u}},"undefined"!=typeof trustedTypes&&trustedTypes.createPolicy&&(e=trustedTypes.createPolicy("angular#bundler",e))),e}}(),n.tu=function(e){return n.tt().createScriptURL(e)},n.p="",function(){var e={666:0};n.f.j=function(o,a){var i=n.o(e,o)?e[o]:void 0;if(0!==i)if(i)a.push(i[2]);else if(666!=o){var r=new Promise(function(c,l){i=e[o]=[c,l]});a.push(i[2]=r);var d=n.p+n.u(o),f=new Error;n.l(d,function(c){if(n.o(e,o)&&(0!==(i=e[o])&&(e[o]=void 0),i)){var l=c&&("load"===c.type?"missing":c.type),p=c&&c.target&&c.target.src;f.message="Loading chunk "+o+" failed.\n("+l+": "+p+")",f.name="ChunkLoadError",f.type=l,f.request=p,i[1](f)}},"chunk-"+o,o)}else e[o]=0},n.O.j=function(o){return 0===e[o]};var u=function(o,a){var f,s,i=a[0],r=a[1],d=a[2],c=0;if(i.some(function(p){return 0!==e[p]})){for(f in r)n.o(r,f)&&(n.m[f]=r[f]);if(d)var l=d(n)}for(o&&o(a);c<i.length;c++)s=i[c],n.o(e,s)&&e[s]&&e[s][0](),e[s]=0;return n.O(l)},t=self.webpackChunkgns3_web_ui=self.webpackChunkgns3_web_ui||[];t.forEach(u.bind(null,0)),t.push=u.bind(null,t.push.bind(t))}()}();

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -188,7 +188,12 @@ class AsyncioTelnetServer:
sock = network_writer.get_extra_info("socket")
sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
# log.debug("New connection from {}".format(sock.getpeername()))
# 60 sec keep alives, close tcp session after 4 missed
# Will keep a firewall from aging out telnet console.
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 60)
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 10)
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 4)
#log.debug("New connection from {}".format(sock.getpeername()))
# Keep track of connected clients
connection = self._connection_factory(network_reader, network_writer, self._window_size_changed_callback)
@ -286,9 +291,17 @@ class AsyncioTelnetServer:
reader_read = await self._get_reader(network_reader)
# Replicate the output on all clients
for connection in self._connections.values():
connection.writer.write(data)
await connection.writer.drain()
for connection_key in list(self._connections.keys()):
client_info = connection_key.get_extra_info("socket").getpeername()
connection = self._connections[connection_key]
try:
connection.writer.write(data)
await asyncio.wait_for(connection.writer.drain(), timeout=10)
except:
log.debug(f"Timeout while sending data to client: {client_info}, closing and removing from connection table.")
connection.close()
del self._connections[connection_key]
async def _read(self, cmd, buffer, location, reader):
""" Reads next op from the buffer or reader"""

@ -22,8 +22,8 @@
# or negative for a release candidate or beta (after the base version
# number has been incremented)
__version__ = "3.0.0.dev10"
__version_info__ = (3, 0, 0, 99)
__version__ = "3.0.0b2"
__version_info__ = (3, 0, 0, -99)
if "dev" in __version__:
try:

@ -1,5 +1,9 @@
version: 2
build:
image: latest
os: "ubuntu-22.04"
tools:
python: "3.12"
python:
version: 3.6
sphinx:
configuration: docs/conf.py

@ -1,24 +1,23 @@
uvicorn==0.23.2
fastapi==0.104.0
python-multipart==0.0.6
uvicorn==0.29.0
fastapi==0.110.1
python-multipart==0.0.9
websockets==12.0
aiohttp>=3.8.6,<3.9; python_version < '3.12'
aiohttp==3.9.0b0; python_version == '3.12'
aiohttp==3.9.3
async-timeout==4.0.3
aiofiles==23.2.1
Jinja2>=3.1.2,<3.2
sentry-sdk==1.32.0,<1.33
psutil==5.9.6
distro>=1.8.0
Jinja2>=3.1.3,<3.2
sentry-sdk==1.40.6,<1.41
psutil==5.9.8
distro>=1.9.0
py-cpuinfo==9.0.0
sqlalchemy==2.0.22
aiosqlite==0.19.0
alembic==1.12.0
passlib[bcrypt]==1.7.4
sqlalchemy==2.0.29
aiosqlite==0.20.0
alembic==1.12.1
bcrypt==4.1.2
python-jose==3.3.0
email-validator==2.0.0.post2
email-validator==2.1.1
watchfiles==0.21.0
zstandard==0.21.0
platformdirs==3.11.0
zstandard==0.22.0
platformdirs==4.2.0
importlib-resources>=1.3; python_version <= '3.9'
truststore>=0.8.0; python_version >= '3.10'
truststore>=0.8.0; python_version >= '3.10'

@ -163,9 +163,9 @@ log "Install GNS3 packages"
apt-get install -y gns3-server
log "Create user GNS3 with /opt/gns3 as home directory"
if [ ! -d "/opt/gns3/" ]
if [ ! -d "/opt/gns3" ]
then
useradd -m -d /opt/gns3/ gns3
useradd -m -d /opt/gns3 gns3
fi
@ -304,6 +304,11 @@ log "GNS3 installed with success"
if [ $WELCOME_SETUP == 1 ]
then
cat <<EOFI > /etc/sudoers.d/gns3
gns3 ALL = (ALL) NOPASSWD: /usr/bin/apt-key
gns3 ALL = (ALL) NOPASSWD: /usr/bin/apt-get
gns3 ALL = (ALL) NOPASSWD: /usr/sbin/reboot
EOFI
NEEDRESTART_MODE=a apt-get install -y net-tools
NEEDRESTART_MODE=a apt-get install -y python3-pip
NEEDRESTART_MODE=a apt-get install -y dialog
@ -462,4 +467,4 @@ NEEDRESTART_MODE=a apt-get upgrade
python3 -c 'import sys; sys.path.append("/usr/local/bin/"); import welcome; ws = welcome.Welcome_dialog(); ws.repair_remote_install()'
cd /opt/gns3
su gns3
fi
fi

@ -163,19 +163,38 @@ class Welcome_dialog:
def update(self, force=False):
if not force:
if self.display.yesno("PLEASE SNAPSHOT THE VM BEFORE RUNNING THE UPGRADE IN CASE OF FAILURE. The server will reboot at the end of the upgrade process. Continue?") != self.display.OK:
if self.display.yesno("It is recommended to ensure all Nodes are shutdown before upgrading. Continue?") != self.display.OK:
return
release = self.get_release()
if release == "2.2":
if self.display.yesno("It is recommended to run GNS3 version 2.2 with lastest GNS3 VM based on Ubuntu 18.04 LTS, please download this VM from our website or continue at your own risk!") != self.display.OK:
code, option = self.display.menu("Select an option",
choices=[("Upgrade GNS3", "Upgrades only the GNS3 pakage and dependences."),
("Upgrade All", "Upgrades all avaiable packages"),
("Dist Upgrade", "Upgrades all avaiable packages and the Linux Kernel. Requires a reboot.")])
if code == Dialog.OK:
if option == "Upgrade GNS3":
ret = os.system(
"sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A2E3EF7B \
&& sudo apt-get update \
&& sudo apt-get install -y --only-upgrade gns3-server"
)
elif option == "Upgrade All":
ret = os.system(
'sudo apt-key adv --refresh-keys --keyserver keyserver.ubuntu.com \
&& sudo apt-get update \
&& sudo apt-get upgrade --yes --force-yes -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold"'
)
elif option == "Dist Upgrade":
ret = os.system(
'sudo apt-key adv --refresh-keys --keyserver keyserver.ubuntu.com \
&& sudo apt-get update \
&& sudo apt-get dist-upgrade --yes --force-yes -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold"'
)
if ret != 0:
print("ERROR DURING UPGRADE PROCESS PLEASE TAKE A SCREENSHOT IF YOU NEED SUPPORT")
time.sleep(15)
return
if release.endswith("dev"):
ret = os.system("curl -Lk https://raw.githubusercontent.com/GNS3/gns3-vm/unstable/scripts/update_{}.sh > /tmp/update.sh && bash -x /tmp/update.sh".format(release))
else:
ret = os.system("curl -Lk https://raw.githubusercontent.com/GNS3/gns3-vm/master/scripts/update_{}.sh > /tmp/update.sh && bash -x /tmp/update.sh".format(release))
if ret != 0:
print("ERROR DURING UPGRADE PROCESS PLEASE TAKE A SCREENSHOT IF YOU NEED SUPPORT")
time.sleep(15)
if option == "Dist Upgrade":
if self.display.yesno("Reboot now?") == self.display.OK:
os.system("sudo reboot now")
def migrate(self):
@ -438,7 +457,8 @@ Images and projects are located in /opt/gns3
self.display.clear()
if code == Dialog.OK:
if tag == "Shell":
os.execvp("bash", ['/bin/bash'])
print("Type: 'welcome.py' to get back to the dialog menu.")
sys.exit(0)
elif tag == "Version":
self.mode()
elif tag == "Restore":

@ -223,7 +223,8 @@ async def test_install_busybox():
with patch("gns3server.compute.docker.shutil.which", return_value="/usr/bin/busybox"):
with asyncio_patch("gns3server.compute.docker.asyncio.create_subprocess_exec", return_value=mock_process) as create_subprocess_mock:
with patch("gns3server.compute.docker.shutil.copy2") as copy2_mock:
await Docker.install_busybox()
dst_dir = Docker.resources_path()
await Docker.install_busybox(dst_dir)
create_subprocess_mock.assert_called_with(
"ldd",
"/usr/bin/busybox",
@ -244,7 +245,8 @@ async def test_install_busybox_dynamic_linked():
with patch("gns3server.compute.docker.shutil.which", return_value="/usr/bin/busybox"):
with asyncio_patch("gns3server.compute.docker.asyncio.create_subprocess_exec", return_value=mock_process):
with pytest.raises(DockerError) as e:
await Docker.install_busybox()
dst_dir = Docker.resources_path()
await Docker.install_busybox(dst_dir)
assert str(e.value) == "No busybox executable could be found"
@ -254,5 +256,6 @@ async def test_install_busybox_no_executables():
with patch("gns3server.compute.docker.os.path.isfile", return_value=False):
with patch("gns3server.compute.docker.shutil.which", return_value=None):
with pytest.raises(DockerError) as e:
await Docker.install_busybox()
dst_dir = Docker.resources_path()
await Docker.install_busybox(dst_dir)
assert str(e.value) == "No busybox executable could be found"

@ -29,7 +29,6 @@ from gns3server.compute.ubridge.ubridge_error import UbridgeNamespaceError
from gns3server.compute.docker.docker_vm import DockerVM
from gns3server.compute.docker.docker_error import DockerError, DockerHttp404Error
from gns3server.compute.docker import Docker
from gns3server.utils.get_resource import get_resource
from unittest.mock import patch, MagicMock, call
@ -108,7 +107,7 @@ async def test_create(compute_project, manager):
"Mounts": [
{
"Type": "bind",
"Source": get_resource("compute/docker/resources"),
"Source": Docker.resources_path(),
"Target": "/gns3",
"ReadOnly": True
},
@ -158,7 +157,7 @@ async def test_create_with_tag(compute_project, manager):
"Mounts": [
{
"Type": "bind",
"Source": get_resource("compute/docker/resources"),
"Source": Docker.resources_path(),
"Target": "/gns3",
"ReadOnly": True
},
@ -211,7 +210,7 @@ async def test_create_vnc(compute_project, manager):
"Mounts": [
{
"Type": "bind",
"Source": get_resource("compute/docker/resources"),
"Source": Docker.resources_path(),
"Target": "/gns3",
"ReadOnly": True
},
@ -362,7 +361,7 @@ async def test_create_start_cmd(compute_project, manager):
"Mounts": [
{
"Type": "bind",
"Source": get_resource("compute/docker/resources"),
"Source": Docker.resources_path(),
"Target": "/gns3",
"ReadOnly": True
},
@ -474,7 +473,7 @@ async def test_create_image_not_available(compute_project, manager):
"Mounts": [
{
"Type": "bind",
"Source": get_resource("compute/docker/resources"),
"Source": Docker.resources_path(),
"Target": "/gns3",
"ReadOnly": True
},
@ -529,7 +528,7 @@ async def test_create_with_user(compute_project, manager):
"Mounts": [
{
"Type": "bind",
"Source": get_resource("compute/docker/resources"),
"Source": Docker.resources_path(),
"Target": "/gns3",
"ReadOnly": True
},
@ -627,7 +626,7 @@ async def test_create_with_extra_volumes_duplicate_1_image(compute_project, mana
"Mounts": [
{
"Type": "bind",
"Source": get_resource("compute/docker/resources"),
"Source": Docker.resources_path(),
"Target": "/gns3",
"ReadOnly": True
},
@ -682,7 +681,7 @@ async def test_create_with_extra_volumes_duplicate_2_user(compute_project, manag
"Mounts": [
{
"Type": "bind",
"Source": get_resource("compute/docker/resources"),
"Source": Docker.resources_path(),
"Target": "/gns3",
"ReadOnly": True
},
@ -737,7 +736,7 @@ async def test_create_with_extra_volumes_duplicate_3_subdir(compute_project, man
"Mounts": [
{
"Type": "bind",
"Source": get_resource("compute/docker/resources"),
"Source": Docker.resources_path(),
"Target": "/gns3",
"ReadOnly": True
},
@ -792,7 +791,7 @@ async def test_create_with_extra_volumes_duplicate_4_backslash(compute_project,
"Mounts": [
{
"Type": "bind",
"Source": get_resource("compute/docker/resources"),
"Source": Docker.resources_path(),
"Target": "/gns3",
"ReadOnly": True
},
@ -847,7 +846,7 @@ async def test_create_with_extra_volumes_duplicate_5_subdir_issue_1595(compute_p
"Mounts": [
{
"Type": "bind",
"Source": get_resource("compute/docker/resources"),
"Source": Docker.resources_path(),
"Target": "/gns3",
"ReadOnly": True
},
@ -897,7 +896,7 @@ async def test_create_with_extra_volumes_duplicate_6_subdir_issue_1595(compute_p
"Mounts": [
{
"Type": "bind",
"Source": get_resource("compute/docker/resources"),
"Source": Docker.resources_path(),
"Target": "/gns3",
"ReadOnly": True
},
@ -953,7 +952,7 @@ async def test_create_with_extra_volumes(compute_project, manager):
"Mounts": [
{
"Type": "bind",
"Source": get_resource("compute/docker/resources"),
"Source": Docker.resources_path(),
"Target": "/gns3",
"ReadOnly": True
},
@ -1058,7 +1057,7 @@ async def test_unpause(vm):
@pytest.mark.asyncio
async def test_start(vm, manager, free_console_port):
async def test_start(vm, manager, free_console_port, tmpdir):
assert vm.status != "started"
vm.adapters = 1
@ -1087,6 +1086,32 @@ async def test_start(vm, manager, free_console_port):
assert vm.status == "started"
@pytest.mark.asyncio
async def test_resources_installed(vm, manager, tmpdir):
assert vm.status != "started"
vm.adapters = 1
docker_resources_path = os.path.join(tmpdir, "docker", "resources")
os.makedirs(docker_resources_path, exist_ok=True)
manager.resources_path = MagicMock(return_value=docker_resources_path)
with asyncio_patch("gns3server.compute.docker.DockerVM._get_container_state", return_value="stopped"):
with asyncio_patch("gns3server.compute.docker.Docker.query"):
with asyncio_patch("gns3server.compute.docker.DockerVM._start_ubridge"):
with asyncio_patch("gns3server.compute.docker.DockerVM._get_namespace", return_value=42):
with asyncio_patch("gns3server.compute.docker.DockerVM._add_ubridge_connection"):
with asyncio_patch("gns3server.compute.docker.DockerVM._start_console"):
await vm.start()
assert vm.status == "started"
assert os.path.exists(os.path.join(docker_resources_path, "init.sh"))
assert os.path.exists(os.path.join(docker_resources_path, "run-cmd.sh"))
assert os.path.exists(os.path.join(docker_resources_path, "bin", "busybox"))
assert os.path.exists(os.path.join(docker_resources_path, "bin", "udhcpc"))
assert os.path.exists(os.path.join(docker_resources_path, "etc", "udhcpc", "default.script"))
@pytest.mark.asyncio
async def test_start_namespace_failed(vm, manager, free_console_port):
@ -1213,7 +1238,7 @@ async def test_update(vm):
"Mounts": [
{
"Type": "bind",
"Source": get_resource("compute/docker/resources"),
"Source": Docker.resources_path(),
"Target": "/gns3",
"ReadOnly": True
},
@ -1294,7 +1319,7 @@ async def test_update_running(vm):
"Mounts": [
{
"Type": "bind",
"Source": get_resource("compute/docker/resources"),
"Source": Docker.resources_path(),
"Target": "/gns3",
"ReadOnly": True
},
@ -1583,7 +1608,7 @@ async def test_mount_binds(vm):
assert vm._mount_binds(image_infos) == [
{
"Type": "bind",
"Source": get_resource("compute/docker/resources"),
"Source": Docker.resources_path(),
"Target": "/gns3",
"ReadOnly": True
},
@ -1727,7 +1752,7 @@ async def test_cpus(compute_project, manager):
"Mounts": [
{
"Type": "bind",
"Source": get_resource("compute/docker/resources"),
"Source": Docker.resources_path(),
"Target": "/gns3",
"ReadOnly": True
},
@ -1777,7 +1802,7 @@ async def test_memory(compute_project, manager):
"Mounts": [
{
"Type": "bind",
"Source": get_resource("compute/docker/resources"),
"Source": Docker.resources_path(),
"Target": "/gns3",
"ReadOnly": True
},

@ -172,8 +172,8 @@ async def client(base_client: AsyncClient) -> AsyncClient:
@pytest_asyncio.fixture
async def compute_client(base_client: AsyncClient) -> AsyncClient:
# default compute username is 'admin'
base64_credentials = base64.b64encode(b"admin:").decode("ascii")
# default compute username is 'gns3'
base64_credentials = base64.b64encode(b"gns3:").decode("ascii")
base_client.headers = {
**base_client.headers,
"Authorization": f"Basic {base64_credentials}",

@ -224,7 +224,9 @@ async def test_compute_httpQuery_project(compute):
response = MagicMock()
with asyncio_patch("aiohttp.ClientSession.request", return_value=response) as mock:
response.status = 200
project = Project(name="Test")
with patch('gns3server.controller.project.Project.emit_controller_notification') as mock_notification:
project = Project(name="Test")
mock_notification.assert_called()
await compute.post("/projects", project)
mock.assert_called_with("POST", "https://example.com:84/v3/compute/projects", data=json.dumps(project.asdict()), headers={'content-type': 'application/json'}, auth=None, chunked=None, timeout=20)
await compute.close()

@ -114,6 +114,7 @@ async def test_export(tmpdir, project):
f.write("HELLO")
with open(os.path.join(path, "vm-1", "dynamips", "test_log.txt"), 'w+') as f:
f.write("LOG")
os.makedirs(os.path.join(path, "vm-1", "dynamips", "empty-dir"))
os.makedirs(os.path.join(path, "project-files", "snapshots"))
with open(os.path.join(path, "project-files", "snapshots", "test"), 'w+') as f:
f.write("WORLD")
@ -130,6 +131,7 @@ async def test_export(tmpdir, project):
assert 'test.gns3' not in myzip.namelist()
assert 'project.gns3' in myzip.namelist()
assert 'vm-1/dynamips/empty-dir/' in myzip.namelist()
assert 'project-files/snapshots/test' not in myzip.namelist()
assert 'vm-1/dynamips/test_log.txt' not in myzip.namelist()

@ -47,16 +47,20 @@ async def node(controller, project):
@pytest.mark.asyncio
async def test_affect_uuid():
p = Project(name="Test")
assert len(p.id) == 36
p = Project(project_id='00010203-0405-0607-0809-0a0b0c0d0e0f', name="Test 2")
assert p.id == '00010203-0405-0607-0809-0a0b0c0d0e0f'
with patch('gns3server.controller.project.Project.emit_controller_notification') as mock_notification:
p = Project(name="Test")
mock_notification.assert_called()
assert len(p.id) == 36
p = Project(project_id='00010203-0405-0607-0809-0a0b0c0d0e0f', name="Test 2")
assert p.id == '00010203-0405-0607-0809-0a0b0c0d0e0f'
@pytest.mark.asyncio
async def test_json():
p = Project(name="Test")
with patch('gns3server.controller.project.Project.emit_controller_notification') as mock_notification:
p = Project(name="Test")
mock_notification.assert_called()
assert p.asdict() == {
"name": "Test",
@ -85,11 +89,11 @@ async def test_json():
async def test_update(controller):
project = Project(controller=controller, name="Hello")
project.emit_notification = MagicMock()
project.emit_controller_notification = MagicMock()
assert project.name == "Hello"
await project.update(name="World")
assert project.name == "World"
project.emit_notification.assert_any_call("project.updated", project.asdict())
project.emit_controller_notification.assert_any_call("project.updated", project.asdict())
@pytest.mark.asyncio
@ -110,7 +114,9 @@ async def test_path(projects_dir):
directory = projects_dir
with patch("gns3server.utils.path.get_default_project_directory", return_value=directory):
p = Project(project_id=str(uuid4()), name="Test")
with patch('gns3server.controller.project.Project.emit_controller_notification') as mock_notification:
p = Project(project_id=str(uuid4()), name="Test")
mock_notification.assert_called()
assert p.path == os.path.join(directory, p.id)
assert os.path.exists(os.path.join(directory, p.id))
@ -129,25 +135,29 @@ def test_path_exist(tmpdir):
@pytest.mark.asyncio
async def test_init_path(projects_dir):
project_id = str(uuid4())
p = Project(project_id=project_id, name="Test")
assert p.path == os.path.join(projects_dir, project_id)
with patch('gns3server.controller.project.Project.emit_controller_notification') as mock_notification:
project_id = str(uuid4())
p = Project(project_id=project_id, name="Test")
mock_notification.assert_called()
assert p.path == os.path.join(projects_dir, project_id)
@pytest.mark.asyncio
async def test_changing_path_with_quote_not_allowed(projects_dir):
with pytest.raises(ControllerForbiddenError):
p = Project(project_id=str(uuid4()), name="Test")
p.path = os.path.join(projects_dir, "project\"53")
with patch('gns3server.controller.project.Project.emit_controller_notification'):
p = Project(project_id=str(uuid4()), name="Test")
p.path = os.path.join(projects_dir, "project\"53")
@pytest.mark.asyncio
async def test_captures_directory(tmpdir):
p = Project(name="Test")
assert p.captures_directory == str(p.path + os.path.sep + "project-files" + os.path.sep + "captures")
assert os.path.exists(p.captures_directory)
with patch('gns3server.controller.project.Project.emit_controller_notification'):
p = Project(name="Test")
assert p.captures_directory == str(p.path + os.path.sep + "project-files" + os.path.sep + "captures")
assert os.path.exists(p.captures_directory)
@pytest.mark.asyncio
@ -682,38 +692,41 @@ async def test_dump(projects_dir):
directory = projects_dir
with patch("gns3server.utils.path.get_default_project_directory", return_value=directory):
p = Project(project_id='00010203-0405-0607-0809-0a0b0c0d0e0f', name="Test")
p.dump()
with open(os.path.join(directory, p.id, "Test.gns3")) as f:
content = f.read()
assert "00010203-0405-0607-0809-0a0b0c0d0e0f" in content
with patch('gns3server.controller.project.Project.emit_controller_notification'):
p = Project(project_id='00010203-0405-0607-0809-0a0b0c0d0e0f', name="Test")
p.dump()
with open(os.path.join(directory, p.id, "Test.gns3")) as f:
content = f.read()
assert "00010203-0405-0607-0809-0a0b0c0d0e0f" in content
@pytest.mark.asyncio
async def test_open_close(controller):
project = Project(controller=controller, name="Test")
assert project.status == "opened"
await project.close()
project.start_all = AsyncioMagicMock()
await project.open()
assert not project.start_all.called
assert project.status == "opened"
project.emit_notification = MagicMock()
await project.close()
assert project.status == "closed"
project.emit_notification.assert_any_call("project.closed", project.asdict())
with patch('gns3server.controller.project.Project.emit_controller_notification'):
project = Project(controller=controller, name="Test")
assert project.status == "opened"
await project.close()
project.start_all = AsyncioMagicMock()
await project.open()
assert not project.start_all.called
assert project.status == "opened"
project.emit_controller_notification = MagicMock()
await project.close()
assert project.status == "closed"
project.emit_controller_notification.assert_any_call("project.closed", project.asdict())
@pytest.mark.asyncio
async def test_open_auto_start(controller):
project = Project(controller=controller, name="Test", auto_start=True)
assert project.status == "opened"
await project.close()
project.start_all = AsyncioMagicMock()
await project.open()
assert project.start_all.called
with patch('gns3server.controller.project.Project.emit_controller_notification'):
project = Project(controller=controller, name="Test", auto_start=True)
assert project.status == "opened"
await project.close()
project.start_all = AsyncioMagicMock()
await project.open()
assert project.start_all.called
def test_is_running(project, node):

@ -18,7 +18,7 @@
import json
import uuid
import pytest
from unittest.mock import MagicMock
from unittest.mock import MagicMock, patch
from tests.utils import asyncio_patch
from gns3server.controller.project import Project
@ -31,35 +31,36 @@ from gns3server.version import __version__
@pytest.mark.asyncio
async def test_project_to_topology_empty(tmpdir):
project = Project(name="Test")
topo = project_to_topology(project)
assert topo == {
"project_id": project.id,
"name": "Test",
"auto_start": False,
"auto_close": True,
"auto_open": False,
"scene_width": 2000,
"scene_height": 1000,
"revision": GNS3_FILE_FORMAT_REVISION,
"zoom": 100,
"show_grid": False,
"show_interface_labels": False,
"show_layers": False,
"snap_to_grid": False,
"grid_size": 75,
"drawing_grid_size": 25,
"topology": {
"nodes": [],
"links": [],
"computes": [],
"drawings": []
},
"type": "topology",
"supplier": None,
"variables": None,
"version": __version__
}
with patch('gns3server.controller.project.Project.emit_controller_notification'):
project = Project(name="Test")
topo = project_to_topology(project)
assert topo == {
"project_id": project.id,
"name": "Test",
"auto_start": False,
"auto_close": True,
"auto_open": False,
"scene_width": 2000,
"scene_height": 1000,
"revision": GNS3_FILE_FORMAT_REVISION,
"zoom": 100,
"show_grid": False,
"show_interface_labels": False,
"show_layers": False,
"snap_to_grid": False,
"grid_size": 75,
"drawing_grid_size": 25,
"topology": {
"nodes": [],
"links": [],
"computes": [],
"drawings": []
},
"type": "topology",
"supplier": None,
"variables": None,
"version": __version__
}
@pytest.mark.asyncio

Loading…
Cancel
Save