Compare commits

...

71 Commits

Author SHA1 Message Date
Jeremy Grossmann 5bab4131e1
Merge pull request #2361 from GNS3/release/v2.2.46
4 months ago
grossmj e8a7e80456
Release v2.2.46
4 months ago
grossmj ab05784c33
Sync appliances
4 months ago
grossmj 6983e686ca
Bundle web-ui v2.2.46
4 months ago
grossmj 3631202ce5
Merge remote-tracking branch 'origin/2.2' into 2.2
4 months ago
grossmj 8d8a3247c4
Bundle web-ui v2.2.46
4 months ago
Jeremy Grossmann d100a132d6
Merge pull request #2360 from GNS3/bugfix/3555
4 months ago
Jeremy Grossmann 52638a9a8d
Merge branch '2.2' into bugfix/3555
4 months ago
grossmj 9a5af82a28
Save empty directories when exporting a project
4 months ago
Jeremy Grossmann 14a618766d
Merge pull request #2359 from GNS3/bugfix/2357
4 months ago
grossmj 0137688ba7
Test busybox is installed
4 months ago
grossmj 84db3b9996
Fix not all Docker resources are copied to a writable location
4 months ago
Jeremy Grossmann 0a08031d85
Merge pull request #2355 from GNS3/refactor/busybox
4 months ago
grossmj 1a53c9aabf
Backport from v3: install Docker resources in a writable location at runtime.
4 months ago
grossmj 1f5085608c
Use Docker API v1.24 to get version.
4 months ago
Jeremy Grossmann 16f72b4d3d
Merge pull request #2347 from SpikefishSolutions/DeadTelnetConsoleFix
4 months ago
grossmj 3ced41633f
Upgrade dependencies
4 months ago
Jeremy Grossmann 4fa10be5aa
Merge pull request #2353 from GNS3/feature/drop-python-3.6
4 months ago
grossmj f050fc7e00
Change runtime checks for Python version
4 months ago
grossmj c93aafc9af
Fix aiohttp dependency for Python 3.7
4 months ago
grossmj 93520b4d6c
Do not test with Python 3.6
4 months ago
grossmj 1fb0260ae6
Drop support for Python 3.6
4 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.
4 months ago
grossmj 17aabd6cda
Merge branch 'master' into 2.2
5 months ago
grossmj e2a3d391d8
Merge remote-tracking branch 'origin/2.2' into 2.2
5 months ago
John Fleming 6c5f54fe57
Update telnet_server.py
5 months ago
John Fleming 082fbee1bd
Update telnet_server.py
5 months ago
Xatrekak 6d97feaced
Fixed updating system and GNS3.
5 months ago
Dustin 8d35089661
Update welcome.py
5 months ago
Dustin 6455f62447
Update remote-install.sh
5 months ago
grossmj 2fb3b1ebab
Use Python 3.8 to publish API doc
5 months ago
grossmj 58399a9fa8
Upgrade sentry-sdk, psutil and distro dependencies
5 months ago
grossmj 907b305ecf
Development on 2.2.46.dev1
5 months ago
grossmj 57f92db124
Release v2.2.45
5 months ago
grossmj 0f6f943a83
Bundle web-ui v2.2.45
5 months ago
grossmj 2cb76b2274
Remove old web-ui files
5 months ago
grossmj 2d2db52a8b
Bundle web-ui v2.2.45
5 months ago
grossmj a8d0818e07
Sync appliances
5 months ago
grossmj f7eb2492d9
Fix mouse offset issues with VNC in Qemu. Fixes #2335
5 months ago
grossmj 218522b08c
Fix issues when generating docs
5 months ago
grossmj 470f13f448
Update readthedocs.yml to use Python 3.12
5 months ago
grossmj 317aa669ac
Update readthedocs.yml
5 months ago
grossmj 8d160ad5ed
Update documentation
5 months ago
grossmj e3493870b2
Add project.created, project.opened and project.deleted controller notification stream.
5 months ago
grossmj d466c85385
Do not stop searching for Qemu binaries if one binary cannot be executed. Ref #2306
5 months ago
grossmj 35d4391fc0
Fix Ethernet switch and Ethernet hub port validations. Fixes #2334
5 months ago
grossmj eea0ab69bd
Bundle dev version of the web-ui
5 months ago
grossmj 22ade94118
Update CORS policy
5 months ago
grossmj 08ee40548f
Add custom executable paths on Windows
5 months ago
grossmj 7f05a06766
Upgrade sentry-sdk and aiohttp
5 months ago
grossmj 9fd2f58ef7
Development in 2.2.45.dev3
5 months ago
grossmj b9b802ebab
Release v2.2.44.1
5 months ago
grossmj 1d86e322e9
Do not compute checksums on macOS
5 months ago
grossmj 8eb5f10971
Add multiprocessing.set_start_method()
5 months ago
grossmj cc4783ab98
Bump version to v2.2.45.dev2
5 months ago
grossmj 19792f328d
Have freeze support for macOS as well
5 months ago
grossmj 596d1274a3
Catch exceptions when computing image checksums. Ref https://github.com/GNS3/gns3-server/issues/2228
5 months ago
grossmj 2f765747b8
Add freeze_support() for multiprocessing
5 months ago
grossmj 4fe57b6a15
Development on 2.2.45.dev1
5 months ago
grossmj 1177626a53
Release v2.2.44
5 months ago
grossmj c4cc346864
Sync appliances
5 months ago
grossmj 8915dfffa5
Bundle web-ui v2.2.44
5 months ago
grossmj ff027ebd17
Upgrade sentry-sdk
5 months ago
grossmj 27d5ac537f
Non-blocking checksums computation when server starts. Fixes #2228
5 months ago
grossmj fe246cd413
Fix timeout issue when creating Qemu disk image. Fixes https://github.com/GNS3/gns3-server/issues/2313
5 months ago
grossmj 2bbb560b8e
Support for web socket console over HTTPS
5 months ago
grossmj 1624c7d6ad
Add back script create_cert.sh
5 months ago
grossmj 2a3bb81076
Bundle web-ui
5 months ago
grossmj e0a0dd83d7
Use Python 3.8 in appveyor.yml
5 months ago
grossmj 16ea395618
Upgrade sentry-sdk, psutil and distro dependencies
5 months ago
grossmj 7b7af33920
Development on 2.2.46.dev1
5 months ago

@ -13,10 +13,10 @@ on:
jobs: jobs:
build: build:
runs-on: ubuntu-20.04 # Downgrade Ubuntu to 20.04 to fix missing Python 3.6 runs-on: ubuntu-22.04
strategy: strategy:
matrix: matrix:
python-version: ["3.6", "3.7", "3.8", "3.9", "3.10", "3.11", "3.12"] python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"]
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3

@ -1,5 +1,18 @@
# Change Log # Change Log
## 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 ## 2.2.45 12/01/2024
* Bundle web-ui v2.2.45 * Bundle web-ui v2.2.45

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

@ -47,6 +47,34 @@
"filesize": 1990983680, "filesize": 1990983680,
"download_url": "https://software.cisco.com/download/home/286312239/type/282088129/release/10.1(1)" "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", "filename": "nexus9500v.9.3.9.qcow2",
"version": "9500v 9.3.9", "version": "9500v 9.3.9",
@ -226,6 +254,34 @@
"hda_disk_image": "nexus9300v.10.1.1.qcow2" "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", "name": "9500v 9.3.9",
"images": { "images": {

@ -29,6 +29,13 @@
"kvm": "allow" "kvm": "allow"
}, },
"images": [ "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", "filename": "FAZ_VM64_KVM-v7.4.1-build2308-FORTINET.out.kvm.qcow2",
"version": "7.4.1", "version": "7.4.1",
@ -57,6 +64,13 @@
"filesize": 340631552, "filesize": 340631552,
"download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx" "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", "filename": "FAZ_VM64_KVM-v7.0.9-build0489-FORTINET.out.kvm.qcow2",
"version": "7.0.9", "version": "7.0.9",
@ -78,6 +92,13 @@
"filesize": 334184448, "filesize": 334184448,
"download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx" "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", "filename": "FAZ_VM64_KVM-v6.4.12-build2610-FORTINET.out.kvm.qcow2",
"version": "6.4.12", "version": "6.4.12",
@ -235,6 +256,13 @@
} }
], ],
"versions": [ "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", "name": "7.4.1",
"images": { "images": {
@ -263,6 +291,13 @@
"hdb_disk_image": "empty30G.qcow2" "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", "name": "7.0.9",
"images": { "images": {
@ -284,6 +319,13 @@
"hdb_disk_image": "empty30G.qcow2" "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", "name": "6.4.12",
"images": { "images": {

@ -28,6 +28,13 @@
"kvm": "allow" "kvm": "allow"
}, },
"images": [ "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", "filename": "FGT_VM64_KVM-v7.4.1.F-build2463-FORTINET.out.kvm.qcow2",
"version": "7.4.1", "version": "7.4.1",
@ -35,6 +42,13 @@
"filesize": 116064256, "filesize": 116064256,
"download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx" "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", "filename": "FGT_VM64_KVM-v7.2.6.F-build1575-FORTINET.out.kvm.qcow2",
"version": "7.2.6", "version": "7.2.6",
@ -63,6 +77,13 @@
"filesize": 86704128, "filesize": 86704128,
"download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx" "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", "filename": "FGT_VM64_KVM-v7.0.13.M-build0566-FORTINET.out.kvm.qcow2",
"version": "7.0.13", "version": "7.0.13",
@ -91,6 +112,13 @@
"filesize": 77135872, "filesize": 77135872,
"download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx" "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", "filename": "FGT_VM64_KVM-v6.4.14.M-build2093-FORTINET.out.kvm.qcow2",
"version": "6.4.14", "version": "6.4.14",
@ -339,6 +367,13 @@
} }
], ],
"versions": [ "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", "name": "7.4.1",
"images": { "images": {
@ -346,6 +381,13 @@
"hdb_disk_image": "empty30G.qcow2" "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", "name": "7.2.6",
"images": { "images": {
@ -374,6 +416,13 @@
"hdb_disk_image": "empty30G.qcow2" "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", "name": "7.0.13",
"images": { "images": {
@ -402,6 +451,13 @@
"hdb_disk_image": "empty30G.qcow2" "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", "name": "6.4.14",
"images": { "images": {

@ -13,7 +13,7 @@
"status": "stable", "status": "stable",
"maintainer": "Ean Towne", "maintainer": "Ean Towne",
"maintainer_email": "ean.fortinet@gmail.com", "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", "symbol": "fortinet.svg",
"port_name_format": "Port{port1}", "port_name_format": "Port{port1}",
"qemu": { "qemu": {
@ -29,6 +29,13 @@
"kvm": "allow" "kvm": "allow"
}, },
"images": [ "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", "filename": "FMG_VM64_KVM-v7.4.1-build2308-FORTINET.out.kvm.qcow2",
"version": "7.4.1", "version": "7.4.1",
@ -57,6 +64,13 @@
"filesize": 242814976, "filesize": 242814976,
"download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx" "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", "filename": "FMG_VM64_KVM-v7.0.9-build0489-FORTINET.out.kvm.qcow2",
"version": "7.0.9", "version": "7.0.9",
@ -78,6 +92,13 @@
"filesize": 237535232, "filesize": 237535232,
"download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx" "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", "filename": "FMG_VM64_KVM-v6.4.12-build2610-FORTINET.out.kvm.qcow2",
"version": "6.4.12", "version": "6.4.12",
@ -235,6 +256,13 @@
} }
], ],
"versions": [ "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", "name": "7.4.1",
"images": { "images": {
@ -263,6 +291,13 @@
"hdb_disk_image": "empty30G.qcow2" "hdb_disk_image": "empty30G.qcow2"
} }
}, },
{
"name": "7.0.11",
"images": {
"hda_disk_image": "FMG_VM64_KVM-v7.0.11-build0595-FORTINET.out.kvm.qcow2",
"hdb_disk_image": "empty30G.qcow2"
}
},
{ {
"name": "7.0.9", "name": "7.0.9",
"images": { "images": {
@ -284,6 +319,13 @@
"hdb_disk_image": "empty30G.qcow2" "hdb_disk_image": "empty30G.qcow2"
} }
}, },
{
"name": "6.4.14",
"images": {
"hda_disk_image": "FMG_VM64_KVM-v6.4.14-build2660-FORTINET.out.kvm.qcow2",
"hdb_disk_image": "empty30G.qcow2"
}
},
{ {
"name": "6.4.12", "name": "6.4.12",
"images": { "images": {

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

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

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

@ -19,11 +19,15 @@
Docker server module. Docker server module.
""" """
import os
import sys import sys
import json import json
import asyncio import asyncio
import logging import logging
import aiohttp import aiohttp
import shutil
import platformdirs
from gns3server.utils import parse_version from gns3server.utils import parse_version
from gns3server.utils.asyncio import locking from gns3server.utils.asyncio import locking
from gns3server.compute.base_manager import BaseManager from gns3server.compute.base_manager import BaseManager
@ -55,6 +59,62 @@ class Docker(BaseManager):
self._session = None self._session = None
self._api_version = DOCKER_MINIMUM_API_VERSION self._api_version = DOCKER_MINIMUM_API_VERSION
@staticmethod
async def install_busybox(dst_dir):
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"):
busybox_path = shutil.which(busybox_exec)
if busybox_path:
try:
# check that busybox is statically linked
# (dynamically linked busybox will fail to run in a container)
proc = await asyncio.create_subprocess_exec(
"ldd",
busybox_path,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.DEVNULL
)
stdout, _ = await proc.communicate()
if proc.returncode == 1:
# ldd returns 1 if the file is not a dynamic executable
log.info(f"Installing busybox from '{busybox_path}' to '{dst_busybox}'")
shutil.copy2(busybox_path, dst_busybox, follow_symlinks=True)
return
else:
log.warning(f"Busybox '{busybox_path}' is dynamically linked\n"
f"{stdout.decode('utf-8', errors='ignore').strip()}")
except OSError as e:
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): async def _check_connection(self):
if not self._connected: if not self._connected:
@ -135,7 +195,7 @@ class Docker(BaseManager):
timeout = 60 * 60 * 24 * 31 # One month timeout timeout = 60 * 60 * 24 * 31 # One month timeout
if path == 'version': if path == 'version':
url = "http://docker/v1.12/" + path # API of docker v1.0 url = "http://docker/v1.24/" + path
else: else:
url = "http://docker/v" + DOCKER_MINIMUM_API_VERSION + "/" + path url = "http://docker/v" + DOCKER_MINIMUM_API_VERSION + "/" + path
try: try:

@ -242,10 +242,13 @@ class DockerVM(BaseNode):
:returns: Return the path that we need to map to local folders :returns: Return the path that we need to map to local folders
""" """
resources = get_resource("compute/docker/resources") try:
if not os.path.exists(resources): resources_path = self.manager.resources_path()
raise DockerError("{} is missing can't start Docker containers".format(resources)) except OSError as e:
binds = ["{}:/gns3:ro".format(resources)] raise DockerError(f"Cannot access resources: {e}")
log.info(f'Mount resources from "{resources_path}"')
binds = ["{}:/gns3:ro".format(resources_path)]
# We mount our own etc/network # We mount our own etc/network
try: try:
@ -460,6 +463,8 @@ class DockerVM(BaseNode):
Starts this Docker container. Starts this Docker container.
""" """
await self.manager.install_resources()
try: try:
state = await self._get_container_state() state = await self._get_container_state()
except DockerHttp404Error: except DockerHttp404Error:

@ -300,6 +300,9 @@ class Controller:
if entry.is_file() and not os.path.exists(full_path): if entry.is_file() and not os.path.exists(full_path):
log.debug(f'Installing {resource_name} resource file "{entry.name}" to "{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)) 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): def _install_base_configs(self):
""" """

@ -83,6 +83,11 @@ async def export_project(zstream, project, temporary_dir, include_images=False,
continue continue
_patch_mtime(path) _patch_mtime(path)
zstream.write(path, os.path.relpath(path, project._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: except FileNotFoundError as e:
log.warning("Cannot export local file: {}".format(e)) log.warning("Cannot export local file: {}".format(e))
continue continue

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

@ -235,9 +235,9 @@ def run():
return return
log.info("HTTP authentication is enabled with username '{}'".format(user)) log.info("HTTP authentication is enabled with username '{}'".format(user))
# we only support Python 3 version >= 3.6 # we only support Python 3 version >= 3.7
if sys.version_info < (3, 6, 0): if sys.version_info < (3, 7, 0):
raise SystemExit("Python 3.6 or higher is required") raise SystemExit("Python 3.7 or higher is required")
user_log.info("Running with Python {major}.{minor}.{micro} and has PID {pid}".format(major=sys.version_info[0], minor=sys.version_info[1], user_log.info("Running with Python {major}.{minor}.{micro} and has PID {pid}".format(major=sys.version_info[0], minor=sys.version_info[1],
micro=sys.version_info[2], pid=os.getpid())) micro=sys.version_info[2], pid=os.getpid()))

@ -46,6 +46,6 @@
gtag('config', 'G-5D6FZL9923'); gtag('config', 'G-5D6FZL9923');
</script> </script>
<script src="runtime.ecefb4ce510d1c218e7d.js" defer></script><script src="polyfills-es5.865074f5cd9a121111a2.js" nomodule defer></script><script src="polyfills.2f91a039d848e57ff02e.js" defer></script><script src="main.054d2e56a89c0b6f1b49.js" defer></script> <script src="runtime.ecefb4ce510d1c218e7d.js" defer></script><script src="polyfills-es5.865074f5cd9a121111a2.js" nomodule defer></script><script src="polyfills.2f91a039d848e57ff02e.js" defer></script><script src="main.93bb4a803caf93e6752d.js" defer></script>
</body></html> </body></html>

@ -297,9 +297,17 @@ class AsyncioTelnetServer:
reader_read = await self._get_reader(network_reader) reader_read = await self._get_reader(network_reader)
# Replicate the output on all clients # Replicate the output on all clients
for connection in self._connections.values(): 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) connection.writer.write(data)
await connection.writer.drain() 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): async def _read(self, cmd, buffer, location, reader):
""" Reads next op from the buffer or reader""" """ Reads next op from the buffer or reader"""

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

@ -1,19 +1,15 @@
jsonschema>=4.17.3,<4.18; python_version >= '3.7' # v4.17.3 is the last version to support Python 3.7 jsonschema>=4.17.3,<4.18 # v4.17.3 is the last version to support Python 3.7
jsonschema==3.2.0; python_version < '3.7' # v3.2.0 is the last version to support Python 3.6 aiohttp>=3.8.6,<3.9; python_version == '3.7' # v3.8.6 is the last version to support Python 3.7
aiohttp>=3.8.5,<3.9; python_version <= '3.7' aiohttp>=3.9.3,<3.10; python_version > '3.7'
aiohttp>=3.9.0,<3.10; python_version > '3.7'
aiohttp-cors>=0.7.0,<0.8 aiohttp-cors>=0.7.0,<0.8
aiofiles>=23.2.1,<23.3; python_version >= '3.7' aiofiles>=23.2.1,<23.3
aiofiles==0.8.0; python_version < '3.7' # v0.8.0 is the last version to support Python 3.6 Jinja2>=3.1.3,<3.2
Jinja2>=3.1.2,<3.2; python_version >= '3.7' sentry-sdk==1.39.2,<1.40
Jinja2==3.0.3; python_version < '3.7' # v3.0.3 is the last version to support Python 3.6 psutil==5.9.8
sentry-sdk==1.36.0,<1.37 async-timeout>=4.0.3,<4.1
psutil==5.9.6 distro>=1.9.0
async-timeout>=4.0.2,<4.1
distro>=1.8.0
py-cpuinfo>=9.0.0,<10.0 py-cpuinfo>=9.0.0,<10.0
platformdirs>=2.4.0 platformdirs>=2.4.0
importlib-resources>=1.3; python_version < '3.9' importlib-resources>=1.3; python_version < '3.9'
truststore>=0.8.0; python_version >= '3.10' truststore>=0.8.0; python_version >= '3.10'
setuptools>=60.8.1; python_version >= '3.7' setuptools>=60.8.1
setuptools==59.6.0; python_version < '3.7' # v59.6.0 is the last version to support Python 3.6

@ -23,9 +23,9 @@ import subprocess
from setuptools import setup, find_packages from setuptools import setup, find_packages
from setuptools.command.test import test as TestCommand from setuptools.command.test import test as TestCommand
# we only support Python 3 version >= 3.5.3 # we only support Python 3 version >= 3.7
if len(sys.argv) >= 2 and sys.argv[1] == "install" and sys.version_info < (3, 5, 3): if len(sys.argv) >= 2 and sys.argv[1] == "install" and sys.version_info < (3, 7):
raise SystemExit("Python 3.5.3 or higher is required") raise SystemExit("Python 3.7 or higher is required")
class PyTest(TestCommand): class PyTest(TestCommand):
@ -43,28 +43,6 @@ class PyTest(TestCommand):
sys.exit(errcode) sys.exit(errcode)
BUSYBOX_PATH = "gns3server/compute/docker/resources/bin/busybox"
def copy_busybox():
if not sys.platform.startswith("linux"):
return
if os.path.isfile(BUSYBOX_PATH):
return
for bb_cmd in ("busybox-static", "busybox.static", "busybox"):
bb_path = shutil.which(bb_cmd)
if bb_path:
if subprocess.call(["ldd", bb_path],
stdin=subprocess.DEVNULL,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL):
shutil.copy2(bb_path, BUSYBOX_PATH, follow_symlinks=True)
break
else:
raise SystemExit("No static busybox found")
copy_busybox()
dependencies = open("requirements.txt", "r").read().splitlines() dependencies = open("requirements.txt", "r").read().splitlines()
setup( setup(
@ -89,7 +67,7 @@ setup(
include_package_data=True, include_package_data=True,
zip_safe=False, zip_safe=False,
platforms="any", platforms="any",
python_requires='>=3.6.0', python_requires='>=3.7',
setup_requires=["setuptools>=17.1"], setup_requires=["setuptools>=17.1"],
classifiers=[ classifiers=[
"Development Status :: 5 - Production/Stable", "Development Status :: 5 - Production/Stable",
@ -103,7 +81,6 @@ setup(
"Operating System :: Microsoft :: Windows", "Operating System :: Microsoft :: Windows",
"Programming Language :: Python", "Programming Language :: Python",
"Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.9",

@ -15,6 +15,7 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import asyncio
import pytest import pytest
from unittest.mock import MagicMock, patch from unittest.mock import MagicMock, patch
@ -200,3 +201,52 @@ async def test_docker_check_connection_docker_preferred_version_against_older(vm
vm._connected = False vm._connected = False
await vm._check_connection() await vm._check_connection()
assert vm._api_version == DOCKER_MINIMUM_API_VERSION assert vm._api_version == DOCKER_MINIMUM_API_VERSION
@pytest.mark.asyncio
async def test_install_busybox():
mock_process = MagicMock()
mock_process.returncode = 1 # means that busybox is not dynamically linked
mock_process.communicate = AsyncioMagicMock(return_value=(b"", b"not a dynamic executable"))
with patch("gns3server.compute.docker.os.path.isfile", return_value=False):
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:
dst_dir = Docker.resources_path()
await Docker.install_busybox(dst_dir)
create_subprocess_mock.assert_called_with(
"ldd",
"/usr/bin/busybox",
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.DEVNULL,
)
assert copy2_mock.called
@pytest.mark.asyncio
async def test_install_busybox_dynamic_linked():
mock_process = MagicMock()
mock_process.returncode = 0 # means that busybox is dynamically linked
mock_process.communicate = AsyncioMagicMock(return_value=(b"Dynamically linked library", b""))
with patch("os.path.isfile", return_value=False):
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:
dst_dir = Docker.resources_path()
await Docker.install_busybox(dst_dir)
assert str(e.value) == "No busybox executable could be found"
@pytest.mark.asyncio
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:
dst_dir = Docker.resources_path()
await Docker.install_busybox(dst_dir)
assert str(e.value) == "No busybox executable could be found"

@ -25,10 +25,8 @@ from tests.utils import asyncio_patch, AsyncioMagicMock
from gns3server.ubridge.ubridge_error import UbridgeNamespaceError from gns3server.ubridge.ubridge_error import UbridgeNamespaceError
from gns3server.compute.docker.docker_vm import DockerVM from gns3server.compute.docker.docker_vm import DockerVM
from gns3server.compute.docker.docker_error import DockerError, DockerHttp404Error, DockerHttp304Error from gns3server.compute.docker.docker_error import DockerError, DockerHttp404Error
from gns3server.compute.docker import Docker from gns3server.compute.docker import Docker
from gns3server.utils.get_resource import get_resource
from unittest.mock import patch, MagicMock, call from unittest.mock import patch, MagicMock, call
@ -101,7 +99,7 @@ async def test_create(compute_project, manager):
{ {
"CapAdd": ["ALL"], "CapAdd": ["ALL"],
"Binds": [ "Binds": [
"{}:/gns3:ro".format(get_resource("compute/docker/resources")), "{}:/gns3:ro".format(Docker.resources_path()),
"{}:/gns3volumes/etc/network".format(os.path.join(vm.working_dir, "etc", "network")) "{}:/gns3volumes/etc/network".format(os.path.join(vm.working_dir, "etc", "network"))
], ],
"Privileged": True "Privileged": True
@ -139,7 +137,7 @@ async def test_create_with_tag(compute_project, manager):
{ {
"CapAdd": ["ALL"], "CapAdd": ["ALL"],
"Binds": [ "Binds": [
"{}:/gns3:ro".format(get_resource("compute/docker/resources")), "{}:/gns3:ro".format(Docker.resources_path()),
"{}:/gns3volumes/etc/network".format(os.path.join(vm.working_dir, "etc", "network")) "{}:/gns3volumes/etc/network".format(os.path.join(vm.working_dir, "etc", "network"))
], ],
"Privileged": True "Privileged": True
@ -180,7 +178,7 @@ async def test_create_vnc(compute_project, manager):
{ {
"CapAdd": ["ALL"], "CapAdd": ["ALL"],
"Binds": [ "Binds": [
"{}:/gns3:ro".format(get_resource("compute/docker/resources")), "{}:/gns3:ro".format(Docker.resources_path()),
"{}:/gns3volumes/etc/network".format(os.path.join(vm.working_dir, "etc", "network")), "{}:/gns3volumes/etc/network".format(os.path.join(vm.working_dir, "etc", "network")),
"/tmp/.X11-unix/X{0}:/tmp/.X11-unix/X{0}:ro".format(vm._display) "/tmp/.X11-unix/X{0}:/tmp/.X11-unix/X{0}:ro".format(vm._display)
], ],
@ -310,7 +308,7 @@ async def test_create_start_cmd(compute_project, manager):
{ {
"CapAdd": ["ALL"], "CapAdd": ["ALL"],
"Binds": [ "Binds": [
"{}:/gns3:ro".format(get_resource("compute/docker/resources")), "{}:/gns3:ro".format(Docker.resources_path()),
"{}:/gns3volumes/etc/network".format(os.path.join(vm.working_dir, "etc", "network")) "{}:/gns3volumes/etc/network".format(os.path.join(vm.working_dir, "etc", "network"))
], ],
"Privileged": True "Privileged": True
@ -408,7 +406,7 @@ async def test_create_image_not_available(compute_project, manager):
{ {
"CapAdd": ["ALL"], "CapAdd": ["ALL"],
"Binds": [ "Binds": [
"{}:/gns3:ro".format(get_resource("compute/docker/resources")), "{}:/gns3:ro".format(Docker.resources_path()),
"{}:/gns3volumes/etc/network".format(os.path.join(vm.working_dir, "etc", "network")) "{}:/gns3volumes/etc/network".format(os.path.join(vm.working_dir, "etc", "network"))
], ],
"Privileged": True "Privileged": True
@ -451,7 +449,7 @@ async def test_create_with_user(compute_project, manager):
{ {
"CapAdd": ["ALL"], "CapAdd": ["ALL"],
"Binds": [ "Binds": [
"{}:/gns3:ro".format(get_resource("compute/docker/resources")), "{}:/gns3:ro".format(Docker.resources_path()),
"{}:/gns3volumes/etc/network".format(os.path.join(vm.working_dir, "etc", "network")) "{}:/gns3volumes/etc/network".format(os.path.join(vm.working_dir, "etc", "network"))
], ],
"Privileged": True "Privileged": True
@ -533,7 +531,7 @@ async def test_create_with_extra_volumes_duplicate_1_image(compute_project, mana
{ {
"CapAdd": ["ALL"], "CapAdd": ["ALL"],
"Binds": [ "Binds": [
"{}:/gns3:ro".format(get_resource("compute/docker/resources")), "{}:/gns3:ro".format(Docker.resources_path()),
"{}:/gns3volumes/etc/network".format(os.path.join(vm.working_dir, "etc", "network")), "{}:/gns3volumes/etc/network".format(os.path.join(vm.working_dir, "etc", "network")),
"{}:/gns3volumes/vol/1".format(os.path.join(vm.working_dir, "vol", "1")), "{}:/gns3volumes/vol/1".format(os.path.join(vm.working_dir, "vol", "1")),
], ],
@ -572,7 +570,7 @@ async def test_create_with_extra_volumes_duplicate_2_user(compute_project, manag
{ {
"CapAdd": ["ALL"], "CapAdd": ["ALL"],
"Binds": [ "Binds": [
"{}:/gns3:ro".format(get_resource("compute/docker/resources")), "{}:/gns3:ro".format(Docker.resources_path()),
"{}:/gns3volumes/etc/network".format(os.path.join(vm.working_dir, "etc", "network")), "{}:/gns3volumes/etc/network".format(os.path.join(vm.working_dir, "etc", "network")),
"{}:/gns3volumes/vol/1".format(os.path.join(vm.working_dir, "vol", "1")), "{}:/gns3volumes/vol/1".format(os.path.join(vm.working_dir, "vol", "1")),
], ],
@ -611,7 +609,7 @@ async def test_create_with_extra_volumes_duplicate_3_subdir(compute_project, man
{ {
"CapAdd": ["ALL"], "CapAdd": ["ALL"],
"Binds": [ "Binds": [
"{}:/gns3:ro".format(get_resource("compute/docker/resources")), "{}:/gns3:ro".format(Docker.resources_path()),
"{}:/gns3volumes/etc/network".format(os.path.join(vm.working_dir, "etc", "network")), "{}:/gns3volumes/etc/network".format(os.path.join(vm.working_dir, "etc", "network")),
"{}:/gns3volumes/vol".format(os.path.join(vm.working_dir, "vol")), "{}:/gns3volumes/vol".format(os.path.join(vm.working_dir, "vol")),
], ],
@ -650,7 +648,7 @@ async def test_create_with_extra_volumes_duplicate_4_backslash(compute_project,
{ {
"CapAdd": ["ALL"], "CapAdd": ["ALL"],
"Binds": [ "Binds": [
"{}:/gns3:ro".format(get_resource("compute/docker/resources")), "{}:/gns3:ro".format(Docker.resources_path()),
"{}:/gns3volumes/etc/network".format(os.path.join(vm.working_dir, "etc", "network")), "{}:/gns3volumes/etc/network".format(os.path.join(vm.working_dir, "etc", "network")),
"{}:/gns3volumes/vol".format(os.path.join(vm.working_dir, "vol")), "{}:/gns3volumes/vol".format(os.path.join(vm.working_dir, "vol")),
], ],
@ -689,7 +687,7 @@ async def test_create_with_extra_volumes_duplicate_5_subdir_issue_1595(compute_p
{ {
"CapAdd": ["ALL"], "CapAdd": ["ALL"],
"Binds": [ "Binds": [
"{}:/gns3:ro".format(get_resource("compute/docker/resources")), "{}:/gns3:ro".format(Docker.resources_path()),
"{}:/gns3volumes/etc".format(os.path.join(vm.working_dir, "etc")), "{}:/gns3volumes/etc".format(os.path.join(vm.working_dir, "etc")),
], ],
"Privileged": True "Privileged": True
@ -727,7 +725,7 @@ async def test_create_with_extra_volumes_duplicate_6_subdir_issue_1595(compute_p
{ {
"CapAdd": ["ALL"], "CapAdd": ["ALL"],
"Binds": [ "Binds": [
"{}:/gns3:ro".format(get_resource("compute/docker/resources")), "{}:/gns3:ro".format(Docker.resources_path()),
"{}:/gns3volumes/etc".format(os.path.join(vm.working_dir, "etc")), "{}:/gns3volumes/etc".format(os.path.join(vm.working_dir, "etc")),
], ],
"Privileged": True "Privileged": True
@ -771,7 +769,7 @@ async def test_create_with_extra_volumes(compute_project, manager):
{ {
"CapAdd": ["ALL"], "CapAdd": ["ALL"],
"Binds": [ "Binds": [
"{}:/gns3:ro".format(get_resource("compute/docker/resources")), "{}:/gns3:ro".format(Docker.resources_path()),
"{}:/gns3volumes/etc/network".format(os.path.join(vm.working_dir, "etc", "network")), "{}:/gns3volumes/etc/network".format(os.path.join(vm.working_dir, "etc", "network")),
"{}:/gns3volumes/vol/1".format(os.path.join(vm.working_dir, "vol", "1")), "{}:/gns3volumes/vol/1".format(os.path.join(vm.working_dir, "vol", "1")),
"{}:/gns3volumes/vol/2".format(os.path.join(vm.working_dir, "vol", "2")), "{}:/gns3volumes/vol/2".format(os.path.join(vm.working_dir, "vol", "2")),
@ -854,7 +852,7 @@ async def test_unpause(vm):
mock.assert_called_with("POST", "containers/e90e34656842/unpause") mock.assert_called_with("POST", "containers/e90e34656842/unpause")
async def test_start(vm, manager, free_console_port): async def test_start(vm, manager, free_console_port, tmpdir):
assert vm.status != "started" assert vm.status != "started"
vm.adapters = 1 vm.adapters = 1
@ -882,6 +880,31 @@ async def test_start(vm, manager, free_console_port):
assert vm.status == "started" assert vm.status == "started"
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"))
async def test_start_namespace_failed(vm, manager, free_console_port): async def test_start_namespace_failed(vm, manager, free_console_port):
assert vm.status != "started" assert vm.status != "started"
@ -996,7 +1019,7 @@ async def test_update(vm):
{ {
"CapAdd": ["ALL"], "CapAdd": ["ALL"],
"Binds": [ "Binds": [
"{}:/gns3:ro".format(get_resource("compute/docker/resources")), "{}:/gns3:ro".format(Docker.resources_path()),
"{}:/gns3volumes/etc/network".format(os.path.join(vm.working_dir, "etc", "network")) "{}:/gns3volumes/etc/network".format(os.path.join(vm.working_dir, "etc", "network"))
], ],
"Privileged": True "Privileged": True
@ -1064,7 +1087,7 @@ async def test_update_running(vm):
{ {
"CapAdd": ["ALL"], "CapAdd": ["ALL"],
"Binds": [ "Binds": [
"{}:/gns3:ro".format(get_resource("compute/docker/resources")), "{}:/gns3:ro".format(Docker.resources_path()),
"{}:/gns3volumes/etc/network".format(os.path.join(vm.working_dir, "etc", "network")) "{}:/gns3volumes/etc/network".format(os.path.join(vm.working_dir, "etc", "network"))
], ],
"Privileged": True "Privileged": True
@ -1325,7 +1348,7 @@ async def test_mount_binds(vm):
dst = os.path.join(vm.working_dir, "test/experimental") dst = os.path.join(vm.working_dir, "test/experimental")
assert vm._mount_binds(image_infos) == [ assert vm._mount_binds(image_infos) == [
"{}:/gns3:ro".format(get_resource("compute/docker/resources")), "{}:/gns3:ro".format(Docker.resources_path()),
"{}:/gns3volumes/etc/network".format(os.path.join(vm.working_dir, "etc", "network")), "{}:/gns3volumes/etc/network".format(os.path.join(vm.working_dir, "etc", "network")),
"{}:/gns3volumes{}".format(dst, "/test/experimental") "{}:/gns3volumes{}".format(dst, "/test/experimental")
] ]

@ -111,6 +111,7 @@ async def test_export(tmpdir, project):
f.write("HELLO") f.write("HELLO")
with open(os.path.join(path, "vm-1", "dynamips", "test_log.txt"), 'w+') as f: with open(os.path.join(path, "vm-1", "dynamips", "test_log.txt"), 'w+') as f:
f.write("LOG") f.write("LOG")
os.makedirs(os.path.join(path, "vm-1", "dynamips", "empty-dir"))
os.makedirs(os.path.join(path, "project-files", "snapshots")) os.makedirs(os.path.join(path, "project-files", "snapshots"))
with open(os.path.join(path, "project-files", "snapshots", "test"), 'w+') as f: with open(os.path.join(path, "project-files", "snapshots", "test"), 'w+') as f:
f.write("WORLD") f.write("WORLD")
@ -127,6 +128,7 @@ async def test_export(tmpdir, project):
assert 'test.gns3' not in myzip.namelist() assert 'test.gns3' not in myzip.namelist()
assert 'project.gns3' 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 'project-files/snapshots/test' not in myzip.namelist()
assert 'vm-1/dynamips/test_log.txt' not in myzip.namelist() assert 'vm-1/dynamips/test_log.txt' not in myzip.namelist()

Loading…
Cancel
Save