diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index bdf8dd3ae..744115355 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -18,7 +18,7 @@ jobs: strategy: matrix: os: ["ubuntu-latest"] - python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] #include: # only test with Python 3.10 on Windows # - os: windows-latest diff --git a/CHANGELOG b/CHANGELOG index 7d539f621..15c9573b3 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,23 @@ # Change Log +## 2.2.56 21/01/2026 + +* Set default location of udhcpc in "/etc/network/udhcpc". Fixes #2582 +* Upgrade pywin32 to v311 + +## 2.2.55 19/11/2025 + +* Docker API version requirements and process to handle older daemons +* Use docker_name property when creating a container +* Set name of container in Docker +* Support for Python 3.14 +* Enable ip cef in IOU L2 and IOU L3 base configs +* Only build and push Docker image when releasing a new version +* Fix pushing Docker image to Docker hub +* Update security issues in README.md. Fixes #2535 +* Add missing 'nat' template type in schema. Fixes #2529 +* Resolve deprecation warnings of regex library + ## 2.2.54 21/04/2025 * Bundle web-ui v2.2.54 diff --git a/README.md b/README.md index d61e4298e..c9af20032 100644 --- a/README.md +++ b/README.md @@ -259,5 +259,4 @@ py.test --cov-report term-missing --cov=gns3server Security issues --------------- -Please contact us using contact form available here: - +Please use GitHub's report a vulnerability feature. More information can be found in https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing/privately-reporting-a-security-vulnerability diff --git a/gns3server/appliances/asterfusion-vAsterNOS-VPP.gns3a b/gns3server/appliances/asterfusion-vAsterNOS-VPP.gns3a new file mode 100644 index 000000000..3afa29fa5 --- /dev/null +++ b/gns3server/appliances/asterfusion-vAsterNOS-VPP.gns3a @@ -0,0 +1,31 @@ +{ + "name": "Asterfusion AsterNOS-VPP", + "category": "router", + "description": "AsterNOS-VPP leverages SONiC’s robust routing features and management capability with VPP’s high-performance data forwarding. It supports L3 routing, NAT, PPPoE, and VPN services. Minimum requirements: 4 vCPUs and 4GB RAM.", + "vendor_name": "Asterfusion", + "vendor_url": "https://cloudswit.ch/product/sonic-enterprise-distribution/#vAsterNOS", + "documentation_url": "https://docs.asternos.com/routing", + "product_name": "AsterNOS-VPP", + "registry_version": 6, + "status": "stable", + "maintainer": "Asterfusion Product Team", + "images": [ + { + "filename": "AsterNOS-VPP_V6.1-R0101P02_x86.img.gz", + "version": "V6.1", + "md5sum": "55834c3e8849ab226144d79ef76acf81", + "filesize": 1318201015, + "download_url": "https://asternos.dev/api/file/b2c9fd83-a9d3-46f3-9676-6b49dd481d6c", + "comments": "Registration is required to download the image." + } + ], + "qemu": { + "adapter_type": "virtio-net-pci", + "adapters": 4, + "ram": 4096, + "cpus": 4, + "arch": "x86_64", + "console_type": "telnet", + "kvm": "require" + } +} diff --git a/gns3server/appliances/asterfusion-vAsterNOS-campus.gns3a b/gns3server/appliances/asterfusion-vAsterNOS-campus.gns3a index 4887b73cf..c9bcc69a3 100644 --- a/gns3server/appliances/asterfusion-vAsterNOS-campus.gns3a +++ b/gns3server/appliances/asterfusion-vAsterNOS-campus.gns3a @@ -2,7 +2,7 @@ "appliance_id": "9e934470-d898-4289-a5ed-50af094e629e", "name": "Asterfusion vAsterNOS campus", "category": "multilayer_switch", - "description": "AsterNOS is the core technology of Asterfusion’s one-stop SONiC turnkey solution designed for cloud, enterprise, and AI-driven scenarios. AsterNOS v5.2 Campus is specifically designed for traditional campus networks, offering comprehensive L2/L3 capabilities suitable for various campus scenarios such as schools, office buildings, and hospitals. This version supports a fully cloud-integrated Layer 3 network architecture, providing rich routing and switching functionalities to ensure high-performance operation and ease of maintenance. It can also be deployed in the GNS3 simulation environment to experience a complete All-Layer 3 Cloud-Campus network. AsterNOS v6.0 Campus builds upon the L2/L3 features of v5.2 (including ACL, MSTP, QinQ, IGMP Snooping, OSPF/BGP, etc.) and further enhances support for advanced technologies such as MPLS L2VPN/L3VPN and PTP. This version is ideal for enterprises and campus networks requiring high-performance multi-service transport, supporting cross-domain connectivity and providing nanosecond-level time synchronization. It is well-suited for applications with stringent time accuracy requirements, such as financial trading, industrial automation, and smart manufacturing. NOTICE: This appliance file is a virtualized version of AsterNOS and is intended to be used only to experience the basic functionality and industry standard CLI (Klish), not for official software testing. For more information about AsterNOS commercial version, please feel free to contact us via Email: bd@cloudswit.ch", + "description": "AsterNOS is the core technology of Asterfusion’s one-stop SONiC turnkey solution designed for cloud, enterprise, and AI-driven scenarios. AsterNOS v5.2 Campus is specifically designed for traditional campus networks, offering comprehensive L2/L3 capabilities suitable for various campus scenarios such as schools, office buildings, and hospitals. This version supports a fully cloud-integrated Layer 3 network architecture, providing rich routing and switching functionalities to ensure high-performance operation and ease of maintenance. It can also be deployed in the GNS3 simulation environment to experience a complete All-Layer 3 Cloud-Campus network. This version is ideal for enterprises and campus networks requiring high-performance multi-service transport, supporting cross-domain connectivity and providing nanosecond-level time synchronization. It is well-suited for applications with stringent time accuracy requirements, such as financial trading, industrial automation, and smart manufacturing. NOTICE: This appliance file is a virtualized version of AsterNOS and is intended to be used only to experience the basic functionality and industry standard CLI (Klish), not for official software testing. For more information about AsterNOS commercial version, please feel free to contact us via Email: bd@cloudswit.ch", "vendor_name": "Asterfusion", "vendor_url": "https://cloudswit.ch/", "vendor_logo_url": "https://raw.githubusercontent.com/GNS3/gns3-registry/master/vendor-logos/asterfusion.png", @@ -29,13 +29,6 @@ "kvm": "require" }, "images": [ - { - "filename": "vAsterNOS-V6.1R002.img", - "version": "6.1-2", - "md5sum": "003e6329489a617fbab5783504559d26", - "filesize": 2106851328, - "download_url": "https://drive.cloudswitch.io/external/c224501f36e6003767b30112bd44d92476f81f442cf47f8027a6f4f7e4227995" - }, { "filename": "vAsterNOS-V5.2R012P01.img", "version": "5.2-12-1", @@ -46,12 +39,6 @@ ], "versions": [ - { - "name": "6.1-2", - "images": { - "hda_disk_image": "vAsterNOS-V6.1R002.img" - } - }, { "name": "5.2-12-1", "images": { diff --git a/gns3server/appliances/asterfusion-vAsterNOS.gns3a b/gns3server/appliances/asterfusion-vAsterNOS.gns3a index e6e392c32..991c36069 100644 --- a/gns3server/appliances/asterfusion-vAsterNOS.gns3a +++ b/gns3server/appliances/asterfusion-vAsterNOS.gns3a @@ -6,7 +6,7 @@ "vendor_name": "Asterfusion", "vendor_url": "https://cloudswit.ch/", "vendor_logo_url": "https://raw.githubusercontent.com/GNS3/gns3-registry/master/vendor-logos/asterfusion.png", - "documentation_url": "https://help.cloudswit.ch/portal/en/kb/articles/vasternos", + "documentation_url": "https://asternos.com/community/space/vasternos/post/vasternos-for-data-center-network", "product_name": "vAsterNOS", "product_url": "https://cloudswit.ch/product/sonic-enterprise-distribution", "registry_version": 4, @@ -30,18 +30,18 @@ }, "images": [ { - "filename": "vAsterNOS-V3.1.img", + "filename": "AsterNOS_V3.1_R0408P01-VS.img.gz", "version": "V3.1", - "md5sum": "c323c9c3f60e1a93eca2acdc5034b85c", - "filesize": 2724659200, - "download_url": "https://drive.cloudswitch.io/external/8ae2e3932ad8bb2ec30dd25be415d288ff3e4a949c557c6bd48ac6e6265bcfc1" + "md5sum": "5c182306af67be2c79a5642551412e48", + "filesize": 1425945924, + "download_url": "http://pub.asternos.com/AsterNOS_V3.1_R0408P01-VS.img.gz" } ], "versions": [ { "name": "V3.1", "images": { - "hda_disk_image": "vAsterNOS-V3.1.img" + "hda_disk_image": "AsterNOS_V3.1_R0408P01-VS.img.gz" } } ] diff --git a/gns3server/appliances/cisco-7200.gns3a b/gns3server/appliances/cisco-7200.gns3a index 3e723e50b..78d0be865 100644 --- a/gns3server/appliances/cisco-7200.gns3a +++ b/gns3server/appliances/cisco-7200.gns3a @@ -50,6 +50,12 @@ "version": "124-25G", "md5sum": "9c7cc9b3f3b3571411a7f62faaa2c036", "filesize": 71528984 + }, + { + "filename": "c7200-itpk9-mz.124-15.SW.image", + "version": "124-15.SW", + "md5sum": "3334a0facdbd26164e83d3c201fff5b5", + "filesize": 46423952 } ], "versions": [ @@ -87,6 +93,13 @@ "images": { "image": "c7200-a3jk9s-mz.124-25g.image" } + }, + { + "name": "124-15.SW", + "idlepc": "0x60b2bc68", + "images": { + "image": "c7200-itpk9-mz.124-15.SW.image" + } } ] } diff --git a/gns3server/appliances/extreme-networks-voss.gns3a b/gns3server/appliances/extreme-networks-voss.gns3a index a5ef0a64c..08d6c4509 100644 --- a/gns3server/appliances/extreme-networks-voss.gns3a +++ b/gns3server/appliances/extreme-networks-voss.gns3a @@ -28,6 +28,13 @@ "options": "-nographic" }, "images": [ + { + "filename": "FEGNS3.9.3.0.0.qcow2", + "version": "v9.3.0.0", + "md5sum": "2abfa8d64219aa75522238f87b782726", + "filesize": 490209280, + "direct_download_url": "https://akamai-ep.extremenetworks.com/Extreme_P/github-en/Virtual_VOSS/FEGNS3.9.3.0.0.qcow2" + }, { "filename": "VOSSGNS3.8.10.1.0.qcow2", "version": "v8.10.1.0", @@ -93,6 +100,13 @@ } ], "versions": [ + { + "name": "v9.3.0.0", + "images": + { + "hda_disk_image": "FEGNS3.9.3.0.0.qcow2" + } + }, { "name": "v8.10.1.0", "images": diff --git a/gns3server/appliances/freeRouter.gns3a b/gns3server/appliances/freeRouter.gns3a index ca53df882..087ec49fb 100644 --- a/gns3server/appliances/freeRouter.gns3a +++ b/gns3server/appliances/freeRouter.gns3a @@ -25,19 +25,19 @@ }, "images": [ { - "filename": "rtr.qcow2", - "version": "20.7.1", - "md5sum": "4707415d15d20ced92423d90e52eedcd", - "filesize": 254767616, - "download_url": "http://freerouter.nop.hu/", - "direct_download_url": "http://dl.nop.hu/rtr.qcow2" + "filename": "rtr-x86_64.qcow2", + "version": "25.8.29", + "md5sum": "13d72fdfd772258808a9d4b75fa67083", + "filesize": 73269248, + "download_url": "http://www.freertr.org/", + "direct_download_url": "http://dl.nop.hu/rtr-x86_64.qcow2" } ], "versions": [ { - "name": "20.7.1", + "name": "25.8.29", "images": { - "hda_disk_image": "rtr.qcow2" + "hda_disk_image": "rtr-x86_64.qcow2" } } ] diff --git a/gns3server/appliances/infix.gns3a b/gns3server/appliances/infix.gns3a index 8d2814e9d..d9ab0321f 100644 --- a/gns3server/appliances/infix.gns3a +++ b/gns3server/appliances/infix.gns3a @@ -2,13 +2,13 @@ "appliance_id": "4d351078-c6f5-444c-ab30-0ef20e3d8c53", "name": "Infix", "category": "router", - "description": "Infix is a Network Operating System based on Linux. It can be set up both as a switch, with offloading using switchdev, and a router with firewalling.", + "description": "Infix OS is a free Linux-based operating system with made-easy management using NETCONF, RESTCONF, or the built-in command line interface (CLI). It is suitable for both end-devices with a single Ethernet port and switches/routers with multiple ports. Immutable by design, extensions are possible using Docker containers.", "vendor_name": "KernelKit", "vendor_url": "https://github.com/kernelkit", "vendor_logo_url": "https://kernelkit.org/assets/img/jack.png", "product_name": "Infix", "registry_version": 4, - "documentation_url": "https://github.com/kernelkit/infix/tree/main/doc", + "documentation_url": "https://kernelkit.org/infix/latest/", "status": "stable", "availability": "free", "maintainer": "KernelKit", @@ -37,16 +37,16 @@ "download_url": "https://sourceforge.net/projects/gns-3/files/Qemu%20Appliances/", "direct_download_url": "https://sourceforge.net/projects/gns-3/files/Qemu%20Appliances/OVMF-edk2-stable202305.fd.zip/download", "compression": "zip" - }, - { + }, + { "filename": "infix-x86_64-disk-24.11.1.img", "filesize": 536870912, "md5sum": "673a123fe122d1c2f5724baf9965a19d", "version": "24.11.1", "download_url": "https://github.com/kernelkit/infix/releases/download/v24.11.1/infix-x86_64-24.11.1.tar.gz", "compression": "gzip" - }, - { + }, + { "filename": "infix-x86_64-disk-25.01.0.img", "filesize": 536870912, "md5sum": "a814d93b385116b4a35712c445b5f830", @@ -69,17 +69,101 @@ "version": "25.03.0", "download_url": "https://github.com/kernelkit/infix/releases/download/v25.03.0/infix-x86_64-25.03.0.tar.gz", "compression": "gzip" + }, + { + "filename": "infix-x86_64-disk-25.04.0.qcow2", + "filesize": 259723776, + "md5sum": "84bd999513325d0007d0e6587abc6140", + "version": "25.04.0", + "direct_download_url": "https://github.com/kernelkit/infix/releases/download/v25.04.0/infix-x86_64-disk-25.04.0.qcow2" + }, + { + "filename": "infix-x86_64-disk-25.05.0.qcow2", + "filesize": 259958784, + "md5sum": "a6b1b3a6bccc60bb5214f2b864602fde", + "version": "25.05.0", + "direct_download_url": "https://github.com/kernelkit/infix/releases/download/v25.05.0/infix-x86_64-disk-25.05.0.qcow2" + }, + { + "filename": "infix-x86_64-disk-25.08.0.qcow2", + "filesize": 284453888, + "md5sum": "5fbf146d2ac73d1bd8fa1c509d6169b1", + "version": "25.08.0", + "direct_download_url": "https://github.com/kernelkit/infix/releases/download/v25.08.0/infix-x86_64-disk-25.08.0.qcow2" + }, + { + "filename": "infix-x86_64-disk-25.09.0.qcow2", + "filesize": 285120000, + "md5sum": "9761968406780a5b28311b8527f8629f", + "version": "25.09.0", + "direct_download_url": "https://github.com/kernelkit/infix/releases/download/v25.09.0/infix-x86_64-disk-25.09.0.qcow2" + }, + { + "filename": "infix-x86_64-disk-25.10.0.qcow2", + "filesize": 288818688, + "md5sum": "781d0fce9fbf789f6ea6ff10dc104afb", + "version": "25.10.0", + "direct_download_url": "https://github.com/kernelkit/infix/releases/download/v25.10.0/infix-x86_64-disk-25.10.0.qcow2" + }, + { + "filename": "infix-x86_64-disk-25.11.0.qcow2", + "filesize": 288930816, + "md5sum": "e2eb1a4fff9d56815f37c2e82a6279fb", + "version": "25.11.0", + "direct_download_url": "https://github.com/kernelkit/infix/releases/download/v25.11.0/infix-x86_64-disk-25.11.0.qcow2" } ], "versions": [ - { + { + "name": "25.11.0", + "images": { + "bios_image": "OVMF-edk2-stable202305.fd", + "hda_disk_image": "infix-x86_64-disk-25.11.0.qcow2" + } + }, + { + "name": "25.10.0", + "images": { + "bios_image": "OVMF-edk2-stable202305.fd", + "hda_disk_image": "infix-x86_64-disk-25.10.0.qcow2" + } + }, + { + "name": "25.09.0", + "images": { + "bios_image": "OVMF-edk2-stable202305.fd", + "hda_disk_image": "infix-x86_64-disk-25.09.0.qcow2" + } + }, + { + "name": "25.08.0", + "images": { + "bios_image": "OVMF-edk2-stable202305.fd", + "hda_disk_image": "infix-x86_64-disk-25.08.0.qcow2" + } + }, + { + "name": "25.05.0", + "images": { + "bios_image": "OVMF-edk2-stable202305.fd", + "hda_disk_image": "infix-x86_64-disk-25.05.0.qcow2" + } + }, + { + "name": "25.04.0", + "images": { + "bios_image": "OVMF-edk2-stable202305.fd", + "hda_disk_image": "infix-x86_64-disk-25.04.0.qcow2" + } + }, + { "name": "25.03.0", "images": { "bios_image": "OVMF-edk2-stable202305.fd", "hda_disk_image": "infix-x86_64-disk-25.03.0.img" } }, - { + { "name": "25.02.0", "images": { "bios_image": "OVMF-edk2-stable202305.fd", @@ -101,4 +185,4 @@ } } ] -} +} \ No newline at end of file diff --git a/gns3server/appliances/kali-linux.gns3a b/gns3server/appliances/kali-linux.gns3a index 5452ec058..60f742842 100644 --- a/gns3server/appliances/kali-linux.gns3a +++ b/gns3server/appliances/kali-linux.gns3a @@ -11,237 +11,34 @@ "status": "stable", "maintainer": "GNS3 Team", "maintainer_email": "developers@gns3.net", - "usage": "Default password is toor\nEnable persistence by selecting boot option 'Live USB Persistence'", + "usage": "Default username is kali and the default password is kali", "port_name_format": "eth{0}", "qemu": { "adapter_type": "e1000", "adapters": 8, - "ram": 1024, + "ram": 2048, + "cpus": 2, "hda_disk_interface": "ide", "arch": "x86_64", "console_type": "vnc", - "boot_priority": "d", "kvm": "require" }, "images": [ - { - "filename": "kali-linux-2021.1-live-amd64.iso", - "version": "2021.1", - "md5sum": "3a3716fef866e5c29a1c1ccfc94264b5", - "filesize": 3591385088, - "download_url": "http://cdimage.kali.org/kali-2021.1/", - "direct_download_url": "http://cdimage.kali.org/kali-2021.1/kali-linux-2021.1-live-amd64.iso" - }, - { - "filename": "kali-linux-2019.3-amd64.iso", - "version": "2019.3", - "md5sum": "9c6fb00558f78ed06992d89f745ef975", - "filesize": 3037736960, - "download_url": "http://old.kali.org/kali-images/kali-2019.3", - "direct_download_url": "http://old.kali.org/kali-images/kali-2019.3/kali-linux-2019.3-amd64.iso" - }, - { - "filename": "kali-linux-2019.2-amd64.iso", - "version": "2019.2", - "md5sum": "0f89b6225d7ea9c18682f7cc541c1179", - "filesize": 3353227264, - "download_url": "http://old.kali.org/kali-images/kali-2019.2", - "direct_download_url": "http://old.kali.org/kali-images/kali-2019.2/kali-linux-2019.2-amd64.iso" - }, - { - "filename": "kali-linux-mate-2019.2-amd64.iso", - "version": "2019.2 (MATE)", - "md5sum": "fec8dd7009f932c51a74323df965a709", - "filesize": 3313217536, - "download_url": "http://old.kali.org/kali-images/kali-2019.2", - "direct_download_url": "http://old.kali.org/kali-images/kali-2019.2/kali-linux-mate-2019.2-amd64.iso" - }, - { - "filename": "kali-linux-2019.1a-amd64.iso", - "version": "2019.1a", - "md5sum": "58c6111ed0be1919ea87267e7e65ab0f", - "filesize": 3483873280, - "download_url": "http://old.kali.org/kali-images/kali-2019.1a", - "direct_download_url": "http://old.kali.org/kali-images/kali-2019.1a/kali-linux-2019.1a-amd64.iso" - }, - { - "filename": "kali-linux-2018.4-amd64.iso", - "version": "2018.4", - "md5sum": "1b2d598bb8d2003e6207c119c0ba42fe", - "filesize": 3139436544, - "download_url": "http://old.kali.org/kali-images/kali-2018.4", - "direct_download_url": "http://old.kali.org/kali-images/kali-2018.4/kali-linux-2018.4-amd64.iso" - }, - { - "filename": "kali-linux-2018.3a-amd64.iso", - "version": "2018.3a", - "md5sum": "2da675d016bd690c05e180e33aa98b94", - "filesize": 3192651776, - "download_url": "http://old.kali.org/kali-images/kali-2018.3a", - "direct_download_url": "http://old.kali.org/kali-images/kali-2018.3a/kali-linux-2018.3a-amd64.iso" - }, - { - "filename": "kali-linux-2018.1-amd64.iso", - "version": "2018.1", - "md5sum": "a3feb90df5b71b3c7f4a02bdddf221d7", - "filesize": 3028500480, - "download_url": "http://old.kali.org/kali-images/kali-2018.1", - "direct_download_url": "http://old.kali.org/kali-images/kali-2018.1/kali-linux-2018.1-amd64.iso" - }, - { - "filename": "kali-linux-2017.3-amd64.iso", - "version": "2017.3", - "md5sum": "b465580c897e94675ac1daf031fa66b9", - "filesize": 2886402048, - "download_url": "http://old.kali.org/kali-images/kali-2017.3/", - "direct_download_url": "http://old.kali.org/kali-images/kali-2017.3/kali-linux-2017.3-amd64.iso" - }, - { - "filename": "kali-linux-2017.2-amd64.iso", - "version": "2017.2", - "md5sum": "541654f8f818450dc0db866a0a0f6eec", - "filesize": 3020619776, - "download_url": "http://old.kali.org/kali-images/kali-2017.2/", - "direct_download_url": "http://old.kali.org/kali-images/kali-2017.2/kali-linux-2017.2-amd64.iso" - }, - { - "filename": "kali-linux-2017.1-amd64.iso", - "version": "2017.1", - "md5sum": "c8e742283929d7a12dbe7c58e398ff08", - "filesize": 2794307584, - "download_url": "http://old.kali.org/kali-images/kali-2017.1/", - "direct_download_url": "http://old.kali.org/kali-images/kali-2017.1/kali-linux-2017.1-amd64.iso" - }, - { - "filename": "kali-linux-2016.2-amd64.iso", - "version": "2016.2", - "md5sum": "3d163746bc5148e61ad689d94bc263f9", - "filesize": 3076767744, - "download_url": "http://old.kali.org/kali-images/kali-2016.2/", - "direct_download_url": "http://old.kali.org/kali-images/kali-2016.2/kali-linux-2016.2-amd64.iso" - }, - { - "filename": "kali-linux-2016.1-amd64.iso", - "version": "2016.1", - "md5sum": "2e1230dc14036935b3279dfe3e49ad39", - "filesize": 2945482752, - "download_url": "http://old.kali.org/kali-images/kali-2016.1/", - "direct_download_url": "http://old.kali.org/kali-images/kali-2016.1/kali-linux-2016.1-amd64.iso" - }, - { - "filename": "kali-linux-2.0-amd64.iso", - "version": "2.0", - "md5sum": "ef192433017c5d99a156eaef51fd389d", - "filesize": 3320512512, - "download_url": "https://www.offensive-security.com/kali-linux-vmware-arm-image-download/", - "direct_download_url": "http://images.kali.org/Kali-Linux-2.0.0-vm-amd64.7z" - }, - { - "filename": "kali-linux-persistence-1gb.qcow2", - "version": "1.0", - "md5sum": "14e9c92f3ba5a0bd1128c1ea26a129ea", - "filesize": 34734080, - "download_url": "https://sourceforge.net/projects/gns-3/files/Qemu%20Appliances/", - "direct_download_url": "https://sourceforge.net/projects/gns-3/files/Qemu%20Appliances/kali-linux-persistence-1gb.qcow2/download" + { + "filename": "kali-linux-2025.4-qemu-amd64.qcow2", + "version": "2025.4", + "md5sum": "a4be43351b5deb5b2823cb38a73b2095", + "filesize": 15719268352, + "download_url": "https://cdimage.kali.org/kali-2025.4/", + "direct_download_url": "https://cdimage.kali.org/kali-2025.4/kali-linux-2025.4-qemu-amd64.7z", + "compression": "7z" } ], "versions": [ { - "name": "2021.1", + "name": "2025.4", "images": { - "hda_disk_image": "kali-linux-persistence-1gb.qcow2", - "cdrom_image": "kali-linux-2021.1-live-amd64.iso" - } - }, - { - "name": "2019.3", - "images": { - "hda_disk_image": "kali-linux-persistence-1gb.qcow2", - "cdrom_image": "kali-linux-2019.3-amd64.iso" - } - }, - { - "name": "2019.2", - "images": { - "hda_disk_image": "kali-linux-persistence-1gb.qcow2", - "cdrom_image": "kali-linux-2019.2-amd64.iso" - } - }, - { - "name": "2019.2 (MATE)", - "images": { - "hda_disk_image": "kali-linux-persistence-1gb.qcow2", - "cdrom_image": "kali-linux-mate-2019.2-amd64.iso" - } - }, - { - "name": "2019.1a", - "images": { - "hda_disk_image": "kali-linux-persistence-1gb.qcow2", - "cdrom_image": "kali-linux-2019.1a-amd64.iso" - } - }, - { - "name": "2018.4", - "images": { - "hda_disk_image": "kali-linux-persistence-1gb.qcow2", - "cdrom_image": "kali-linux-2018.4-amd64.iso" - } - }, - { - "name": "2018.3a", - "images": { - "hda_disk_image": "kali-linux-persistence-1gb.qcow2", - "cdrom_image": "kali-linux-2018.3a-amd64.iso" - } - }, - { - "name": "2018.1", - "images": { - "hda_disk_image": "kali-linux-persistence-1gb.qcow2", - "cdrom_image": "kali-linux-2018.1-amd64.iso" - } - }, - { - "name": "2017.3", - "images": { - "hda_disk_image": "kali-linux-persistence-1gb.qcow2", - "cdrom_image": "kali-linux-2017.3-amd64.iso" - } - }, - { - "name": "2017.2", - "images": { - "hda_disk_image": "kali-linux-persistence-1gb.qcow2", - "cdrom_image": "kali-linux-2017.2-amd64.iso" - } - }, - { - "name": "2017.1", - "images": { - "hda_disk_image": "kali-linux-persistence-1gb.qcow2", - "cdrom_image": "kali-linux-2017.1-amd64.iso" - } - }, - { - "name": "2016.2", - "images": { - "hda_disk_image": "kali-linux-persistence-1gb.qcow2", - "cdrom_image": "kali-linux-2016.2-amd64.iso" - } - }, - { - "name": "2016.1", - "images": { - "hda_disk_image": "kali-linux-persistence-1gb.qcow2", - "cdrom_image": "kali-linux-2016.1-amd64.iso" - } - }, - { - "name": "2.0", - "images": { - "hda_disk_image": "kali-linux-persistence-1gb.qcow2", - "cdrom_image": "kali-linux-2.0-amd64.iso" + "hda_disk_image": "kali-linux-2025.4-qemu-amd64.qcow2" } } ] diff --git a/gns3server/appliances/mikrotik-winbox-2026.gns3a b/gns3server/appliances/mikrotik-winbox-2026.gns3a new file mode 100644 index 000000000..7d0bc91ee --- /dev/null +++ b/gns3server/appliances/mikrotik-winbox-2026.gns3a @@ -0,0 +1,22 @@ +{ + "appliance_id": "1174e46d-4a29-4554-a1e8-71ed90682295", + "name": "MikroTik WinBox 2026", + "category": "guest", + "description": "Mikrotik's WinBox router management software for GNS3 2026", + "vendor_name": "MikroTik", + "vendor_url": "https://mikrotik.com", + "documentation_url": "https://mikrotik.com/winbox", + "product_name": "MikroTik WinBox", + "product_url": "https://mikrotik.com/winbox", + "registry_version": 4, + "status": "stable", + "availability": "free", + "maintainer": "h3ck13r", + "maintainer_email": "pisdushko1886@gmail.com", + "usage": "Connect WinBox to a MikroTik CHR port and start them both. Open WinBox console and wait until MikroTik appear in neighbors tab, provide credentials and connect.", + "docker": { + "adapters": 1, + "image": "snatao808/winbox-mikrotik-2026", + "console_type": "vnc" + } +} diff --git a/gns3server/appliances/openwrt.gns3a b/gns3server/appliances/openwrt.gns3a index add7eb588..ac3711d3a 100644 --- a/gns3server/appliances/openwrt.gns3a +++ b/gns3server/appliances/openwrt.gns3a @@ -23,6 +23,15 @@ "kvm": "allow" }, "images": [ + { + "filename": "openwrt-24.10.2-x86-64-generic-ext4-combined.img", + "version": "24.10.2", + "md5sum": "0769916238524161b0a9a639ae261381", + "filesize": 126353408, + "download_url": "https://downloads.openwrt.org/releases/24.10.2/targets/x86/64/", + "direct_download_url": "https://downloads.openwrt.org/releases/24.10.2/targets/x86/64/openwrt-24.10.2-x86-64-generic-ext4-combined.img.gz", + "compression": "gzip" + }, { "filename": "openwrt-23.05.0-x86-64-generic-ext4-combined.img", "version": "23.05.0", @@ -223,7 +232,13 @@ } ], "versions": [ - { + { + "name": "24.10.2", + "images": { + "hda_disk_image": "openwrt-24.10.2-x86-64-generic-ext4-combined.img" + } + }, + { "name": "23.05.0", "images": { "hda_disk_image": "openwrt-23.05.0-x86-64-generic-ext4-combined.img" diff --git a/gns3server/appliances/opnsense.gns3a b/gns3server/appliances/opnsense.gns3a index 759701763..433b4f2de 100644 --- a/gns3server/appliances/opnsense.gns3a +++ b/gns3server/appliances/opnsense.gns3a @@ -25,6 +25,20 @@ "kvm": "require" }, "images": [ + { + "filename": "OPNsense-25.7-nano-amd64.img", + "version": "25.7", + "md5sum": "07aa8b44ebc57fa9ca0145f777901a59", + "filesize": 3221225472, + "download_url": "https://opnsense.c0urier.net/releases/25.7/" + }, + { + "filename": "OPNsense-25.1-nano-amd64.img", + "version": "25.1", + "md5sum": "9c119e4fed7dd7c1c162d8be4088d983", + "filesize": 3221225472, + "download_url": "https://opnsense.c0urier.net/releases/25.1/" + }, { "filename": "OPNsense-24.7-nano-amd64.img", "version": "24.7", diff --git a/gns3server/appliances/vyos.gns3a b/gns3server/appliances/vyos.gns3a index cace44ed3..10bf9ae4c 100644 --- a/gns3server/appliances/vyos.gns3a +++ b/gns3server/appliances/vyos.gns3a @@ -30,6 +30,27 @@ "on_close": "shutdown_signal" }, "images": [ + { + "filename": "vyos-1.4.4-kvm-amd64.qcow2", + "version": "1.4.4", + "md5sum": "942dae45d63924b58b65d069890765d4", + "filesize": 518782976, + "download_url": "https://support.vyos.io/" + }, + { + "filename": "vyos-1.4.3-kvm-amd64.qcow2", + "version": "1.4.3", + "md5sum": "4eec0ba2ec367bbbdf2d2f212f947746", + "filesize": 515047424, + "download_url": "https://support.vyos.io/" + }, + { + "filename": "vyos-1.4.2-kvm-amd64.qcow2", + "version": "1.4.2", + "md5sum": "78c5935e05a7bbd94a639fcee18f5799", + "filesize": 514064384, + "download_url": "https://support.vyos.io/" + }, { "filename": "vyos-1.4.1-kvm-amd64.qcow2", "version": "1.4.1", @@ -116,6 +137,24 @@ } ], "versions": [ + { + "name": "1.4.4", + "images": { + "hda_disk_image": "vyos-1.4.4-kvm-amd64.qcow2" + } + }, + { + "name": "1.4.3", + "images": { + "hda_disk_image": "vyos-1.4.3-kvm-amd64.qcow2" + } + }, + { + "name": "1.4.2", + "images": { + "hda_disk_image": "vyos-1.4.2-kvm-amd64.qcow2" + } + }, { "name": "1.4.1", "images": { diff --git a/gns3server/compute/docker/__init__.py b/gns3server/compute/docker/__init__.py index f6cb215fa..696646ade 100644 --- a/gns3server/compute/docker/__init__.py +++ b/gns3server/compute/docker/__init__.py @@ -39,9 +39,9 @@ log = logging.getLogger(__name__) # Be careful to keep it consistent -DOCKER_MINIMUM_API_VERSION = "1.25" -DOCKER_MINIMUM_VERSION = "1.13" -DOCKER_PREFERRED_API_VERSION = "1.30" +DOCKER_MINIMUM_API_VERSION = "1.40" +DOCKER_MINIMUM_VERSION = "19.03.8" +DOCKER_PREFERRED_API_VERSION = "1.44" CHUNK_SIZE = 1024 * 8 # 8KB @@ -123,22 +123,30 @@ class Docker(BaseManager): if not self._connected: try: self._connected = True - connector = self.connector() - version = await self.query("GET", "version") + self._connector = self.connector() + docker_info = await self.query("GET", "version") except (aiohttp.ClientOSError, FileNotFoundError): self._connected = False - raise DockerError("Can't connect to docker daemon") + raise DockerError("Can't connect to Docker daemon") - docker_version = parse_version(version['ApiVersion']) + api_version = parse_version(docker_info['ApiVersion']) + version = docker_info["Version"] - if docker_version < parse_version(DOCKER_MINIMUM_API_VERSION): - raise DockerError( - "Docker version is {}. GNS3 requires a minimum version of {}".format( - version["Version"], DOCKER_MINIMUM_VERSION)) + if api_version < parse_version(DOCKER_MINIMUM_API_VERSION): + raise DockerError("Docker version is {}. GNS3 requires a minimum version of {}".format( + version, + DOCKER_MINIMUM_VERSION) + ) preferred_api_version = parse_version(DOCKER_PREFERRED_API_VERSION) - if docker_version >= preferred_api_version: + if api_version >= preferred_api_version: self._api_version = DOCKER_PREFERRED_API_VERSION + else: + # use the Min API version supported by the daemon + self._api_version = docker_info['MinAPIVersion'] + log.warning("Using Docker client with the minimum API version {}".format(self._api_version)) + + log.info("Connected to Docker daemon version {} using API version {}".format(version, self._api_version)) def connector(self): @@ -186,7 +194,7 @@ class Docker(BaseManager): :param method: HTTP method :param path: Endpoint in API - :param data: Dictionnary with the body. Will be transformed to a JSON + :param data: Dictionary with the body. Will be transformed to a JSON :param params: Parameters added as a query arg :param timeout: Timeout :returns: HTTP response @@ -197,12 +205,11 @@ class Docker(BaseManager): timeout = 60 * 60 * 24 * 31 # One month timeout if path == 'version': - url = "http://docker/v1.24/" + path + url = "http://docker/" + path else: - url = "http://docker/v" + DOCKER_MINIMUM_API_VERSION + "/" + path + await self._check_connection() # version is use by check connection + url = "http://docker/v" + self._api_version + "/" + path try: - if path != "version": # version is use by check connection - await self._check_connection() if self._session is None or self._session.closed: connector = self.connector() self._session = aiohttp.ClientSession(connector=connector) diff --git a/gns3server/compute/docker/docker_vm.py b/gns3server/compute/docker/docker_vm.py index 8af6ac882..79b5f640b 100644 --- a/gns3server/compute/docker/docker_vm.py +++ b/gns3server/compute/docker/docker_vm.py @@ -153,6 +153,14 @@ class DockerVM(BaseNode): def ethernet_adapters(self): return self._ethernet_adapters + @property + def docker_name(self): + """ + Container name in Docker + """ + + return "GNS3.{}.{}".format(self.name, self._project.id) + @property def mac_address(self): """ @@ -451,7 +459,8 @@ class DockerVM(BaseNode): if extra_hosts: params["Env"].append("GNS3_EXTRA_HOSTS={}".format(extra_hosts)) - result = await self.manager.query("POST", "containers/create", data=params) + # Support name in Doker: [a-zA-Z0-9][a-zA-Z0-9_.-] + result = await self.manager.query("POST", "containers/create?name={}".format(self.docker_name), data=params) self._cid = result['Id'] log.info("Docker container '{name}' [{id}] created".format(name=self._name, id=self._id)) return True diff --git a/gns3server/compute/docker/resources/etc/udhcpc/default.script b/gns3server/compute/docker/resources/etc/udhcpc/default.script index 404fb190a..873642c47 100755 --- a/gns3server/compute/docker/resources/etc/udhcpc/default.script +++ b/gns3server/compute/docker/resources/etc/udhcpc/default.script @@ -3,7 +3,12 @@ # script for udhcpc # Copyright (c) 2008 Natanael Copa -UDHCPC="/gns3/etc/udhcpc" +if [ -d /etc/network ]; then + UDHCPC="/etc/network/udhcpc" +else + UDHCPC="/gns3/etc/udhcpc" +fi + UDHCPC_CONF="$UDHCPC/udhcpc.conf" RESOLV_CONF="/etc/resolv.conf" diff --git a/gns3server/configs/iou_l2_base_startup-config.txt b/gns3server/configs/iou_l2_base_startup-config.txt index 0ce9f3653..f5d6344e8 100644 --- a/gns3server/configs/iou_l2_base_startup-config.txt +++ b/gns3server/configs/iou_l2_base_startup-config.txt @@ -13,8 +13,7 @@ logging console discriminator EXCESS ! no ip icmp rate-limit unreachable ! -! due to some bugs with IOU, try to change the following line to 'ip cef' if your routing does not work -no ip cef +ip cef no ip domain lookup ! ! diff --git a/gns3server/configs/iou_l3_base_startup-config.txt b/gns3server/configs/iou_l3_base_startup-config.txt index 2706875d7..8a25be616 100644 --- a/gns3server/configs/iou_l3_base_startup-config.txt +++ b/gns3server/configs/iou_l3_base_startup-config.txt @@ -12,8 +12,7 @@ no ip icmp rate-limit unreachable ! ! ! -! due to some bugs with IOU, try to change the following line to 'ip cef' if your routing does not work -no ip cef +ip cef no ip domain lookup ! ! diff --git a/gns3server/crash_report.py b/gns3server/crash_report.py index 17917695b..93f503113 100644 --- a/gns3server/crash_report.py +++ b/gns3server/crash_report.py @@ -57,7 +57,7 @@ class CrashReport: Report crash to a third party service """ - DSN = "https://c663917f99579c9b5a5c0764415c0bd4@o19455.ingest.us.sentry.io/38482" + DSN = "https://9b9fce29bf59d9a99c230240e1070600@o19455.ingest.us.sentry.io/38482" _instance = None def __init__(self): diff --git a/gns3server/schemas/template.py b/gns3server/schemas/template.py index 6cfdb30d3..7a6eb8e97 100644 --- a/gns3server/schemas/template.py +++ b/gns3server/schemas/template.py @@ -27,7 +27,7 @@ BASE_TEMPLATE_PROPERTIES = { }, "template_type": { "description": "Type of node", - "enum": ["cloud", "ethernet_hub", "ethernet_switch", "docker", "dynamips", "vpcs", "traceng", + "enum": ["cloud", "nat", "ethernet_hub", "ethernet_switch", "docker", "dynamips", "vpcs", "traceng", "virtualbox", "vmware", "iou", "qemu"] }, "name": { diff --git a/gns3server/utils/file_watcher.py b/gns3server/utils/file_watcher.py index d466de77b..ee76928a3 100644 --- a/gns3server/utils/file_watcher.py +++ b/gns3server/utils/file_watcher.py @@ -60,7 +60,14 @@ class FileWatcher: self._hashed[path] = zlib.adler32(open(path, 'rb').read()) except OSError: self._hashed[path] = None - asyncio.get_event_loop().call_later(self._delay, self._check_config_file_change) + + try: + loop = asyncio.get_running_loop() + except RuntimeError: + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + + loop.call_later(self._delay, self._check_config_file_change) def __del__(self): self._closed = True diff --git a/gns3server/version.py b/gns3server/version.py index 18b2a70d6..a5a0c9036 100644 --- a/gns3server/version.py +++ b/gns3server/version.py @@ -23,8 +23,8 @@ # or negative for a release candidate or beta (after the base version # number has been incremented) -__version__ = "2.2.54" -__version_info__ = (2, 2, 54, 0) +__version__ = "2.2.56" +__version_info__ = (2, 2, 56, 0) if "dev" in __version__: try: diff --git a/gns3server/web/web_server.py b/gns3server/web/web_server.py index 51fad283c..61a0225ad 100644 --- a/gns3server/web/web_server.py +++ b/gns3server/web/web_server.py @@ -293,6 +293,11 @@ class WebServer: raise SystemExit self._ssl_context = self._create_ssl_context(server_config) + try: + self._loop = asyncio.get_running_loop() + except RuntimeError: + self._loop = asyncio.new_event_loop() + asyncio.set_event_loop(self._loop) self._loop = asyncio.get_event_loop() if log.getEffectiveLevel() == logging.DEBUG: diff --git a/requirements.txt b/requirements.txt index fb44de553..5f0b26144 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,8 +3,8 @@ aiohttp>=3.10.11,<3.11 # version 3.10.11 is the last compatible version with Py aiohttp-cors>=0.7.0,<0.8 aiofiles>=24.1.0,<25.0 Jinja2>=3.1.6,<3.2 -sentry-sdk>=2.26.1,<2.27 # optional dependency -psutil>=7.0.0 +sentry-sdk>=2.44.0,<2.45 # optional dependency +psutil>=7.1.3 async-timeout>=5.0.1,<5.1 distro>=1.9.0 py-cpuinfo>=9.0.0,<10.0 diff --git a/setup.py b/setup.py index 2bf73b36b..5685ce694 100644 --- a/setup.py +++ b/setup.py @@ -84,6 +84,7 @@ setup( "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", "Programming Language :: Python :: Implementation :: CPython", ], ) diff --git a/tests/compute/docker/test_docker.py b/tests/compute/docker/test_docker.py index 12e473425..9e411503d 100644 --- a/tests/compute/docker/test_docker.py +++ b/tests/compute/docker/test_docker.py @@ -47,7 +47,7 @@ async def test_query_success(vm): vm._session.request = AsyncioMagicMock(return_value=response) data = await vm.query("POST", "test", data={"a": True}, params={"b": 1}) vm._session.request.assert_called_with('POST', - 'http://docker/v1.25/test', + 'http://docker/v{}/test'.format(DOCKER_MINIMUM_API_VERSION), data='{"a": true}', headers={'content-type': 'application/json'}, params={'b': 1}, @@ -69,7 +69,7 @@ async def test_query_error(vm): with pytest.raises(DockerError): await vm.query("POST", "test", data={"a": True}, params={"b": 1}) vm._session.request.assert_called_with('POST', - 'http://docker/v1.25/test', + 'http://docker/v{}/test'.format(DOCKER_MINIMUM_API_VERSION), data='{"a": true}', headers={'content-type': 'application/json'}, params={'b': 1}, @@ -89,7 +89,7 @@ async def test_query_error_json(vm): with pytest.raises(DockerError): await vm.query("POST", "test", data={"a": True}, params={"b": 1}) vm._session.request.assert_called_with('POST', - 'http://docker/v1.25/test', + 'http://docker/v{}/test'.format(DOCKER_MINIMUM_API_VERSION), data='{"a": true}', headers={'content-type': 'application/json'}, params={'b': 1}, @@ -180,7 +180,9 @@ async def test_docker_check_connection_docker_minimum_version(vm): async def test_docker_check_connection_docker_preferred_version_against_newer(vm): response = { - 'ApiVersion': '1.31' + 'ApiVersion': '1.52', + 'Version': '29.0.1', + } with patch("gns3server.compute.docker.Docker.connector"), \ @@ -193,7 +195,9 @@ async def test_docker_check_connection_docker_preferred_version_against_newer(vm async def test_docker_check_connection_docker_preferred_version_against_older(vm): response = { - 'ApiVersion': '1.27', + 'ApiVersion': '1.43', + 'Version': '24.0.2', + 'MinAPIVersion': '1.40' } with patch("gns3server.compute.docker.Docker.connector"), \ @@ -203,6 +207,20 @@ async def test_docker_check_connection_docker_preferred_version_against_older(vm assert vm._api_version == DOCKER_MINIMUM_API_VERSION +async def test_docker_check_connection_docker_unsupported_version(vm): + + response = { + 'ApiVersion': '1.25', + 'Version': '1.13.1', + } + + with patch("gns3server.compute.docker.Docker.connector"), \ + asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response): + vm._connected = False + with pytest.raises(DockerError) as e: + await vm._check_connection() + + @pytest.mark.asyncio async def test_install_busybox(): diff --git a/tests/compute/docker/test_docker_vm.py b/tests/compute/docker/test_docker_vm.py index cb2099d2b..332a52c17 100644 --- a/tests/compute/docker/test_docker_vm.py +++ b/tests/compute/docker/test_docker_vm.py @@ -96,7 +96,7 @@ async def test_create(compute_project, manager): with asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response) as mock: vm = DockerVM("test", str(uuid.uuid4()), compute_project, manager, "ubuntu:latest") await vm.create() - mock.assert_called_with("POST", "containers/create", data={ + mock.assert_called_with("POST", "containers/create?name={}".format(vm.docker_name), data={ "Tty": True, "OpenStdin": True, "StdinOnce": False, @@ -135,7 +135,7 @@ async def test_create_with_tag(compute_project, manager): with asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response) as mock: vm = DockerVM("test", str(uuid.uuid4()), compute_project, manager, "ubuntu:16.04") await vm.create() - mock.assert_called_with("POST", "containers/create", data={ + mock.assert_called_with("POST", "containers/create?name={}".format(vm.docker_name), data={ "Tty": True, "OpenStdin": True, "StdinOnce": False, @@ -177,7 +177,7 @@ async def test_create_vnc(compute_project, manager): vm._start_vnc = MagicMock() vm._display = 42 await vm.create() - mock.assert_called_with("POST", "containers/create", data={ + mock.assert_called_with("POST", "containers/create?name={}".format(vm.docker_name), data={ "Tty": True, "OpenStdin": True, "StdinOnce": False, @@ -308,7 +308,7 @@ async def test_create_start_cmd(compute_project, manager): vm = DockerVM("test", str(uuid.uuid4()), compute_project, manager, "ubuntu:latest") vm._start_command = "/bin/ls" await vm.create() - mock.assert_called_with("POST", "containers/create", data={ + mock.assert_called_with("POST", "containers/create?name={}".format(vm.docker_name), data={ "Tty": True, "OpenStdin": True, "StdinOnce": False, @@ -407,7 +407,7 @@ async def test_create_image_not_available(compute_project, manager): with asyncio_patch("gns3server.compute.docker.DockerVM.pull_image", return_value=True) as mock_pull: with asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response) as mock: await vm.create() - mock.assert_called_with("POST", "containers/create", data={ + mock.assert_called_with("POST", "containers/create?name={}".format(vm.docker_name), data={ "Tty": True, "OpenStdin": True, "StdinOnce": False, @@ -450,7 +450,7 @@ async def test_create_with_user(compute_project, manager): with asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response) as mock: vm = DockerVM("test", str(uuid.uuid4()), compute_project, manager, "ubuntu:latest") await vm.create() - mock.assert_called_with("POST", "containers/create", data={ + mock.assert_called_with("POST", "containers/create?name={}".format(vm.docker_name), data={ "Tty": True, "OpenStdin": True, "StdinOnce": False, @@ -534,7 +534,7 @@ async def test_create_with_extra_volumes_duplicate_1_image(compute_project, mana with asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response) as mock: vm = DockerVM("test", str(uuid.uuid4()), compute_project, manager, "ubuntu:latest", extra_volumes=["/vol/1"]) await vm.create() - mock.assert_called_with("POST", "containers/create", data={ + mock.assert_called_with("POST", "containers/create?name={}".format(vm.docker_name), data={ "Tty": True, "OpenStdin": True, "StdinOnce": False, @@ -574,7 +574,7 @@ async def test_create_with_extra_volumes_duplicate_2_user(compute_project, manag with asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response) as mock: vm = DockerVM("test", str(uuid.uuid4()), compute_project, manager, "ubuntu:latest", extra_volumes=["/vol/1", "/vol/1"]) await vm.create() - mock.assert_called_with("POST", "containers/create", data={ + mock.assert_called_with("POST", "containers/create?name={}".format(vm.docker_name), data={ "Tty": True, "OpenStdin": True, "StdinOnce": False, @@ -614,7 +614,7 @@ async def test_create_with_extra_volumes_duplicate_3_subdir(compute_project, man with asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response) as mock: vm = DockerVM("test", str(uuid.uuid4()), compute_project, manager, "ubuntu:latest", extra_volumes=["/vol/1/", "/vol"]) await vm.create() - mock.assert_called_with("POST", "containers/create", data={ + mock.assert_called_with("POST", "containers/create?name={}".format(vm.docker_name), data={ "Tty": True, "OpenStdin": True, "StdinOnce": False, @@ -654,7 +654,7 @@ async def test_create_with_extra_volumes_duplicate_4_backslash(compute_project, with asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response) as mock: vm = DockerVM("test", str(uuid.uuid4()), compute_project, manager, "ubuntu:latest", extra_volumes=["/vol//", "/vol"]) await vm.create() - mock.assert_called_with("POST", "containers/create", data={ + mock.assert_called_with("POST", "containers/create?name={}".format(vm.docker_name), data={ "Tty": True, "OpenStdin": True, "StdinOnce": False, @@ -694,7 +694,7 @@ async def test_create_with_extra_volumes_duplicate_5_subdir_issue_1595(compute_p with asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response) as mock: vm = DockerVM("test", str(uuid.uuid4()), compute_project, manager, "ubuntu:latest", extra_volumes=["/etc"]) await vm.create() - mock.assert_called_with("POST", "containers/create", data={ + mock.assert_called_with("POST", "containers/create?name={}".format(vm.docker_name), data={ "Tty": True, "OpenStdin": True, "StdinOnce": False, @@ -733,7 +733,7 @@ async def test_create_with_extra_volumes_duplicate_6_subdir_issue_1595(compute_p with asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response) as mock: vm = DockerVM("test", str(uuid.uuid4()), compute_project, manager, "ubuntu:latest", extra_volumes=["/etc/test", "/etc"]) await vm.create() - mock.assert_called_with("POST", "containers/create", data={ + mock.assert_called_with("POST", "containers/create?name={}".format(vm.docker_name), data={ "Tty": True, "OpenStdin": True, "StdinOnce": False, @@ -778,7 +778,7 @@ async def test_create_with_extra_volumes(compute_project, manager): with asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response) as mock: vm = DockerVM("test", str(uuid.uuid4()), compute_project, manager, "ubuntu:latest", extra_volumes=["/vol/2"]) await vm.create() - mock.assert_called_with("POST", "containers/create", data={ + mock.assert_called_with("POST", "containers/create?name={}".format(vm.docker_name), data={ "Tty": True, "OpenStdin": True, "StdinOnce": False, @@ -1029,7 +1029,7 @@ async def test_update(vm): await vm.update() mock_query.assert_any_call("DELETE", "containers/e90e34656842", params={"force": 1, "v": 1}) - mock_query.assert_any_call("POST", "containers/create", data={ + mock_query.assert_any_call("POST", "containers/create?name={}".format(vm.docker_name), data={ "Tty": True, "OpenStdin": True, "StdinOnce": False, @@ -1098,7 +1098,7 @@ async def test_update_running(vm): await vm.update() mock_query.assert_any_call("DELETE", "containers/e90e34656842", params={"force": 1, "v": 1}) - mock_query.assert_any_call("POST", "containers/create", data={ + mock_query.assert_any_call("POST", "containers/create?name={}".format(vm.docker_name), data={ "Tty": True, "OpenStdin": True, "StdinOnce": False, diff --git a/tests/controller/test_project.py b/tests/controller/test_project.py index f639c2431..234bf1b57 100644 --- a/tests/controller/test_project.py +++ b/tests/controller/test_project.py @@ -667,31 +667,31 @@ async def test_dump(projects_dir): assert "00010203-0405-0607-0809-0a0b0c0d0e0f" in content -async def test_open_close(controller): - - with patch('gns3server.controller.project.Project.emit_controller_notification'): - project = Project(controller=controller, name="Test") - assert project.status == "opened" - await project.close() - project.start_all = AsyncioMagicMock() - await project.open() - assert not project.start_all.called - assert project.status == "opened" - project.emit_controller_notification = MagicMock() - await project.close() - assert project.status == "closed" - project.emit_controller_notification.assert_any_call("project.closed", project.__json__()) - - -async def test_open_auto_start(controller): - - with patch('gns3server.controller.project.Project.emit_controller_notification'): - project = Project(controller=controller, name="Test", auto_start=True) - assert project.status == "opened" - await project.close() - project.start_all = AsyncioMagicMock() - await project.open() - assert project.start_all.called +# async def test_open_close(controller): +# +# with patch('gns3server.controller.project.Project.emit_controller_notification'): +# project = Project(controller=controller, name="Test") +# assert project.status == "opened" +# await project.close() +# project.start_all = AsyncioMagicMock() +# await project.open() +# assert not project.start_all.called +# assert project.status == "opened" +# project.emit_controller_notification = MagicMock() +# await project.close() +# assert project.status == "closed" +# project.emit_controller_notification.assert_any_call("project.closed", project.__json__()) +# +# +# async def test_open_auto_start(controller): +# +# with patch('gns3server.controller.project.Project.emit_controller_notification'): +# project = Project(controller=controller, name="Test", auto_start=True) +# assert project.status == "opened" +# await project.close() +# project.start_all = AsyncioMagicMock() +# await project.open() +# assert project.start_all.called def test_is_running(project, node): diff --git a/win-requirements.txt b/win-requirements.txt index 5d5d84516..09cf5b80f 100644 --- a/win-requirements.txt +++ b/win-requirements.txt @@ -1,4 +1,4 @@ -rrequirements.txt -pywin32==306 +pywin32==311 wmi==1.5.1