Merge branch 'master' into setuptools

pull/1880/head
Jeremy Grossmann 3 years ago committed by GitHub
commit f71e7aac9f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
matrix: matrix:
python-version: [3.6, 3.7, 3.8] python-version: [3.6, 3.7, 3.8, 3.9]
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2

@ -1,5 +1,52 @@
# Change Log # 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 asyncios 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 ## 2.2.19 05/03/2021
* Launch projects marked for auto open after SIGHUP is received * Launch projects marked for auto open after SIGHUP is received

@ -1,6 +1,6 @@
-rrequirements.txt -rrequirements.txt
pytest==5.4.3 pytest==6.2.4
flake8==3.8.3 flake8==3.9.2
pytest-timeout==1.4.1 pytest-timeout==1.4.2
pytest-aiohttp==0.3.0 pytest-aiohttp==0.3.0

@ -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"
}
}
]
}

@ -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"
}
}
]
}

@ -27,10 +27,10 @@
}, },
"images": [ "images": [
{ {
"filename": "vEOS-lab-4.25.0F.vmdk", "filename": "vEOS-lab-4.25.3M.vmdk",
"version": "4.25.0F", "version": "4.25.3M",
"md5sum": "d420763fdf3bc50e7e5b88418bd9d1fd", "md5sum": "2f196969036b4d283e86f15118d59c26",
"filesize": 468779008, "filesize": 451543040,
"download_url": "https://www.arista.com/en/support/software-download" "download_url": "https://www.arista.com/en/support/software-download"
}, },
{ {
@ -211,10 +211,10 @@
], ],
"versions": [ "versions": [
{ {
"name": "4.25.0F", "name": "4.25.3M",
"images": { "images": {
"hda_disk_image": "Aboot-veos-serial-8.0.0.iso", "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"
} }
}, },
{ {

@ -23,12 +23,19 @@
"hdb_disk_interface": "ide", "hdb_disk_interface": "ide",
"hdc_disk_interface": "ide", "hdc_disk_interface": "ide",
"arch": "x86_64", "arch": "x86_64",
"console_type": "vnc", "console_type": "telnet",
"kvm": "require", "kvm": "require",
"options": "-nographic", "options": "-nographic",
"process_priority": "normal" "process_priority": "normal"
}, },
"images": [ "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", "filename": "arubaoscx-disk-image-genericx86-p4-20200311173823.vmdk",
"version": "10.04.1000", "version": "10.04.1000",
@ -45,6 +52,12 @@
} }
], ],
"versions": [ "versions": [
{
"name": "10.06.0001",
"images": {
"hda_disk_image": "arubaoscx-disk-image-genericx86-p4-20201110192651.vmdk"
}
},
{ {
"name": "10.04.1000", "name": "10.04.1000",
"images": { "images": {

@ -5,8 +5,8 @@
"vendor_name": "HPE Aruba", "vendor_name": "HPE Aruba",
"vendor_url": "arubanetworks.com", "vendor_url": "arubanetworks.com",
"documentation_url": "https://asp.arubanetworks.com/downloads;products=Aruba%20SD-WAN", "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_name": "Aruba SD-WAN Virtual Gateway",
"product_url": "https://www.arubanetworks.com/products/networking/gateways-and-controllers/",
"registry_version": 4, "registry_version": 4,
"status": "stable", "status": "stable",
"availability": "service-contract", "availability": "service-contract",

@ -22,7 +22,7 @@
"console_type": "telnet", "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", "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", "kvm": "disable",
"options": "-no-kvm -icount auto -hdachs 980,16,32", "options": "-machine accel=tcg -icount auto -hdachs 980,16,32",
"cpu_throttling": 80, "cpu_throttling": 80,
"process_priority": "low" "process_priority": "low"
}, },

@ -25,6 +25,20 @@
"kvm": "require" "kvm": "require"
}, },
"images": [ "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", "filename": "asav9-12-2-9.qcow2",
"version": "9.12.2-9", "version": "9.12.2-9",
@ -90,6 +104,18 @@
} }
], ],
"versions": [ "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", "name": "9.12.2-9",
"images": { "images": {

@ -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"
}
}
]
}

@ -31,6 +31,20 @@
"download_url": "https://sourceforge.net/projects/gns-3/files", "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" "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", "filename": "vios-adventerprisek9-m.spa.158-3.m2.qcow2",
"version": "15.8(3)M2", "version": "15.8(3)M2",
@ -68,6 +82,20 @@
} }
], ],
"versions": [ "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", "name": "15.8(3)M2",
"images": { "images": {

@ -23,6 +23,20 @@
"kvm": "require" "kvm": "require"
}, },
"images": [ "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", "filename": "vios_l2-adventerprisek9-m.SSA.high_iron_20180619.qcow2",
"version": "15.2.1", "version": "15.2.1",
@ -46,6 +60,18 @@
} }
], ],
"versions": [ "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", "name": "15.2.1",
"images": { "images": {

@ -25,6 +25,20 @@
"kvm": "require" "kvm": "require"
}, },
"images": [ "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", "filename": "nexus9500v.9.3.3.qcow2",
"version": "9500v 9.3.3", "version": "9500v 9.3.3",
@ -148,6 +162,20 @@
} }
], ],
"versions": [ "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", "name": "9500v 9.3.3",
"images": { "images": {

@ -11,19 +11,27 @@
"status": "stable", "status": "stable",
"maintainer": "GNS3 Team", "maintainer": "GNS3 Team",
"maintainer_email": "developers@gns3.net", "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", "first_port_name": "eth0",
"port_name_format": "swp{port1}", "port_name_format": "swp{port1}",
"qemu": { "qemu": {
"adapter_type": "virtio-net-pci", "adapter_type": "virtio-net-pci",
"adapters": 7, "adapters": 7,
"ram": 512, "ram": 768,
"hda_disk_interface": "ide", "hda_disk_interface": "ide",
"arch": "x86_64", "arch": "x86_64",
"console_type": "telnet", "console_type": "telnet",
"kvm": "require" "kvm": "require"
}, },
"images": [ "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", "filename": "cumulus-linux-4.2.0-vx-amd64-qemu.qcow2",
"version": "4.2.0", "version": "4.2.0",
@ -222,6 +230,12 @@
} }
], ],
"versions": [ "versions": [
{
"name": "4.3.0",
"images": {
"hda_disk_image": "cumulus-linux-4.3.0-vx-amd64-qemu.qcow2"
}
},
{ {
"name": "4.2.0", "name": "4.2.0",
"images": { "images": {

@ -11,29 +11,29 @@
"status": "stable", "status": "stable",
"maintainer": "GNS3 Team", "maintainer": "GNS3 Team",
"maintainer_email": "developers@gns3.net", "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", "symbol": ":/symbols/affinity/circle/gray/router_cloud.svg",
"port_name_format": "dp0p{1}s{0}", "port_name_format": "dp0s{3}",
"qemu": { "qemu": {
"adapter_type": "virtio-net-pci", "adapter_type": "virtio-net-pci",
"adapters": 3, "adapters": 8,
"ram": 4096, "ram": 4096,
"cpus": 2, "cpus": 4,
"hda_disk_interface": "ide", "hda_disk_interface": "ide",
"arch": "x86_64", "arch": "x86_64",
"console_type": "telnet", "console_type": "telnet",
"boot_priority": "dc", "boot_priority": "cd",
"kvm": "allow", "kvm": "require",
"options": "-cpu host" "options": "-cpu host"
}, },
"images": [ "images": [
{ {
"filename": "danos-1908-amd64-vrouter.iso", "filename": "danos-2012-base-amd64.iso",
"version": "1908", "version": "2012",
"md5sum": "e850b6aa2859de1075c11b9149fa50f4", "md5sum": "fb7a60dc9afecdb274464832b3ab1ccb",
"filesize": 409993216, "filesize": 441450496,
"download_url": "https://danosproject.atlassian.net/wiki/spaces/DAN/pages/753667/DANOS+1908", "download_url": "https://danosproject.atlassian.net/wiki/spaces/DAN/pages/892141595/DANOS+2012",
"direct_download_url": "http://repos.danosproject.org.s3-website-us-west-1.amazonaws.com/images/danos-1908-amd64-vrouter.iso" "direct_download_url": "https://s3-us-west-1.amazonaws.com/2012.repos.danosproject.org/2012/iso/danos-2012-base-amd64.iso"
}, },
{ {
"filename": "empty8G.qcow2", "filename": "empty8G.qcow2",
@ -46,10 +46,10 @@
], ],
"versions": [ "versions": [
{ {
"name": "1908", "name": "2012",
"images": { "images": {
"hda_disk_image": "empty8G.qcow2", "hda_disk_image": "empty8G.qcow2",
"cdrom_image": "danos-1908-amd64-vrouter.iso" "cdrom_image": "danos-2012-base-amd64.iso"
} }
} }
] ]

@ -26,7 +26,7 @@
"options": "-cpu core2duo" "options": "-cpu core2duo"
}, },
"images": [ "images": [
{ {
"filename": "EXOS-VM_v31.1.1.3.qcow2", "filename": "EXOS-VM_v31.1.1.3.qcow2",
"version": "31.1.1.3", "version": "31.1.1.3",
"md5sum": "e4936ad94a5304bfeeca8dfc6f285cc0", "md5sum": "e4936ad94a5304bfeeca8dfc6f285cc0",

@ -26,19 +26,26 @@
"options": "-nographic" "options": "-nographic"
}, },
"images": [ "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", "filename": "VOSSGNS3.8.3.0.0.qcow2",
"version": "v8.3.0.0", "version": "v8.3.0.0",
"md5sum": "e1c789e439c5951728e349cf44690230", "md5sum": "e1c789e439c5951728e349cf44690230",
"filesize": 384696320, "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", "filename": "VOSSGNS3.8.2.0.0.qcow2",
"version": "v8.2.0.0", "version": "v8.2.0.0",
"md5sum": "9a0cd77c08644abbf3a69771c125c011", "md5sum": "9a0cd77c08644abbf3a69771c125c011",
"filesize": 331808768, "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", "filename": "VOSSGNS3.8.1.5.0.qcow2",
@ -70,18 +77,24 @@
} }
], ],
"versions": [ "versions": [
{
"name": "v8.4.0.0",
"images": {
"hda_disk_image": "VOSSGNS3.8.4.0.0.qcow2"
}
},
{ {
"name": "v8.3.0.0", "name": "v8.3.0.0",
"images": { "images": {
"hda_disk_image": "VOSSGNS3.8.3.0.0.qcow2" "hda_disk_image": "VOSSGNS3.8.3.0.0.qcow2"
} }
}, },
{ {
"name": "v8.2.0.0", "name": "v8.2.0.0",
"images": { "images": {
"hda_disk_image": "VOSSGNS3.8.2.0.0.qcow2" "hda_disk_image": "VOSSGNS3.8.2.0.0.qcow2"
} }
}, },
{ {
"name": "8.1.5.0", "name": "8.1.5.0",
"images": { "images": {

@ -17,7 +17,7 @@
"qemu": { "qemu": {
"adapter_type": "e1000", "adapter_type": "e1000",
"adapters": 4, "adapters": 4,
"ram": 1024, "ram": 4096,
"hda_disk_interface": "virtio", "hda_disk_interface": "virtio",
"hdb_disk_interface": "virtio", "hdb_disk_interface": "virtio",
"arch": "x86_64", "arch": "x86_64",
@ -26,6 +26,13 @@
"kvm": "allow" "kvm": "allow"
}, },
"images": [ "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", "filename": "FAZ_VM64_KVM-v6-build1183-FORTINET.out.kvm.qcow2",
"version": "6.2.2", "version": "6.2.2",
@ -169,6 +176,13 @@
} }
], ],
"versions": [ "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", "name": "6.2.2",
"images": { "images": {

@ -26,6 +26,13 @@
"kvm": "allow" "kvm": "allow"
}, },
"images": [ "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", "filename": "FGT_VM64_KVM-v6-build1010-FORTINET.out.kvm.qcow2",
"version": "6.2.2", "version": "6.2.2",
@ -246,6 +253,13 @@
} }
], ],
"versions": [ "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", "name": "6.2.2",
"images": { "images": {

@ -17,7 +17,7 @@
"qemu": { "qemu": {
"adapter_type": "virtio-net-pci", "adapter_type": "virtio-net-pci",
"adapters": 4, "adapters": 4,
"ram": 1024, "ram": 2048,
"hda_disk_interface": "virtio", "hda_disk_interface": "virtio",
"hdb_disk_interface": "virtio", "hdb_disk_interface": "virtio",
"arch": "x86_64", "arch": "x86_64",
@ -26,6 +26,20 @@
"kvm": "allow" "kvm": "allow"
}, },
"images": [ "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", "filename": "FMG_VM64_KVM-v6-build1183-FORTINET.out.kvm.qcow2",
"version": "6.2.2", "version": "6.2.2",
@ -162,6 +176,20 @@
} }
], ],
"versions": [ "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", "name": "6.2.2",
"images": { "images": {

@ -21,6 +21,14 @@
"kvm": "allow" "kvm": "allow"
}, },
"images": [ "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", "filename": "frr-7.3.1.qcow2",
"version": "7.3.1", "version": "7.3.1",
@ -31,6 +39,12 @@
} }
], ],
"versions": [ "versions": [
{
"name": "7.5.1",
"images": {
"hda_disk_image": "frr-7.5.1.qcow2"
}
},
{ {
"name": "7.3.1", "name": "7.3.1",
"images": { "images": {

@ -6,7 +6,7 @@
"vendor_url": "https://www.huawei.com", "vendor_url": "https://www.huawei.com",
"product_name": "HuaWei AR1000v", "product_name": "HuaWei AR1000v",
"product_url": "https://support.huawei.com/enterprise/en/routers/ar1000v-pid-21768212", "product_url": "https://support.huawei.com/enterprise/en/routers/ar1000v-pid-21768212",
"registry_version": 5, "registry_version": 4,
"status": "experimental", "status": "experimental",
"availability": "service-contract", "availability": "service-contract",
"maintainer": "none", "maintainer": "none",

@ -5,7 +5,7 @@
"vendor_name": "HuaWei", "vendor_name": "HuaWei",
"vendor_url": "https://www.huawei.com", "vendor_url": "https://www.huawei.com",
"product_name": "HuaWei CE12800", "product_name": "HuaWei CE12800",
"registry_version": 5, "registry_version": 4,
"status": "experimental", "status": "experimental",
"availability": "service-contract", "availability": "service-contract",
"maintainer": "none", "maintainer": "none",
@ -33,10 +33,10 @@
], ],
"versions": [ "versions": [
{ {
"name": "V200R005C10SPC607B607",
"images": { "images": {
"hda_disk_image": "ce12800-V200R005C10SPC607B607.qcow2" "hda_disk_image": "ce12800-V200R005C10SPC607B607.qcow2"
}, }
"name": "V200R005C10SPC607B607"
} }
] ]
} }

@ -6,7 +6,7 @@
"vendor_url": "https://www.huawei.com", "vendor_url": "https://www.huawei.com",
"product_name": "HuaWei NE40E", "product_name": "HuaWei NE40E",
"product_url": "https://e.huawei.com/en/products/enterprise-networking/routers/ne/ne40e", "product_url": "https://e.huawei.com/en/products/enterprise-networking/routers/ne/ne40e",
"registry_version": 5, "registry_version": 4,
"status": "experimental", "status": "experimental",
"availability": "service-contract", "availability": "service-contract",
"maintainer": "none", "maintainer": "none",
@ -35,10 +35,10 @@
], ],
"versions": [ "versions": [
{ {
"name": "V800R011C00SPC607B607",
"images": { "images": {
"hda_disk_image": "ne40e-V800R011C00SPC607B607.qcow2" "hda_disk_image": "ne40e-V800R011C00SPC607B607.qcow2"
}, }
"name": "V800R011C00SPC607B607"
} }
] ]
} }

@ -6,7 +6,7 @@
"vendor_url": "https://www.huawei.com", "vendor_url": "https://www.huawei.com",
"product_name": "HuaWei USG6000v", "product_name": "HuaWei USG6000v",
"product_url": "https://e.huawei.com/en/products/enterprise-networking/security/firewall-gateway/usg6000v", "product_url": "https://e.huawei.com/en/products/enterprise-networking/security/firewall-gateway/usg6000v",
"registry_version": 5, "registry_version": 4,
"status": "experimental", "status": "experimental",
"availability": "service-contract", "availability": "service-contract",
"maintainer": "none", "maintainer": "none",

@ -23,101 +23,109 @@
"kvm": "require" "kvm": "require"
}, },
"images": [ "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", "filename": "kali-linux-2019.3-amd64.iso",
"version": "2019.3", "version": "2019.3",
"md5sum": "9c6fb00558f78ed06992d89f745ef975", "md5sum": "9c6fb00558f78ed06992d89f745ef975",
"filesize": 3037736960, "filesize": 3037736960,
"download_url": "https://www.kali.org/downloads/", "download_url": "http://old.kali.org/kali-images/kali-2019.3",
"direct_download_url": "http://cdimage.kali.org/kali-2019.3/kali-linux-2019.3-amd64.iso" "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", "filename": "kali-linux-2019.2-amd64.iso",
"version": "2019.2", "version": "2019.2",
"md5sum": "0f89b6225d7ea9c18682f7cc541c1179", "md5sum": "0f89b6225d7ea9c18682f7cc541c1179",
"filesize": 3353227264, "filesize": 3353227264,
"download_url": "https://www.kali.org/downloads/", "download_url": "http://old.kali.org/kali-images/kali-2019.2",
"direct_download_url": "http://cdimage.kali.org/kali-2019.2/kali-linux-2019.2-amd64.iso" "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", "filename": "kali-linux-mate-2019.2-amd64.iso",
"version": "2019.2 (MATE)", "version": "2019.2 (MATE)",
"md5sum": "fec8dd7009f932c51a74323df965a709", "md5sum": "fec8dd7009f932c51a74323df965a709",
"filesize": 3313217536, "filesize": 3313217536,
"download_url": "https://www.kali.org/downloads/", "download_url": "http://old.kali.org/kali-images/kali-2019.2",
"direct_download_url": "http://cdimage.kali.org/kali-2019.2/kali-linux-mate-2019.2-amd64.iso" "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", "filename": "kali-linux-2019.1a-amd64.iso",
"version": "2019.1a", "version": "2019.1a",
"md5sum": "58c6111ed0be1919ea87267e7e65ab0f", "md5sum": "58c6111ed0be1919ea87267e7e65ab0f",
"filesize": 3483873280, "filesize": 3483873280,
"download_url": "https://www.kali.org/downloads/", "download_url": "http://old.kali.org/kali-images/kali-2019.1a",
"direct_download_url": "http://cdimage.kali.org/kali-2019.1a/kali-linux-2019.1a-amd64.iso" "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", "filename": "kali-linux-2018.4-amd64.iso",
"version": "2018.4", "version": "2018.4",
"md5sum": "1b2d598bb8d2003e6207c119c0ba42fe", "md5sum": "1b2d598bb8d2003e6207c119c0ba42fe",
"filesize": 3139436544, "filesize": 3139436544,
"download_url": "https://www.kali.org/downloads/", "download_url": "http://old.kali.org/kali-images/kali-2018.4",
"direct_download_url": "http://cdimage.kali.org/kali-2018.4/kali-linux-2018.4-amd64.iso" "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", "filename": "kali-linux-2018.3a-amd64.iso",
"version": "2018.3a", "version": "2018.3a",
"md5sum": "2da675d016bd690c05e180e33aa98b94", "md5sum": "2da675d016bd690c05e180e33aa98b94",
"filesize": 3192651776, "filesize": 3192651776,
"download_url": "https://www.kali.org/downloads/", "download_url": "http://old.kali.org/kali-images/kali-2018.3a",
"direct_download_url": "http://cdimage.kali.org/kali-2018.3a/kali-linux-2018.3a-amd64.iso" "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", "filename": "kali-linux-2018.1-amd64.iso",
"version": "2018.1", "version": "2018.1",
"md5sum": "a3feb90df5b71b3c7f4a02bdddf221d7", "md5sum": "a3feb90df5b71b3c7f4a02bdddf221d7",
"filesize": 3028500480, "filesize": 3028500480,
"download_url": "https://www.kali.org/downloads/", "download_url": "http://old.kali.org/kali-images/kali-2018.1",
"direct_download_url": "http://cdimage.kali.org/kali-2018.1/kali-linux-2018.1-amd64.iso" "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", "filename": "kali-linux-2017.3-amd64.iso",
"version": "2017.3", "version": "2017.3",
"md5sum": "b465580c897e94675ac1daf031fa66b9", "md5sum": "b465580c897e94675ac1daf031fa66b9",
"filesize": 2886402048, "filesize": 2886402048,
"download_url": "http://cdimage.kali.org/kali-2017.3/", "download_url": "http://old.kali.org/kali-images/kali-2017.3/",
"direct_download_url": "http://cdimage.kali.org/kali-2017.3/kali-linux-2017.3-amd64.iso" "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", "filename": "kali-linux-2017.2-amd64.iso",
"version": "2017.2", "version": "2017.2",
"md5sum": "541654f8f818450dc0db866a0a0f6eec", "md5sum": "541654f8f818450dc0db866a0a0f6eec",
"filesize": 3020619776, "filesize": 3020619776,
"download_url": "http://cdimage.kali.org/kali-2017.2/", "download_url": "http://old.kali.org/kali-images/kali-2017.2/",
"direct_download_url": "http://cdimage.kali.org/kali-2017.2/kali-linux-2017.2-amd64.iso" "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", "filename": "kali-linux-2017.1-amd64.iso",
"version": "2017.1", "version": "2017.1",
"md5sum": "c8e742283929d7a12dbe7c58e398ff08", "md5sum": "c8e742283929d7a12dbe7c58e398ff08",
"filesize": 2794307584, "filesize": 2794307584,
"download_url": "http://cdimage.kali.org/kali-2017.1/", "download_url": "http://old.kali.org/kali-images/kali-2017.1/",
"direct_download_url": "http://cdimage.kali.org/kali-2017.1/kali-linux-2017.1-amd64.iso" "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", "filename": "kali-linux-2016.2-amd64.iso",
"version": "2016.2", "version": "2016.2",
"md5sum": "3d163746bc5148e61ad689d94bc263f9", "md5sum": "3d163746bc5148e61ad689d94bc263f9",
"filesize": 3076767744, "filesize": 3076767744,
"download_url": "http://cdimage.kali.org/kali-2016.2/", "download_url": "http://old.kali.org/kali-images/kali-2016.2/",
"direct_download_url": "http://cdimage.kali.org/kali-2016.2/kali-linux-2016.2-amd64.iso" "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", "filename": "kali-linux-2016.1-amd64.iso",
"version": "2016.1", "version": "2016.1",
"md5sum": "2e1230dc14036935b3279dfe3e49ad39", "md5sum": "2e1230dc14036935b3279dfe3e49ad39",
"filesize": 2945482752, "filesize": 2945482752,
"download_url": "http://cdimage.kali.org/kali-2016.1/", "download_url": "http://old.kali.org/kali-images/kali-2016.1/",
"direct_download_url": "http://cdimage.kali.org/kali-2016.1/kali-linux-2016.1-amd64.iso" "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", "filename": "kali-linux-2.0-amd64.iso",
@ -137,6 +145,13 @@
} }
], ],
"versions": [ "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", "name": "2019.3",
"images": { "images": {

@ -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"
}
}

@ -3,16 +3,18 @@
"category": "guest", "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.", "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_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", "product_name": "ntopng",
"registry_version": 3, "registry_version": 3,
"status": "stable", "status": "stable",
"maintainer": "GNS3 Team", "maintainer": "GNS3 Team",
"maintainer_email": "developers@gns3.net", "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": { "docker": {
"adapters": 1, "adapters": 1,
"image": "lucaderi/ntopng-docker:latest", "image": "ntop/ntopng:stable",
"start_command": "--dns-mode 2 --interface eth0",
"console_type": "http", "console_type": "http",
"console_http_port": 3000, "console_http_port": 3000,
"console_http_path": "/" "console_http_path": "/"

@ -19,7 +19,7 @@
"ram": 2048, "ram": 2048,
"hda_disk_interface": "ide", "hda_disk_interface": "ide",
"hdb_disk_interface": "ide", "hdb_disk_interface": "ide",
"arch": "x86_64", "arch": "x86_64",
"console_type": "vnc", "console_type": "vnc",
"boot_priority": "dc", "boot_priority": "dc",
"kvm": "require" "kvm": "require"
@ -52,4 +52,4 @@
} }
} }
] ]
} }

@ -11,7 +11,7 @@
"maintainer": "GNS3 Team", "maintainer": "GNS3 Team",
"maintainer_email": "developers@gns3.net", "maintainer_email": "developers@gns3.net",
"usage": "User root, password gns3", "usage": "User root, password gns3",
"first_port_name": "fxp0", "first_port_name": "em0",
"port_name_format": "em{0}", "port_name_format": "em{0}",
"qemu": { "qemu": {
"adapter_type": "e1000", "adapter_type": "e1000",

@ -22,7 +22,7 @@
"kvm": "allow" "kvm": "allow"
}, },
"images": [ "images": [
{ {
"filename": "openwrt-19.07.7-x86-64-combined-ext4.img", "filename": "openwrt-19.07.7-x86-64-combined-ext4.img",
"version": "19.07.7", "version": "19.07.7",
"md5sum": "0cfa752fab87014419ab00b18a6cc5a6", "md5sum": "0cfa752fab87014419ab00b18a6cc5a6",

@ -26,7 +26,7 @@
"filename": "fossapup64-9.5.iso", "filename": "fossapup64-9.5.iso",
"version": "9.5", "version": "9.5",
"md5sum": "6a45e7a305b7d3172ebd9eab5ca460e4", "md5sum": "6a45e7a305b7d3172ebd9eab5ca460e4",
"filesize": 428867584, "filesize": 428867584,
"download_url": "http://puppylinux.com/index.html", "download_url": "http://puppylinux.com/index.html",
"direct_download_url": "http://distro.ibiblio.org/puppylinux/puppy-fossa/fossapup64-9.5.iso" "direct_download_url": "http://distro.ibiblio.org/puppylinux/puppy-fossa/fossapup64-9.5.iso"
}, },

@ -7,7 +7,7 @@
"documentation_url": "https://access.redhat.com/solutions/641193", "documentation_url": "https://access.redhat.com/solutions/641193",
"product_name": "Red Hat Enterprise Linux KVM Guest Image", "product_name": "Red Hat Enterprise Linux KVM Guest Image",
"product_url": "https://www.redhat.com/en/technologies/linux-platforms/enterprise-linux", "product_url": "https://www.redhat.com/en/technologies/linux-platforms/enterprise-linux",
"registry_version": 5, "registry_version": 4,
"status": "stable", "status": "stable",
"availability": "service-contract", "availability": "service-contract",
"maintainer": "Neyder Achahuanco", "maintainer": "Neyder Achahuanco",
@ -56,25 +56,25 @@
], ],
"versions": [ "versions": [
{ {
"name": "8.3",
"images": { "images": {
"hda_disk_image": "rhel-8.3-x86_64-kvm.qcow2", "hda_disk_image": "rhel-8.3-x86_64-kvm.qcow2",
"cdrom_image": "rhel-cloud-init.iso" "cdrom_image": "rhel-cloud-init.iso"
}, }
"name": "8.3"
}, },
{ {
"name": "7.9",
"images": { "images": {
"hda_disk_image": "rhel-server-7.9-x86_64-kvm.qcow2", "hda_disk_image": "rhel-server-7.9-x86_64-kvm.qcow2",
"cdrom_image": "rhel-cloud-init.iso" "cdrom_image": "rhel-cloud-init.iso"
}, }
"name": "7.9"
}, },
{ {
"name": "6.10",
"images": { "images": {
"hda_disk_image": "rhel-server-6.10-update-11-x86_64-kvm.qcow2", "hda_disk_image": "rhel-server-6.10-update-11-x86_64-kvm.qcow2",
"cdrom_image": "rhel-cloud-init.iso" "cdrom_image": "rhel-cloud-init.iso"
}, }
"name": "6.10"
} }
] ]
} }

@ -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"
}
}
]
}

@ -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"
}
}

@ -1,7 +1,7 @@
{ {
"name": "Tiny Core Linux", "name": "Tiny Core Linux",
"category": "guest", "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_name": "Team Tiny Core",
"vendor_url": "http://distro.ibiblio.org/tinycorelinux", "vendor_url": "http://distro.ibiblio.org/tinycorelinux",
"documentation_url": "http://wiki.tinycorelinux.net/", "documentation_url": "http://wiki.tinycorelinux.net/",

@ -25,6 +25,20 @@
"options": "-vga virtio" "options": "-vga virtio"
}, },
"images": [ "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", "filename": "Ubuntu 20.04 (64bit).vmdk",
"version": "20.04", "version": "20.04",
@ -62,6 +76,18 @@
} }
], ],
"versions": [ "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", "name": "20.04",
"images": { "images": {

@ -26,19 +26,25 @@
}, },
"images": [ "images": [
{ {
"filename": "vyos-1.3-rolling-202101-qemu.qcow2", "filename": "vyos-1.3.0-rc5-amd64.qcow2",
"version": "1.3-snapshot-202101", "version": "1.3.0-rc5",
"md5sum": "b05a1f8a879c42342ea90f65ebe62f05", "md5sum": "dd704f59afc0fccdf601cc750bf2c438",
"filesize": 315359232, "filesize": 361955328,
"download_url": "https://vyos.net/get/snapshots/", "direct_download_url": "https://www.mediafire.com/file/taspgxh4vj0a4j1/vyos-1.3.0-rc5-amd64.qcow2/file"
"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.2.6-S1-amd64.iso", "filename": "vyos-1.2.8-amd64.iso",
"version": "1.2.6-S1", "version": "1.2.8",
"md5sum": "449873ae455d5f3f509db7a614e86984", "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, "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", "filename": "vyos-1.1.8-amd64.iso",
@ -59,16 +65,23 @@
], ],
"versions": [ "versions": [
{ {
"name": "1.3-snapshot-202101", "name": "1.3.0-rc5",
"images": { "images": {
"hda_disk_image": "vyos-1.3-rolling-202101-qemu.qcow2" "hda_disk_image": "vyos-1.3.0-rc5-amd64.qcow2"
}
},
{
"name": "1.2.8",
"images": {
"hda_disk_image": "empty8G.qcow2",
"cdrom_image": "vyos-1.2.8-amd64.iso"
} }
}, },
{ {
"name": "1.2.6-S1", "name": "1.2.7",
"images": { "images": {
"hda_disk_image": "empty8G.qcow2", "hda_disk_image": "empty8G.qcow2",
"cdrom_image": "vyos-1.2.6-S1-amd64.iso" "cdrom_image": "vyos-1.2.7-amd64.iso"
} }
}, },
{ {

@ -481,7 +481,7 @@ class BaseManager:
if not path: if not path:
return "" return ""
orig_path = path orig_path = os.path.normpath(path)
server_config = self.config.get_section_config("Server") server_config = self.config.get_section_config("Server")
img_directory = self.get_images_directory() img_directory = self.get_images_directory()
@ -494,7 +494,8 @@ class BaseManager:
if re.match(r"^[A-Z]:", path) is not None: 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)) 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: for directory in valid_directory_prefices:
log.debug("Searching for image '{}' in '{}'".format(orig_path, directory)) log.debug("Searching for image '{}' in '{}'".format(orig_path, directory))
path = self._recursive_search_file_in_directory(directory, orig_path) path = self._recursive_search_file_in_directory(directory, orig_path)
@ -503,6 +504,12 @@ class BaseManager:
# Not found we try the default directory # Not found we try the default directory
log.debug("Searching for image '{}' in default directory".format(orig_path)) 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) s = os.path.split(orig_path)
path = force_unix_path(os.path.join(img_directory, *s)) path = force_unix_path(os.path.join(img_directory, *s))
if os.path.exists(path): if os.path.exists(path):
@ -517,7 +524,6 @@ class BaseManager:
return path return path
raise ImageMissingError(orig_path) raise ImageMissingError(orig_path)
# Check to see if path is an absolute path to a valid directory
path = force_unix_path(path) path = force_unix_path(path)
for directory in valid_directory_prefices: for directory in valid_directory_prefices:
log.debug("Searching for image '{}' in '{}'".format(orig_path, directory)) log.debug("Searching for image '{}' in '{}'".format(orig_path, directory))
@ -525,7 +531,8 @@ class BaseManager:
if os.path.exists(path): if os.path.exists(path):
return path return path
raise ImageMissingError(orig_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): def _recursive_search_file_in_directory(self, directory, searched_file):
""" """
@ -533,12 +540,11 @@ class BaseManager:
:returns: Path or None if not found :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 root, dirs, files in os.walk(directory):
for file in files: for file in files:
# If filename is the same if s[1] == file and (s[0] == '' or root == os.path.join(directory, s[0])):
if s[1] == file and (s[0] == '' or s[0] == os.path.basename(root)):
path = os.path.normpath(os.path.join(root, s[1])) path = os.path.normpath(os.path.join(root, s[1]))
if os.path.exists(path): if os.path.exists(path):
return path return path

@ -430,7 +430,7 @@ class BaseNode:
telnet_writer.write(msg.data.encode()) telnet_writer.write(msg.data.encode())
await telnet_writer.drain() await telnet_writer.drain()
elif msg.type == aiohttp.WSMsgType.BINARY: elif msg.type == aiohttp.WSMsgType.BINARY:
await telnet_writer.write(msg.data) telnet_writer.write(msg.data)
await telnet_writer.drain() await telnet_writer.drain()
elif msg.type == aiohttp.WSMsgType.ERROR: elif msg.type == aiohttp.WSMsgType.ERROR:
log.debug("Websocket connection closed with exception {}".format(ws.exception())) log.debug("Websocket connection closed with exception {}".format(ws.exception()))

@ -362,12 +362,12 @@ class Cloud(BaseNode):
""" """
# Wireless adapters are not well supported by the libpcap on OSX # 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") raise NodeError("Connecting to a Wireless adapter is not supported on Mac OS")
if port_info["interface"].startswith("vmnet"): if port_info["interface"].startswith("vmnet"):
# Use a special NIO to connect to VMware vmnet interfaces on OSX (libpcap doesn't support them) # 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, await self._ubridge_send('bridge add_nio_fusion_vmnet {name} "{interface}"'.format(name=bridge_name,
interface=port_info["interface"])) interface=port_info["interface"]))
return return
if not gns3server.utils.interfaces.has_netmask(port_info["interface"]): if not gns3server.utils.interfaces.has_netmask(port_info["interface"]):
raise NodeError("Interface {} has no netmask, interface down?".format(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) await self._add_ubridge_connection(nio, port_number)
self._nios[port_number] = nio self._nios[port_number] = nio
except (NodeError, UbridgeError) as e: except (NodeError, UbridgeError) as e:
log.error('Cannot add NIO on cloud "{name}": {error}'.format(name=self._name, error=e))
await self._stop_ubridge() await self._stop_ubridge()
self.status = "stopped" self.status = "stopped"
self._nios[port_number] = nio self._nios[port_number] = nio

@ -35,13 +35,22 @@ class Nat(Cloud):
def __init__(self, name, node_id, project, manager, ports=None): 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"): if sys.platform.startswith("linux"):
nat_interface = Config.instance().get_section_config("Server").get("default_nat_interface", "virbr0") 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()]: 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)) raise NodeError("NAT interface {} is missing, please install libvirt".format(nat_interface))
interface = nat_interface interface = nat_interface
else: else:
nat_interface = Config.instance().get_section_config("Server").get("default_nat_interface", "vmnet8") 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(), interfaces = list(filter(lambda x: nat_interface in x.lower(),
[interface["name"] for interface in gns3server.utils.interfaces.interfaces()])) [interface["name"] for interface in gns3server.utils.interfaces.interfaces()]))
if not len(interfaces): if not len(interfaces):

@ -152,8 +152,6 @@ class Qemu(BaseManager):
log.debug("Searching for Qemu binaries in '{}'".format(path)) log.debug("Searching for Qemu binaries in '{}'".format(path))
try: try:
for f in os.listdir(path): 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 \ 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.access(os.path.join(path, f), os.X_OK) and \
os.path.isfile(os.path.join(path, f)): os.path.isfile(os.path.join(path, f)):

@ -615,7 +615,7 @@ class QemuVM(BaseNode):
if not mac_address: if not mac_address:
# use the node UUID to generate a random 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: else:
self._mac_address = mac_address self._mac_address = mac_address
@ -829,20 +829,26 @@ class QemuVM(BaseNode):
id=self._id, id=self._id,
options=options)) options=options))
if not sys.platform.startswith("linux"): # "-no-kvm" and "-no-hax' are deprecated since Qemu v5.2
if "-no-kvm" in options: if "-no-kvm" in options:
options = options.replace("-no-kvm", "") options = options.replace("-no-kvm", "-machine accel=tcg")
if "-enable-kvm" in options: 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", "") options = options.replace("-enable-kvm", "")
else: else:
if "-no-hax" in options: options = options.replace("-enable-kvm", "-machine accel=kvm")
options = options.replace("-no-hax", "")
if "-enable-hax" in options: if "-enable-hax" in options:
if not sys.platform.startswith("win"):
# HAXM is only available on Windows
options = options.replace("-enable-hax", "") options = options.replace("-enable-hax", "")
if "-icount" in options and ("-no-kvm" not in options): else:
# automatically add the -no-kvm option if -icount is detected options = options.replace("-enable-hax", "-machine accel=hax")
# to help with the migration of ASA VMs created before version 1.4
options = "-no-kvm " + options
self._options = options.strip() self._options = options.strip()
@property @property
@ -1675,6 +1681,24 @@ class QemuVM(BaseNode):
try: try:
qemu_img_path = self._get_qemu_img() qemu_img_path = self._get_qemu_img()
command = [qemu_img_path, "create", "-o", "backing_file={}".format(disk_image), "-f", "qcow2", disk] 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) retcode = await self._qemu_img_exec(command)
if retcode: if retcode:
stdout = self.read_qemu_img_stdout() stdout = self.read_qemu_img_stdout()
@ -1845,6 +1869,7 @@ class QemuVM(BaseNode):
log.warning("Qemu image {} is corrupted".format(disk_image)) 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: 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)}) 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: except (OSError, subprocess.SubprocessError) as e:
stdout = self.read_qemu_img_stdout() stdout = self.read_qemu_img_stdout()
raise QemuError("Could not check '{}' disk image: {}\n{}".format(disk_name, e, 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 # The disk exists we check if the clone works
try: try:
qcow2 = Qcow2(disk) qcow2 = Qcow2(disk)
await qcow2.rebase(qemu_img_path, disk_image) await qcow2.validate(qemu_img_path)
except (Qcow2Error, OSError) as e: 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: else:
disk = disk_image disk = disk_image
@ -2078,7 +2103,7 @@ class QemuVM(BaseNode):
if require_kvm is not None: if require_kvm is not None:
require_hardware_accel = require_kvm 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 # Turn OFF hardware acceleration for non x86 architectures
if sys.platform.startswith("win"): 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"] supported_binaries = ["qemu-system-x86_64.exe", "qemu-system-x86_64w.exe", "qemu-system-i386.exe", "qemu-system-i386w.exe"]

@ -58,11 +58,12 @@ class Qcow2:
# uint64_t snapshots_offset; # uint64_t snapshots_offset;
# } QCowHeader; # } QCowHeader;
struct_format = ">IIQi" struct_format = ">IIQiiQi"
with open(self._path, 'rb') as f: with open(self._path, 'rb') as f:
content = f.read(struct.calcsize(struct_format)) content = f.read(struct.calcsize(struct_format))
try: 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: except struct.error:
raise Qcow2Error("Invalid file header for {}".format(self._path)) raise Qcow2Error("Invalid file header for {}".format(self._path))
@ -103,3 +104,15 @@ class Qcow2:
if retcode != 0: if retcode != 0:
raise Qcow2Error("Could not rebase the image") raise Qcow2Error("Could not rebase the image")
self._reload() 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")

@ -27,6 +27,7 @@ import asyncio
import subprocess import subprocess
import logging import logging
import codecs import codecs
import ipaddress
from collections import OrderedDict from collections import OrderedDict
from gns3server.utils.interfaces import interfaces from gns3server.utils.interfaces import interfaces
@ -51,6 +52,7 @@ class VMware(BaseManager):
self._vmrun_path = None self._vmrun_path = None
self._host_type = None self._host_type = None
self._vmnets = [] self._vmnets = []
self._vmnets_info = {}
self._vmnet_start_range = 2 self._vmnet_start_range = 2
if sys.platform.startswith("win"): if sys.platform.startswith("win"):
self._vmnet_end_range = 19 self._vmnet_end_range = 19
@ -273,7 +275,7 @@ class VMware(BaseManager):
else: else:
# location on Linux # location on Linux
vmware_networking_file = "/etc/vmware/networking" vmware_networking_file = "/etc/vmware/networking"
vmnet_interfaces = [] vmnet_interfaces = {}
try: try:
with open(vmware_networking_file, "r", encoding="utf-8") as f: with open(vmware_networking_file, "r", encoding="utf-8") as f:
for line in f.read().splitlines(): for line in f.read().splitlines():
@ -281,7 +283,20 @@ class VMware(BaseManager):
if match: if match:
vmnet = "vmnet{}".format(match.group(1)) vmnet = "vmnet{}".format(match.group(1))
if vmnet not in ("vmnet0", "vmnet1", "vmnet8"): 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: except OSError as e:
raise VMwareError("Cannot open {}: {}".format(vmware_networking_file, e)) raise VMwareError("Cannot open {}: {}".format(vmware_networking_file, e))
return vmnet_interfaces 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)) 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) 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): def refresh_vmnet_list(self, ubridge=True):
if ubridge: if ubridge:
@ -331,6 +365,8 @@ class VMware(BaseManager):
vmnet_interfaces = self._get_vmnet_interfaces_ubridge() vmnet_interfaces = self._get_vmnet_interfaces_ubridge()
else: else:
vmnet_interfaces = self._get_vmnet_interfaces() vmnet_interfaces = self._get_vmnet_interfaces()
vmnet_interfaces = list(vmnet_interfaces.keys())
self._vmnets_info = vmnet_interfaces.copy()
# remove vmnets already in use # remove vmnets already in use
for vmware_vm in self._nodes.values(): for vmware_vm in self._nodes.values():
@ -734,5 +770,4 @@ class VMware(BaseManager):
if __name__ == '__main__': if __name__ == '__main__':
loop = asyncio.get_event_loop() loop = asyncio.get_event_loop()
vmware = VMware.instance() vmware = VMware.instance()
print("=> Check version")
loop.run_until_complete(asyncio.ensure_future(vmware.check_vmware_version())) loop.run_until_complete(asyncio.ensure_future(vmware.check_vmware_version()))

@ -23,9 +23,11 @@ import sys
import os import os
import asyncio import asyncio
import tempfile import tempfile
import platform
from gns3server.utils.asyncio.telnet_server import AsyncioTelnetServer from gns3server.utils.asyncio.telnet_server import AsyncioTelnetServer
from gns3server.utils.asyncio.serial import asyncio_open_serial from gns3server.utils.asyncio.serial import asyncio_open_serial
from gns3server.utils import parse_version
from gns3server.utils.asyncio import locking from gns3server.utils.asyncio import locking
from collections import OrderedDict from collections import OrderedDict
from .vmware_error import VMwareError from .vmware_error import VMwareError
@ -252,8 +254,13 @@ class VMwareVM(BaseNode):
if self._get_vmx_setting(connected): if self._get_vmx_setting(connected):
del self._vmx_pairs[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 # then configure VMware network adapters
self.manager.refresh_vmnet_list()
for adapter_number in range(0, self._adapters): for adapter_number in range(0, self._adapters):
custom_adapter = self._get_custom_adapter_settings(adapter_number) 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]) vmnet_interface = os.path.basename(self._vmx_pairs[vnet])
if sys.platform.startswith("darwin"): if sys.platform.startswith("darwin"):
# special case on OSX, we cannot bind VMnet interfaces using the libpcap if parse_version(platform.mac_ver()[0]) >= parse_version("11.0.0"):
await self._ubridge_send('bridge add_nio_fusion_vmnet {name} "{interface}"'.format(name=vnet, interface=vmnet_interface)) # 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: else:
block_host_traffic = self.manager.config.get_section_config("VMware").getboolean("block_host_traffic", False) 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) await self._add_ubridge_ethernet_connection(vnet, vmnet_interface, block_host_traffic)
@ -426,7 +442,7 @@ class VMwareVM(BaseNode):
if self.status == "started": if self.status == "started":
return return
if (await self.is_running()): if await self.is_running():
raise VMwareError("The VM is already running in VMware") raise VMwareError("The VM is already running in VMware")
ubridge_path = self.ubridge_path ubridge_path = self.ubridge_path
@ -476,7 +492,7 @@ class VMwareVM(BaseNode):
await self._stop_ubridge() await self._stop_ubridge()
try: try:
if (await self.is_running()): if await self.is_running():
if self.on_close == "save_vm_state": if self.on_close == "save_vm_state":
await self._control_vm("suspend") await self._control_vm("suspend")
elif self.on_close == "shutdown_signal": elif self.on_close == "shutdown_signal":
@ -728,7 +744,7 @@ class VMwareVM(BaseNode):
# check for the connection type # check for the connection type
connection_type = "ethernet{}.connectiontype".format(adapter_number) 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 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}. " 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], "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, adapter_number=adapter_number,

@ -53,6 +53,7 @@ class Controller:
self._notification = Notification(self) self._notification = Notification(self)
self.gns3vm = GNS3VM(self) self.gns3vm = GNS3VM(self)
self.symbols = Symbols() self.symbols = Symbols()
self._ssl_context = None
self._appliance_manager = ApplianceManager() self._appliance_manager = ApplianceManager()
self._template_manager = TemplateManager() self._template_manager = TemplateManager()
self._iou_license_settings = {"iourc_content": "", self._iou_license_settings = {"iourc_content": "",
@ -82,9 +83,9 @@ class Controller:
computes = self._load_controller_settings() computes = self._load_controller_settings()
from gns3server.web.web_server import WebServer 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") 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)) log.warning("Protocol changed to 'https' for local compute because SSL is enabled".format(port))
protocol = "https" protocol = "https"
try: try:
@ -97,7 +98,7 @@ class Controller:
user=server_config.get("user", ""), user=server_config.get("user", ""),
password=server_config.get("password", ""), password=server_config.get("password", ""),
force=True, force=True,
ssl_context=ssl_context) ssl_context=self._ssl_context)
except aiohttp.web.HTTPConflict: 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)) log.fatal("Cannot access to the local server, make sure something else is not running on the TCP port {}".format(port))
sys.exit(1) sys.exit(1)
@ -115,6 +116,13 @@ class Controller:
await self.load_projects() await self.load_projects()
await self._project_auto_open() await self._project_auto_open()
def ssl_context(self):
"""
Returns the SSL context for the server.
"""
return self._ssl_context
def _update_config(self): def _update_config(self):
""" """
Call this when the server configuration file changes. Call this when the server configuration file changes.

@ -16,6 +16,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import os import os
import shutil
import json import json
import uuid import uuid
import asyncio import asyncio
@ -181,6 +182,7 @@ class ApplianceManager:
Controller.instance().save() Controller.instance().save()
json_data = await response.json() json_data = await response.json()
appliances_dir = get_resource('appliances') appliances_dir = get_resource('appliances')
downloaded_appliance_files = []
for appliance in json_data: for appliance in json_data:
if appliance["type"] == "file": if appliance["type"] == "file":
appliance_name = appliance["name"] appliance_name = appliance["name"]
@ -201,6 +203,21 @@ class ApplianceManager:
f.write(appliance_data) f.write(appliance_data)
except OSError as e: except OSError as e:
raise aiohttp.web.HTTPConflict(text="Could not write appliance file '{}': {}".format(path, 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: except ValueError as e:
raise aiohttp.web.HTTPConflict(text="Could not read appliances information from GitHub: {}".format(e)) raise aiohttp.web.HTTPConflict(text="Could not read appliances information from GitHub: {}".format(e))

@ -312,7 +312,7 @@ class Compute:
url = self._getUrl("/projects/{}/files/{}".format(project.id, path)) url = self._getUrl("/projects/{}/files/{}".format(project.id, path))
response = await self._session().request("GET", url, auth=self._auth) response = await self._session().request("GET", url, auth=self._auth)
if response.status == 404: 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 return response
async def download_image(self, image_type, image): async def download_image(self, image_type, image):
@ -327,7 +327,7 @@ class Compute:
url = self._getUrl("/{}/images/{}".format(image_type, image)) url = self._getUrl("/{}/images/{}".format(image_type, image))
response = await self._session().request("GET", url, auth=self._auth) response = await self._session().request("GET", url, auth=self._auth)
if response.status == 404: 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 return response
async def http_query(self, method, path, data=None, dont_connect=False, **kwargs): async def http_query(self, method, path, data=None, dont_connect=False, **kwargs):

@ -66,22 +66,26 @@ async def export_project(zstream, project, temporary_dir, include_images=False,
# Export the local files # Export the local files
for root, dirs, files in os.walk(project._path, topdown=True, followlinks=False): 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)] try:
for file in files: files = [f for f in files if _is_exportable(os.path.join(root, f), include_snapshots)]
path = os.path.join(root, file) for file in files:
# check if we can export the file path = os.path.join(root, file)
try: # check if we can export the file
open(path).close() try:
except OSError as e: open(path).close()
msg = "Could not export file {}: {}".format(path, e) except OSError as e:
log.warning(msg) msg = "Could not export file {}: {}".format(path, e)
project.emit_notification("log.warning", {"message": msg}) log.warning(msg)
continue project.emit_notification("log.warning", {"message": msg})
# ignore the .gns3 file continue
if file.endswith(".gns3"): # ignore the .gns3 file
continue if file.endswith(".gns3"):
_patch_mtime(path) continue
zstream.write(path, os.path.relpath(path, project._path)) _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 # Export files from remote computes
for compute in project.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): if _is_exportable(compute_file["path"], include_snapshots):
log.debug("Downloading file '{}' from compute '{}'".format(compute_file["path"], compute.id)) log.debug("Downloading file '{}' from compute '{}'".format(compute_file["path"], compute.id))
response = await compute.download_file(project, compute_file["path"]) response = await compute.download_file(project, compute_file["path"])
#if response.status != 200: if response.status != 200:
# raise aiohttp.web.HTTPConflict(text="Cannot export file from compute '{}'. Compute returned status code {}.".format(compute.id, response.status)) 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) (fd, temp_path) = tempfile.mkstemp(dir=temporary_dir)
async with aiofiles.open(fd, 'wb') as f: async with aiofiles.open(fd, 'wb') as f:
while True: while True:

@ -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 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 = load_topology(os.path.join(path, "project.gns3"))
topology["name"] = project_name topology["name"] = project_name
# To avoid unexpected behavior (project start without manual operations just after import) # To avoid unexpected behavior (project start without manual operations just after import)
topology["auto_start"] = False topology["auto_start"] = auto_start
topology["auto_open"] = False topology["auto_open"] = auto_open
topology["auto_close"] = True topology["auto_close"] = auto_close
# Generate a new node id # Generate a new node id
node_old_to_new = {} node_old_to_new = {}

@ -124,6 +124,7 @@ class Link:
self._link_type = "ethernet" self._link_type = "ethernet"
self._suspended = False self._suspended = False
self._filters = {} self._filters = {}
self._link_style = {}
@property @property
def filters(self): def filters(self):
@ -209,6 +210,12 @@ class Link:
self._project.emit_notification("link.updated", self.__json__()) self._project.emit_notification("link.updated", self.__json__())
self._project.dump() 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 @property
def created(self): def created(self):
""" """
@ -454,6 +461,7 @@ class Link:
"nodes": res, "nodes": res,
"link_id": self._id, "link_id": self._id,
"filters": self._filters, "filters": self._filters,
"link_style": self._link_style,
"suspend": self._suspended "suspend": self._suspended
} }
return { return {
@ -466,5 +474,6 @@ class Link:
"capture_compute_id": self.capture_compute_id, "capture_compute_id": self.capture_compute_id,
"link_type": self._link_type, "link_type": self._link_type,
"filters": self._filters, "filters": self._filters,
"link_style": self._link_style,
"suspend": self._suspended "suspend": self._suspended
} }

@ -957,6 +957,8 @@ class Project:
link = await self.add_link(link_id=link_data["link_id"]) link = await self.add_link(link_id=link_data["link_id"])
if "filters" in link_data: if "filters" in link_data:
await link.update_filters(link_data["filters"]) 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", []): for node_link in link_data.get("nodes", []):
node = self.get_node(node_link["node_id"]) node = self.get_node(node_link["node_id"])
port = node.get_port(node_link["adapter_number"], node_link["port_number"]) port = node.get_port(node_link["adapter_number"], node_link["port_number"])

@ -121,7 +121,9 @@ class Snapshot:
if os.path.exists(project_files_path): if os.path.exists(project_files_path):
await wait_run_in_executor(shutil.rmtree, project_files_path) await wait_run_in_executor(shutil.rmtree, project_files_path)
with open(self._path, "rb") as f: 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: except (OSError, PermissionError) as e:
raise aiohttp.web.HTTPConflict(text=str(e)) raise aiohttp.web.HTTPConflict(text=str(e))
await project.open() await project.open()

@ -63,13 +63,13 @@ def _check_topology_schema(topo):
error = "Invalid data in topology file: {} in schema: {}".format( error = "Invalid data in topology file: {} in schema: {}".format(
e.message, e.message,
json.dumps(e.schema)) json.dumps(e.schema))
log.critical(error) log.debug(error)
raise aiohttp.web.HTTPConflict(text=error) raise aiohttp.web.HTTPConflict(text=error)
def project_to_topology(project): 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 = { data = {
"project_id": project.id, "project_id": project.id,

@ -58,7 +58,7 @@ class CrashReport:
Report crash to a third party service 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 _instance = None
def __init__(self): def __init__(self):

@ -470,7 +470,8 @@ class DynamipsVMHandler:
async def upload_image(request, response): async def upload_image(request, response):
dynamips_manager = Dynamips.instance() 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) response.set_status(204)
@Route.get( @Route.get(
@ -484,15 +485,15 @@ class DynamipsVMHandler:
raw=True, raw=True,
description="Download a Dynamips IOS image") description="Download a Dynamips IOS image")
async def download_image(request, response): async def download_image(request, response):
filename = request.match_info["filename"]
dynamips_manager = Dynamips.instance() filename = os.path.normpath(request.match_info["filename"])
image_path = dynamips_manager.get_abs_image_path(filename)
# Raise error if user try to escape # Raise error if user try to escape
if filename[0] == ".": if filename[0] == "." or os.path.sep in filename:
raise aiohttp.web.HTTPForbidden() raise aiohttp.web.HTTPForbidden()
dynamips_manager = Dynamips.instance()
image_path = dynamips_manager.get_abs_image_path(filename)
await response.stream_file(image_path) await response.stream_file(image_path)
@Route.post( @Route.post(

@ -428,7 +428,8 @@ class IOUHandler:
async def upload_image(request, response): async def upload_image(request, response):
iou_manager = IOU.instance() 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) response.set_status(204)
@ -443,15 +444,15 @@ class IOUHandler:
raw=True, raw=True,
description="Download an IOU image") description="Download an IOU image")
async def download_image(request, response): async def download_image(request, response):
filename = request.match_info["filename"]
iou_manager = IOU.instance() filename = os.path.normpath(request.match_info["filename"])
image_path = iou_manager.get_abs_image_path(filename)
# Raise error if user try to escape # Raise error if user try to escape
if filename[0] == ".": if filename[0] == "." or os.path.sep in filename:
raise aiohttp.web.HTTPForbidden() raise aiohttp.web.HTTPForbidden()
iou_manager = IOU.instance()
image_path = iou_manager.get_abs_image_path(filename)
await response.stream_file(image_path) await response.stream_file(image_path)
@Route.get( @Route.get(

@ -20,12 +20,12 @@ import asyncio
import json import json
import os import os
import psutil import psutil
import tempfile
from gns3server.web.route import Route from gns3server.web.route import Route
from gns3server.compute.project_manager import ProjectManager from gns3server.compute.project_manager import ProjectManager
from gns3server.compute import MODULES from gns3server.compute import MODULES
from gns3server.utils.cpu_percent import CpuPercent from gns3server.utils.cpu_percent import CpuPercent
from gns3server.utils.path import is_safe_path
from gns3server.schemas.project import ( from gns3server.schemas.project import (
PROJECT_OBJECT_SCHEMA, PROJECT_OBJECT_SCHEMA,
@ -247,14 +247,13 @@ class ProjectHandler:
pm = ProjectManager.instance() pm = ProjectManager.instance()
project = pm.get_project(request.match_info["project_id"]) project = pm.get_project(request.match_info["project_id"])
path = request.match_info["path"] path = os.path.normpath(request.match_info["path"])
path = os.path.normpath(path)
# Raise error if user try to escape # Raise error if user try to escape
if path[0] == ".": if not is_safe_path(path, project.path):
raise aiohttp.web.HTTPForbidden() raise aiohttp.web.HTTPForbidden()
path = os.path.join(project.path, path)
path = os.path.join(project.path, path)
await response.stream_file(path) await response.stream_file(path)
@Route.post( @Route.post(
@ -273,15 +272,14 @@ class ProjectHandler:
pm = ProjectManager.instance() pm = ProjectManager.instance()
project = pm.get_project(request.match_info["project_id"]) project = pm.get_project(request.match_info["project_id"])
path = request.match_info["path"] path = os.path.normpath(request.match_info["path"])
path = os.path.normpath(path)
# Raise error if user try to escape # Raise error if user try to escape
if path[0] == ".": if not is_safe_path(path, project.path):
raise aiohttp.web.HTTPForbidden() 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: try:
os.makedirs(os.path.dirname(path), exist_ok=True) os.makedirs(os.path.dirname(path), exist_ok=True)

@ -207,7 +207,7 @@ class QEMUHandler:
enable_kvm = qemu_manager.config.get_section_config("Qemu").getboolean("enable_kvm") enable_kvm = qemu_manager.config.get_section_config("Qemu").getboolean("enable_kvm")
if enable_kvm is not None: if enable_kvm is not None:
hardware_accel = enable_kvm 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() pm = ProjectManager.instance()
if pm.check_hardware_virtualization(vm) is False: 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") 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): async def upload_image(request, response):
qemu_manager = Qemu.instance() 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) response.set_status(204)
@Route.get( @Route.get(
@ -566,15 +567,15 @@ class QEMUHandler:
raw=True, raw=True,
description="Download Qemu image") description="Download Qemu image")
async def download_image(request, response): async def download_image(request, response):
filename = request.match_info["filename"]
qemu_manager = Qemu.instance() filename = os.path.normpath(request.match_info["filename"])
image_path = qemu_manager.get_abs_image_path(filename)
# Raise error if user try to escape # Raise error if user try to escape
if filename[0] == ".": if filename[0] == "." or os.path.sep in filename:
raise aiohttp.web.HTTPForbidden() raise aiohttp.web.HTTPForbidden()
qemu_manager = Qemu.instance()
image_path = qemu_manager.get_abs_image_path(filename)
await response.stream_file(image_path) await response.stream_file(image_path)
@Route.get( @Route.get(

@ -64,6 +64,8 @@ class LinkHandler:
link = await project.add_link() link = await project.add_link()
if "filters" in request.json: if "filters" in request.json:
await link.update_filters(request.json["filters"]) 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: if "suspend" in request.json:
await link.update_suspend(request.json["suspend"]) await link.update_suspend(request.json["suspend"])
try: try:
@ -135,6 +137,8 @@ class LinkHandler:
link = project.get_link(request.match_info["link_id"]) link = project.get_link(request.match_info["link_id"])
if "filters" in request.json: if "filters" in request.json:
await link.update_filters(request.json["filters"]) 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: if "suspend" in request.json:
await link.update_suspend(request.json["suspend"]) await link.update_suspend(request.json["suspend"])
if "nodes" in request.json: if "nodes" in request.json:
@ -215,6 +219,7 @@ class LinkHandler:
async def pcap(request, response): async def pcap(request, response):
project = await Controller.instance().get_loaded_project(request.match_info["project_id"]) 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"]) link = project.get_link(request.match_info["link_id"])
if not link.capturing: if not link.capturing:
raise aiohttp.web.HTTPConflict(text="This link has no active packet capture") raise aiohttp.web.HTTPConflict(text="This link has no active packet capture")
@ -226,7 +231,7 @@ class LinkHandler:
headers['Router-Host'] = request.host headers['Router-Host'] = request.host
body = await request.read() 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 aiohttp.ClientSession(connector=connector, headers=headers) as session:
async with session.request(request.method, pcap_streaming_url, timeout=None, data=body) as response: 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) proxied_response = aiohttp.web.Response(headers=response.headers, status=response.status)

@ -409,21 +409,19 @@ class NodeHandler:
path = request.match_info["path"] path = request.match_info["path"]
path = force_unix_path(path) path = force_unix_path(path)
# Raise error if user try to escape # Raise error if user try to escape
if path[0] == ".": if path[0] == "." or "/../" in path:
raise aiohttp.web.HTTPForbidden() raise aiohttp.web.HTTPForbidden()
node_type = node.node_type node_type = node.node_type
path = "/project-files/{}/{}/{}".format(node_type, node.id, path) 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) 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.set_status(res.status)
response.content_type = "application/octet-stream" if res.status == 200:
response.enable_chunked_encoding() response.content_type = "application/octet-stream"
await response.prepare(request) response.enable_chunked_encoding()
await response.write(res.body) await response.prepare(request)
# await response.write_eof() #FIXME: shound't be needed anymore await response.write(res.body)
@Route.post( @Route.post(
r"/projects/{project_id}/nodes/{node_id}/files/{path:.+}", r"/projects/{project_id}/nodes/{node_id}/files/{path:.+}",
@ -446,14 +444,14 @@ class NodeHandler:
path = force_unix_path(path) path = force_unix_path(path)
# Raise error if user try to escape # Raise error if user try to escape
if path[0] == ".": if path[0] == "." or "/../" in path:
raise aiohttp.web.HTTPForbidden() raise aiohttp.web.HTTPForbidden()
node_type = node.node_type node_type = node.node_type
path = "/project-files/{}/{}/{}".format(node_type, node.id, path) path = "/project-files/{}/{}/{}".format(node_type, node.id, path)
data = await request.content.read() #FIXME: are we handling timeout or large files correctly? 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) 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(201) response.set_status(res.status)
@Route.get( @Route.get(
r"/projects/{project_id}/nodes/{node_id}/console/ws", r"/projects/{project_id}/nodes/{node_id}/console/ws",

@ -28,6 +28,7 @@ from gns3server.controller import Controller
from gns3server.controller.import_project import import_project from gns3server.controller.import_project import import_project
from gns3server.controller.export_project import export_project from gns3server.controller.export_project import export_project
from gns3server.utils.asyncio import aiozipstream from gns3server.utils.asyncio import aiozipstream
from gns3server.utils.path import is_safe_path
from gns3server.config import Config from gns3server.config import Config
@ -454,14 +455,12 @@ class ProjectHandler:
controller = Controller.instance() controller = Controller.instance()
project = await controller.get_loaded_project(request.match_info["project_id"]) project = await controller.get_loaded_project(request.match_info["project_id"])
path = request.match_info["path"] path = os.path.normpath(request.match_info["path"])
path = os.path.normpath(path).strip('/')
# Raise error if user try to escape # Raise error if user try to escape
if path[0] == ".": if not is_safe_path(path, project.path):
raise aiohttp.web.HTTPForbidden() raise aiohttp.web.HTTPForbidden()
path = os.path.join(project.path, path) path = os.path.join(project.path, path)
await response.stream_file(path) await response.stream_file(path)
@Route.post( @Route.post(
@ -480,15 +479,13 @@ class ProjectHandler:
controller = Controller.instance() controller = Controller.instance()
project = await controller.get_loaded_project(request.match_info["project_id"]) project = await controller.get_loaded_project(request.match_info["project_id"])
path = request.match_info["path"] path = os.path.normpath(request.match_info["path"])
path = os.path.normpath(path).strip("/")
# Raise error if user try to escape # Raise error if user try to escape
if path[0] == ".": if not is_safe_path(path, project.path):
raise aiohttp.web.HTTPForbidden() raise aiohttp.web.HTTPForbidden()
path = os.path.join(project.path, path) path = os.path.join(project.path, path)
response.set_status(201)
response.set_status(200)
try: try:
async with aiofiles.open(path, 'wb+') as f: async with aiofiles.open(path, 'wb+') as f:

@ -78,6 +78,22 @@ class ServerHandler:
pass pass
response.set_status(201) 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( @Route.get(
r"/version", r"/version",
description="Retrieve the server version number", description="Retrieve the server version number",

@ -23,7 +23,6 @@ import urllib.parse
from gns3server.web.route import Route from gns3server.web.route import Route
from gns3server.controller import Controller from gns3server.controller import Controller
import logging import logging
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -44,6 +43,24 @@ class SymbolHandler:
controller = Controller.instance() controller = Controller.instance()
response.json(controller.symbols.list()) 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( @Route.get(
r"/symbols/{symbol_id:.+}/raw", r"/symbols/{symbol_id:.+}/raw",
description="Get the symbol file", description="Get the symbol file",

@ -92,7 +92,7 @@ class IndexHandler:
filename = os.path.join('static', 'web-ui', filename) filename = os.path.join('static', 'web-ui', filename)
# Raise error if user try to escape # Raise error if user try to escape
if filename[0] == ".": if filename[0] == "." or '/../' in filename:
raise aiohttp.web.HTTPForbidden() raise aiohttp.web.HTTPForbidden()
static = get_resource(filename) static = get_resource(filename)

@ -68,6 +68,27 @@ LINK_OBJECT_SCHEMA = {
"type": "boolean", "type": "boolean",
"description": "Suspend the link" "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, "filters": FILTER_OBJECT_SCHEMA,
"capturing": { "capturing": {
"description": "Read only property. True if a capture running on the link", "description": "Read only property. True if a capture running on the link",

File diff suppressed because one or more lines are too long

@ -23,9 +23,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
@angular-react/core
MIT
@angular/animations @angular/animations
MIT MIT
@ -371,31 +368,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
angular-persistence
MIT
MIT License
Copyright (c) 2017 Scott O'Bryan
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.
angular-resizable-element angular-resizable-element
MIT MIT
The MIT License (MIT) The MIT License (MIT)
@ -421,30 +393,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
angular2-indexeddb
MIT
The MIT License (MIT)
Copyright (c) 2016 Gil Fink and 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.
bootstrap bootstrap
MIT MIT
The MIT License (MIT) The MIT License (MIT)
@ -471,34 +419,9 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. THE SOFTWARE.
classnames
MIT
The MIT License (MIT)
Copyright (c) 2017 Jed Watson
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.
core-js core-js
MIT MIT
Copyright (c) 2014-2020 Denis Pushkarev Copyright (c) 2014-2021 Denis Pushkarev
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
@ -519,55 +442,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. THE SOFTWARE.
css-loader
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.
css-to-style
MIT
The MIT License (MIT)
Copyright (c) 2020 Jacob Buck
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.
css-tree css-tree
MIT MIT
Copyright (C) 2016-2019 by Roman Dvornov Copyright (C) 2016-2019 by Roman Dvornov
@ -591,25 +465,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. THE SOFTWARE.
d
ISC
ISC License
Copyright (c) 2013-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.
d3-array d3-array
BSD-3-Clause BSD-3-Clause
Copyright 2010-2016 Mike Bostock Copyright 2010-2016 Mike Bostock
@ -1687,88 +1542,124 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
dom-helpers mdn-data
MIT CC0-1.0
The MIT License (MIT) CC0 1.0 Universal
Copyright (c) 2015 Jason Quense Statement of Purpose
Permission is hereby granted, free of charge, to any person obtaining a copy The laws of most jurisdictions throughout the world automatically confer
of this software and associated documentation files (the "Software"), to deal exclusive Copyright and Related Rights (defined below) upon the creator and
in the Software without restriction, including without limitation the rights subsequent owner(s) (each and all, an "owner") of an original work of
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell authorship and/or a database (each, a "Work").
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions: Certain owners wish to permanently relinquish those rights to a Work for the
purpose of contributing to a commons of creative, cultural and scientific
The above copyright notice and this permission notice shall be included in all works ("Commons") that the public can reliably and without fear of later
copies or substantial portions of the Software. claims of infringement build upon, modify, incorporate in other works, reuse
and redistribute as freely as possible in any form whatsoever and for any
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR purposes, including without limitation commercial purposes. These owners may
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, contribute to the Commons to promote the ideal of a free culture and the
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE further production of creative, cultural and scientific works, or to gain
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER reputation or greater distribution for their Work in part through the use and
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, efforts of others.
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. For these and/or other purposes and motivations, and without any expectation
of additional consideration or compensation, the person associating CC0 with a
eev Work (the "Affirmer"), to the extent that he or she is an owner of Copyright
MIT and Related Rights in the Work, voluntarily elects to apply CC0 to the Work
and publicly distribute the Work under its terms, with knowledge of his or her
es5-ext Copyright and Related Rights in the Work and the meaning and intended legal
ISC effect of CC0 on those rights.
ISC License
1. Copyright and Related Rights. A Work made available under CC0 may be
Copyright (c) 2011-2019, Mariusz Nowak, @medikoo, medikoo.com protected by copyright and related or neighboring rights ("Copyright and
Related Rights"). Copyright and Related Rights include, but are not limited
Permission to use, copy, modify, and/or distribute this software for any to, the following:
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies. i. the right to reproduce, adapt, distribute, perform, display, communicate,
and translate a Work;
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY ii. moral rights retained by the original author(s) and/or performer(s);
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM iii. publicity and privacy rights pertaining to a person's image or likeness
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE depicted in a Work;
OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. iv. rights protecting against unfair competition in regards to a Work,
subject to the limitations in paragraph 4(a), below;
es6-symbol v. rights protecting the extraction, dissemination, use and reuse of data in
ISC a Work;
ISC License
vi. database rights (such as those arising under Directive 96/9/EC of the
Copyright (c) 2013-2019, Mariusz Nowak, @medikoo, medikoo.com European Parliament and of the Council of 11 March 1996 on the legal
protection of databases, and under any national implementation thereof,
Permission to use, copy, modify, and/or distribute this software for any including any amended or successor version of such directive); and
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies. vii. other similar, equivalent or corresponding rights throughout the world
based on applicable law or treaty, and any national implementations thereof.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 2. Waiver. To the greatest extent permitted by, but not in contravention of,
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM unconditionally waives, abandons, and surrenders all of Affirmer's Copyright
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE and Related Rights and associated claims and causes of action, whether now
OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR known or unknown (including existing as well as future claims and causes of
PERFORMANCE OF THIS SOFTWARE. action), in the Work (i) in all territories worldwide, (ii) for the maximum
duration provided by applicable law or treaty (including future time
extensions), (iii) in any current or future medium and for any number of
ext copies, and (iv) for any purpose whatsoever, including without limitation
ISC commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes
ISC License the Waiver for the benefit of each member of the public at large and to the
detriment of Affirmer's heirs and successors, fully intending that such Waiver
Copyright (c) 2011-2019, Mariusz Nowak, @medikoo, medikoo.com shall not be subject to revocation, rescission, cancellation, termination, or
any other legal or equitable action to disrupt the quiet enjoyment of the Work
Permission to use, copy, modify, and/or distribute this software for any by the public as contemplated by Affirmer's express Statement of Purpose.
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies. 3. Public License Fallback. Should any part of the Waiver for any reason be
judged legally invalid or ineffective under applicable law, then the Waiver
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH shall be preserved to the maximum extent permitted taking into account
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY Affirmer's express Statement of Purpose. In addition, to the extent the Waiver
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, is so judged Affirmer hereby grants to each affected person a royalty-free,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM non transferable, non sublicensable, non exclusive, irrevocable and
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE unconditional license to exercise Affirmer's Copyright and Related Rights in
OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR the Work (i) in all territories worldwide, (ii) for the maximum duration
PERFORMANCE OF THIS SOFTWARE. provided by applicable law or treaty (including future time extensions), (iii)
in any current or future medium and for any number of copies, and (iv) for any
purpose whatsoever, including without limitation commercial, advertising or
promotional purposes (the "License"). The License shall be deemed effective as
of the date CC0 was applied by Affirmer to the Work. Should any part of the
License for any reason be judged legally invalid or ineffective under
applicable law, such partial invalidity or ineffectiveness shall not
invalidate the remainder of the License, and in such case Affirmer hereby
affirms that he or she will not (i) exercise any of his or her remaining
Copyright and Related Rights in the Work or (ii) assert any associated claims
and causes of action with respect to the Work, in either case contrary to
Affirmer's express Statement of Purpose.
4. Limitations and Disclaimers.
a. No trademark or patent rights held by Affirmer are waived, abandoned,
surrendered, licensed or otherwise affected by this document.
b. Affirmer offers the Work as-is and makes no representations or warranties
of any kind concerning the Work, express, implied, statutory or otherwise,
including without limitation warranties of title, merchantability, fitness
for a particular purpose, non infringement, or the absence of latent or
other defects, accuracy, or the present or absence of errors, whether or not
discoverable, all to the greatest extent permissible under applicable law.
c. Affirmer disclaims responsibility for clearing rights of other persons
that may apply to the Work or any use thereof, including without limitation
any person's Copyright and Related Rights in the Work. Further, Affirmer
disclaims responsibility for obtaining any necessary consents, permissions
or other rights required for any use of the Work.
d. Affirmer understands and acknowledges that Creative Commons is not a
party to this document and has no duty or obligation with respect to this
CC0 or use of the Work.
For more information, please see
<http://creativecommons.org/publicdomain/zero/1.0/>
mousetrap mousetrap
@ -1977,6 +1868,7 @@ ngx-childprocess
MIT MIT
ngx-device-detector ngx-device-detector
MIT
ngx-electron ngx-electron
MIT MIT
@ -2187,157 +2079,6 @@ Apache-2.0
limitations under the License. limitations under the License.
object-assign
MIT
The MIT License (MIT)
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (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 regenerator-runtime
MIT MIT
MIT License 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 source-map
BSD-3-Clause BSD-3-Clause
@ -2875,31 +2591,6 @@ WTFPL
0. You just DO WHAT THE FUCK YOU WANT TO. 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 svg-crowbar
MIT MIT
Copyright (c) 2013 The New York Times 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 OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. 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 uuid
MIT MIT
The MIT License (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. 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 xterm
MIT MIT
Copyright (c) 2017-2019, The xterm.js authors (https://github.com/xtermjs/xterm.js) Copyright (c) 2017-2019, The xterm.js authors (https://github.com/xtermjs/xterm.js)
@ -3094,7 +2717,7 @@ zone.js
MIT MIT
The MIT License 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 Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

@ -1,6 +1,9 @@
GNS3 WebUI is web implementation of user interface for GNS3 software. 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 Current version: 2020.4.0-beta.1

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -1 +0,0 @@
!function(e){function r(r){for(var n,l,f=r[0],i=r[1],p=r[2],c=0,s=[];c<f.length;c++)l=f[c],Object.prototype.hasOwnProperty.call(o,l)&&o[l]&&s.push(o[l][0]),o[l]=0;for(n in i)Object.prototype.hasOwnProperty.call(i,n)&&(e[n]=i[n]);for(a&&a(r);s.length;)s.shift()();return u.push.apply(u,p||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,f=1;f<t.length;f++)0!==o[t[f]]&&(n=!1);n&&(u.splice(r--,1),e=l(l.s=t[0]))}return e}var n={},o={0:0},u=[];function l(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,l),t.l=!0,t.exports}l.m=e,l.c=n,l.d=function(e,r,t){l.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},l.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},l.t=function(e,r){if(1&r&&(e=l(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(l.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)l.d(t,n,(function(r){return e[r]}).bind(null,n));return t},l.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return l.d(r,"a",r),r},l.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},l.p="";var f=window.webpackJsonp=window.webpackJsonp||[],i=f.push.bind(f);f.push=r,f=f.slice();for(var p=0;p<f.length;p++)r(f[p]);var a=i;t()}([]);

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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -352,7 +352,7 @@ if __name__ == '__main__':
# Demo using telnet # Demo using telnet
shell = Demo(welcome_message="Welcome!\n") shell = Demo(welcome_message="Welcome!\n")
server = create_telnet_shell(shell, loop=loop) server = create_telnet_shell(shell, loop=loop)
coro = asyncio.start_server(server.run, '127.0.0.1', 4444, loop=loop) coro = asyncio.start_server(server.run, '127.0.0.1', 4444)
s = loop.run_until_complete(coro) s = loop.run_until_complete(coro)
try: try:
loop.run_forever() loop.run_forever()

@ -111,7 +111,7 @@ if __name__ == '__main__':
command = ["nc", "localhost", "80"] command = ["nc", "localhost", "80"]
server = AsyncioRawCommandServer(command, replaces=[(b"work", b"{{HOST}}", )]) server = AsyncioRawCommandServer(command, replaces=[(b"work", b"{{HOST}}", )])
coro = asyncio.start_server(server.run, '0.0.0.0', 4444, loop=loop) coro = asyncio.start_server(server.run, '0.0.0.0', 4444)
s = loop.run_until_complete(coro) s = loop.run_until_complete(coro)
try: try:

@ -424,7 +424,7 @@ if __name__ == '__main__':
stdin=asyncio.subprocess.PIPE))) stdin=asyncio.subprocess.PIPE)))
server = AsyncioTelnetServer(reader=process.stdout, writer=process.stdin, binary=False, echo=False) server = AsyncioTelnetServer(reader=process.stdout, writer=process.stdin, binary=False, echo=False)
coro = asyncio.start_server(server.run, '127.0.0.1', 4444, loop=loop) coro = asyncio.start_server(server.run, '127.0.0.1', 4444)
s = loop.run_until_complete(coro) s = loop.run_until_complete(coro)
try: try:

@ -17,6 +17,7 @@
import os import os
import aiohttp import aiohttp
from pathlib import Path
from ..config import Config from ..config import Config
@ -37,6 +38,16 @@ def get_default_project_directory():
return path return path
def is_safe_path(file_path: str, basedir: str) -> bool:
"""
Check that file path is safe.
(the file is stored inside directory or one of its sub-directory)
"""
test_path = (Path(basedir) / file_path).resolve()
return Path(basedir).resolve() in test_path.resolve().parents
def check_path_allowed(path): def check_path_allowed(path):
""" """
If the server is non local raise an error if If the server is non local raise an error if

@ -92,7 +92,7 @@ def get_size(data, default_width=0, default_height=0):
# End of https://github.com/shibukawa/imagesize_py # End of https://github.com/shibukawa/imagesize_py
# handle SVG # handle SVG
elif size >= 10 and data.startswith(b'<?xml'): elif size >= 10 and (data.startswith(b'<?xml') or data.startswith(b'<svg')):
filetype = "svg" filetype = "svg"
fhandle = io.BytesIO(data) fhandle = io.BytesIO(data)
tree = ElementTree() tree = ElementTree()

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

@ -83,7 +83,7 @@ class WebServer:
def _run_application(self, handler, ssl_context=None): def _run_application(self, handler, ssl_context=None):
try: try:
srv = self._loop.create_server(handler, self._host, self._port, ssl=ssl_context) srv = self._loop.create_server(handler, self._host, self._port, ssl=ssl_context)
self._server, startup_res = self._loop.run_until_complete(asyncio.gather(srv, self._app.startup(), loop=self._loop)) self._server, startup_res = self._loop.run_until_complete(asyncio.gather(srv, self._app.startup()))
except (RuntimeError, OSError, asyncio.CancelledError) as e: except (RuntimeError, OSError, asyncio.CancelledError) as e:
log.critical("Could not start the server: {}".format(e)) log.critical("Could not start the server: {}".format(e))
return False return False

@ -1,11 +1,11 @@
jsonschema==3.2.0 jsonschema==3.2.0
aiohttp==3.6.2 aiohttp==3.7.4.post0
aiohttp-cors==0.7.0 aiohttp-cors==0.7.0
aiofiles==0.5.0 aiofiles==0.7.0
Jinja2==2.11.3 Jinja2==3.0.1
sentry-sdk>=0.14.4 sentry-sdk==1.3.1
psutil==5.6.7 psutil==5.8.0
async-timeout==3.0.1 async-timeout==3.0.1
distro>=1.3.0 distro==1.6.0
py-cpuinfo==7.0.0 py-cpuinfo==8.0.0
setuptools setuptools

@ -23,9 +23,10 @@
function help { function help {
echo "Usage:" >&2 echo "Usage:" >&2
echo "--with-openvpn: Install Open VPN" >&2 echo "--with-openvpn: Install OpenVPN" >&2
echo "--with-iou: Install IOU" >&2 echo "--with-iou: Install IOU" >&2
echo "--with-i386-repository: Add the i386 repositories required by IOU if they are not already available on the system. Warning: this will replace your source.list in order to use the official Ubuntu mirror" >&2 echo "--with-i386-repository: Add the i386 repositories required by IOU if they are not already available on the system. Warning: this will replace your source.list in order to use the official Ubuntu mirror" >&2
echo "--without-kvm: Disable KVM, required if system do not support it (limitation in some hypervisors and cloud providers). Warning: only disable KVM if strictly necessary as this will degrade performance" >&2
echo "--unstable: Use the GNS3 unstable repository" echo "--unstable: Use the GNS3 unstable repository"
echo "--help: This help" >&2 echo "--help: This help" >&2
} }
@ -45,9 +46,10 @@ fi
USE_VPN=0 USE_VPN=0
USE_IOU=0 USE_IOU=0
I386_REPO=0 I386_REPO=0
DISABLE_KVM=0
UNSTABLE=0 UNSTABLE=0
TEMP=`getopt -o h --long with-openvpn,with-iou,with-i386-repository,unstable,help -n 'gns3-remote-install.sh' -- "$@"` TEMP=`getopt -o h --long with-openvpn,with-iou,with-i386-repository,without-kvm,unstable,help -n 'gns3-remote-install.sh' -- "$@"`
if [ $? != 0 ] if [ $? != 0 ]
then then
help help
@ -70,6 +72,10 @@ while true ; do
I386_REPO=1 I386_REPO=1
shift shift
;; ;;
--without-kvm)
DISABLE_KVM=1
shift
;;
--unstable) --unstable)
UNSTABLE=1 UNSTABLE=1
shift shift
@ -147,7 +153,7 @@ apt-get update
log "Upgrade packages" log "Upgrade packages"
apt-get upgrade --yes --force-yes -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" apt-get upgrade --yes --force-yes -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold"
log " Install GNS3 packages" log "Install GNS3 packages"
apt-get install -y gns3-server apt-get install -y gns3-server
log "Create user GNS3 with /opt/gns3 as home directory" log "Create user GNS3 with /opt/gns3 as home directory"
@ -171,7 +177,7 @@ usermod -aG docker gns3
if [ $USE_IOU == 1 ] if [ $USE_IOU == 1 ]
then then
log "IOU setup" log "Setup IOU"
dpkg --add-architecture i386 dpkg --add-architecture i386
apt-get update apt-get update
@ -179,6 +185,8 @@ then
# Force the host name to gns3vm # Force the host name to gns3vm
echo gns3vm > /etc/hostname echo gns3vm > /etc/hostname
hostname gns3vm
HOSTNAME=$(hostname)
# Force hostid for IOU # Force hostid for IOU
dd if=/dev/zero bs=4 count=1 of=/etc/hostid dd if=/dev/zero bs=4 count=1 of=/etc/hostid
@ -204,10 +212,16 @@ configs_path = /opt/gns3/configs
report_errors = True report_errors = True
[Qemu] [Qemu]
enable_kvm = True enable_hardware_acceleration = True
require_kvm = True require_hardware_acceleration = True
EOFC EOFC
if [ $DISABLE_KVM == 1 ]
then
log "Disable KVM support"
sed -i 's/hardware_acceleration = True/hardware_acceleration = False/g' /etc/gns3/gns3_server.conf
fi
chown -R gns3:gns3 /etc/gns3 chown -R gns3:gns3 /etc/gns3
chmod -R 700 /etc/gns3 chmod -R 700 /etc/gns3
@ -286,24 +300,15 @@ if [ $USE_VPN == 1 ]
then then
log "Setup VPN" log "Setup VPN"
cat <<EOFSERVER > /etc/gns3/gns3_server.conf log "Change GNS3 to listen on VPN interface"
[Server]
host = 172.16.253.1
port = 3080
images_path = /opt/gns3/images
projects_path = /opt/gns3/projects
report_errors = True
[Qemu] sed -i 's/host = 0.0.0.0/host = 172.16.253.1/' /etc/gns3/gns3_server.conf
enable_kvm = True
require_kvm = True
EOFSERVER
log "Install packages for Open VPN" log "Install packages for OpenVPN"
apt-get install -y \ apt-get install -y \
openvpn \ openvpn \
uuid \ uuid \
dnsutils \ dnsutils \
nginx-light nginx-light
@ -329,7 +334,6 @@ echo "And remove this file with rm /etc/update-motd.d/70-openvpn"
EOFMOTD EOFMOTD
chmod 755 /etc/update-motd.d/70-openvpn chmod 755 /etc/update-motd.d/70-openvpn
mkdir -p /etc/openvpn/ mkdir -p /etc/openvpn/
[ -d /dev/net ] || mkdir -p /dev/net [ -d /dev/net ] || mkdir -p /dev/net
@ -385,7 +389,7 @@ status openvpn-status-1194.log
log-append /var/log/openvpn-udp1194.log log-append /var/log/openvpn-udp1194.log
EOFUDP EOFUDP
echo "Setup HTTP server for serving client certificate" log "Setup HTTP server for serving client certificate"
mkdir -p /usr/share/nginx/openvpn/$UUID mkdir -p /usr/share/nginx/openvpn/$UUID
cp /root/client.ovpn /usr/share/nginx/openvpn/$UUID/$HOSTNAME.ovpn cp /root/client.ovpn /usr/share/nginx/openvpn/$UUID/$HOSTNAME.ovpn
touch /usr/share/nginx/openvpn/$UUID/index.html touch /usr/share/nginx/openvpn/$UUID/index.html
@ -393,7 +397,7 @@ touch /usr/share/nginx/openvpn/index.html
cat <<EOFNGINX > /etc/nginx/sites-available/openvpn cat <<EOFNGINX > /etc/nginx/sites-available/openvpn
server { server {
listen 8003; listen 8003;
root /usr/share/nginx/openvpn; root /usr/share/nginx/openvpn;
} }
EOFNGINX EOFNGINX
@ -402,11 +406,13 @@ EOFNGINX
service nginx stop service nginx stop
service nginx start service nginx start
log "Restart OpenVPN" log "Restart OpenVPN and GNS3"
set +e set +e
service openvpn stop service openvpn stop
service openvpn start service openvpn start
service gns3 stop
service gns3 start
log "Download http://$MY_IP_ADDR:8003/$UUID/$HOSTNAME.ovpn to setup your OpenVPN client after rebooting the server" log "Download http://$MY_IP_ADDR:8003/$UUID/$HOSTNAME.ovpn to setup your OpenVPN client after rebooting the server"

@ -58,7 +58,7 @@ setup(
"gns3loopback = gns3server.utils.windows_loopback:main" "gns3loopback = gns3server.utils.windows_loopback:main"
] ]
}, },
packages=find_packages(".", exclude=["docs", "tests"]), packages=find_packages(".", exclude=["docs", "tests*"]),
include_package_data=True, include_package_data=True,
zip_safe=False, zip_safe=False,
platforms="any", platforms="any",
@ -79,6 +79,8 @@ setup(
"Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: CPython",
], ],
) )

@ -138,41 +138,41 @@ async def test_export(tmpdir, project):
assert topo["computes"] == [] assert topo["computes"] == []
async def test_export_vm(tmpdir, project): # async def test_export_vm(tmpdir, project):
""" # """
If data is on a remote server export it locally before # If data is on a remote server export it locally before
sending it in the archive. # sending it in the archive.
""" # """
#
compute = MagicMock() # compute = MagicMock()
compute.id = "vm" # compute.id = "vm"
compute.list_files = AsyncioMagicMock(return_value=[{"path": "vm-1/dynamips/test"}]) # compute.list_files = AsyncioMagicMock(return_value=[{"path": "vm-1/dynamips/test"}])
#
# Fake file that will be download from the vm # # Fake file that will be download from the vm
mock_response = AsyncioMagicMock() # mock_response = AsyncioMagicMock()
mock_response.content = AsyncioBytesIO() # mock_response.content = AsyncioBytesIO()
await mock_response.content.write(b"HELLO") # await mock_response.content.write(b"HELLO")
mock_response.content.seek(0) # mock_response.content.seek(0)
compute.download_file = AsyncioMagicMock(return_value=mock_response) # compute.download_file = AsyncioMagicMock(return_value=mock_response)
#
project._project_created_on_compute.add(compute) # project._project_created_on_compute.add(compute)
#
path = project.path # path = project.path
os.makedirs(os.path.join(path, "vm-1", "dynamips")) # os.makedirs(os.path.join(path, "vm-1", "dynamips"))
#
# The .gns3 should be renamed project.gns3 in order to simplify import # # The .gns3 should be renamed project.gns3 in order to simplify import
with open(os.path.join(path, "test.gns3"), 'w+') as f: # with open(os.path.join(path, "test.gns3"), 'w+') as f:
f.write("{}") # f.write("{}")
#
with aiozipstream.ZipFile() as z: # with aiozipstream.ZipFile() as z:
await export_project(z, project, str(tmpdir)) # await export_project(z, project, str(tmpdir))
assert compute.list_files.called # assert compute.list_files.called
await write_file(str(tmpdir / 'zipfile.zip'), z) # await write_file(str(tmpdir / 'zipfile.zip'), z)
#
with zipfile.ZipFile(str(tmpdir / 'zipfile.zip')) as myzip: # with zipfile.ZipFile(str(tmpdir / 'zipfile.zip')) as myzip:
with myzip.open("vm-1/dynamips/test") as myfile: # with myzip.open("vm-1/dynamips/test") as myfile:
content = myfile.read() # content = myfile.read()
assert content == b"HELLO" # assert content == b"HELLO"
async def test_export_disallow_running(tmpdir, project, node): async def test_export_disallow_running(tmpdir, project, node):

@ -213,6 +213,7 @@ async def test_json(project, compute):
} }
], ],
"filters": {}, "filters": {},
"link_style": {},
"suspend": False, "suspend": False,
"link_type": "ethernet", "link_type": "ethernet",
"capturing": False, "capturing": False,
@ -242,6 +243,7 @@ async def test_json(project, compute):
} }
} }
], ],
"link_style": {},
"filters": {}, "filters": {},
"suspend": False "suspend": False
} }

@ -172,7 +172,7 @@ async def test_write_file(compute_api, tmpdir):
project = ProjectManager.instance().create_project(project_id="01010203-0405-0607-0809-0a0b0c0d0e0b") project = ProjectManager.instance().create_project(project_id="01010203-0405-0607-0809-0a0b0c0d0e0b")
response = await compute_api.post("/projects/{project_id}/files/hello".format(project_id=project.id), body="world", raw=True) response = await compute_api.post("/projects/{project_id}/files/hello".format(project_id=project.id), body="world", raw=True)
assert response.status == 200 assert response.status == 201
with open(os.path.join(project.path, "hello")) as f: with open(os.path.join(project.path, "hello")) as f:
assert f.read() == "world" assert f.read() == "world"

@ -108,7 +108,7 @@ async def test_qemu_create_with_params(compute_api, compute_project, base_params
async def test_qemu_create_with_project_file(compute_api, compute_project, base_params, fake_qemu_vm): async def test_qemu_create_with_project_file(compute_api, compute_project, base_params, fake_qemu_vm):
response = await compute_api.post("/projects/{project_id}/files/hello.img".format(project_id=compute_project.id), body="world", raw=True) response = await compute_api.post("/projects/{project_id}/files/hello.img".format(project_id=compute_project.id), body="world", raw=True)
assert response.status == 200 assert response.status == 201
params = base_params params = base_params
params["hda_disk_image"] = "hello.img" params["hda_disk_image"] = "hello.img"
response = await compute_api.post("/projects/{project_id}/qemu/nodes".format(project_id=compute_project.id), params) response = await compute_api.post("/projects/{project_id}/qemu/nodes".format(project_id=compute_project.id), params)
@ -277,8 +277,7 @@ async def test_images(compute_api, fake_qemu_vm):
response = await compute_api.get("/qemu/images") response = await compute_api.get("/qemu/images")
assert response.status == 200 assert response.status == 200
assert response.json == [{'filename': 'config.img', 'filesize': 1048576, 'md5sum': '0ab49056760ae1db6c25376446190b47', 'path': 'config.img'}, assert {"filename": "linux载.img", "path": "linux载.img", "md5sum": "c4ca4238a0b923820dcc509a6f75849b", "filesize": 1} in response.json
{"filename": "linux载.img", "path": "linux载.img", "md5sum": "c4ca4238a0b923820dcc509a6f75849b", "filesize": 1}]
@pytest.mark.skipif(sys.platform.startswith("win"), reason="Does not work on Windows") @pytest.mark.skipif(sys.platform.startswith("win"), reason="Does not work on Windows")

@ -218,6 +218,7 @@ async def test_get_file(controller_api, project, node, compute):
response = MagicMock() response = MagicMock()
response.body = b"world" response.body = b"world"
response.status = 200
compute.http_query = AsyncioMagicMock(return_value=response) compute.http_query = AsyncioMagicMock(return_value=response)
response = await controller_api.get("/projects/{project_id}/nodes/{node_id}/files/hello".format(project_id=project.id, node_id=node.id)) response = await controller_api.get("/projects/{project_id}/nodes/{node_id}/files/hello".format(project_id=project.id, node_id=node.id))
@ -232,7 +233,9 @@ async def test_get_file(controller_api, project, node, compute):
async def test_post_file(controller_api, project, node, compute): async def test_post_file(controller_api, project, node, compute):
compute.http_query = AsyncioMagicMock() response = MagicMock()
response.status = 201
compute.http_query = AsyncioMagicMock(return_value=response)
response = await controller_api.post("/projects/{project_id}/nodes/{node_id}/files/hello".format(project_id=project.id, node_id=node.id), body=b"hello", raw=True) response = await controller_api.post("/projects/{project_id}/nodes/{node_id}/files/hello".format(project_id=project.id, node_id=node.id), body=b"hello", raw=True)
assert response.status == 201 assert response.status == 201
@ -247,6 +250,7 @@ async def test_get_and_post_with_nested_paths_normalization(controller_api, proj
response = MagicMock() response = MagicMock()
response.body = b"world" response.body = b"world"
response.status = 200
compute.http_query = AsyncioMagicMock(return_value=response) compute.http_query = AsyncioMagicMock(return_value=response)
response = await controller_api.get("/projects/{project_id}/nodes/{node_id}/files/hello\\nested".format(project_id=project.id, node_id=node.id)) response = await controller_api.get("/projects/{project_id}/nodes/{node_id}/files/hello\\nested".format(project_id=project.id, node_id=node.id))
assert response.status == 200 assert response.status == 200
@ -254,7 +258,9 @@ async def test_get_and_post_with_nested_paths_normalization(controller_api, proj
compute.http_query.assert_called_with("GET", "/projects/{project_id}/files/project-files/vpcs/{node_id}/hello/nested".format(project_id=project.id, node_id=node.id), timeout=None, raw=True) compute.http_query.assert_called_with("GET", "/projects/{project_id}/files/project-files/vpcs/{node_id}/hello/nested".format(project_id=project.id, node_id=node.id), timeout=None, raw=True)
compute.http_query = AsyncioMagicMock() response = MagicMock()
response.status = 201
compute.http_query = AsyncioMagicMock(return_value=response)
response = await controller_api.post("/projects/{project_id}/nodes/{node_id}/files/hello\\nested".format(project_id=project.id, node_id=node.id), body=b"hello", raw=True) response = await controller_api.post("/projects/{project_id}/nodes/{node_id}/files/hello\\nested".format(project_id=project.id, node_id=node.id), body=b"hello", raw=True)
assert response.status == 201 assert response.status == 201

@ -318,7 +318,7 @@ async def test_get_file(controller_api, project):
async def test_write_file(controller_api, project): async def test_write_file(controller_api, project):
response = await controller_api.post("/projects/{project_id}/files/hello".format(project_id=project.id), body="world", raw=True) response = await controller_api.post("/projects/{project_id}/files/hello".format(project_id=project.id), body="world", raw=True)
assert response.status == 200 assert response.status == 201
with open(os.path.join(project.path, "hello")) as f: with open(os.path.join(project.path, "hello")) as f:
assert f.read() == "world" assert f.read() == "world"

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save