diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml
index 02e2f68d..720d5b83 100644
--- a/.github/workflows/testing.yml
+++ b/.github/workflows/testing.yml
@@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
- python-version: [3.6, 3.7, 3.8]
+ python-version: [3.6, 3.7, 3.8, 3.9]
steps:
- uses: actions/checkout@v2
diff --git a/CHANGELOG b/CHANGELOG
index d33a7d29..67e4d395 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,5 +1,52 @@
# Change Log
+## 2.2.24 25/08/2021
+
+* Release web UI 2.2.24
+* Fix issue when searching for image with relative path. Fixes #1925
+* Fix wrong error when NAT interface is not allowed. Fixes #1943
+* Fix incorrect Qemu binary selected when importing template. Fixes https://github.com/GNS3/gns3-gui/issues/3216
+* Fix error when updating a link style. Fixes https://github.com/GNS3/gns3-gui/issues/2461
+* Some fixes for early support for Python3.10 The loop parameter has been removed from most of asyncio‘s high-level API following deprecation in Python 3.8.
+* Early support for Python3.10 Fixes #1940
+* Bump pywin32 from 300 to 301
+
+## 2.2.23 05/08/2021
+
+* Release web UI 2.2.23
+* Fix hostname inconsistencies during script execution
+* Add option `--without-kvm`
+* Add a `reload` server endpoint. Fixes #1926
+* Handle -no-kvm param deprecated in Qemu >= v5.2
+* Fix binary websocket access to the console
+* Change how to generate random MAC addresses
+* setup.py: prevent installing tests directory
+* Support cloning of encrypted qcow2 base image files
+* Fix VMware VM support on Linux and Windows. Fixes #1919
+
+## 2.2.22 10/06/2021
+
+* Fix VMware support on macOS BigSur
+* Link style support. Fixes https://github.com/GNS3/gns3-gui/issues/2461
+* Release web UI version 2.2.22
+* Preserve auto_start/auto_open/auto_close when restoring snapshot
+* Fix uBridge errors for cloud nodes not visible in logs. Fixes #1895
+* Prevent directory traversal. Fixes #1894
+
+## 2.2.21 10/05/2021
+
+* Release Web-Ui v2.2.21
+* Improvements for get symbol dimensions endpoint. Ref #1885
+
+## 2.2.20 09/04/2021
+
+* Release Web UI version 2.2.20
+* Fix packet capture with HTTPS remote server. Fixes #1882
+* Sync appliance files and remove old ones after sync with online repo. Fixes #1876
+* Upgrade dependencies
+* Fix export for missing files
+* Fix issue when trying to export temporary Dynamips files.
+
## 2.2.19 05/03/2021
* Launch projects marked for auto open after SIGHUP is received
diff --git a/dev-requirements.txt b/dev-requirements.txt
index afc5a106..d727680d 100644
--- a/dev-requirements.txt
+++ b/dev-requirements.txt
@@ -1,6 +1,6 @@
-rrequirements.txt
-pytest==5.4.3
-flake8==3.8.3
-pytest-timeout==1.4.1
+pytest==6.2.4
+flake8==3.9.2
+pytest-timeout==1.4.2
pytest-aiohttp==0.3.0
diff --git a/gns3server/appliances/6wind-turbo-router.gns3a b/gns3server/appliances/6wind-turbo-router.gns3a
new file mode 100644
index 00000000..56c4062b
--- /dev/null
+++ b/gns3server/appliances/6wind-turbo-router.gns3a
@@ -0,0 +1,46 @@
+{
+ "name": "6WIND Turbo Router",
+ "category": "router",
+ "description": "6WIND Turbo Router is a high performance, ready-to-use software virtual router. It can be deployed bare metal or in virtual machines on commercial-off-the-shelf (COTS) servers. It is a carrier-grade solution for Service Prodivers aiming at using white boxes to deploy network functions. Typical use-cases are transit/peering router, IPsec VPN gateway and CGNAT.",
+ "vendor_name": "6WIND",
+ "vendor_url": "https://www.6wind.com/",
+ "documentation_url": "https://doc.6wind.com/turbo-router-3/latest/turbo-router/",
+ "product_name": "6WIND Turbo Router",
+ "product_url": "https://www.6wind.com/vrouter-solutions/turbo-router/",
+ "registry_version": 4,
+ "status": "stable",
+ "maintainer": "GNS3 Team",
+ "maintainer_email": "developers@gns3.net",
+ "usage": "Default username / password is admin / admin.",
+ "symbol": "6wind.svg",
+ "port_name_format": "eth{0}",
+ "qemu": {
+ "adapter_type": "virtio-net-pci",
+ "adapters": 8,
+ "ram": 4096,
+ "cpus": 4,
+ "hda_disk_interface": "virtio",
+ "arch": "x86_64",
+ "console_type": "telnet",
+ "boot_priority": "c",
+ "kvm": "require",
+ "options": "-cpu host"
+ },
+ "images": [
+ {
+ "filename": "6wind-vrouter-tr-ae-x86_64-v3.1.4.m1.qcow2",
+ "version": "3.1.4.m1",
+ "md5sum": "bc84b81fba4f2f01eda6a338469e37a5",
+ "filesize": 693829632,
+ "download_url": "https://portal.6wind.com/register.php?utm_campaign=GNS3-2021-EVAL"
+ }
+ ],
+ "versions": [
+ {
+ "name": "3.1.4.m1",
+ "images": {
+ "hda_disk_image": "6wind-vrouter-tr-ae-x86_64-v3.1.4.m1.qcow2"
+ }
+ }
+ ]
+}
diff --git a/gns3server/appliances/Raspian.gns3a b/gns3server/appliances/Raspian.gns3a
deleted file mode 100644
index 2e7af200..00000000
--- a/gns3server/appliances/Raspian.gns3a
+++ /dev/null
@@ -1,50 +0,0 @@
-{
- "name": "Raspian",
- "category": "guest",
- "description": "Raspberry Pi Desktop comes pre-installed with plenty of software for education, programming and general use; including Python, Scratch, Sonic Pi, Java, and more. Appliance created to demonstrate new_appliance.py - read more at https://nextpertise.net.",
- "vendor_name": "Raspberry Pi Foundation",
- "vendor_url": "https://www.raspberrypi.org",
- "product_name": "Raspberry Pi Desktop",
- "product_url": "https://www.raspberrypi.org/downloads/raspberry-pi-desktop/",
- "registry_version": 3,
- "status": "stable",
- "availability": "free",
- "maintainer": "Brent Stewart",
- "maintainer_email": "brent@stewart.tc",
- "usage": "Default password is raspberry",
- "symbol": "rpi.png",
- "qemu": {
- "adapter_type": "virtio",
- "adapters": 1,
- "ram": 1024,
- "hda_disk_interface": "sata",
- "arch": "x86_64",
- "console_type": "vnc",
- "kvm": "disable"
- },
- "images": [
- {
- "filename": "2020-02-12-rpd-x86-buster.iso",
- "version": "2020-02-12",
- "md5sum": "98f34fb53086752b4c9c452094f30740",
- "filesize": 3128147968,
- "download_url": "https://www.raspberrypi.org/downloads/raspberry-pi-desktop/"
- },
- {
- "filename": "empty8G.qcow2",
- "version": "1",
- "md5sum": "f1d2c25b6990f99bd05b433ab603bdb4",
- "filesize": 197120,
- "download_url": "https://sourceforge.net/projects/gns-3/files/Empty%20Qemu%20disk/empty8G.qcow2/download"
- }
- ],
- "versions": [
- {
- "name": "2020-02-12",
- "images": {
- "hda_disk_image": "empty8G.qcow2",
- "cdrom_image": "2020-02-12-rpd-x86-buster.iso"
- }
- }
- ]
-}
diff --git a/gns3server/appliances/arista-veos.gns3a b/gns3server/appliances/arista-veos.gns3a
index ea6a5aeb..7d49517e 100644
--- a/gns3server/appliances/arista-veos.gns3a
+++ b/gns3server/appliances/arista-veos.gns3a
@@ -27,10 +27,10 @@
},
"images": [
{
- "filename": "vEOS-lab-4.25.0F.vmdk",
- "version": "4.25.0F",
- "md5sum": "d420763fdf3bc50e7e5b88418bd9d1fd",
- "filesize": 468779008,
+ "filename": "vEOS-lab-4.25.3M.vmdk",
+ "version": "4.25.3M",
+ "md5sum": "2f196969036b4d283e86f15118d59c26",
+ "filesize": 451543040,
"download_url": "https://www.arista.com/en/support/software-download"
},
{
@@ -211,10 +211,10 @@
],
"versions": [
{
- "name": "4.25.0F",
+ "name": "4.25.3M",
"images": {
"hda_disk_image": "Aboot-veos-serial-8.0.0.iso",
- "hdb_disk_image": "vEOS-lab-4.25.0F.vmdk"
+ "hdb_disk_image": "vEOS-lab-4.25.3M.vmdk"
}
},
{
diff --git a/gns3server/appliances/aruba-arubaoscx.gns3a b/gns3server/appliances/aruba-arubaoscx.gns3a
index f9823abc..16094598 100644
--- a/gns3server/appliances/aruba-arubaoscx.gns3a
+++ b/gns3server/appliances/aruba-arubaoscx.gns3a
@@ -23,12 +23,19 @@
"hdb_disk_interface": "ide",
"hdc_disk_interface": "ide",
"arch": "x86_64",
- "console_type": "vnc",
+ "console_type": "telnet",
"kvm": "require",
"options": "-nographic",
"process_priority": "normal"
},
"images": [
+ {
+ "filename": "arubaoscx-disk-image-genericx86-p4-20201110192651.vmdk",
+ "version": "10.06.0001",
+ "md5sum": "f8b45bc52f6bad79b5ff563e0c1ea73b",
+ "filesize": 380304896,
+ "download_url": "https://asp.arubanetworks.com/"
+ },
{
"filename": "arubaoscx-disk-image-genericx86-p4-20200311173823.vmdk",
"version": "10.04.1000",
@@ -45,6 +52,12 @@
}
],
"versions": [
+ {
+ "name": "10.06.0001",
+ "images": {
+ "hda_disk_image": "arubaoscx-disk-image-genericx86-p4-20201110192651.vmdk"
+ }
+ },
{
"name": "10.04.1000",
"images": {
diff --git a/gns3server/appliances/aruba-vgw.gns3a b/gns3server/appliances/aruba-vgw.gns3a
index 61b40582..17a709ed 100644
--- a/gns3server/appliances/aruba-vgw.gns3a
+++ b/gns3server/appliances/aruba-vgw.gns3a
@@ -5,8 +5,8 @@
"vendor_name": "HPE Aruba",
"vendor_url": "arubanetworks.com",
"documentation_url": "https://asp.arubanetworks.com/downloads;products=Aruba%20SD-WAN",
- "product_url": "https://www.arubanetworks.com/products/networking/gateways-and-controllers/",
"product_name": "Aruba SD-WAN Virtual Gateway",
+ "product_url": "https://www.arubanetworks.com/products/networking/gateways-and-controllers/",
"registry_version": 4,
"status": "stable",
"availability": "service-contract",
diff --git a/gns3server/appliances/cisco-asa.gns3a b/gns3server/appliances/cisco-asa.gns3a
index 8ccb9831..69afb053 100644
--- a/gns3server/appliances/cisco-asa.gns3a
+++ b/gns3server/appliances/cisco-asa.gns3a
@@ -22,7 +22,7 @@
"console_type": "telnet",
"kernel_command_line": "ide_generic.probe_mask=0x01 ide_core.chs=0.0:980,16,32 auto nousb console=ttyS0,9600 bigphysarea=65536 ide1=noprobe no-hlt",
"kvm": "disable",
- "options": "-no-kvm -icount auto -hdachs 980,16,32",
+ "options": "-machine accel=tcg -icount auto -hdachs 980,16,32",
"cpu_throttling": 80,
"process_priority": "low"
},
diff --git a/gns3server/appliances/cisco-asav.gns3a b/gns3server/appliances/cisco-asav.gns3a
index 8093a531..ca8e47b9 100644
--- a/gns3server/appliances/cisco-asav.gns3a
+++ b/gns3server/appliances/cisco-asav.gns3a
@@ -25,6 +25,20 @@
"kvm": "require"
},
"images": [
+ {
+ "filename": "asav9-15-1.qcow2",
+ "version": "9.15.1",
+ "md5sum": "4e8747667f52e9046979f126128a61d1",
+ "filesize": 252444672,
+ "download_url": "https://software.cisco.com/download/home/286119613/type/280775065/release/9.15.1"
+ },
+ {
+ "filename": "asav9-14-1.qcow2",
+ "version": "9.14.1",
+ "md5sum": "03d89e18e7f8ad00fe8e979c4790587d",
+ "filesize": 211877888,
+ "download_url": "https://software.cisco.com/download/home/286119613/type/280775065/release/9.14.1"
+ },
{
"filename": "asav9-12-2-9.qcow2",
"version": "9.12.2-9",
@@ -90,6 +104,18 @@
}
],
"versions": [
+ {
+ "name": "9.15.1",
+ "images": {
+ "hda_disk_image": "asav9-15-1.qcow2"
+ }
+ },
+ {
+ "name": "9.14.1",
+ "images": {
+ "hda_disk_image": "asav9-14-1.qcow2"
+ }
+ },
{
"name": "9.12.2-9",
"images": {
diff --git a/gns3server/appliances/cisco-c8000v.gns3a b/gns3server/appliances/cisco-c8000v.gns3a
new file mode 100644
index 00000000..d10c4a58
--- /dev/null
+++ b/gns3server/appliances/cisco-c8000v.gns3a
@@ -0,0 +1,55 @@
+{
+ "name": "Cisco Catalyst 8000V",
+ "category": "router",
+ "description": "The Cisco Catalyst 8000V Edge Software is a virtual, form-factor router deployed on a virtual machine (VM) running on an x86 server hardware.",
+ "vendor_name": "Cisco",
+ "vendor_url": "http://www.cisco.com/",
+ "documentation_url": "https://www.cisco.com/c/en/us/td/docs/routers/C8000V/Configuration/c8000v-installation-configuration-guide.html",
+ "product_name": "c8000v",
+ "product_url": "https://www.cisco.com/c/en/us/support/routers/catalyst-8000v-edge-software/series.html",
+ "registry_version": 3,
+ "status": "stable",
+ "maintainer": "GNS3 Team",
+ "maintainer_email": "developers@gns3.net",
+ "usage": "There is no default password and enable password. A default configuration is present.",
+ "port_name_format": "Gi{port1}",
+ "qemu": {
+ "adapter_type": "vmxnet3",
+ "adapters": 4,
+ "ram": 4096,
+ "hda_disk_interface": "ide",
+ "arch": "x86_64",
+ "console_type": "telnet",
+ "kvm": "require"
+ },
+ "images": [
+ {
+ "filename": "c8000v-universalk9_8G_serial.17.04.01a.qcow2",
+ "version": "17.04.01a 8G",
+ "md5sum": "5c1dd1d3757ea43b5b02e0af7a010525",
+ "filesize": 1623130112,
+ "download_url": "https://software.cisco.com/download/home/286327102/type/282046477/release/Bengaluru-17.4.1a"
+ },
+ {
+ "filename": "c8000v-universalk9_8G_serial.17.04.01b.qcow2",
+ "version": "17.04.01b 8G",
+ "md5sum": "84aebb7f5f38bdd4df8e7607643027be",
+ "filesize": 1623130112,
+ "download_url": "https://software.cisco.com/download/home/286327102/type/282046477/release/Bengaluru-17.4.1b"
+ }
+ ],
+ "versions": [
+ {
+ "name": "17.04.01a 8G",
+ "images": {
+ "hda_disk_image": "c8000v-universalk9_8G_serial.17.04.01a.qcow2"
+ }
+ },
+ {
+ "name": "17.04.01b 8G",
+ "images": {
+ "hda_disk_image": "c8000v-universalk9_8G_serial.17.04.01b.qcow2"
+ }
+ }
+ ]
+}
diff --git a/gns3server/appliances/cisco-iosv.gns3a b/gns3server/appliances/cisco-iosv.gns3a
index ba31f5a7..89832630 100644
--- a/gns3server/appliances/cisco-iosv.gns3a
+++ b/gns3server/appliances/cisco-iosv.gns3a
@@ -31,6 +31,20 @@
"download_url": "https://sourceforge.net/projects/gns-3/files",
"direct_download_url": "https://sourceforge.net/projects/gns-3/files/Qemu%20Appliances/IOSv_startup_config.img/download"
},
+ {
+ "filename": "vios-adventerprisek9-m.spa.159-3.m3.qcow2",
+ "version": "15.9(3)M3",
+ "md5sum": "12893843af18e4c62f13d07266755653",
+ "filesize": 57296384,
+ "download_url": "https://learningnetworkstore.cisco.com/myaccount"
+ },
+ {
+ "filename": "vios-adventerprisek9-m.spa.159-3.m2.qcow2",
+ "version": "15.9(3)M2",
+ "md5sum": "a19e998bc3086825c751d125af722329",
+ "filesize": 57308672,
+ "download_url": "https://learningnetworkstore.cisco.com/myaccount"
+ },
{
"filename": "vios-adventerprisek9-m.spa.158-3.m2.qcow2",
"version": "15.8(3)M2",
@@ -68,6 +82,20 @@
}
],
"versions": [
+ {
+ "name": "15.9(3)M3",
+ "images": {
+ "hda_disk_image": "vios-adventerprisek9-m.spa.159-3.m3.qcow2",
+ "hdb_disk_image": "IOSv_startup_config.img"
+ }
+ },
+ {
+ "name": "15.9(3)M2",
+ "images": {
+ "hda_disk_image": "vios-adventerprisek9-m.spa.159-3.m2.qcow2",
+ "hdb_disk_image": "IOSv_startup_config.img"
+ }
+ },
{
"name": "15.8(3)M2",
"images": {
diff --git a/gns3server/appliances/cisco-iosvl2.gns3a b/gns3server/appliances/cisco-iosvl2.gns3a
index e7bb436f..02a7af5b 100644
--- a/gns3server/appliances/cisco-iosvl2.gns3a
+++ b/gns3server/appliances/cisco-iosvl2.gns3a
@@ -23,6 +23,20 @@
"kvm": "require"
},
"images": [
+ {
+ "filename": "vios_l2-adventerprisek9-m.ssa.high_iron_20200929.qcow2",
+ "version": "15.2(20200924:215240)",
+ "md5sum": "99ecab32de12410c533e6abd4e9710aa",
+ "filesize": 90409984,
+ "download_url": "https://learningnetworkstore.cisco.com/myaccount"
+ },
+ {
+ "filename": "vios_l2-adventerprisek9-m.ssa.high_iron_20190423.qcow2",
+ "version": "15.2(6.0.81)E",
+ "md5sum": "71cacb678f98a106f99e889b97b34686",
+ "filesize": 44950016,
+ "download_url": "https://learningnetworkstore.cisco.com/myaccount"
+ },
{
"filename": "vios_l2-adventerprisek9-m.SSA.high_iron_20180619.qcow2",
"version": "15.2.1",
@@ -46,6 +60,18 @@
}
],
"versions": [
+ {
+ "name": "15.2(20200924:215240)",
+ "images": {
+ "hda_disk_image": "vios_l2-adventerprisek9-m.ssa.high_iron_20200929.qcow2"
+ }
+ },
+ {
+ "name": "15.2(6.0.81)E",
+ "images": {
+ "hda_disk_image": "vios_l2-adventerprisek9-m.ssa.high_iron_20190423.qcow2"
+ }
+ },
{
"name": "15.2.1",
"images": {
diff --git a/gns3server/appliances/cisco-nxosv9k.gns3a b/gns3server/appliances/cisco-nxosv9k.gns3a
index 5fde4058..ae3c6e84 100644
--- a/gns3server/appliances/cisco-nxosv9k.gns3a
+++ b/gns3server/appliances/cisco-nxosv9k.gns3a
@@ -25,6 +25,20 @@
"kvm": "require"
},
"images": [
+ {
+ "filename": "nexus9500v64.10.1.1.qcow2",
+ "version": "9500v 10.1.1",
+ "md5sum": "35672370b0f43e725d5b2d92488524f0",
+ "filesize": 1592000512,
+ "download_url": "https://software.cisco.com/download/home/286312239/type/282088129/release/10.1(1)"
+ },
+ {
+ "filename": "nexus9500v.9.3.7.qcow2",
+ "version": "9500v 9.3.7",
+ "md5sum": "65f669e0dd379a05a8cdbb9d7592a064",
+ "filesize": 1986068480,
+ "download_url": "https://software.cisco.com/download/home/286312239/type/282088129/release/9.3(7)"
+ },
{
"filename": "nexus9500v.9.3.3.qcow2",
"version": "9500v 9.3.3",
@@ -148,6 +162,20 @@
}
],
"versions": [
+ {
+ "name": "9500v 10.1.1",
+ "images": {
+ "bios_image": "OVMF-20160813.fd",
+ "hda_disk_image": "nexus9500v64.10.1.1.qcow2"
+ }
+ },
+ {
+ "name": "9500v 9.3.7",
+ "images": {
+ "bios_image": "OVMF-20160813.fd",
+ "hda_disk_image": "nexus9500v.9.3.7.qcow2"
+ }
+ },
{
"name": "9500v 9.3.3",
"images": {
diff --git a/gns3server/appliances/cumulus-vx.gns3a b/gns3server/appliances/cumulus-vx.gns3a
index 67910269..884ac4e5 100644
--- a/gns3server/appliances/cumulus-vx.gns3a
+++ b/gns3server/appliances/cumulus-vx.gns3a
@@ -11,19 +11,27 @@
"status": "stable",
"maintainer": "GNS3 Team",
"maintainer_email": "developers@gns3.net",
- "usage": "Default username is cumulus and password is CumulusLinux!",
+ "usage": "Default username is cumulus and password is CumulusLinux! in version 4.1 and earlier, and cumulus in version 4.2 and later.",
"first_port_name": "eth0",
"port_name_format": "swp{port1}",
"qemu": {
"adapter_type": "virtio-net-pci",
"adapters": 7,
- "ram": 512,
+ "ram": 768,
"hda_disk_interface": "ide",
"arch": "x86_64",
"console_type": "telnet",
"kvm": "require"
},
"images": [
+ {
+ "filename": "cumulus-linux-4.3.0-vx-amd64-qemu.qcow2",
+ "version": "4.3.0",
+ "md5sum": "aba2f0bb462b26a208afb6202bc97d51",
+ "filesize": 2819325952,
+ "download_url": "https://cumulusnetworks.com/cumulus-vx/download/",
+ "direct_download_url": "https://d2cd9e7ca6hntp.cloudfront.net/public/CumulusLinux-4.3.0/cumulus-linux-4.3.0-vx-amd64-qemu.qcow2"
+ },
{
"filename": "cumulus-linux-4.2.0-vx-amd64-qemu.qcow2",
"version": "4.2.0",
@@ -222,6 +230,12 @@
}
],
"versions": [
+ {
+ "name": "4.3.0",
+ "images": {
+ "hda_disk_image": "cumulus-linux-4.3.0-vx-amd64-qemu.qcow2"
+ }
+ },
{
"name": "4.2.0",
"images": {
diff --git a/gns3server/appliances/danos.gns3a b/gns3server/appliances/danos.gns3a
index 8739db48..bb9ed3d3 100644
--- a/gns3server/appliances/danos.gns3a
+++ b/gns3server/appliances/danos.gns3a
@@ -11,29 +11,29 @@
"status": "stable",
"maintainer": "GNS3 Team",
"maintainer_email": "developers@gns3.net",
- "usage": "Default username/password is vyatta/vyatta. DANOS will live boot and drop into a shell. DANOS can then be installed inside the VM by typing install image. Defaults to using a telnet console, but the vnc console can provide additional help if it's not booting.",
+ "usage": "Default username / password is tmpuser / tmppwd. DANOS will live boot and drop into a shell. DANOS can then be installed inside the VM by typing install image. Defaults to using a telnet console, but the vnc console can provide additional help if it's not booting.",
"symbol": ":/symbols/affinity/circle/gray/router_cloud.svg",
- "port_name_format": "dp0p{1}s{0}",
+ "port_name_format": "dp0s{3}",
"qemu": {
"adapter_type": "virtio-net-pci",
- "adapters": 3,
+ "adapters": 8,
"ram": 4096,
- "cpus": 2,
+ "cpus": 4,
"hda_disk_interface": "ide",
"arch": "x86_64",
"console_type": "telnet",
- "boot_priority": "dc",
- "kvm": "allow",
+ "boot_priority": "cd",
+ "kvm": "require",
"options": "-cpu host"
},
"images": [
{
- "filename": "danos-1908-amd64-vrouter.iso",
- "version": "1908",
- "md5sum": "e850b6aa2859de1075c11b9149fa50f4",
- "filesize": 409993216,
- "download_url": "https://danosproject.atlassian.net/wiki/spaces/DAN/pages/753667/DANOS+1908",
- "direct_download_url": "http://repos.danosproject.org.s3-website-us-west-1.amazonaws.com/images/danos-1908-amd64-vrouter.iso"
+ "filename": "danos-2012-base-amd64.iso",
+ "version": "2012",
+ "md5sum": "fb7a60dc9afecdb274464832b3ab1ccb",
+ "filesize": 441450496,
+ "download_url": "https://danosproject.atlassian.net/wiki/spaces/DAN/pages/892141595/DANOS+2012",
+ "direct_download_url": "https://s3-us-west-1.amazonaws.com/2012.repos.danosproject.org/2012/iso/danos-2012-base-amd64.iso"
},
{
"filename": "empty8G.qcow2",
@@ -46,10 +46,10 @@
],
"versions": [
{
- "name": "1908",
+ "name": "2012",
"images": {
"hda_disk_image": "empty8G.qcow2",
- "cdrom_image": "danos-1908-amd64-vrouter.iso"
+ "cdrom_image": "danos-2012-base-amd64.iso"
}
}
]
diff --git a/gns3server/appliances/exos.gns3a b/gns3server/appliances/exos.gns3a
index 7ee67774..5e51b1a3 100644
--- a/gns3server/appliances/exos.gns3a
+++ b/gns3server/appliances/exos.gns3a
@@ -26,7 +26,7 @@
"options": "-cpu core2duo"
},
"images": [
- {
+ {
"filename": "EXOS-VM_v31.1.1.3.qcow2",
"version": "31.1.1.3",
"md5sum": "e4936ad94a5304bfeeca8dfc6f285cc0",
diff --git a/gns3server/appliances/extreme-networks-voss.gns3a b/gns3server/appliances/extreme-networks-voss.gns3a
index f1592e8b..54d99ca9 100644
--- a/gns3server/appliances/extreme-networks-voss.gns3a
+++ b/gns3server/appliances/extreme-networks-voss.gns3a
@@ -26,19 +26,26 @@
"options": "-nographic"
},
"images": [
+ {
+ "filename": "VOSSGNS3.8.4.0.0.qcow2",
+ "version": "v8.4.0.0",
+ "md5sum": "f457e7da3c1dec50670624c421333ef3",
+ "filesize": 386203648,
+ "direct_download_url": "https://akamai-ep.extremenetworks.com/Extreme_P/github-en/Virtual_VOSS/VOSSGNS3.8.4.0.0.qcow2"
+ },
{
"filename": "VOSSGNS3.8.3.0.0.qcow2",
"version": "v8.3.0.0",
"md5sum": "e1c789e439c5951728e349cf44690230",
"filesize": 384696320,
- "direct_download_url": "https://akamai-ep.extremenetworks.com/Extreme_P/github-en/Virtual_VOSS/VOSSGNS3.8.3.0.0.qcow2"
+ "direct_download_url": "https://akamai-ep.extremenetworks.com/Extreme_P/github-en/Virtual_VOSS/VOSSGNS3.8.3.0.0.qcow2"
},
- {
+ {
"filename": "VOSSGNS3.8.2.0.0.qcow2",
"version": "v8.2.0.0",
"md5sum": "9a0cd77c08644abbf3a69771c125c011",
"filesize": 331808768,
- "direct_download_url": "https://akamai-ep.extremenetworks.com/Extreme_P/github-en/Virtual_VOSS/VOSSGNS3.8.2.0.0.qcow2"
+ "direct_download_url": "https://akamai-ep.extremenetworks.com/Extreme_P/github-en/Virtual_VOSS/VOSSGNS3.8.2.0.0.qcow2"
},
{
"filename": "VOSSGNS3.8.1.5.0.qcow2",
@@ -70,18 +77,24 @@
}
],
"versions": [
+ {
+ "name": "v8.4.0.0",
+ "images": {
+ "hda_disk_image": "VOSSGNS3.8.4.0.0.qcow2"
+ }
+ },
{
"name": "v8.3.0.0",
"images": {
"hda_disk_image": "VOSSGNS3.8.3.0.0.qcow2"
}
- },
- {
+ },
+ {
"name": "v8.2.0.0",
"images": {
"hda_disk_image": "VOSSGNS3.8.2.0.0.qcow2"
}
- },
+ },
{
"name": "8.1.5.0",
"images": {
diff --git a/gns3server/appliances/fortianalyzer.gns3a b/gns3server/appliances/fortianalyzer.gns3a
index 3e392806..b16940a2 100644
--- a/gns3server/appliances/fortianalyzer.gns3a
+++ b/gns3server/appliances/fortianalyzer.gns3a
@@ -17,7 +17,7 @@
"qemu": {
"adapter_type": "e1000",
"adapters": 4,
- "ram": 1024,
+ "ram": 4096,
"hda_disk_interface": "virtio",
"hdb_disk_interface": "virtio",
"arch": "x86_64",
@@ -26,6 +26,13 @@
"kvm": "allow"
},
"images": [
+ {
+ "filename": "FAZ_VM64_KVM-v6-build2288-FORTINET.out.kvm.qcow2",
+ "version": "6.4.5",
+ "md5sum": "e220b48c6e86f8ddc660d578295051a9",
+ "filesize": 152698880,
+ "download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx"
+ },
{
"filename": "FAZ_VM64_KVM-v6-build1183-FORTINET.out.kvm.qcow2",
"version": "6.2.2",
@@ -169,6 +176,13 @@
}
],
"versions": [
+ {
+ "name": "6.4.5",
+ "images": {
+ "hda_disk_image": "FAZ_VM64_KVM-v6-build2288-FORTINET.out.kvm.qcow2",
+ "hdb_disk_image": "empty30G.qcow2"
+ }
+ },
{
"name": "6.2.2",
"images": {
diff --git a/gns3server/appliances/fortigate.gns3a b/gns3server/appliances/fortigate.gns3a
index fb9d9ad9..4e933a54 100644
--- a/gns3server/appliances/fortigate.gns3a
+++ b/gns3server/appliances/fortigate.gns3a
@@ -26,6 +26,13 @@
"kvm": "allow"
},
"images": [
+ {
+ "filename": "FGT_VM64_KVM-v6-build1828-FORTINET.out.kvm.qcow2",
+ "version": "6.4.5",
+ "md5sum": "dc064e16fa65461183544d8ddb5d19d9",
+ "filesize": 36175872,
+ "download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx"
+ },
{
"filename": "FGT_VM64_KVM-v6-build1010-FORTINET.out.kvm.qcow2",
"version": "6.2.2",
@@ -246,6 +253,13 @@
}
],
"versions": [
+ {
+ "name": "6.4.5",
+ "images": {
+ "hda_disk_image": "FGT_VM64_KVM-v6-build1828-FORTINET.out.kvm.qcow2",
+ "hdb_disk_image": "empty30G.qcow2"
+ }
+ },
{
"name": "6.2.2",
"images": {
diff --git a/gns3server/appliances/fortimanager.gns3a b/gns3server/appliances/fortimanager.gns3a
index 2dfa4cb8..8f75a84e 100644
--- a/gns3server/appliances/fortimanager.gns3a
+++ b/gns3server/appliances/fortimanager.gns3a
@@ -17,7 +17,7 @@
"qemu": {
"adapter_type": "virtio-net-pci",
"adapters": 4,
- "ram": 1024,
+ "ram": 2048,
"hda_disk_interface": "virtio",
"hdb_disk_interface": "virtio",
"arch": "x86_64",
@@ -26,6 +26,20 @@
"kvm": "allow"
},
"images": [
+ {
+ "filename": "FMG_VM64_KVM-v6-build2288-FORTINET.out.kvm.qcow2",
+ "version": "6.4.5",
+ "md5sum": "bd2791984b03f55a6825297e83c6576a",
+ "filesize": 117014528,
+ "download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx"
+ },
+ {
+ "filename": "FMG_VM64_KVM-v6-build2253-FORTINET.out.kvm.qcow2",
+ "version": "6.4.4",
+ "md5sum": "3554a47fde2dc91d17eec16fd0dc10a3",
+ "filesize": 116621312,
+ "download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx"
+ },
{
"filename": "FMG_VM64_KVM-v6-build1183-FORTINET.out.kvm.qcow2",
"version": "6.2.2",
@@ -162,6 +176,20 @@
}
],
"versions": [
+ {
+ "name": "6.4.5",
+ "images": {
+ "hda_disk_image": "FMG_VM64_KVM-v6-build2288-FORTINET.out.kvm.qcow2",
+ "hdb_disk_image": "empty30G.qcow2"
+ }
+ },
+ {
+ "name": "6.4.4",
+ "images": {
+ "hda_disk_image": "FMG_VM64_KVM-v6-build2253-FORTINET.out.kvm.qcow2",
+ "hdb_disk_image": "empty30G.qcow2"
+ }
+ },
{
"name": "6.2.2",
"images": {
diff --git a/gns3server/appliances/frr.gns3a b/gns3server/appliances/frr.gns3a
index ae25e7cb..2765dd81 100644
--- a/gns3server/appliances/frr.gns3a
+++ b/gns3server/appliances/frr.gns3a
@@ -21,6 +21,14 @@
"kvm": "allow"
},
"images": [
+ {
+ "filename": "frr-7.5.1.qcow2",
+ "version": "7.5.1",
+ "md5sum": "4b3ca0932a396b282ba35f102be1ed3b",
+ "filesize": 51169280,
+ "download_url": "https://sourceforge.net/projects/gns-3/files/Qemu%20Appliances/",
+ "direct_download_url": "http://downloads.sourceforge.net/project/gns-3/Qemu%20Appliances/frr-7.5.1.qcow2"
+ },
{
"filename": "frr-7.3.1.qcow2",
"version": "7.3.1",
@@ -31,6 +39,12 @@
}
],
"versions": [
+ {
+ "name": "7.5.1",
+ "images": {
+ "hda_disk_image": "frr-7.5.1.qcow2"
+ }
+ },
{
"name": "7.3.1",
"images": {
diff --git a/gns3server/appliances/huawei-ar1kv.gns3a b/gns3server/appliances/huawei-ar1kv.gns3a
index fbad35b9..46fba486 100644
--- a/gns3server/appliances/huawei-ar1kv.gns3a
+++ b/gns3server/appliances/huawei-ar1kv.gns3a
@@ -6,7 +6,7 @@
"vendor_url": "https://www.huawei.com",
"product_name": "HuaWei AR1000v",
"product_url": "https://support.huawei.com/enterprise/en/routers/ar1000v-pid-21768212",
- "registry_version": 5,
+ "registry_version": 4,
"status": "experimental",
"availability": "service-contract",
"maintainer": "none",
diff --git a/gns3server/appliances/huawei-ce12800.gns3a b/gns3server/appliances/huawei-ce12800.gns3a
index 4cc5eb0c..135c4522 100644
--- a/gns3server/appliances/huawei-ce12800.gns3a
+++ b/gns3server/appliances/huawei-ce12800.gns3a
@@ -5,7 +5,7 @@
"vendor_name": "HuaWei",
"vendor_url": "https://www.huawei.com",
"product_name": "HuaWei CE12800",
- "registry_version": 5,
+ "registry_version": 4,
"status": "experimental",
"availability": "service-contract",
"maintainer": "none",
@@ -33,10 +33,10 @@
],
"versions": [
{
+ "name": "V200R005C10SPC607B607",
"images": {
"hda_disk_image": "ce12800-V200R005C10SPC607B607.qcow2"
- },
- "name": "V200R005C10SPC607B607"
+ }
}
]
}
diff --git a/gns3server/appliances/huawei-ne40e.gns3a b/gns3server/appliances/huawei-ne40e.gns3a
index 73ed94ca..4c07b542 100644
--- a/gns3server/appliances/huawei-ne40e.gns3a
+++ b/gns3server/appliances/huawei-ne40e.gns3a
@@ -6,7 +6,7 @@
"vendor_url": "https://www.huawei.com",
"product_name": "HuaWei NE40E",
"product_url": "https://e.huawei.com/en/products/enterprise-networking/routers/ne/ne40e",
- "registry_version": 5,
+ "registry_version": 4,
"status": "experimental",
"availability": "service-contract",
"maintainer": "none",
@@ -35,10 +35,10 @@
],
"versions": [
{
+ "name": "V800R011C00SPC607B607",
"images": {
"hda_disk_image": "ne40e-V800R011C00SPC607B607.qcow2"
- },
- "name": "V800R011C00SPC607B607"
+ }
}
]
-}
\ No newline at end of file
+}
diff --git a/gns3server/appliances/huawei-usg6kv.gns3a b/gns3server/appliances/huawei-usg6kv.gns3a
index 4655f92a..21256ac6 100644
--- a/gns3server/appliances/huawei-usg6kv.gns3a
+++ b/gns3server/appliances/huawei-usg6kv.gns3a
@@ -6,7 +6,7 @@
"vendor_url": "https://www.huawei.com",
"product_name": "HuaWei USG6000v",
"product_url": "https://e.huawei.com/en/products/enterprise-networking/security/firewall-gateway/usg6000v",
- "registry_version": 5,
+ "registry_version": 4,
"status": "experimental",
"availability": "service-contract",
"maintainer": "none",
diff --git a/gns3server/appliances/kali-linux.gns3a b/gns3server/appliances/kali-linux.gns3a
index 2661c8a5..ea215748 100644
--- a/gns3server/appliances/kali-linux.gns3a
+++ b/gns3server/appliances/kali-linux.gns3a
@@ -23,101 +23,109 @@
"kvm": "require"
},
"images": [
+ {
+ "filename": "kali-linux-2021.1-live-amd64.iso",
+ "version": "2021.1",
+ "md5sum": "3a3716fef866e5c29a1c1ccfc94264b5",
+ "filesize": 3591385088,
+ "download_url": "http://cdimage.kali.org/kali-2021.1/",
+ "direct_download_url": "http://cdimage.kali.org/kali-2021.1/kali-linux-2021.1-live-amd64.iso"
+ },
{
"filename": "kali-linux-2019.3-amd64.iso",
"version": "2019.3",
"md5sum": "9c6fb00558f78ed06992d89f745ef975",
"filesize": 3037736960,
- "download_url": "https://www.kali.org/downloads/",
- "direct_download_url": "http://cdimage.kali.org/kali-2019.3/kali-linux-2019.3-amd64.iso"
+ "download_url": "http://old.kali.org/kali-images/kali-2019.3",
+ "direct_download_url": "http://old.kali.org/kali-images/kali-2019.3/kali-linux-2019.3-amd64.iso"
},
{
"filename": "kali-linux-2019.2-amd64.iso",
"version": "2019.2",
"md5sum": "0f89b6225d7ea9c18682f7cc541c1179",
"filesize": 3353227264,
- "download_url": "https://www.kali.org/downloads/",
- "direct_download_url": "http://cdimage.kali.org/kali-2019.2/kali-linux-2019.2-amd64.iso"
+ "download_url": "http://old.kali.org/kali-images/kali-2019.2",
+ "direct_download_url": "http://old.kali.org/kali-images/kali-2019.2/kali-linux-2019.2-amd64.iso"
},
{
"filename": "kali-linux-mate-2019.2-amd64.iso",
"version": "2019.2 (MATE)",
"md5sum": "fec8dd7009f932c51a74323df965a709",
"filesize": 3313217536,
- "download_url": "https://www.kali.org/downloads/",
- "direct_download_url": "http://cdimage.kali.org/kali-2019.2/kali-linux-mate-2019.2-amd64.iso"
+ "download_url": "http://old.kali.org/kali-images/kali-2019.2",
+ "direct_download_url": "http://old.kali.org/kali-images/kali-2019.2/kali-linux-mate-2019.2-amd64.iso"
},
{
"filename": "kali-linux-2019.1a-amd64.iso",
"version": "2019.1a",
"md5sum": "58c6111ed0be1919ea87267e7e65ab0f",
"filesize": 3483873280,
- "download_url": "https://www.kali.org/downloads/",
- "direct_download_url": "http://cdimage.kali.org/kali-2019.1a/kali-linux-2019.1a-amd64.iso"
+ "download_url": "http://old.kali.org/kali-images/kali-2019.1a",
+ "direct_download_url": "http://old.kali.org/kali-images/kali-2019.1a/kali-linux-2019.1a-amd64.iso"
},
{
"filename": "kali-linux-2018.4-amd64.iso",
"version": "2018.4",
"md5sum": "1b2d598bb8d2003e6207c119c0ba42fe",
"filesize": 3139436544,
- "download_url": "https://www.kali.org/downloads/",
- "direct_download_url": "http://cdimage.kali.org/kali-2018.4/kali-linux-2018.4-amd64.iso"
+ "download_url": "http://old.kali.org/kali-images/kali-2018.4",
+ "direct_download_url": "http://old.kali.org/kali-images/kali-2018.4/kali-linux-2018.4-amd64.iso"
},
{
"filename": "kali-linux-2018.3a-amd64.iso",
"version": "2018.3a",
"md5sum": "2da675d016bd690c05e180e33aa98b94",
"filesize": 3192651776,
- "download_url": "https://www.kali.org/downloads/",
- "direct_download_url": "http://cdimage.kali.org/kali-2018.3a/kali-linux-2018.3a-amd64.iso"
+ "download_url": "http://old.kali.org/kali-images/kali-2018.3a",
+ "direct_download_url": "http://old.kali.org/kali-images/kali-2018.3a/kali-linux-2018.3a-amd64.iso"
},
{
"filename": "kali-linux-2018.1-amd64.iso",
"version": "2018.1",
"md5sum": "a3feb90df5b71b3c7f4a02bdddf221d7",
"filesize": 3028500480,
- "download_url": "https://www.kali.org/downloads/",
- "direct_download_url": "http://cdimage.kali.org/kali-2018.1/kali-linux-2018.1-amd64.iso"
+ "download_url": "http://old.kali.org/kali-images/kali-2018.1",
+ "direct_download_url": "http://old.kali.org/kali-images/kali-2018.1/kali-linux-2018.1-amd64.iso"
},
{
"filename": "kali-linux-2017.3-amd64.iso",
"version": "2017.3",
"md5sum": "b465580c897e94675ac1daf031fa66b9",
"filesize": 2886402048,
- "download_url": "http://cdimage.kali.org/kali-2017.3/",
- "direct_download_url": "http://cdimage.kali.org/kali-2017.3/kali-linux-2017.3-amd64.iso"
+ "download_url": "http://old.kali.org/kali-images/kali-2017.3/",
+ "direct_download_url": "http://old.kali.org/kali-images/kali-2017.3/kali-linux-2017.3-amd64.iso"
},
{
"filename": "kali-linux-2017.2-amd64.iso",
"version": "2017.2",
"md5sum": "541654f8f818450dc0db866a0a0f6eec",
"filesize": 3020619776,
- "download_url": "http://cdimage.kali.org/kali-2017.2/",
- "direct_download_url": "http://cdimage.kali.org/kali-2017.2/kali-linux-2017.2-amd64.iso"
+ "download_url": "http://old.kali.org/kali-images/kali-2017.2/",
+ "direct_download_url": "http://old.kali.org/kali-images/kali-2017.2/kali-linux-2017.2-amd64.iso"
},
{
"filename": "kali-linux-2017.1-amd64.iso",
"version": "2017.1",
"md5sum": "c8e742283929d7a12dbe7c58e398ff08",
"filesize": 2794307584,
- "download_url": "http://cdimage.kali.org/kali-2017.1/",
- "direct_download_url": "http://cdimage.kali.org/kali-2017.1/kali-linux-2017.1-amd64.iso"
+ "download_url": "http://old.kali.org/kali-images/kali-2017.1/",
+ "direct_download_url": "http://old.kali.org/kali-images/kali-2017.1/kali-linux-2017.1-amd64.iso"
},
{
"filename": "kali-linux-2016.2-amd64.iso",
"version": "2016.2",
"md5sum": "3d163746bc5148e61ad689d94bc263f9",
"filesize": 3076767744,
- "download_url": "http://cdimage.kali.org/kali-2016.2/",
- "direct_download_url": "http://cdimage.kali.org/kali-2016.2/kali-linux-2016.2-amd64.iso"
+ "download_url": "http://old.kali.org/kali-images/kali-2016.2/",
+ "direct_download_url": "http://old.kali.org/kali-images/kali-2016.2/kali-linux-2016.2-amd64.iso"
},
{
"filename": "kali-linux-2016.1-amd64.iso",
"version": "2016.1",
"md5sum": "2e1230dc14036935b3279dfe3e49ad39",
"filesize": 2945482752,
- "download_url": "http://cdimage.kali.org/kali-2016.1/",
- "direct_download_url": "http://cdimage.kali.org/kali-2016.1/kali-linux-2016.1-amd64.iso"
+ "download_url": "http://old.kali.org/kali-images/kali-2016.1/",
+ "direct_download_url": "http://old.kali.org/kali-images/kali-2016.1/kali-linux-2016.1-amd64.iso"
},
{
"filename": "kali-linux-2.0-amd64.iso",
@@ -137,6 +145,13 @@
}
],
"versions": [
+ {
+ "name": "2021.1",
+ "images": {
+ "hda_disk_image": "kali-linux-persistence-1gb.qcow2",
+ "cdrom_image": "kali-linux-2021.1-live-amd64.iso"
+ }
+ },
{
"name": "2019.3",
"images": {
diff --git a/gns3server/appliances/mcjoin.gns3a b/gns3server/appliances/mcjoin.gns3a
new file mode 100644
index 00000000..74515a34
--- /dev/null
+++ b/gns3server/appliances/mcjoin.gns3a
@@ -0,0 +1,18 @@
+{
+ "name": "mcjoin",
+ "category": "guest",
+ "description": "mcjoin is a very simple and easy-to-use tool to test IPv4 and IPv6 multicast.",
+ "vendor_name": "Joachim Nilsson",
+ "vendor_url": "https://github.com/troglobit",
+ "product_name": "mcjoin",
+ "registry_version": 3,
+ "status": "stable",
+ "maintainer": "GNS3 Team",
+ "maintainer_email": "developers@gns3.net",
+ "symbol": "linux_guest.svg",
+ "docker": {
+ "adapters": 1,
+ "image": "troglobit/mcjoin:latest",
+ "console_type": "telnet"
+ }
+}
diff --git a/gns3server/appliances/ntopng.gns3a b/gns3server/appliances/ntopng.gns3a
index c2f3c940..6c5b58c6 100644
--- a/gns3server/appliances/ntopng.gns3a
+++ b/gns3server/appliances/ntopng.gns3a
@@ -3,16 +3,18 @@
"category": "guest",
"description": "ntopng is the next generation version of the original ntop, a network traffic probe that shows the network usage, similar to what the popular top Unix command does. ntopng is based on libpcap and it has been written in a portable way in order to virtually run on every Unix platform, MacOSX and on Windows as well. ntopng users can use a a web browser to navigate through ntop (that acts as a web server) traffic information and get a dump of the network status. In the latter case, ntopng can be seen as a simple RMON-like agent with an embedded web interface.",
"vendor_name": "ntop",
- "vendor_url": "http://www.ntop.org/",
+ "vendor_url": "https://www.ntop.org/",
+ "documentation_url": "https://www.ntop.org/guides/ntopng/",
"product_name": "ntopng",
"registry_version": 3,
"status": "stable",
"maintainer": "GNS3 Team",
"maintainer_email": "developers@gns3.net",
- "usage": "In the web interface login as admin/admin",
+ "usage": "In the web interface login as admin/admin\n\nPersistent configuration:\n- Add \"/var/lib/redis\" as an additional persistent directory.\n- Use \"redis-cli save\" in an auxiliary console to save the configuration.",
"docker": {
"adapters": 1,
- "image": "lucaderi/ntopng-docker:latest",
+ "image": "ntop/ntopng:stable",
+ "start_command": "--dns-mode 2 --interface eth0",
"console_type": "http",
"console_http_port": 3000,
"console_http_path": "/"
diff --git a/gns3server/appliances/open-media-vault.gns3a b/gns3server/appliances/open-media-vault.gns3a
index 9e0d624c..6bc5ef04 100644
--- a/gns3server/appliances/open-media-vault.gns3a
+++ b/gns3server/appliances/open-media-vault.gns3a
@@ -19,7 +19,7 @@
"ram": 2048,
"hda_disk_interface": "ide",
"hdb_disk_interface": "ide",
- "arch": "x86_64",
+ "arch": "x86_64",
"console_type": "vnc",
"boot_priority": "dc",
"kvm": "require"
@@ -52,4 +52,4 @@
}
}
]
-}
\ No newline at end of file
+}
diff --git a/gns3server/appliances/openbsd.gns3a b/gns3server/appliances/openbsd.gns3a
index 71c7a3ce..87bd0738 100644
--- a/gns3server/appliances/openbsd.gns3a
+++ b/gns3server/appliances/openbsd.gns3a
@@ -11,7 +11,7 @@
"maintainer": "GNS3 Team",
"maintainer_email": "developers@gns3.net",
"usage": "User root, password gns3",
- "first_port_name": "fxp0",
+ "first_port_name": "em0",
"port_name_format": "em{0}",
"qemu": {
"adapter_type": "e1000",
diff --git a/gns3server/appliances/openwrt.gns3a b/gns3server/appliances/openwrt.gns3a
index f5782536..a8b8fca8 100644
--- a/gns3server/appliances/openwrt.gns3a
+++ b/gns3server/appliances/openwrt.gns3a
@@ -22,7 +22,7 @@
"kvm": "allow"
},
"images": [
- {
+ {
"filename": "openwrt-19.07.7-x86-64-combined-ext4.img",
"version": "19.07.7",
"md5sum": "0cfa752fab87014419ab00b18a6cc5a6",
diff --git a/gns3server/appliances/puppy-linux.gns3a b/gns3server/appliances/puppy-linux.gns3a
index 24315993..a0d6a435 100644
--- a/gns3server/appliances/puppy-linux.gns3a
+++ b/gns3server/appliances/puppy-linux.gns3a
@@ -26,7 +26,7 @@
"filename": "fossapup64-9.5.iso",
"version": "9.5",
"md5sum": "6a45e7a305b7d3172ebd9eab5ca460e4",
- "filesize": 428867584,
+ "filesize": 428867584,
"download_url": "http://puppylinux.com/index.html",
"direct_download_url": "http://distro.ibiblio.org/puppylinux/puppy-fossa/fossapup64-9.5.iso"
},
diff --git a/gns3server/appliances/rhel.gns3a b/gns3server/appliances/rhel.gns3a
index 29a87362..22b3ab72 100644
--- a/gns3server/appliances/rhel.gns3a
+++ b/gns3server/appliances/rhel.gns3a
@@ -7,7 +7,7 @@
"documentation_url": "https://access.redhat.com/solutions/641193",
"product_name": "Red Hat Enterprise Linux KVM Guest Image",
"product_url": "https://www.redhat.com/en/technologies/linux-platforms/enterprise-linux",
- "registry_version": 5,
+ "registry_version": 4,
"status": "stable",
"availability": "service-contract",
"maintainer": "Neyder Achahuanco",
@@ -56,25 +56,25 @@
],
"versions": [
{
+ "name": "8.3",
"images": {
"hda_disk_image": "rhel-8.3-x86_64-kvm.qcow2",
"cdrom_image": "rhel-cloud-init.iso"
- },
- "name": "8.3"
+ }
},
{
+ "name": "7.9",
"images": {
"hda_disk_image": "rhel-server-7.9-x86_64-kvm.qcow2",
"cdrom_image": "rhel-cloud-init.iso"
- },
- "name": "7.9"
+ }
},
{
+ "name": "6.10",
"images": {
"hda_disk_image": "rhel-server-6.10-update-11-x86_64-kvm.qcow2",
"cdrom_image": "rhel-cloud-init.iso"
- },
- "name": "6.10"
+ }
}
]
}
diff --git a/gns3server/appliances/rockylinux.gns3a b/gns3server/appliances/rockylinux.gns3a
new file mode 100644
index 00000000..789918d3
--- /dev/null
+++ b/gns3server/appliances/rockylinux.gns3a
@@ -0,0 +1,45 @@
+{
+ "name": "RockyLinux",
+ "category": "guest",
+ "description": "Rocky Linux is a community enterprise operating system designed to be 100% bug-for-bug compatible with Red Hat Enterprise Linux (RHEL).",
+ "vendor_name": "Rocky Enterprise Software Foundation",
+ "vendor_url": "https://rockylinux.org",
+ "documentation_url": "https://docs.rockylinux.org",
+ "product_name": "Rocky Linux",
+ "registry_version": 4,
+ "status": "experimental",
+ "maintainer": "Bernhard Ehlers",
+ "maintainer_email": "none@b-ehlers.de",
+ "usage": "Username:\trockylinux\nPassword:\trockylinux\nTo become root, use \"sudo su\".\n\nTo improve performance, increase RAM and vCPUs in the VM settings.",
+ "symbol": "linux_guest.svg",
+ "port_name_format": "ens{port4}",
+ "qemu": {
+ "adapter_type": "virtio-net-pci",
+ "adapters": 1,
+ "ram": 1536,
+ "hda_disk_interface": "sata",
+ "arch": "x86_64",
+ "console_type": "vnc",
+ "kvm": "require",
+ "options": "-usbdevice tablet"
+ },
+ "images": [
+ {
+ "filename": "RockyLinux_8.4_VMG_LinuxVMImages.COM.vmdk",
+ "version": "8.4",
+ "md5sum": "3452d5b0fbb4cdcf3ac6fe8de8d0ac08",
+ "filesize": 5273878528,
+ "download_url": "https://www.linuxvmimages.com/images/rockylinux-8",
+ "direct_download_url": "https://sourceforge.net/projects/linuxvmimages/files/VMware/R/rockylinux/8/RockyLinux_8.4_VMM.7z/download",
+ "compression": "7z"
+ }
+ ],
+ "versions": [
+ {
+ "name": "8.4",
+ "images": {
+ "hda_disk_image": "RockyLinux_8.4_VMG_LinuxVMImages.COM.vmdk"
+ }
+ }
+ ]
+}
diff --git a/gns3server/appliances/stonework.gns3a b/gns3server/appliances/stonework.gns3a
new file mode 100644
index 00000000..ae1d9644
--- /dev/null
+++ b/gns3server/appliances/stonework.gns3a
@@ -0,0 +1,20 @@
+{
+ "name": "StoneWork",
+ "category": "router",
+ "description": "StoneWork is VPP and Ligato based routing platform",
+ "vendor_name": "Pantheon.tech StoneWork router",
+ "vendor_url": "https://pantheon.tech/",
+ "documentation_url": "https://pantheon.tech/documentation-stonework-gns3/",
+ "product_name": "StoneWork",
+ "registry_version": 4,
+ "status": "experimental",
+ "availability": "free",
+ "maintainer": "Julius Milan",
+ "maintainer_email": "julius.milan@pantheon.tech",
+ "docker": {
+ "adapters": 5,
+ "image": "ghcr.io/pantheontech/stonework",
+ "start_command": "/root/stonework-gns3-startup.sh",
+ "environment": "INITIAL_LOGLVL=debug,\nMICROSERVICE_LABEL=stonework,\nETCD_CONFIG=,\nCNF_MGMT_SUBNET=127.0.0.1/8"
+ }
+}
diff --git a/gns3server/appliances/tinycore-linux.gns3a b/gns3server/appliances/tinycore-linux.gns3a
index 1fcee667..17e4825b 100644
--- a/gns3server/appliances/tinycore-linux.gns3a
+++ b/gns3server/appliances/tinycore-linux.gns3a
@@ -1,7 +1,7 @@
{
"name": "Tiny Core Linux",
"category": "guest",
- "description": "Core Linux is a smaller variant of Tiny Core without a graphical desktop.\n\nIt provides a complete Linux system using only a few MiB." ,
+ "description": "Core Linux is a smaller variant of Tiny Core without a graphical desktop.\n\nIt provides a complete Linux system using only a few MiB.",
"vendor_name": "Team Tiny Core",
"vendor_url": "http://distro.ibiblio.org/tinycorelinux",
"documentation_url": "http://wiki.tinycorelinux.net/",
diff --git a/gns3server/appliances/ubuntu-gui.gns3a b/gns3server/appliances/ubuntu-gui.gns3a
index b6b30be0..c551da7c 100644
--- a/gns3server/appliances/ubuntu-gui.gns3a
+++ b/gns3server/appliances/ubuntu-gui.gns3a
@@ -25,6 +25,20 @@
"options": "-vga virtio"
},
"images": [
+ {
+ "filename": "Ubuntu 20.10 (64bit).vmdk",
+ "version": "20.10",
+ "md5sum": "d7fb9d7b5f6e55349204d493d00507d2",
+ "filesize": 7512915968,
+ "download_url": "http://www.osboxes.org/ubuntu/"
+ },
+ {
+ "filename": "Ubuntu 20.04.2 (64bit).vmdk",
+ "version": "20.04.2",
+ "md5sum": "e995e5768c1dbee94bc02072d841bb50",
+ "filesize": 7625179136,
+ "download_url": "http://www.osboxes.org/ubuntu/"
+ },
{
"filename": "Ubuntu 20.04 (64bit).vmdk",
"version": "20.04",
@@ -62,6 +76,18 @@
}
],
"versions": [
+ {
+ "name": "20.10",
+ "images": {
+ "hda_disk_image": "Ubuntu 20.10 (64bit).vmdk"
+ }
+ },
+ {
+ "name": "20.04.2",
+ "images": {
+ "hda_disk_image": "Ubuntu 20.04.2 (64bit).vmdk"
+ }
+ },
{
"name": "20.04",
"images": {
diff --git a/gns3server/appliances/vyos.gns3a b/gns3server/appliances/vyos.gns3a
index 24aaa0eb..20a1f3c3 100644
--- a/gns3server/appliances/vyos.gns3a
+++ b/gns3server/appliances/vyos.gns3a
@@ -26,19 +26,25 @@
},
"images": [
{
- "filename": "vyos-1.3-rolling-202101-qemu.qcow2",
- "version": "1.3-snapshot-202101",
- "md5sum": "b05a1f8a879c42342ea90f65ebe62f05",
- "filesize": 315359232,
- "download_url": "https://vyos.net/get/snapshots/",
- "direct_download_url": "https://s3.amazonaws.com/s3-us.vyos.io/snapshot/vyos-1.3-rolling-202101/qemu/vyos-1.3-rolling-202101-qemu.qcow2"
+ "filename": "vyos-1.3.0-rc5-amd64.qcow2",
+ "version": "1.3.0-rc5",
+ "md5sum": "dd704f59afc0fccdf601cc750bf2c438",
+ "filesize": 361955328,
+ "direct_download_url": "https://www.mediafire.com/file/taspgxh4vj0a4j1/vyos-1.3.0-rc5-amd64.qcow2/file"
},
{
- "filename": "vyos-1.2.6-S1-amd64.iso",
- "version": "1.2.6-S1",
- "md5sum": "449873ae455d5f3f509db7a614e86984",
+ "filename": "vyos-1.2.8-amd64.iso",
+ "version": "1.2.8",
+ "md5sum": "0ad879db903efdbf1c39dc945e165931",
+ "filesize": 429916160,
+ "download_url": "https://support.vyos.io/en/downloads/files/vyos-1-2-8-generic-iso-image"
+ },
+ {
+ "filename": "vyos-1.2.7-amd64.iso",
+ "version": "1.2.7",
+ "md5sum": "1a06255edfac63fa3ea89353317130bf",
"filesize": 428867584,
- "download_url": "https://support.vyos.io/en/downloads/files/vyos-1-2-6-s1-generic-iso-image"
+ "download_url": "https://support.vyos.io/en/downloads/files/vyos-1-2-7-generic-iso-image"
},
{
"filename": "vyos-1.1.8-amd64.iso",
@@ -59,16 +65,23 @@
],
"versions": [
{
- "name": "1.3-snapshot-202101",
+ "name": "1.3.0-rc5",
"images": {
- "hda_disk_image": "vyos-1.3-rolling-202101-qemu.qcow2"
+ "hda_disk_image": "vyos-1.3.0-rc5-amd64.qcow2"
}
},
{
- "name": "1.2.6-S1",
+ "name": "1.2.8",
"images": {
"hda_disk_image": "empty8G.qcow2",
- "cdrom_image": "vyos-1.2.6-S1-amd64.iso"
+ "cdrom_image": "vyos-1.2.8-amd64.iso"
+ }
+ },
+ {
+ "name": "1.2.7",
+ "images": {
+ "hda_disk_image": "empty8G.qcow2",
+ "cdrom_image": "vyos-1.2.7-amd64.iso"
}
},
{
diff --git a/gns3server/appliances/windows-xp+ie.gns3a b/gns3server/appliances/windows-xp+ie.gns3a
index 6bd31aa9..f5aeb0c6 100644
--- a/gns3server/appliances/windows-xp+ie.gns3a
+++ b/gns3server/appliances/windows-xp+ie.gns3a
@@ -48,4 +48,4 @@
}
}
]
-}
\ No newline at end of file
+}
diff --git a/gns3server/compute/base_manager.py b/gns3server/compute/base_manager.py
index 23656a43..e4a6442f 100644
--- a/gns3server/compute/base_manager.py
+++ b/gns3server/compute/base_manager.py
@@ -481,7 +481,7 @@ class BaseManager:
if not path:
return ""
- orig_path = path
+ orig_path = os.path.normpath(path)
server_config = self.config.get_section_config("Server")
img_directory = self.get_images_directory()
@@ -494,7 +494,8 @@ class BaseManager:
if re.match(r"^[A-Z]:", path) is not None:
raise NodeError("{} is not allowed on this remote server. Please only use a file from '{}'".format(path, img_directory))
- if not os.path.isabs(path):
+ if not os.path.isabs(orig_path):
+
for directory in valid_directory_prefices:
log.debug("Searching for image '{}' in '{}'".format(orig_path, directory))
path = self._recursive_search_file_in_directory(directory, orig_path)
@@ -503,6 +504,12 @@ class BaseManager:
# Not found we try the default directory
log.debug("Searching for image '{}' in default directory".format(orig_path))
+
+ # check that the image path is in the default image directory
+ #common_prefix = os.path.commonprefix([orig_path, img_directory])
+ #if common_prefix != img_directory:
+ # raise NodeError("{} is not allowed. Please only use a file from '{}'".format(orig_path, img_directory))
+
s = os.path.split(orig_path)
path = force_unix_path(os.path.join(img_directory, *s))
if os.path.exists(path):
@@ -517,7 +524,6 @@ class BaseManager:
return path
raise ImageMissingError(orig_path)
- # Check to see if path is an absolute path to a valid directory
path = force_unix_path(path)
for directory in valid_directory_prefices:
log.debug("Searching for image '{}' in '{}'".format(orig_path, directory))
@@ -525,7 +531,8 @@ class BaseManager:
if os.path.exists(path):
return path
raise ImageMissingError(orig_path)
- raise NodeError("{} is not allowed on this remote server. Please only use a file from '{}'".format(path, img_directory))
+ raise NodeError("{} is not allowed on this remote server. Please only use a file from '{}'"
+ .format(path, img_directory))
def _recursive_search_file_in_directory(self, directory, searched_file):
"""
@@ -533,12 +540,11 @@ class BaseManager:
:returns: Path or None if not found
"""
- s = os.path.split(searched_file)
+ s = os.path.split(searched_file)
for root, dirs, files in os.walk(directory):
for file in files:
- # If filename is the same
- if s[1] == file and (s[0] == '' or s[0] == os.path.basename(root)):
+ if s[1] == file and (s[0] == '' or root == os.path.join(directory, s[0])):
path = os.path.normpath(os.path.join(root, s[1]))
if os.path.exists(path):
return path
diff --git a/gns3server/compute/base_node.py b/gns3server/compute/base_node.py
index 4716439e..0341d101 100644
--- a/gns3server/compute/base_node.py
+++ b/gns3server/compute/base_node.py
@@ -430,7 +430,7 @@ class BaseNode:
telnet_writer.write(msg.data.encode())
await telnet_writer.drain()
elif msg.type == aiohttp.WSMsgType.BINARY:
- await telnet_writer.write(msg.data)
+ telnet_writer.write(msg.data)
await telnet_writer.drain()
elif msg.type == aiohttp.WSMsgType.ERROR:
log.debug("Websocket connection closed with exception {}".format(ws.exception()))
diff --git a/gns3server/compute/builtin/nodes/cloud.py b/gns3server/compute/builtin/nodes/cloud.py
index 02bcdce5..94ca0823 100644
--- a/gns3server/compute/builtin/nodes/cloud.py
+++ b/gns3server/compute/builtin/nodes/cloud.py
@@ -362,12 +362,12 @@ class Cloud(BaseNode):
"""
# Wireless adapters are not well supported by the libpcap on OSX
- if (await self._is_wifi_adapter_osx(port_info["interface"])):
+ if await self._is_wifi_adapter_osx(port_info["interface"]):
raise NodeError("Connecting to a Wireless adapter is not supported on Mac OS")
if port_info["interface"].startswith("vmnet"):
# Use a special NIO to connect to VMware vmnet interfaces on OSX (libpcap doesn't support them)
await self._ubridge_send('bridge add_nio_fusion_vmnet {name} "{interface}"'.format(name=bridge_name,
- interface=port_info["interface"]))
+ interface=port_info["interface"]))
return
if not gns3server.utils.interfaces.has_netmask(port_info["interface"]):
raise NodeError("Interface {} has no netmask, interface down?".format(port_info["interface"]))
@@ -402,6 +402,7 @@ class Cloud(BaseNode):
await self._add_ubridge_connection(nio, port_number)
self._nios[port_number] = nio
except (NodeError, UbridgeError) as e:
+ log.error('Cannot add NIO on cloud "{name}": {error}'.format(name=self._name, error=e))
await self._stop_ubridge()
self.status = "stopped"
self._nios[port_number] = nio
diff --git a/gns3server/compute/builtin/nodes/nat.py b/gns3server/compute/builtin/nodes/nat.py
index 35984315..a32b8700 100644
--- a/gns3server/compute/builtin/nodes/nat.py
+++ b/gns3server/compute/builtin/nodes/nat.py
@@ -35,13 +35,22 @@ class Nat(Cloud):
def __init__(self, name, node_id, project, manager, ports=None):
+ allowed_interfaces = Config.instance().get_section_config("Server").get("allowed_interfaces", None)
+ if allowed_interfaces:
+ allowed_interfaces = allowed_interfaces.split(',')
if sys.platform.startswith("linux"):
nat_interface = Config.instance().get_section_config("Server").get("default_nat_interface", "virbr0")
+ if allowed_interfaces and nat_interface not in allowed_interfaces:
+ raise NodeError("NAT interface {} is not allowed be used on this server. "
+ "Please check the server configuration file.".format(nat_interface))
if nat_interface not in [interface["name"] for interface in gns3server.utils.interfaces.interfaces()]:
raise NodeError("NAT interface {} is missing, please install libvirt".format(nat_interface))
interface = nat_interface
else:
nat_interface = Config.instance().get_section_config("Server").get("default_nat_interface", "vmnet8")
+ if allowed_interfaces and nat_interface not in allowed_interfaces:
+ raise NodeError("NAT interface {} is not allowed be used on this server. "
+ "Please check the server configuration file.".format(nat_interface))
interfaces = list(filter(lambda x: nat_interface in x.lower(),
[interface["name"] for interface in gns3server.utils.interfaces.interfaces()]))
if not len(interfaces):
diff --git a/gns3server/compute/qemu/__init__.py b/gns3server/compute/qemu/__init__.py
index bca490cc..be834425 100644
--- a/gns3server/compute/qemu/__init__.py
+++ b/gns3server/compute/qemu/__init__.py
@@ -152,8 +152,6 @@ class Qemu(BaseManager):
log.debug("Searching for Qemu binaries in '{}'".format(path))
try:
for f in os.listdir(path):
- if f.endswith("-spice"):
- continue
if (f.startswith("qemu-system") or f.startswith("qemu-kvm") or f == "qemu" or f == "qemu.exe") and \
os.access(os.path.join(path, f), os.X_OK) and \
os.path.isfile(os.path.join(path, f)):
diff --git a/gns3server/compute/qemu/qemu_vm.py b/gns3server/compute/qemu/qemu_vm.py
index 15ad6834..b5ae36e3 100644
--- a/gns3server/compute/qemu/qemu_vm.py
+++ b/gns3server/compute/qemu/qemu_vm.py
@@ -615,7 +615,7 @@ class QemuVM(BaseNode):
if not mac_address:
# use the node UUID to generate a random MAC address
- self._mac_address = "0c:%s:%s:%s:%s:00" % (self.project.id[-4:-2], self.project.id[-2:], self.id[-4:-2], self.id[-2:])
+ self._mac_address = "0c:%s:%s:%s:00:00" % (self.id[2:4], self.id[4:6], self.id[6:8])
else:
self._mac_address = mac_address
@@ -829,20 +829,26 @@ class QemuVM(BaseNode):
id=self._id,
options=options))
- if not sys.platform.startswith("linux"):
- if "-no-kvm" in options:
- options = options.replace("-no-kvm", "")
- if "-enable-kvm" in options:
+ # "-no-kvm" and "-no-hax' are deprecated since Qemu v5.2
+ if "-no-kvm" in options:
+ options = options.replace("-no-kvm", "-machine accel=tcg")
+ if "-no-hax" in options:
+ options = options.replace("-no-hax", "-machine accel=tcg")
+
+ if "-enable-kvm" in options:
+ if not sys.platform.startswith("linux"):
+ # KVM can only be enabled on Linux
options = options.replace("-enable-kvm", "")
- else:
- if "-no-hax" in options:
- options = options.replace("-no-hax", "")
- if "-enable-hax" in options:
+ else:
+ options = options.replace("-enable-kvm", "-machine accel=kvm")
+
+ if "-enable-hax" in options:
+ if not sys.platform.startswith("win"):
+ # HAXM is only available on Windows
options = options.replace("-enable-hax", "")
- if "-icount" in options and ("-no-kvm" not in options):
- # automatically add the -no-kvm option if -icount is detected
- # to help with the migration of ASA VMs created before version 1.4
- options = "-no-kvm " + options
+ else:
+ options = options.replace("-enable-hax", "-machine accel=hax")
+
self._options = options.strip()
@property
@@ -1675,6 +1681,24 @@ class QemuVM(BaseNode):
try:
qemu_img_path = self._get_qemu_img()
command = [qemu_img_path, "create", "-o", "backing_file={}".format(disk_image), "-f", "qcow2", disk]
+ try:
+ base_qcow2 = Qcow2(disk_image)
+ if base_qcow2.crypt_method:
+ # Workaround for https://gitlab.com/qemu-project/qemu/-/issues/441
+ # Also embed a secret name so it doesn't have to be passed to qemu -drive ...
+ options = {
+ "encrypt.key-secret": os.path.basename(disk_image),
+ "driver": "qcow2",
+ "file": {
+ "driver": "file",
+ "filename": disk_image,
+ },
+ }
+ command = [qemu_img_path, "create", "-b", "json:"+json.dumps(options, separators=(',', ':')),
+ "-f", "qcow2", "-u", disk, str(base_qcow2.size)]
+ except Qcow2Error:
+ pass # non-qcow2 base images are acceptable (e.g. vmdk, raw image)
+
retcode = await self._qemu_img_exec(command)
if retcode:
stdout = self.read_qemu_img_stdout()
@@ -1845,6 +1869,7 @@ class QemuVM(BaseNode):
log.warning("Qemu image {} is corrupted".format(disk_image))
if (await self._qemu_img_exec([qemu_img_path, "check", "-r", "all", "{}".format(disk_image)])) == 2:
self.project.emit("log.warning", {"message": "Qemu image '{}' is corrupted and could not be fixed".format(disk_image)})
+ # ignore retcode == 1. One reason is that the image is encrypted and there is no encrypt.key-secret available
except (OSError, subprocess.SubprocessError) as e:
stdout = self.read_qemu_img_stdout()
raise QemuError("Could not check '{}' disk image: {}\n{}".format(disk_name, e, stdout))
@@ -1858,9 +1883,9 @@ class QemuVM(BaseNode):
# The disk exists we check if the clone works
try:
qcow2 = Qcow2(disk)
- await qcow2.rebase(qemu_img_path, disk_image)
+ await qcow2.validate(qemu_img_path)
except (Qcow2Error, OSError) as e:
- raise QemuError("Could not use qcow2 disk image '{}' for {} {}".format(disk_image, disk_name, e))
+ raise QemuError("Could not use qcow2 disk image '{}' for {}: {}".format(disk_image, disk_name, e))
else:
disk = disk_image
@@ -2078,7 +2103,7 @@ class QemuVM(BaseNode):
if require_kvm is not None:
require_hardware_accel = require_kvm
- if enable_hardware_accel and "-no-kvm" not in options and "-no-hax" not in options:
+ if enable_hardware_accel and "-machine accel=tcg" not in options:
# Turn OFF hardware acceleration for non x86 architectures
if sys.platform.startswith("win"):
supported_binaries = ["qemu-system-x86_64.exe", "qemu-system-x86_64w.exe", "qemu-system-i386.exe", "qemu-system-i386w.exe"]
diff --git a/gns3server/compute/qemu/utils/qcow2.py b/gns3server/compute/qemu/utils/qcow2.py
index efbb54fe..5a4d7979 100644
--- a/gns3server/compute/qemu/utils/qcow2.py
+++ b/gns3server/compute/qemu/utils/qcow2.py
@@ -58,11 +58,12 @@ class Qcow2:
# uint64_t snapshots_offset;
# } QCowHeader;
- struct_format = ">IIQi"
+ struct_format = ">IIQiiQi"
with open(self._path, 'rb') as f:
content = f.read(struct.calcsize(struct_format))
try:
- self.magic, self.version, self.backing_file_offset, self.backing_file_size = struct.unpack_from(struct_format, content)
+ (self.magic, self.version, self.backing_file_offset, self.backing_file_size,
+ self.cluster_bits, self.size, self.crypt_method) = struct.unpack_from(struct_format, content)
except struct.error:
raise Qcow2Error("Invalid file header for {}".format(self._path))
@@ -103,3 +104,15 @@ class Qcow2:
if retcode != 0:
raise Qcow2Error("Could not rebase the image")
self._reload()
+
+ async def validate(self, qemu_img):
+ """
+ Run qemu-img info to validate the file and its backing images
+
+ :param qemu_img: Path to the qemu-img binary
+ """
+ command = [qemu_img, "info", "--backing-chain", self._path]
+ process = await asyncio.create_subprocess_exec(*command)
+ retcode = await process.wait()
+ if retcode != 0:
+ raise Qcow2Error("Could not validate the image")
diff --git a/gns3server/compute/vmware/__init__.py b/gns3server/compute/vmware/__init__.py
index 59357490..37a640a2 100644
--- a/gns3server/compute/vmware/__init__.py
+++ b/gns3server/compute/vmware/__init__.py
@@ -27,6 +27,7 @@ import asyncio
import subprocess
import logging
import codecs
+import ipaddress
from collections import OrderedDict
from gns3server.utils.interfaces import interfaces
@@ -51,6 +52,7 @@ class VMware(BaseManager):
self._vmrun_path = None
self._host_type = None
self._vmnets = []
+ self._vmnets_info = {}
self._vmnet_start_range = 2
if sys.platform.startswith("win"):
self._vmnet_end_range = 19
@@ -273,7 +275,7 @@ class VMware(BaseManager):
else:
# location on Linux
vmware_networking_file = "/etc/vmware/networking"
- vmnet_interfaces = []
+ vmnet_interfaces = {}
try:
with open(vmware_networking_file, "r", encoding="utf-8") as f:
for line in f.read().splitlines():
@@ -281,7 +283,20 @@ class VMware(BaseManager):
if match:
vmnet = "vmnet{}".format(match.group(1))
if vmnet not in ("vmnet0", "vmnet1", "vmnet8"):
- vmnet_interfaces.append(vmnet)
+ vmnet_interfaces[vmnet] = {}
+ with open(vmware_networking_file, "r", encoding="utf-8") as f:
+ for line in f.read().splitlines():
+ match = re.search(r"VNET_([0-9]+)_HOSTONLY_SUBNET\s+(.*)", line)
+ if match:
+ vmnet = "vmnet{}".format(match.group(1))
+ if vmnet in vmnet_interfaces.keys():
+ vmnet_interfaces[vmnet]["subnet"] = match.group(2)
+ match = re.search(r"VNET_([0-9]+)_HOSTONLY_NETMASK\s+(.*)", line)
+ if match:
+ vmnet = "vmnet{}".format(match.group(1))
+ if vmnet in vmnet_interfaces.keys():
+ vmnet_interfaces[vmnet]["netmask"] = match.group(2)
+
except OSError as e:
raise VMwareError("Cannot open {}: {}".format(vmware_networking_file, e))
return vmnet_interfaces
@@ -324,6 +339,25 @@ class VMware(BaseManager):
raise VMwareError("No VMnet interface available between vmnet{} and vmnet{}. Go to preferences VMware / Network / Configure to add more interfaces.".format(self._vmnet_start_range, self._vmnet_end_range))
return self._vmnets.pop(0)
+ def find_bridge_interface(self, vmnet_interface):
+ """
+ Find the bridge interface that is used for the vmnet interface in VMware.
+ """
+
+ if vmnet_interface in self._vmnets_info.keys():
+ subnet = self._vmnets_info[vmnet_interface].get("subnet", None)
+ netmask = self._vmnets_info[vmnet_interface].get("netmask", None)
+ if subnet and netmask:
+ for interface in interfaces():
+ try:
+ network = ipaddress.ip_network(f"{subnet}/{netmask}")
+ ip = ipaddress.ip_address(interface["ip_address"])
+ except ValueError:
+ continue
+ if ip in network:
+ return interface["name"]
+ return None
+
def refresh_vmnet_list(self, ubridge=True):
if ubridge:
@@ -331,6 +365,8 @@ class VMware(BaseManager):
vmnet_interfaces = self._get_vmnet_interfaces_ubridge()
else:
vmnet_interfaces = self._get_vmnet_interfaces()
+ vmnet_interfaces = list(vmnet_interfaces.keys())
+ self._vmnets_info = vmnet_interfaces.copy()
# remove vmnets already in use
for vmware_vm in self._nodes.values():
@@ -734,5 +770,4 @@ class VMware(BaseManager):
if __name__ == '__main__':
loop = asyncio.get_event_loop()
vmware = VMware.instance()
- print("=> Check version")
loop.run_until_complete(asyncio.ensure_future(vmware.check_vmware_version()))
diff --git a/gns3server/compute/vmware/vmware_vm.py b/gns3server/compute/vmware/vmware_vm.py
index 28b67ca2..60bacea5 100644
--- a/gns3server/compute/vmware/vmware_vm.py
+++ b/gns3server/compute/vmware/vmware_vm.py
@@ -23,9 +23,11 @@ import sys
import os
import asyncio
import tempfile
+import platform
from gns3server.utils.asyncio.telnet_server import AsyncioTelnetServer
from gns3server.utils.asyncio.serial import asyncio_open_serial
+from gns3server.utils import parse_version
from gns3server.utils.asyncio import locking
from collections import OrderedDict
from .vmware_error import VMwareError
@@ -252,8 +254,13 @@ class VMwareVM(BaseNode):
if self._get_vmx_setting(connected):
del self._vmx_pairs[connected]
+ use_ubridge = True
+ # use alternative method to find vmnet interfaces on macOS >= 11.0 (BigSur)
+ # because "bridge" interfaces are used instead and they are only created on the VM starts
+ if sys.platform.startswith("darwin") and parse_version(platform.mac_ver()[0]) >= parse_version("11.0.0"):
+ use_ubridge = False
+ self.manager.refresh_vmnet_list(ubridge=use_ubridge)
# then configure VMware network adapters
- self.manager.refresh_vmnet_list()
for adapter_number in range(0, self._adapters):
custom_adapter = self._get_custom_adapter_settings(adapter_number)
@@ -333,8 +340,17 @@ class VMwareVM(BaseNode):
vmnet_interface = os.path.basename(self._vmx_pairs[vnet])
if sys.platform.startswith("darwin"):
- # special case on OSX, we cannot bind VMnet interfaces using the libpcap
- await self._ubridge_send('bridge add_nio_fusion_vmnet {name} "{interface}"'.format(name=vnet, interface=vmnet_interface))
+ if parse_version(platform.mac_ver()[0]) >= parse_version("11.0.0"):
+ # a bridge interface (bridge100, bridge101 etc.) is used instead of a vmnet interface
+ # on macOS >= 11.0 (Big Sur)
+ vmnet_interface = self.manager.find_bridge_interface(vmnet_interface)
+ if not vmnet_interface:
+ raise VMwareError(f"Could not find bridge interface linked with {vmnet_interface}")
+ block_host_traffic = self.manager.config.get_section_config("VMware").getboolean("block_host_traffic", False)
+ await self._add_ubridge_ethernet_connection(vnet, vmnet_interface, block_host_traffic)
+ else:
+ # special case on macOS, we cannot bind VMnet interfaces using the libpcap
+ await self._ubridge_send('bridge add_nio_fusion_vmnet {name} "{interface}"'.format(name=vnet, interface=vmnet_interface))
else:
block_host_traffic = self.manager.config.get_section_config("VMware").getboolean("block_host_traffic", False)
await self._add_ubridge_ethernet_connection(vnet, vmnet_interface, block_host_traffic)
@@ -426,7 +442,7 @@ class VMwareVM(BaseNode):
if self.status == "started":
return
- if (await self.is_running()):
+ if await self.is_running():
raise VMwareError("The VM is already running in VMware")
ubridge_path = self.ubridge_path
@@ -476,7 +492,7 @@ class VMwareVM(BaseNode):
await self._stop_ubridge()
try:
- if (await self.is_running()):
+ if await self.is_running():
if self.on_close == "save_vm_state":
await self._control_vm("suspend")
elif self.on_close == "shutdown_signal":
@@ -728,7 +744,7 @@ class VMwareVM(BaseNode):
# check for the connection type
connection_type = "ethernet{}.connectiontype".format(adapter_number)
if not self._use_any_adapter and connection_type in self._vmx_pairs and self._vmx_pairs[connection_type] in ("nat", "bridged", "hostonly"):
- if (await self.is_running()):
+ if await self.is_running():
raise VMwareError("Attachment '{attachment}' is configured on network adapter {adapter_number}. "
"Please stop VMware VM '{name}' to link to this adapter and allow GNS3 to change the attachment type.".format(attachment=self._vmx_pairs[connection_type],
adapter_number=adapter_number,
diff --git a/gns3server/controller/__init__.py b/gns3server/controller/__init__.py
index 1bca4141..e000ea06 100644
--- a/gns3server/controller/__init__.py
+++ b/gns3server/controller/__init__.py
@@ -53,6 +53,7 @@ class Controller:
self._notification = Notification(self)
self.gns3vm = GNS3VM(self)
self.symbols = Symbols()
+ self._ssl_context = None
self._appliance_manager = ApplianceManager()
self._template_manager = TemplateManager()
self._iou_license_settings = {"iourc_content": "",
@@ -82,9 +83,9 @@ class Controller:
computes = self._load_controller_settings()
from gns3server.web.web_server import WebServer
- ssl_context = WebServer.instance(host=host, port=port).ssl_context()
+ self._ssl_context = WebServer.instance(host=host, port=port).ssl_context()
protocol = server_config.get("protocol", "http")
- if ssl_context and protocol != "https":
+ if self._ssl_context and protocol != "https":
log.warning("Protocol changed to 'https' for local compute because SSL is enabled".format(port))
protocol = "https"
try:
@@ -97,7 +98,7 @@ class Controller:
user=server_config.get("user", ""),
password=server_config.get("password", ""),
force=True,
- ssl_context=ssl_context)
+ ssl_context=self._ssl_context)
except aiohttp.web.HTTPConflict:
log.fatal("Cannot access to the local server, make sure something else is not running on the TCP port {}".format(port))
sys.exit(1)
@@ -115,6 +116,13 @@ class Controller:
await self.load_projects()
await self._project_auto_open()
+ def ssl_context(self):
+ """
+ Returns the SSL context for the server.
+ """
+
+ return self._ssl_context
+
def _update_config(self):
"""
Call this when the server configuration file changes.
diff --git a/gns3server/controller/appliance_manager.py b/gns3server/controller/appliance_manager.py
index e0ea0a4a..cebcad58 100644
--- a/gns3server/controller/appliance_manager.py
+++ b/gns3server/controller/appliance_manager.py
@@ -16,6 +16,7 @@
# along with this program. If not, see .
import os
+import shutil
import json
import uuid
import asyncio
@@ -181,6 +182,7 @@ class ApplianceManager:
Controller.instance().save()
json_data = await response.json()
appliances_dir = get_resource('appliances')
+ downloaded_appliance_files = []
for appliance in json_data:
if appliance["type"] == "file":
appliance_name = appliance["name"]
@@ -201,6 +203,21 @@ class ApplianceManager:
f.write(appliance_data)
except OSError as e:
raise aiohttp.web.HTTPConflict(text="Could not write appliance file '{}': {}".format(path, e))
+ downloaded_appliance_files.append(appliance_name)
+
+ # delete old appliance files
+ for filename in os.listdir(appliances_dir):
+ file_path = os.path.join(appliances_dir, filename)
+ if filename in downloaded_appliance_files:
+ continue
+ try:
+ if os.path.isfile(file_path) or os.path.islink(file_path):
+ log.info("Deleting old appliance file {}".format(file_path))
+ os.unlink(file_path)
+ except OSError as e:
+ log.warning("Could not delete old appliance file '{}': {}".format(file_path, e))
+ continue
+
except ValueError as e:
raise aiohttp.web.HTTPConflict(text="Could not read appliances information from GitHub: {}".format(e))
diff --git a/gns3server/controller/compute.py b/gns3server/controller/compute.py
index 117a71e3..2ea049a5 100644
--- a/gns3server/controller/compute.py
+++ b/gns3server/controller/compute.py
@@ -312,7 +312,7 @@ class Compute:
url = self._getUrl("/projects/{}/files/{}".format(project.id, path))
response = await self._session().request("GET", url, auth=self._auth)
if response.status == 404:
- raise aiohttp.web.HTTPNotFound(text="{} not found on compute".format(path))
+ raise aiohttp.web.HTTPNotFound(text="File '{}' not found on compute".format(path))
return response
async def download_image(self, image_type, image):
@@ -327,7 +327,7 @@ class Compute:
url = self._getUrl("/{}/images/{}".format(image_type, image))
response = await self._session().request("GET", url, auth=self._auth)
if response.status == 404:
- raise aiohttp.web.HTTPNotFound(text="{} not found on compute".format(image))
+ raise aiohttp.web.HTTPNotFound(text="Image '{}' not found on compute".format(image))
return response
async def http_query(self, method, path, data=None, dont_connect=False, **kwargs):
diff --git a/gns3server/controller/export_project.py b/gns3server/controller/export_project.py
index 6d8e4210..fc7226fe 100644
--- a/gns3server/controller/export_project.py
+++ b/gns3server/controller/export_project.py
@@ -66,22 +66,26 @@ async def export_project(zstream, project, temporary_dir, include_images=False,
# Export the local files
for root, dirs, files in os.walk(project._path, topdown=True, followlinks=False):
- files = [f for f in files if _is_exportable(os.path.join(root, f), include_snapshots)]
- for file in files:
- path = os.path.join(root, file)
- # check if we can export the file
- try:
- open(path).close()
- except OSError as e:
- msg = "Could not export file {}: {}".format(path, e)
- log.warning(msg)
- project.emit_notification("log.warning", {"message": msg})
- continue
- # ignore the .gns3 file
- if file.endswith(".gns3"):
- continue
- _patch_mtime(path)
- zstream.write(path, os.path.relpath(path, project._path))
+ try:
+ files = [f for f in files if _is_exportable(os.path.join(root, f), include_snapshots)]
+ for file in files:
+ path = os.path.join(root, file)
+ # check if we can export the file
+ try:
+ open(path).close()
+ except OSError as e:
+ msg = "Could not export file {}: {}".format(path, e)
+ log.warning(msg)
+ project.emit_notification("log.warning", {"message": msg})
+ continue
+ # ignore the .gns3 file
+ if file.endswith(".gns3"):
+ continue
+ _patch_mtime(path)
+ zstream.write(path, os.path.relpath(path, project._path))
+ except FileNotFoundError as e:
+ log.warning("Cannot export local file: {}".format(e))
+ continue
# Export files from remote computes
for compute in project.computes:
@@ -91,8 +95,9 @@ async def export_project(zstream, project, temporary_dir, include_images=False,
if _is_exportable(compute_file["path"], include_snapshots):
log.debug("Downloading file '{}' from compute '{}'".format(compute_file["path"], compute.id))
response = await compute.download_file(project, compute_file["path"])
- #if response.status != 200:
- # raise aiohttp.web.HTTPConflict(text="Cannot export file from compute '{}'. Compute returned status code {}.".format(compute.id, response.status))
+ if response.status != 200:
+ log.warning("Cannot export file from compute '{}'. Compute returned status code {}.".format(compute.id, response.status))
+ continue
(fd, temp_path) = tempfile.mkstemp(dir=temporary_dir)
async with aiofiles.open(fd, 'wb') as f:
while True:
diff --git a/gns3server/controller/import_project.py b/gns3server/controller/import_project.py
index 678de2ac..aa7886f4 100644
--- a/gns3server/controller/import_project.py
+++ b/gns3server/controller/import_project.py
@@ -38,7 +38,8 @@ Handle the import of project from a .gns3project
"""
-async def import_project(controller, project_id, stream, location=None, name=None, keep_compute_id=False):
+async def import_project(controller, project_id, stream, location=None, name=None, keep_compute_id=False,
+ auto_start=False, auto_open=False, auto_close=True):
"""
Import a project contain in a zip file
@@ -98,9 +99,9 @@ async def import_project(controller, project_id, stream, location=None, name=Non
topology = load_topology(os.path.join(path, "project.gns3"))
topology["name"] = project_name
# To avoid unexpected behavior (project start without manual operations just after import)
- topology["auto_start"] = False
- topology["auto_open"] = False
- topology["auto_close"] = True
+ topology["auto_start"] = auto_start
+ topology["auto_open"] = auto_open
+ topology["auto_close"] = auto_close
# Generate a new node id
node_old_to_new = {}
diff --git a/gns3server/controller/link.py b/gns3server/controller/link.py
index 6700babc..9c2c3160 100644
--- a/gns3server/controller/link.py
+++ b/gns3server/controller/link.py
@@ -124,6 +124,7 @@ class Link:
self._link_type = "ethernet"
self._suspended = False
self._filters = {}
+ self._link_style = {}
@property
def filters(self):
@@ -209,6 +210,12 @@ class Link:
self._project.emit_notification("link.updated", self.__json__())
self._project.dump()
+ async def update_link_style(self, link_style):
+ if link_style != self._link_style:
+ self._link_style = link_style
+ self._project.emit_notification("link.updated", self.__json__())
+ self._project.dump()
+
@property
def created(self):
"""
@@ -454,6 +461,7 @@ class Link:
"nodes": res,
"link_id": self._id,
"filters": self._filters,
+ "link_style": self._link_style,
"suspend": self._suspended
}
return {
@@ -466,5 +474,6 @@ class Link:
"capture_compute_id": self.capture_compute_id,
"link_type": self._link_type,
"filters": self._filters,
+ "link_style": self._link_style,
"suspend": self._suspended
}
diff --git a/gns3server/controller/project.py b/gns3server/controller/project.py
index 33bb27a8..39b0895e 100644
--- a/gns3server/controller/project.py
+++ b/gns3server/controller/project.py
@@ -957,6 +957,8 @@ class Project:
link = await self.add_link(link_id=link_data["link_id"])
if "filters" in link_data:
await link.update_filters(link_data["filters"])
+ if "link_style" in link_data:
+ await link.update_link_style(link_data["link_style"])
for node_link in link_data.get("nodes", []):
node = self.get_node(node_link["node_id"])
port = node.get_port(node_link["adapter_number"], node_link["port_number"])
diff --git a/gns3server/controller/snapshot.py b/gns3server/controller/snapshot.py
index 8bb2b00c..792a9261 100644
--- a/gns3server/controller/snapshot.py
+++ b/gns3server/controller/snapshot.py
@@ -121,7 +121,9 @@ class Snapshot:
if os.path.exists(project_files_path):
await wait_run_in_executor(shutil.rmtree, project_files_path)
with open(self._path, "rb") as f:
- project = await import_project(self._project.controller, self._project.id, f, location=self._project.path)
+ project = await import_project(self._project.controller, self._project.id, f, location=self._project.path,
+ auto_start=self._project.auto_start, auto_open=self._project.auto_open,
+ auto_close=self._project.auto_close)
except (OSError, PermissionError) as e:
raise aiohttp.web.HTTPConflict(text=str(e))
await project.open()
diff --git a/gns3server/controller/topology.py b/gns3server/controller/topology.py
index dd57d3f7..f93e78f2 100644
--- a/gns3server/controller/topology.py
+++ b/gns3server/controller/topology.py
@@ -63,13 +63,13 @@ def _check_topology_schema(topo):
error = "Invalid data in topology file: {} in schema: {}".format(
e.message,
json.dumps(e.schema))
- log.critical(error)
+ log.debug(error)
raise aiohttp.web.HTTPConflict(text=error)
def project_to_topology(project):
"""
- :return: A dictionnary with the topology ready to dump to a .gns3
+ :return: A dictionary with the topology ready to dump to a .gns3
"""
data = {
"project_id": project.id,
diff --git a/gns3server/crash_report.py b/gns3server/crash_report.py
index 31236e71..70902371 100644
--- a/gns3server/crash_report.py
+++ b/gns3server/crash_report.py
@@ -58,7 +58,7 @@ class CrashReport:
Report crash to a third party service
"""
- DSN = "https://289e75a5996944f9af6de8b071c99cf0:b1caf92ea47a4b489b569e70cd85c02b@o19455.ingest.sentry.io/38482"
+ DSN = "https://95f189bae52543e38be226cc1de2c8f3:e06825958e234a3e9ae5a81eaa21993d@o19455.ingest.sentry.io/38482"
_instance = None
def __init__(self):
diff --git a/gns3server/handlers/api/compute/dynamips_vm_handler.py b/gns3server/handlers/api/compute/dynamips_vm_handler.py
index ef64e482..378dc1dd 100644
--- a/gns3server/handlers/api/compute/dynamips_vm_handler.py
+++ b/gns3server/handlers/api/compute/dynamips_vm_handler.py
@@ -470,7 +470,8 @@ class DynamipsVMHandler:
async def upload_image(request, response):
dynamips_manager = Dynamips.instance()
- await dynamips_manager.write_image(request.match_info["filename"], request.content)
+ filename = os.path.normpath(request.match_info["filename"])
+ await dynamips_manager.write_image(filename, request.content)
response.set_status(204)
@Route.get(
@@ -484,15 +485,15 @@ class DynamipsVMHandler:
raw=True,
description="Download a Dynamips IOS image")
async def download_image(request, response):
- filename = request.match_info["filename"]
+
+ filename = os.path.normpath(request.match_info["filename"])
+
+ # Raise error if user try to escape
+ if filename[0] == "." or os.path.sep in filename:
+ raise aiohttp.web.HTTPForbidden()
dynamips_manager = Dynamips.instance()
image_path = dynamips_manager.get_abs_image_path(filename)
-
- # Raise error if user try to escape
- if filename[0] == ".":
- raise aiohttp.web.HTTPForbidden()
-
await response.stream_file(image_path)
@Route.post(
diff --git a/gns3server/handlers/api/compute/iou_handler.py b/gns3server/handlers/api/compute/iou_handler.py
index 9a6649fc..ad27201e 100644
--- a/gns3server/handlers/api/compute/iou_handler.py
+++ b/gns3server/handlers/api/compute/iou_handler.py
@@ -428,7 +428,8 @@ class IOUHandler:
async def upload_image(request, response):
iou_manager = IOU.instance()
- await iou_manager.write_image(request.match_info["filename"], request.content)
+ filename = os.path.normpath(request.match_info["filename"])
+ await iou_manager.write_image(filename, request.content)
response.set_status(204)
@@ -443,15 +444,15 @@ class IOUHandler:
raw=True,
description="Download an IOU image")
async def download_image(request, response):
- filename = request.match_info["filename"]
+
+ filename = os.path.normpath(request.match_info["filename"])
+
+ # Raise error if user try to escape
+ if filename[0] == "." or os.path.sep in filename:
+ raise aiohttp.web.HTTPForbidden()
iou_manager = IOU.instance()
image_path = iou_manager.get_abs_image_path(filename)
-
- # Raise error if user try to escape
- if filename[0] == ".":
- raise aiohttp.web.HTTPForbidden()
-
await response.stream_file(image_path)
@Route.get(
diff --git a/gns3server/handlers/api/compute/project_handler.py b/gns3server/handlers/api/compute/project_handler.py
index d8eb0e11..ac9de943 100644
--- a/gns3server/handlers/api/compute/project_handler.py
+++ b/gns3server/handlers/api/compute/project_handler.py
@@ -20,12 +20,12 @@ import asyncio
import json
import os
import psutil
-import tempfile
from gns3server.web.route import Route
from gns3server.compute.project_manager import ProjectManager
from gns3server.compute import MODULES
from gns3server.utils.cpu_percent import CpuPercent
+from gns3server.utils.path import is_safe_path
from gns3server.schemas.project import (
PROJECT_OBJECT_SCHEMA,
@@ -247,14 +247,13 @@ class ProjectHandler:
pm = ProjectManager.instance()
project = pm.get_project(request.match_info["project_id"])
- path = request.match_info["path"]
- path = os.path.normpath(path)
+ path = os.path.normpath(request.match_info["path"])
# Raise error if user try to escape
- if path[0] == ".":
+ if not is_safe_path(path, project.path):
raise aiohttp.web.HTTPForbidden()
- path = os.path.join(project.path, path)
+ path = os.path.join(project.path, path)
await response.stream_file(path)
@Route.post(
@@ -273,15 +272,14 @@ class ProjectHandler:
pm = ProjectManager.instance()
project = pm.get_project(request.match_info["project_id"])
- path = request.match_info["path"]
- path = os.path.normpath(path)
+ path = os.path.normpath(request.match_info["path"])
# Raise error if user try to escape
- if path[0] == ".":
+ if not is_safe_path(path, project.path):
raise aiohttp.web.HTTPForbidden()
- path = os.path.join(project.path, path)
- response.set_status(200)
+ path = os.path.join(project.path, path)
+ response.set_status(201)
try:
os.makedirs(os.path.dirname(path), exist_ok=True)
diff --git a/gns3server/handlers/api/compute/qemu_handler.py b/gns3server/handlers/api/compute/qemu_handler.py
index 448fc702..0952da09 100644
--- a/gns3server/handlers/api/compute/qemu_handler.py
+++ b/gns3server/handlers/api/compute/qemu_handler.py
@@ -207,7 +207,7 @@ class QEMUHandler:
enable_kvm = qemu_manager.config.get_section_config("Qemu").getboolean("enable_kvm")
if enable_kvm is not None:
hardware_accel = enable_kvm
- if hardware_accel and "-no-kvm" not in vm.options and "-no-hax" not in vm.options:
+ if hardware_accel and "-machine accel=tcg" not in vm.options:
pm = ProjectManager.instance()
if pm.check_hardware_virtualization(vm) is False:
raise aiohttp.web.HTTPConflict(text="Cannot start VM with hardware acceleration (KVM/HAX) enabled because hardware virtualization (VT-x/AMD-V) is already used by another software like VMware or VirtualBox")
@@ -552,7 +552,8 @@ class QEMUHandler:
async def upload_image(request, response):
qemu_manager = Qemu.instance()
- await qemu_manager.write_image(request.match_info["filename"], request.content)
+ filename = os.path.normpath(request.match_info["filename"])
+ await qemu_manager.write_image(filename, request.content)
response.set_status(204)
@Route.get(
@@ -566,15 +567,15 @@ class QEMUHandler:
raw=True,
description="Download Qemu image")
async def download_image(request, response):
- filename = request.match_info["filename"]
+
+ filename = os.path.normpath(request.match_info["filename"])
+
+ # Raise error if user try to escape
+ if filename[0] == "." or os.path.sep in filename:
+ raise aiohttp.web.HTTPForbidden()
qemu_manager = Qemu.instance()
image_path = qemu_manager.get_abs_image_path(filename)
-
- # Raise error if user try to escape
- if filename[0] == ".":
- raise aiohttp.web.HTTPForbidden()
-
await response.stream_file(image_path)
@Route.get(
diff --git a/gns3server/handlers/api/controller/link_handler.py b/gns3server/handlers/api/controller/link_handler.py
index 7854583a..95f9702b 100644
--- a/gns3server/handlers/api/controller/link_handler.py
+++ b/gns3server/handlers/api/controller/link_handler.py
@@ -64,6 +64,8 @@ class LinkHandler:
link = await project.add_link()
if "filters" in request.json:
await link.update_filters(request.json["filters"])
+ if "link_style" in request.json:
+ await link.update_link_style(request.json["link_style"])
if "suspend" in request.json:
await link.update_suspend(request.json["suspend"])
try:
@@ -135,6 +137,8 @@ class LinkHandler:
link = project.get_link(request.match_info["link_id"])
if "filters" in request.json:
await link.update_filters(request.json["filters"])
+ if "link_style" in request.json:
+ await link.update_link_style(request.json["link_style"])
if "suspend" in request.json:
await link.update_suspend(request.json["suspend"])
if "nodes" in request.json:
@@ -215,6 +219,7 @@ class LinkHandler:
async def pcap(request, response):
project = await Controller.instance().get_loaded_project(request.match_info["project_id"])
+ ssl_context = Controller.instance().ssl_context()
link = project.get_link(request.match_info["link_id"])
if not link.capturing:
raise aiohttp.web.HTTPConflict(text="This link has no active packet capture")
@@ -226,7 +231,7 @@ class LinkHandler:
headers['Router-Host'] = request.host
body = await request.read()
- connector = aiohttp.TCPConnector(limit=None, force_close=True)
+ connector = aiohttp.TCPConnector(limit=None, force_close=True, ssl_context=ssl_context)
async with aiohttp.ClientSession(connector=connector, headers=headers) as session:
async with session.request(request.method, pcap_streaming_url, timeout=None, data=body) as response:
proxied_response = aiohttp.web.Response(headers=response.headers, status=response.status)
diff --git a/gns3server/handlers/api/controller/node_handler.py b/gns3server/handlers/api/controller/node_handler.py
index 51d68e7e..874b8fbb 100644
--- a/gns3server/handlers/api/controller/node_handler.py
+++ b/gns3server/handlers/api/controller/node_handler.py
@@ -409,21 +409,19 @@ class NodeHandler:
path = request.match_info["path"]
path = force_unix_path(path)
-
# Raise error if user try to escape
- if path[0] == ".":
+ if path[0] == "." or "/../" in path:
raise aiohttp.web.HTTPForbidden()
node_type = node.node_type
path = "/project-files/{}/{}/{}".format(node_type, node.id, path)
-
res = await node.compute.http_query("GET", "/projects/{project_id}/files{path}".format(project_id=project.id, path=path), timeout=None, raw=True)
- response.set_status(200)
- response.content_type = "application/octet-stream"
- response.enable_chunked_encoding()
- await response.prepare(request)
- await response.write(res.body)
- # await response.write_eof() #FIXME: shound't be needed anymore
+ response.set_status(res.status)
+ if res.status == 200:
+ response.content_type = "application/octet-stream"
+ response.enable_chunked_encoding()
+ await response.prepare(request)
+ await response.write(res.body)
@Route.post(
r"/projects/{project_id}/nodes/{node_id}/files/{path:.+}",
@@ -446,14 +444,14 @@ class NodeHandler:
path = force_unix_path(path)
# Raise error if user try to escape
- if path[0] == ".":
+ if path[0] == "." or "/../" in path:
raise aiohttp.web.HTTPForbidden()
node_type = node.node_type
path = "/project-files/{}/{}/{}".format(node_type, node.id, path)
data = await request.content.read() #FIXME: are we handling timeout or large files correctly?
- await node.compute.http_query("POST", "/projects/{project_id}/files{path}".format(project_id=project.id, path=path), data=data, timeout=None, raw=True)
- response.set_status(201)
+ res = await node.compute.http_query("POST", "/projects/{project_id}/files{path}".format(project_id=project.id, path=path), data=data, timeout=None, raw=True)
+ response.set_status(res.status)
@Route.get(
r"/projects/{project_id}/nodes/{node_id}/console/ws",
diff --git a/gns3server/handlers/api/controller/project_handler.py b/gns3server/handlers/api/controller/project_handler.py
index bbe4582e..408c09e5 100644
--- a/gns3server/handlers/api/controller/project_handler.py
+++ b/gns3server/handlers/api/controller/project_handler.py
@@ -28,6 +28,7 @@ from gns3server.controller import Controller
from gns3server.controller.import_project import import_project
from gns3server.controller.export_project import export_project
from gns3server.utils.asyncio import aiozipstream
+from gns3server.utils.path import is_safe_path
from gns3server.config import Config
@@ -454,14 +455,12 @@ class ProjectHandler:
controller = Controller.instance()
project = await controller.get_loaded_project(request.match_info["project_id"])
- path = request.match_info["path"]
- path = os.path.normpath(path).strip('/')
+ path = os.path.normpath(request.match_info["path"])
# Raise error if user try to escape
- if path[0] == ".":
+ if not is_safe_path(path, project.path):
raise aiohttp.web.HTTPForbidden()
path = os.path.join(project.path, path)
-
await response.stream_file(path)
@Route.post(
@@ -480,15 +479,13 @@ class ProjectHandler:
controller = Controller.instance()
project = await controller.get_loaded_project(request.match_info["project_id"])
- path = request.match_info["path"]
- path = os.path.normpath(path).strip("/")
+ path = os.path.normpath(request.match_info["path"])
# Raise error if user try to escape
- if path[0] == ".":
+ if not is_safe_path(path, project.path):
raise aiohttp.web.HTTPForbidden()
path = os.path.join(project.path, path)
-
- response.set_status(200)
+ response.set_status(201)
try:
async with aiofiles.open(path, 'wb+') as f:
diff --git a/gns3server/handlers/api/controller/server_handler.py b/gns3server/handlers/api/controller/server_handler.py
index 915faa39..d4b6f6d4 100644
--- a/gns3server/handlers/api/controller/server_handler.py
+++ b/gns3server/handlers/api/controller/server_handler.py
@@ -78,6 +78,22 @@ class ServerHandler:
pass
response.set_status(201)
+ @classmethod
+ @Route.post(
+ r"/reload",
+ description="Reload the local server",
+ status_codes={
+ 200: "Server has reloaded"
+ })
+ async def reload(request, response):
+
+ from gns3server.web.web_server import WebServer
+ server = WebServer.instance()
+ try:
+ asyncio.ensure_future(server.reload_server())
+ except asyncio.CancelledError:
+ pass
+
@Route.get(
r"/version",
description="Retrieve the server version number",
diff --git a/gns3server/handlers/api/controller/symbol_handler.py b/gns3server/handlers/api/controller/symbol_handler.py
index 42d34635..f0c2d9eb 100644
--- a/gns3server/handlers/api/controller/symbol_handler.py
+++ b/gns3server/handlers/api/controller/symbol_handler.py
@@ -23,7 +23,6 @@ import urllib.parse
from gns3server.web.route import Route
from gns3server.controller import Controller
-
import logging
log = logging.getLogger(__name__)
@@ -44,6 +43,24 @@ class SymbolHandler:
controller = Controller.instance()
response.json(controller.symbols.list())
+ @Route.get(
+ r"/symbols/{symbol_id:.+}/dimensions",
+ description="Get the symbol dimensions",
+ status_codes={
+ 200: "Symbol dimensions returned"
+ })
+ async def get_dimensions(request, response):
+
+ controller = Controller.instance()
+ symbol_id = urllib.parse.unquote(request.match_info["symbol_id"])
+ try:
+ width, height, _ = controller.symbols.get_size(symbol_id)
+ symbol_dimensions = {'width': width, 'height': height}
+ response.json(symbol_dimensions)
+ except (KeyError, OSError, ValueError) as e:
+ log.warning("Could not get symbol dimensions: {}".format(e))
+ response.set_status(404)
+
@Route.get(
r"/symbols/{symbol_id:.+}/raw",
description="Get the symbol file",
diff --git a/gns3server/handlers/index_handler.py b/gns3server/handlers/index_handler.py
index 12c4c5ba..bcad4a8e 100644
--- a/gns3server/handlers/index_handler.py
+++ b/gns3server/handlers/index_handler.py
@@ -92,7 +92,7 @@ class IndexHandler:
filename = os.path.join('static', 'web-ui', filename)
# Raise error if user try to escape
- if filename[0] == ".":
+ if filename[0] == "." or '/../' in filename:
raise aiohttp.web.HTTPForbidden()
static = get_resource(filename)
diff --git a/gns3server/schemas/link.py b/gns3server/schemas/link.py
index 6dd5ffe4..257161b7 100644
--- a/gns3server/schemas/link.py
+++ b/gns3server/schemas/link.py
@@ -68,6 +68,27 @@ LINK_OBJECT_SCHEMA = {
"type": "boolean",
"description": "Suspend the link"
},
+ "link_style": {
+ "type": "object",
+ "description": "Link line style",
+ "items": {
+ "type": "object",
+ "properties": {
+ "color": {
+ "description": "Link line color",
+ "type": "string"
+ },
+ "width": {
+ "description": "Link line width",
+ "type": "integer"
+ },
+ "type": {
+ "description": "Link line type",
+ "type": "integer"
+ }
+ }
+ }
+ },
"filters": FILTER_OBJECT_SCHEMA,
"capturing": {
"description": "Read only property. True if a capture running on the link",
diff --git a/gns3server/static/web-ui/26.b66762bd9b75f566201f.js b/gns3server/static/web-ui/26.b66762bd9b75f566201f.js
new file mode 100644
index 00000000..204670a2
--- /dev/null
+++ b/gns3server/static/web-ui/26.b66762bd9b75f566201f.js
@@ -0,0 +1 @@
+"use strict";(self.webpackChunkgns3_web_ui=self.webpackChunkgns3_web_ui||[]).push([[26],{91026:function(q,c,a){a.r(c),a.d(c,{TopologySummaryComponent:function(){return N}});var t=a(65508),d=a(96852),_=a(14200),m=a(36889),h=a(3941),f=a(15132),p=a(40098),v=a(39095),u=a(88802),y=a(73044),g=a(59412),x=a(93386);function T(i,e){if(1&i){var o=t.EpF();t.TgZ(0,"div",2),t.NdJ("mousemove",function(r){return t.CHM(o),t.oxw().dragWidget(r)},!1,t.evT)("mouseup",function(){return t.CHM(o),t.oxw().toggleDragging(!1)},!1,t.evT),t.qZA()}}function C(i,e){1&i&&(t.O4$(),t.TgZ(0,"svg",28),t._UZ(1,"rect",29),t.qZA())}function S(i,e){1&i&&(t.O4$(),t.TgZ(0,"svg",28),t._UZ(1,"rect",30),t.qZA())}function b(i,e){1&i&&(t.O4$(),t.TgZ(0,"svg",28),t._UZ(1,"rect",31),t.qZA())}function E(i,e){if(1&i&&(t.TgZ(0,"div"),t._uU(1),t.qZA()),2&i){var o=t.oxw().$implicit;t.xp6(1),t.lnq(" ",o.console_type," ",o.console_host,":",o.console," ")}}function Z(i,e){1&i&&(t.TgZ(0,"div"),t._uU(1," none "),t.qZA())}function O(i,e){if(1&i&&(t.TgZ(0,"div",25),t.TgZ(1,"div"),t.YNc(2,C,2,0,"svg",26),t.YNc(3,S,2,0,"svg",26),t.YNc(4,b,2,0,"svg",26),t._uU(5),t.qZA(),t.YNc(6,E,2,3,"div",27),t.YNc(7,Z,2,0,"div",27),t.qZA()),2&i){var o=e.$implicit;t.xp6(2),t.Q6J("ngIf","started"===o.status),t.xp6(1),t.Q6J("ngIf","suspended"===o.status),t.xp6(1),t.Q6J("ngIf","stopped"===o.status),t.xp6(1),t.hij(" ",o.name," "),t.xp6(1),t.Q6J("ngIf",null!=o.console&&null!=o.console&&"none"!=o.console_type),t.xp6(1),t.Q6J("ngIf",null==o.console||"none"===o.console_type)}}function M(i,e){1&i&&(t.O4$(),t.TgZ(0,"svg",28),t._UZ(1,"rect",29),t.qZA())}function w(i,e){1&i&&(t.O4$(),t.TgZ(0,"svg",28),t._UZ(1,"rect",31),t.qZA())}function A(i,e){if(1&i&&(t.TgZ(0,"div",25),t.TgZ(1,"div"),t.YNc(2,M,2,0,"svg",26),t.YNc(3,w,2,0,"svg",26),t._uU(4),t.qZA(),t.TgZ(5,"div"),t._uU(6),t.qZA(),t.TgZ(7,"div"),t._uU(8),t.qZA(),t.qZA()),2&i){var o=e.$implicit,s=t.oxw(2);t.xp6(2),t.Q6J("ngIf",o.connected),t.xp6(1),t.Q6J("ngIf",!o.connected),t.xp6(1),t.hij(" ",o.name," "),t.xp6(2),t.hij(" ",o.host," "),t.xp6(2),t.hij(" ",s.server.location," ")}}var P=function(i){return{lightTheme:i}},F=function(){return{right:!0,left:!0,bottom:!0,top:!0}};function D(i,e){if(1&i){var o=t.EpF();t.TgZ(0,"div",3),t.NdJ("mousedown",function(){return t.CHM(o),t.oxw().toggleDragging(!0)})("resizeStart",function(){return t.CHM(o),t.oxw().toggleDragging(!1)})("resizeEnd",function(n){return t.CHM(o),t.oxw().onResizeEnd(n)}),t.TgZ(1,"div",4),t.TgZ(2,"mat-tab-group"),t.TgZ(3,"mat-tab",5),t.NdJ("click",function(){return t.CHM(o),t.oxw().toggleTopologyVisibility(!0)}),t.TgZ(4,"div",6),t.TgZ(5,"div",7),t.TgZ(6,"mat-select",8),t.TgZ(7,"mat-optgroup",9),t.TgZ(8,"mat-option",10),t.NdJ("onSelectionChange",function(){return t.CHM(o),t.oxw().applyStatusFilter("started")}),t._uU(9,"started"),t.qZA(),t.TgZ(10,"mat-option",11),t.NdJ("onSelectionChange",function(){return t.CHM(o),t.oxw().applyStatusFilter("suspended")}),t._uU(11,"suspended"),t.qZA(),t.TgZ(12,"mat-option",12),t.NdJ("onSelectionChange",function(){return t.CHM(o),t.oxw().applyStatusFilter("stopped")}),t._uU(13,"stopped"),t.qZA(),t.qZA(),t.TgZ(14,"mat-optgroup",13),t.TgZ(15,"mat-option",14),t.NdJ("onSelectionChange",function(){return t.CHM(o),t.oxw().applyCaptureFilter("capture")}),t._uU(16,"active capture(s)"),t.qZA(),t.TgZ(17,"mat-option",15),t.NdJ("onSelectionChange",function(){return t.CHM(o),t.oxw().applyCaptureFilter("packet")}),t._uU(18,"active packet captures"),t.qZA(),t.qZA(),t.qZA(),t.qZA(),t.TgZ(19,"div",16),t.TgZ(20,"mat-select",17),t.NdJ("selectionChange",function(){return t.CHM(o),t.oxw().setSortingOrder()})("valueChange",function(n){return t.CHM(o),t.oxw().sortingOrder=n}),t.TgZ(21,"mat-option",18),t._uU(22,"sort by name ascending"),t.qZA(),t.TgZ(23,"mat-option",19),t._uU(24,"sort by name descending"),t.qZA(),t.qZA(),t.qZA(),t._UZ(25,"mat-divider",20),t.TgZ(26,"div",21),t.YNc(27,O,8,6,"div",22),t.qZA(),t.qZA(),t.qZA(),t.TgZ(28,"mat-tab",23),t.NdJ("click",function(){return t.CHM(o),t.oxw().toggleTopologyVisibility(!1)}),t.TgZ(29,"div",6),t.TgZ(30,"div",24),t.YNc(31,A,9,5,"div",22),t.qZA(),t.qZA(),t.qZA(),t.qZA(),t.qZA(),t.qZA()}if(2&i){var s=t.oxw();t.Q6J("ngStyle",s.style)("ngClass",t.VKq(9,P,s.isLightThemeEnabled))("validateResize",s.validate)("resizeEdges",t.DdM(11,F))("enableGhostResize",!0),t.xp6(20),t.Q6J("value",s.sortingOrder),t.xp6(6),t.Q6J("ngStyle",s.styleInside),t.xp6(1),t.Q6J("ngForOf",s.filteredNodes),t.xp6(4),t.Q6J("ngForOf",s.computes)}}var N=function(){function i(e,o,s,r,n){this.nodesDataSource=e,this.projectService=o,this.computeService=s,this.linksDataSource=r,this.themeService=n,this.closeTopologySummary=new t.vpe,this.style={},this.styleInside={height:"280px"},this.subscriptions=[],this.nodes=[],this.filteredNodes=[],this.sortingOrder="asc",this.startedStatusFilterEnabled=!1,this.suspendedStatusFilterEnabled=!1,this.stoppedStatusFilterEnabled=!1,this.captureFilterEnabled=!1,this.packetFilterEnabled=!1,this.computes=[],this.isTopologyVisible=!0,this.isDraggingEnabled=!1,this.isLightThemeEnabled=!1}return i.prototype.ngOnInit=function(){var e=this;this.isLightThemeEnabled="light"===this.themeService.getActualTheme(),this.subscriptions.push(this.nodesDataSource.changes.subscribe(function(o){e.nodes=o,e.nodes.forEach(function(s){("0.0.0.0"===s.console_host||"0:0:0:0:0:0:0:0"===s.console_host||"::"===s.console_host)&&(s.console_host=e.server.host)}),e.filteredNodes=o.sort("asc"===e.sortingOrder?e.compareAsc:e.compareDesc)})),this.projectService.getStatistics(this.server,this.project.project_id).subscribe(function(o){e.projectsStatistics=o}),this.computeService.getComputes(this.server).subscribe(function(o){e.computes=o}),this.style={top:"60px",right:"0px",width:"320px",height:"400px"}},i.prototype.toggleDragging=function(e){this.isDraggingEnabled=e},i.prototype.dragWidget=function(e){var o=Number(e.movementX),s=Number(e.movementY),r=Number(this.style.width.split("px")[0]),n=Number(this.style.height.split("px")[0]),l=Number(this.style.top.split("px")[0])+s;if(this.style.left){var I=Number(this.style.left.split("px")[0])+o;this.style={position:"fixed",left:I+"px",top:l+"px",width:r+"px",height:n+"px"}}else{var U=Number(this.style.right.split("px")[0])-o;this.style={position:"fixed",right:U+"px",top:l+"px",width:r+"px",height:n+"px"}}},i.prototype.validate=function(e){return!(e.rectangle.width&&e.rectangle.height&&(e.rectangle.width<290||e.rectangle.height<260))},i.prototype.onResizeEnd=function(e){this.style={position:"fixed",left:e.rectangle.left+"px",top:e.rectangle.top+"px",width:e.rectangle.width+"px",height:e.rectangle.height+"px"},this.styleInside={height:e.rectangle.height-120+"px"}},i.prototype.toggleTopologyVisibility=function(e){this.isTopologyVisible=e},i.prototype.compareAsc=function(e,o){return e.name
mousetrap
@@ -1977,6 +1868,7 @@ ngx-childprocess
MIT
ngx-device-detector
+MIT
ngx-electron
MIT
@@ -2187,157 +2079,6 @@ Apache-2.0
limitations under the License.
-object-assign
-MIT
-The MIT License (MIT)
-
-Copyright (c) Sindre Sorhus (sindresorhus.com)
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
-
-
-prop-types
-MIT
-MIT License
-
-Copyright (c) 2013-present, Facebook, Inc.
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-
-
-prop-types-extra
-MIT
-The MIT License (MIT)
-
-Copyright (c) 2015 react-bootstrap
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-
-
-
-react
-MIT
-MIT License
-
-Copyright (c) Facebook, Inc. and its affiliates.
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-
-
-react-bootstrap
-MIT
-The MIT License (MIT)
-
-Copyright (c) 2014-present Stephen J. Collings, Matthew Honnibal, Pieter Vanderwerff
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
-
-
-react-dom
-MIT
-MIT License
-
-Copyright (c) Facebook, Inc. and its affiliates.
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-
-
regenerator-runtime
MIT
MIT License
@@ -2801,31 +2542,6 @@ THE SOFTWARE.
-scheduler
-MIT
-MIT License
-
-Copyright (c) Facebook, Inc. and its affiliates.
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-
-
source-map
BSD-3-Clause
@@ -2875,31 +2591,6 @@ WTFPL
0. You just DO WHAT THE FUCK YOU WANT TO.
-stylenames
-MIT
-MIT License
-
-Copyright (c) 2016 Kevin
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-
-
svg-crowbar
MIT
Copyright (c) 2013 The New York Times
@@ -2926,25 +2617,6 @@ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
-type
-ISC
-ISC License
-
-Copyright (c) 2019, Mariusz Nowak, @medikoo, medikoo.com
-
-Permission to use, copy, modify, and/or distribute this software for any
-purpose with or without fee is hereby granted, provided that the above
-copyright notice and this permission notice appear in all copies.
-
-THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
-REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
-INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-PERFORMANCE OF THIS SOFTWARE.
-
-
uuid
MIT
The MIT License (MIT)
@@ -2970,55 +2642,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-warning
-MIT
-MIT License
-
-Copyright (c) 2013-present, Facebook, Inc.
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-
-
-webpack
-MIT
-Copyright JS Foundation and other contributors
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-'Software'), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-
xterm
MIT
Copyright (c) 2017-2019, The xterm.js authors (https://github.com/xtermjs/xterm.js)
@@ -3094,7 +2717,7 @@ zone.js
MIT
The MIT License
-Copyright (c) 2010-2020 Google LLC. http://angular.io/license
+Copyright (c) 2010-2020 Google LLC. https://angular.io/license
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/gns3server/static/web-ui/ReleaseNotes.txt b/gns3server/static/web-ui/ReleaseNotes.txt
index fc9bd32b..17a32fc2 100644
--- a/gns3server/static/web-ui/ReleaseNotes.txt
+++ b/gns3server/static/web-ui/ReleaseNotes.txt
@@ -1,6 +1,9 @@
GNS3 WebUI is web implementation of user interface for GNS3 software.
-Current version: 2.2.19
+Current version: 2.2.24
+
+Bug Fixes & enhancements
+- security fixes
Current version: 2020.4.0-beta.1
diff --git a/gns3server/static/web-ui/index.html b/gns3server/static/web-ui/index.html
index d7689329..98a9049b 100644
--- a/gns3server/static/web-ui/index.html
+++ b/gns3server/static/web-ui/index.html
@@ -1,10 +1,8 @@
-
-
-
-
+
+
GNS3 Web UI
-
+
-
-
+
+
-
+
@@ -41,12 +39,13 @@
-
-
+
+
+