mirror of
https://github.com/GNS3/gns3-server.git
synced 2024-11-16 16:54:51 +02:00
commit
5bab4131e1
4
.github/workflows/testing.yml
vendored
4
.github/workflows/testing.yml
vendored
@ -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
|
||||||
|
13
CHANGELOG
13
CHANGELOG
@ -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>
|
File diff suppressed because one or more lines are too long
@ -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()):
|
||||||
connection.writer.write(data)
|
client_info = connection_key.get_extra_info("socket").getpeername()
|
||||||
await connection.writer.drain()
|
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):
|
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
|
|
||||||
|
31
setup.py
31
setup.py
@ -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…
Reference in New Issue
Block a user