From f89d645b5631b96af7c4f6c2185ce8880373e4ef Mon Sep 17 00:00:00 2001 From: grossmj Date: Fri, 31 Aug 2018 19:33:03 +0700 Subject: [PATCH 01/18] Update appliances. --- gns3server/appliances/arista-ceos.gns3a | 19 ++++++ gns3server/appliances/bsdrp.gns3a | 15 +++++ gns3server/appliances/cisco-asav.gns3a | 13 ++++ gns3server/appliances/cisco-iosxrv9k.gns3a | 33 ++++++++-- gns3server/appliances/cumulus-vx.gns3a | 14 ++++ gns3server/appliances/exos.gns3a | 15 +++++ .../appliances/extreme-networks-voss.gns3a | 64 +++++++++++++++++++ gns3server/appliances/pfsense.gns3a | 1 - gns3server/appliances/tacacs-gui.gns3a | 43 +++++++++++++ 9 files changed, 212 insertions(+), 5 deletions(-) create mode 100644 gns3server/appliances/arista-ceos.gns3a create mode 100644 gns3server/appliances/extreme-networks-voss.gns3a create mode 100644 gns3server/appliances/tacacs-gui.gns3a diff --git a/gns3server/appliances/arista-ceos.gns3a b/gns3server/appliances/arista-ceos.gns3a new file mode 100644 index 00000000..3c7851eb --- /dev/null +++ b/gns3server/appliances/arista-ceos.gns3a @@ -0,0 +1,19 @@ +{ + "name": "cEOS", + "category": "multilayer_switch", + "description": "Arista cEOS\u2122 introduces the containerized packaging of EOS software and its agents for deployment in cloud infrastructure with the same proven EOS software image that runs on all Arista products. These flexible deployment options empower cloud network operators that are customizing their operating environments to provide a uniform workflow for development, testing and deployment of differentiated services.", + "vendor_name": "Arista", + "vendor_url": "http://www.arista.com/", + "product_name": "cEOS", + "registry_version": 3, + "status": "experimental", + "maintainer": "GNS3 Team", + "maintainer_email": "developers@gns3.net", + "usage": "Download:\nCreate a (free) Arista account and login.\nThen navigate to Support / Software Download and download the cEOS-lab image.\n\nInstallation:\nCopy the image to your GNS3VM (or other Linux) server, then run the following commands:\n\ndocker import cEOS-lab.tar.xz ceosimage:4.20.5F\necho \"rm /etc/systemd/system/getty.target.wants/getty@tty1.service\" | \\\ndocker run --name=ceos-container -e CEOS=1 -e container=docker -e EOS_PLATFORM=ceossim -e SKIP_ZEROTOUCH_BARRIER_IN_SYSDBINIT=1 -e ETBA=1 -e INTFTYPE=eth -i ceosimage:4.20.5F sh\ndocker commit --change='CMD [\"/sbin/init\"]' --change='VOLUME /mnt/flash' ceos-container ceosimage:GNS3\ndocker rm ceos-container\n\nUsage:\nThe login is admin, with no password by default", + "symbol": ":/symbols/multilayer_switch.svg", + "docker": { + "adapters": 8, + "image": "ceosimage:GNS3", + "console_type": "telnet" + } +} diff --git a/gns3server/appliances/bsdrp.gns3a b/gns3server/appliances/bsdrp.gns3a index d7319ea8..838441cf 100644 --- a/gns3server/appliances/bsdrp.gns3a +++ b/gns3server/appliances/bsdrp.gns3a @@ -19,6 +19,15 @@ "kvm": "allow" }, "images": [ + { + "filename": "BSDRP-1.90-full-amd64-serial.img", + "version": "1.90", + "md5sum": "78b7182bed93888d17d18a67d5fec3a7", + "filesize": 1000000000, + "download_url": "https://bsdrp.net/downloads", + "direct_download_url": "https://sourceforge.net/projects/bsdrp/files/BSD_Router_Project/1.90/amd64/BSDRP-1.90-full-amd64-serial.img.xz/download", + "compression": "xz" + }, { "filename": "BSDRP-1.80-full-amd64-serial.img", "version": "1.80", @@ -39,6 +48,12 @@ } ], "versions": [ + { + "name": "1.90", + "images": { + "hda_disk_image": "BSDRP-1.90-full-amd64-serial.img" + } + }, { "name": "1.80", "images": { diff --git a/gns3server/appliances/cisco-asav.gns3a b/gns3server/appliances/cisco-asav.gns3a index b61e6405..9d45fb46 100644 --- a/gns3server/appliances/cisco-asav.gns3a +++ b/gns3server/appliances/cisco-asav.gns3a @@ -25,6 +25,13 @@ "kvm": "require" }, "images": [ + { + "filename": "asav992.qcow2", + "version": "9.9.2", + "md5sum": "0cba453dbf70313d8d63a00700618f52", + "filesize": 204865536, + "download_url": "https://software.cisco.com/download/home/286119613/type/280775065/release/9.9.2" + }, { "filename": "asav983.qcow2", "version": "9.8.3", @@ -104,6 +111,12 @@ } ], "versions": [ + { + "name": "9.9.2", + "images": { + "hda_disk_image": "asav992.qcow2" + } + }, { "name": "9.8.3", "images": { diff --git a/gns3server/appliances/cisco-iosxrv9k.gns3a b/gns3server/appliances/cisco-iosxrv9k.gns3a index 8025c734..8618d0e8 100644 --- a/gns3server/appliances/cisco-iosxrv9k.gns3a +++ b/gns3server/appliances/cisco-iosxrv9k.gns3a @@ -11,19 +11,32 @@ "status": "experimental", "maintainer": "GNS3 Team", "maintainer_email": "developers@gns3.net", - "usage": "Default username/password: admin/admin, cisco/cisco and lab/lab. There is no default configuration present.", + "usage": "Default username/password: admin/admin, cisco/cisco and lab/lab. There is no default configuration present. Interfaces may take several minutes to be usable after appliance boot.", "first_port_name": "MgmtEth0/0/CPU0/0", "port_name_format": "GigabitEthernet0/0/0/{0}", "qemu": { - "adapter_type": "e1000", - "adapters": 4, + "adapter_type": "virtio-net-pci", + "adapters": 7, "ram": 16384, "arch": "x86_64", "console_type": "telnet", "kvm": "require", - "options": "-smp 4" + "options": "-smp 4 -cpu host" }, "images": [ + { + "filename": "xrv9k-fullk9-x-6.4.2.qcow2", + "version": "6.4.2", + "md5sum": "6958763192c7bb59a1b8049d377de1b4", + "filesize": 1311703040, + "download_url": "https://software.cisco.com/download/home/286288939/type/280805694/release/6.4.2" + }, + { "filename": "xrv9k-fullk9-x-6.4.1.qcow2", + "version": "6.4.1", + "md5sum": "9c56b684e307706005a503e289cb9317", + "filesize": 1304887296, + "download_url": "https://software.cisco.com/download/home/286288939/type/280805694/release/6.4.1" + }, { "filename": "xrv9k-fullk9-x-6.2.25.qcow2", "version": "6.2.25", @@ -47,6 +60,18 @@ } ], "versions": [ + { + "name": "6.4.2", + "images": { + "hda_disk_image": "xrv9k-fullk9-x-6.4.2.qcow2" + } + }, + { + "name": "6.4.1", + "images": { + "hda_disk_image": "xrv9k-fullk9-x-6.4.1.qcow2" + } + }, { "name": "6.2.25", "images": { diff --git a/gns3server/appliances/cumulus-vx.gns3a b/gns3server/appliances/cumulus-vx.gns3a index ffed05fd..e5baedfa 100644 --- a/gns3server/appliances/cumulus-vx.gns3a +++ b/gns3server/appliances/cumulus-vx.gns3a @@ -23,6 +23,14 @@ "kvm": "require" }, "images": [ + { + "filename": "cumulus-linux-3.6.2-vx-amd64.qcow2", + "version": "3.6.2", + "md5sum": "02b0621d68fb2e709b4bcc48748c7b6f", + "filesize": 1150746624, + "download_url": "https://cumulusnetworks.com/cumulus-vx/download/", + "direct_download_url": "https://s3.amazonaws.com/cumulusfiles/CumulusLinux-3.6.2/cumulus-linux-3.6.2-vx-amd64.qcow2" + }, { "filename": "cumulus-linux-3.6.1-vx-amd64.qcow2", "version": "3.6.1", @@ -165,6 +173,12 @@ } ], "versions": [ + { + "name": "3.6.2", + "images": { + "hda_disk_image": "cumulus-linux-3.6.2-vx-amd64.qcow2" + } + }, { "name": "3.6.1", "images": { diff --git a/gns3server/appliances/exos.gns3a b/gns3server/appliances/exos.gns3a index 3f09c669..2aa8522d 100644 --- a/gns3server/appliances/exos.gns3a +++ b/gns3server/appliances/exos.gns3a @@ -26,6 +26,14 @@ "options": "-smp 2 -cpu core2duo" }, "images": [ + { + "filename": "exosvm-22.5.1.7.iso", + "version": "22.5.1.7", + "md5sum": "132ac87de368ab55d1f496f292338cd4", + "filesize": 51779584, + "download_url": "https://github.com/extremenetworks/Virtual_EXOS", + "direct_download_url": "https://github.com/extremenetworks/Virtual_EXOS/raw/master/vm-22.5.1.7.iso" + }, { "filename": "exosvm-22.4.1.4.iso", "version": "22.4.1.4", @@ -100,6 +108,13 @@ } ], "versions": [ + { + "name": "22.5.1.7", + "images": { + "hda_disk_image": "empty8G.qcow2", + "cdrom_image": "exosvm-22.5.1.7.iso" + } + }, { "name": "22.4.1.4", "images": { diff --git a/gns3server/appliances/extreme-networks-voss.gns3a b/gns3server/appliances/extreme-networks-voss.gns3a new file mode 100644 index 00000000..ff15d60b --- /dev/null +++ b/gns3server/appliances/extreme-networks-voss.gns3a @@ -0,0 +1,64 @@ +{ + "name": "VOSS VM", + "category": "multilayer_switch", + "description": "The VOSS VM is a software emulation of a VSP8K switch.", + "vendor_name": "Extreme Networks", + "vendor_url": "http://www.extremenetworks.com", + "documentation_url": "http://www.extremenetworks.com/support/documentation", + "product_name": "VOSS_VM", + "registry_version": 3, + "status": "experimental", + "maintainer": "Extreme Networks", + "maintainer_email": "voss@extremenetworks.com", + "usage": "Boot up and login is rwa/rwa", + "symbol": "ethernet_switch.svg", + "first_port_name": "Mgmt", + "port_name_format": "1/{port1}", + "qemu": + { + "adapter_type": "e1000", + "adapters": 9, + "ram": 1024, + "hda_disk_interface": "ide", + "arch": "i386", + "console_type": "telnet", + "kvm": "allow", + "options": "-nographic" + }, + + "images": + [ + { + "filename": "VOSSGNS3.7.1.0.0.qcow2", + "version": "voss_7.1.0.0", + "md5sum": "7bb2974efe1a1ab857debd9fa894dbe7", + "filesize": 193724416, + "direct_download_url": "https://stackingtool.extremenetworks.com/github/VOSSGNS3.7.1.0.0.qcow2" + }, + { + "filename": "VOSSGNS3.7.0.0.0.img", + "version": "voss_7.0.0.0", + "md5sum": "65fe97461156aa88c836a90be1287649", + "filesize": 419430400, + "direct_download_url": "https://stackingtool.extremenetworks.com/github/VOSSGNS3.7.0.0.0.img" + } + ], + + "versions": + [ + { + "name": "7.1.0.0", + "images": + { + "hda_disk_image": "VOSSGNS3.7.1.0.0.qcow2" + } + }, + { + "name": "7.0.0.0", + "images": { + "hda_disk_image": "VOSSGNS3.7.0.0.0.img" + } + } + ] + +} diff --git a/gns3server/appliances/pfsense.gns3a b/gns3server/appliances/pfsense.gns3a index 2c521949..8b14db06 100644 --- a/gns3server/appliances/pfsense.gns3a +++ b/gns3server/appliances/pfsense.gns3a @@ -17,7 +17,6 @@ "ram": 2048, "arch": "x86_64", "console_type": "telnet", - "boot_priority": "dc", "kvm": "allow", "process_priority": "normal" }, diff --git a/gns3server/appliances/tacacs-gui.gns3a b/gns3server/appliances/tacacs-gui.gns3a new file mode 100644 index 00000000..c135df72 --- /dev/null +++ b/gns3server/appliances/tacacs-gui.gns3a @@ -0,0 +1,43 @@ +{ + "name": "TacacsGUI", + "category": "guest", + "description": "TacacsGUI Free Access Control Server for Your Network Devices. GUI for powerful daemon. The project of Alexey Mochalin, based on tacacs daemon by Marc Huber", + "vendor_name": "TacacsGUI", + "vendor_url": "https://tacacsgui.com/", + "documentation_url": "https://tacacsgui.com/documentation/", + "product_name": "TacacsGUI", + "product_url": "https://drive.google.com/open?id=1U8tbj14NqEyCmarayhZm54qTyjgsJm4B", + "registry_version": 3, + "status": "stable", + "maintainer": "GNS3 Team", + "maintainer_email": "developers@gns3.net", + "usage": "Credentials: SSH ---> username: root ---> password: 1234 MySQL DB: ---> username: root --> password: tacacs Web interface: ---> username: tacgui ---> password: abc123", + "port_name_format": "Port{port1}", + "qemu": { + "adapter_type": "e1000", + "adapters": 1, + "ram": 1024, + "hda_disk_interface": "ide", + "arch": "x86_64", + "console_type": "telnet", + "boot_priority": "c", + "kvm": "allow" + }, + "images": [ + { + "filename": "tac_plus.qcow2", + "version": "201710201114", + "md5sum": "6b5e66590051124dae586b8640b2eb11", + "filesize": 160301056, + "download_url": "https://drive.google.com/open?id=1U8tbj14NqEyCmarayhZm54qTyjgsJm4B" + } + ], + "versions": [ + { + "name": "201710201114", + "images": { + "hda_disk_image": "tac_plus.qcow2" + } + } + ] +} From 4a6202fa84153bb7c2369720039e2f604e8e1dbf Mon Sep 17 00:00:00 2001 From: grossmj Date: Sun, 2 Sep 2018 15:32:33 +0700 Subject: [PATCH 02/18] Update setup.py and fix minor issues. --- gns3server/compute/dynamips/nodes/ethernet_switch.py | 6 ++++-- setup.py | 8 ++++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/gns3server/compute/dynamips/nodes/ethernet_switch.py b/gns3server/compute/dynamips/nodes/ethernet_switch.py index 594b3cb5..9f66b4d2 100644 --- a/gns3server/compute/dynamips/nodes/ethernet_switch.py +++ b/gns3server/compute/dynamips/nodes/ethernet_switch.py @@ -87,6 +87,7 @@ class EthernetSwitch(Device): self._mappings = {} self._telnet_console = None self._telnet_shell = None + self._telnet_server = None self._console = self._manager.port_manager.get_free_tcp_port(self._project) if ports is None: # create 8 ports by default @@ -217,8 +218,9 @@ class EthernetSwitch(Device): Deletes this Ethernet switch. """ yield from self._telnet.close() - self._telnet_server.close() - + if self._telnet_server: + self._telnet_server.close() + for nio in self._nios.values(): if nio: yield from nio.close() diff --git a/setup.py b/setup.py index 02663755..46f9b2cb 100644 --- a/setup.py +++ b/setup.py @@ -62,17 +62,21 @@ setup( zip_safe=False, platforms="any", classifiers=[ - "Development Status :: 4 - Beta", + "Development Status :: 5 - Production/Stable", "Environment :: Console", "Intended Audience :: Information Technology", "Topic :: System :: Networking", "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", "Natural Language :: English", - "Operating System :: OS Independent", + "Operating System :: POSIX", + "Operating System :: MacOS :: MacOS X", + "Operating System :: Microsoft :: Windows", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", "Programming Language :: Python :: Implementation :: CPython", ], ) From abfbadecd6aabdeb1ad94b31f57825b7cff146bb Mon Sep 17 00:00:00 2001 From: grossmj Date: Sun, 2 Sep 2018 20:41:45 +0700 Subject: [PATCH 03/18] Update aiohttp verion requirement in order to support Python 3.7. Fixes https://github.com/GNS3/gns3-gui/issues/2566 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 8f13ef5d..edd65a31 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ jsonschema>=2.4.0 -aiohttp>=2.2.0,<2.4.0 # pyup: ignore +aiohttp>=2.3.3,<2.4.0 # pyup: ignore aiohttp-cors>=0.5.3,<0.6.0 # pyup: ignore yarl>=0.11 Jinja2>=2.7.3 From c620d0be841d03a3b55fba90cf3e04ab0d32c809 Mon Sep 17 00:00:00 2001 From: grossmj Date: Wed, 5 Sep 2018 13:35:42 +0800 Subject: [PATCH 04/18] Improve the invalid port format detection. Fixes https://github.com/GNS3/gns3-gui/issues/2580 --- gns3server/controller/ports/port_factory.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gns3server/controller/ports/port_factory.py b/gns3server/controller/ports/port_factory.py index 35d66b41..53d1d821 100644 --- a/gns3server/controller/ports/port_factory.py +++ b/gns3server/controller/ports/port_factory.py @@ -72,7 +72,7 @@ class StandardPortFactory: segment_number, adapter=adapter_number, **cls._generate_replacement(interface_number, segment_number)) - except (ValueError, KeyError) as e: + except (IndexError, ValueError, KeyError) as e: raise aiohttp.web.HTTPConflict(text="Invalid port name format {}: {}".format(port_name_format, str(e))) port = PortFactory(port_name, segment_number, adapter_number, port_number, "ethernet") interface_number += 1 From f8ecd61a9890907678feafbe672a661d225eb820 Mon Sep 17 00:00:00 2001 From: grossmj Date: Wed, 5 Sep 2018 15:16:07 +0800 Subject: [PATCH 05/18] Notify users if xvfb process or x11vnc process have crashed. Ref #1401. --- gns3server/compute/docker/docker_vm.py | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/gns3server/compute/docker/docker_vm.py b/gns3server/compute/docker/docker_vm.py index 8db488f8..852483d6 100644 --- a/gns3server/compute/docker/docker_vm.py +++ b/gns3server/compute/docker/docker_vm.py @@ -30,6 +30,7 @@ from gns3server.utils.asyncio.telnet_server import AsyncioTelnetServer from gns3server.utils.asyncio.raw_command_server import AsyncioRawCommandServer from gns3server.utils.asyncio import wait_for_file_creation from gns3server.utils.asyncio import asyncio_ensure_future +from gns3server.utils.asyncio import monitor_process from gns3server.utils.get_resource import get_resource from gns3server.ubridge.ubridge_error import UbridgeError, UbridgeNamespaceError @@ -505,7 +506,7 @@ class DockerVM(BaseNode): self._display = self._get_free_display_port() if shutil.which("Xvfb") is None or shutil.which("x11vnc") is None: - raise DockerError("Please install Xvfb and x11vnc before using the VNC support") + raise DockerError("Please install Xvfb and x11vnc before using VNC support") self._xvfb_process = yield from asyncio.create_subprocess_exec("Xvfb", "-nolisten", "tcp", ":{}".format(self._display), "-screen", "0", self._console_resolution + "x16") # We pass a port for TCPV6 due to a crash in X11VNC if not here: https://github.com/GNS3/gns3-server/issues/569 self._x11vnc_process = yield from asyncio.create_subprocess_exec("x11vnc", "-forever", "-nopw", "-shared", "-geometry", self._console_resolution, "-display", "WAIT:{}".format(self._display), "-rfbport", str(self.console), "-rfbportv6", str(self.console), "-noncache", "-listen", self._manager.port_manager.console_host) @@ -513,6 +514,29 @@ class DockerVM(BaseNode): x11_socket = os.path.join("/tmp/.X11-unix/", "X{}".format(self._display)) yield from wait_for_file_creation(x11_socket) + monitor_process(self._xvfb_process, self._xvfb_callback) + monitor_process(self._x11vnc_process, self._x11vnc_callback) + + def _xvfb_callback(self, returncode): + """ + Called when the process has stopped. + + :param returncode: Process returncode + """ + + if returncode != 0: + self.project.emit("log.error", {"message": "The Xvfb process has stopped, return code: {}.".format(returncode)}) + + def _x11vnc_callback(self, returncode): + """ + Called when the process has stopped. + + :param returncode: Process returncode + """ + + if returncode != 0: + self.project.emit("log.error", {"message": "The x11vnc process has stopped, return code: {}.".format(returncode)}) + @asyncio.coroutine def _start_http(self): """ From 341e2e2e3a5547fe2ad3e083230ef9127584eff3 Mon Sep 17 00:00:00 2001 From: grossmj Date: Thu, 6 Sep 2018 07:02:32 +0200 Subject: [PATCH 06/18] Check if serial pipe can be opened for VMware and VirtualBox VMs. --- gns3server/compute/virtualbox/virtualbox_vm.py | 7 ++++++- gns3server/compute/vmware/vmware_vm.py | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/gns3server/compute/virtualbox/virtualbox_vm.py b/gns3server/compute/virtualbox/virtualbox_vm.py index caefb0c9..a3611458 100644 --- a/gns3server/compute/virtualbox/virtualbox_vm.py +++ b/gns3server/compute/virtualbox/virtualbox_vm.py @@ -938,7 +938,12 @@ class VirtualBoxVM(BaseNode): """ Starts remote console support for this VM. """ - self._remote_pipe = yield from asyncio_open_serial(self._get_pipe_name()) + + pipe_name = self._get_pipe_name() + try: + self._remote_pipe = yield from asyncio_open_serial(pipe_name) + except OSError as e: + raise VirtualBoxError("Could not open serial pipe '{}': {}".format(pipe_name, e)) server = AsyncioTelnetServer(reader=self._remote_pipe, writer=self._remote_pipe, binary=True, diff --git a/gns3server/compute/vmware/vmware_vm.py b/gns3server/compute/vmware/vmware_vm.py index 15fac4f5..64a26e50 100644 --- a/gns3server/compute/vmware/vmware_vm.py +++ b/gns3server/compute/vmware/vmware_vm.py @@ -845,7 +845,12 @@ class VMwareVM(BaseNode): """ Starts remote console support for this VM. """ - self._remote_pipe = yield from asyncio_open_serial(self._get_pipe_name()) + + pipe_name = self._get_pipe_name() + try: + self._remote_pipe = yield from asyncio_open_serial(self._get_pipe_name()) + except OSError as e: + raise VMwareError("Could not open serial pipe '{}': {}".format(pipe_name, e)) server = AsyncioTelnetServer(reader=self._remote_pipe, writer=self._remote_pipe, binary=True, From 4021a13651ecf02fe02a9a2e9181c6d8e3eb32f7 Mon Sep 17 00:00:00 2001 From: grossmj Date: Thu, 6 Sep 2018 09:49:12 +0200 Subject: [PATCH 07/18] Catch exceptions in various locations to fix small issues reported by Sentry. --- gns3server/compute/docker/docker_vm.py | 70 +++++++++++++---------- gns3server/compute/qemu/qemu_vm.py | 2 +- gns3server/controller/__init__.py | 4 +- gns3server/controller/node.py | 2 +- gns3server/controller/project.py | 2 +- gns3server/utils/asyncio/telnet_server.py | 6 +- gns3server/utils/ping_stats.py | 12 +++- gns3server/web/route.py | 5 ++ 8 files changed, 63 insertions(+), 40 deletions(-) diff --git a/gns3server/compute/docker/docker_vm.py b/gns3server/compute/docker/docker_vm.py index 852483d6..7c07bcaa 100644 --- a/gns3server/compute/docker/docker_vm.py +++ b/gns3server/compute/docker/docker_vm.py @@ -228,11 +228,13 @@ class DockerVM(BaseNode): binds = ["{}:/gns3:ro".format(ressources)] # We mount our own etc/network - network_config = self._create_network_config() + try: + network_config = self._create_network_config() + except OSError as e: + raise DockerError("Could not create network config in the container: {}".format(e)) binds.append("{}:/gns3volumes/etc/network:rw".format(network_config)) self._volumes = ["/etc/network"] - volumes = image_infos.get("Config", {}).get("Volumes") if volumes is None: return binds @@ -285,11 +287,12 @@ class DockerVM(BaseNode): try: image_infos = yield from self._get_image_information() except DockerHttp404Error: - log.info("Image %s is missing pulling it from docker hub", self._image) + log.info("Image '{}' is missing, pulling it from Docker hub...".format(self._image)) yield from self.pull_image(self._image) image_infos = yield from self._get_image_information() - if image_infos is None: - raise DockerError("Can't get image informations, please try again.") + + if image_infos is None: + raise DockerError("Cannot get information for image '{}', please try again.".format(self._image)) params = { "Hostname": self._name, @@ -313,7 +316,10 @@ class DockerVM(BaseNode): if params["Entrypoint"] is None: params["Entrypoint"] = [] if self._start_command: - params["Cmd"] = shlex.split(self._start_command) + try: + params["Cmd"] = shlex.split(self._start_command) + except ValueError as e: + raise DockerError("Invalid start command '{}': {}".format(self._start_command, e)) if len(params["Cmd"]) == 0: params["Cmd"] = image_infos.get("Config", {"Cmd": []})["Cmd"] if params["Cmd"] is None: @@ -355,8 +361,7 @@ class DockerVM(BaseNode): result = yield from self.manager.query("POST", "containers/create", data=params) self._cid = result['Id'] - log.info("Docker container '{name}' [{id}] created".format( - name=self._name, id=self._id)) + log.info("Docker container '{name}' [{id}] created".format(name=self._name, id=self._id)) return True def _format_env(self, variables, env): @@ -449,16 +454,19 @@ class DockerVM(BaseNode): @asyncio.coroutine def _start_aux(self): """ - Start an auxilary console + Start an auxiliary console """ # We can not use the API because docker doesn't expose a websocket api for exec # https://github.com/GNS3/gns3-gui/issues/1039 - process = yield from asyncio.subprocess.create_subprocess_exec( - "docker", "exec", "-i", self._cid, "/gns3/bin/busybox", "script", "-qfc", "while true; do TERM=vt100 /gns3/bin/busybox sh; done", "/dev/null", - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.STDOUT, - stdin=asyncio.subprocess.PIPE) + try: + process = yield from asyncio.subprocess.create_subprocess_exec( + "docker", "exec", "-i", self._cid, "/gns3/bin/busybox", "script", "-qfc", "while true; do TERM=vt100 /gns3/bin/busybox sh; done", "/dev/null", + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.STDOUT, + stdin=asyncio.subprocess.PIPE) + except OSError as e: + raise DockerError("Could not start auxiliary console process: {}".format(e)) server = AsyncioTelnetServer(reader=process.stdout, writer=process.stdin, binary=True, echo=True) try: self._telnet_servers.append((yield from asyncio.start_server(server.run, self._manager.port_manager.console_host, self.aux))) @@ -481,21 +489,25 @@ class DockerVM(BaseNode): for volume in self._volumes: log.debug("Docker container '{name}' [{image}] fix ownership on {path}".format( name=self._name, image=self._image, path=volume)) - process = yield from asyncio.subprocess.create_subprocess_exec( - "docker", - "exec", - self._cid, - "/gns3/bin/busybox", - "sh", - "-c", - "(" - "/gns3/bin/busybox find \"{path}\" -depth -print0" - " | /gns3/bin/busybox xargs -0 /gns3/bin/busybox stat -c '%a:%u:%g:%n' > \"{path}/.gns3_perms\"" - ")" - " && /gns3/bin/busybox chmod -R u+rX \"{path}\"" - " && /gns3/bin/busybox chown {uid}:{gid} -R \"{path}\"" - .format(uid=os.getuid(), gid=os.getgid(), path=volume), - ) + + try: + process = yield from asyncio.subprocess.create_subprocess_exec( + "docker", + "exec", + self._cid, + "/gns3/bin/busybox", + "sh", + "-c", + "(" + "/gns3/bin/busybox find \"{path}\" -depth -print0" + " | /gns3/bin/busybox xargs -0 /gns3/bin/busybox stat -c '%a:%u:%g:%n' > \"{path}/.gns3_perms\"" + ")" + " && /gns3/bin/busybox chmod -R u+rX \"{path}\"" + " && /gns3/bin/busybox chown {uid}:{gid} -R \"{path}\"" + .format(uid=os.getuid(), gid=os.getgid(), path=volume), + ) + except OSError as e: + raise DockerError("Could not fix permissions for {}: {}".format(volume, e)) yield from process.wait() @asyncio.coroutine diff --git a/gns3server/compute/qemu/qemu_vm.py b/gns3server/compute/qemu/qemu_vm.py index 894cad8c..16c9ebdf 100644 --- a/gns3server/compute/qemu/qemu_vm.py +++ b/gns3server/compute/qemu/qemu_vm.py @@ -1028,7 +1028,7 @@ class QemuVM(BaseNode): if expect in line: result = line.decode("utf-8").strip() break - except EOFError as e: + except (ConnectionError, EOFError) as e: log.warn("Could not read from QEMU monitor: {}".format(e)) writer.close() return result diff --git a/gns3server/controller/__init__.py b/gns3server/controller/__init__.py index 5467c4ba..e2e75370 100644 --- a/gns3server/controller/__init__.py +++ b/gns3server/controller/__init__.py @@ -69,7 +69,7 @@ class Controller: for directory, builtin in ( (get_resource('appliances'), True,), (self.appliances_path(), False,) ): - if os.path.isdir(directory): + if directory and os.path.isdir(directory): for file in os.listdir(directory): if not file.endswith('.gns3a') and not file.endswith('.gns3appliance'): continue @@ -509,7 +509,7 @@ class Controller: return self._computes[compute_id] except KeyError: if compute_id == "vm": - raise aiohttp.web.HTTPNotFound(text="You try to use a node on the GNS3 VM server but the GNS3 VM is not configured") + raise aiohttp.web.HTTPNotFound(text="A node is set to use the GNS3 VM server but the GNS3 VM is not configured") raise aiohttp.web.HTTPNotFound(text="Compute ID {} doesn't exist".format(compute_id)) def has_compute(self, compute_id): diff --git a/gns3server/controller/node.py b/gns3server/controller/node.py index f496e3d9..2226484a 100644 --- a/gns3server/controller/node.py +++ b/gns3server/controller/node.py @@ -177,7 +177,7 @@ class Node: try: with open(path) as f: return f.read() - except (PermissionError, OSError): + except OSError: return None @property diff --git a/gns3server/controller/project.py b/gns3server/controller/project.py index b067806f..6a6446ba 100644 --- a/gns3server/controller/project.py +++ b/gns3server/controller/project.py @@ -871,7 +871,7 @@ class Project: try: if os.path.exists(path + ".backup"): shutil.copy(path + ".backup", path) - except (PermissionError, OSError): + except OSError: pass self._status = "closed" self._loading = False diff --git a/gns3server/utils/asyncio/telnet_server.py b/gns3server/utils/asyncio/telnet_server.py index 604ab914..abed2e06 100644 --- a/gns3server/utils/asyncio/telnet_server.py +++ b/gns3server/utils/asyncio/telnet_server.py @@ -197,11 +197,9 @@ class AsyncioTelnetServer: yield from self._write_intro(network_writer, echo=self._echo, binary=self._binary, naws=self._naws) yield from connection.connected() yield from self._process(network_reader, network_writer, connection) - except ConnectionResetError: + except ConnectionError: with (yield from self._lock): - network_writer.close() - if self._reader_process == network_reader: self._reader_process = None # Cancel current read from this reader @@ -217,7 +215,7 @@ class AsyncioTelnetServer: try: writer.write_eof() yield from writer.drain() - except ConnectionResetError: + except ConnectionError: continue @asyncio.coroutine diff --git a/gns3server/utils/ping_stats.py b/gns3server/utils/ping_stats.py index 3252f9c2..88734793 100644 --- a/gns3server/utils/ping_stats.py +++ b/gns3server/utils/ping_stats.py @@ -43,8 +43,16 @@ class PingStats: cur_time > cls._last_measurement + 1.9: cls._last_measurement = cur_time # Non blocking call to get cpu usage. First call will return 0 - cls._last_cpu_percent = psutil.cpu_percent(interval=None) - cls._last_mem_percent = psutil.virtual_memory().percent + try: + cls._last_cpu_percent = psutil.cpu_percent(interval=None) + cls._last_mem_percent = psutil.virtual_memory().percent + except RuntimeError: + # ignore the following error: + # RuntimeError: host_statistics(HOST_CPU_LOAD_INFO) syscall failed: (ipc/send) invalid reply port + pass + except PermissionError: + # [Errno 13] Permission denied: '/proc/stat' + pass stats["cpu_usage_percent"] = cls._last_cpu_percent stats["memory_usage_percent"] = cls._last_mem_percent return stats diff --git a/gns3server/web/route.py b/gns3server/web/route.py index 97adbf4a..9ceda349 100644 --- a/gns3server/web/route.py +++ b/gns3server/web/route.py @@ -225,6 +225,11 @@ class Route(object): response = Response(request=request, route=route) response.set_status(408) response.json({"message": "Client error", "status": 408}) + except MemoryError: + log.error("Memory error detected, server has run out of memory!", exc_info=1) + response = Response(request=request, route=route) + response.set_status(500) + response.json({"message": "Memory error", "status": 500}) except Exception as e: log.error("Uncaught exception detected: {type}".format(type=type(e)), exc_info=1) response = Response(request=request, route=route) From 0aa9ab53d19ef6afa434313774e093d53a9ff33f Mon Sep 17 00:00:00 2001 From: grossmj Date: Fri, 7 Sep 2018 09:34:17 +0200 Subject: [PATCH 08/18] Import encodings.idna to avoid LookupError when standard library is in a zip file. --- gns3server/web/web_server.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/gns3server/web/web_server.py b/gns3server/web/web_server.py index 25427af4..80cd2bbe 100644 --- a/gns3server/web/web_server.py +++ b/gns3server/web/web_server.py @@ -29,6 +29,10 @@ import functools import time import atexit +# Import encoding now, to avoid implicit import later. +# Implicit import within threads may cause LookupError when standard library is in a ZIP +import encodings.idna + from .route import Route from ..config import Config from ..compute import MODULES From 17d657c919d76ef93311ebb5d784bd8be19c9491 Mon Sep 17 00:00:00 2001 From: grossmj Date: Tue, 11 Sep 2018 15:06:01 +0200 Subject: [PATCH 09/18] Fix small errors like unhandled exceptions etc. --- gns3server/compute/builtin/nodes/cloud.py | 2 +- gns3server/compute/docker/docker_vm.py | 4 ++-- gns3server/compute/iou/iou_vm.py | 4 ++-- gns3server/compute/qemu/__init__.py | 4 ++-- .../compute/virtualbox/virtualbox_vm.py | 3 +++ gns3server/controller/__init__.py | 6 ++---- gns3server/controller/node.py | 2 +- gns3server/controller/project.py | 2 +- gns3server/controller/symbols.py | 19 ++++++++++++++----- gns3server/controller/topology.py | 8 ++++---- .../handlers/api/controller/symbol_handler.py | 7 ++++--- gns3server/ubridge/hypervisor.py | 2 +- 12 files changed, 37 insertions(+), 26 deletions(-) diff --git a/gns3server/compute/builtin/nodes/cloud.py b/gns3server/compute/builtin/nodes/cloud.py index 31796964..53254ca6 100644 --- a/gns3server/compute/builtin/nodes/cloud.py +++ b/gns3server/compute/builtin/nodes/cloud.py @@ -163,7 +163,7 @@ class Cloud(BaseNode): try: output = yield from gns3server.utils.asyncio.subprocess_check_output("networksetup", "-listallhardwareports") - except (FileNotFoundError, subprocess.SubprocessError) as e: + except (OSError, subprocess.SubprocessError) as e: log.warn("Could not execute networksetup: {}".format(e)) return False diff --git a/gns3server/compute/docker/docker_vm.py b/gns3server/compute/docker/docker_vm.py index 7c07bcaa..ad24a987 100644 --- a/gns3server/compute/docker/docker_vm.py +++ b/gns3server/compute/docker/docker_vm.py @@ -526,8 +526,8 @@ class DockerVM(BaseNode): x11_socket = os.path.join("/tmp/.X11-unix/", "X{}".format(self._display)) yield from wait_for_file_creation(x11_socket) - monitor_process(self._xvfb_process, self._xvfb_callback) - monitor_process(self._x11vnc_process, self._x11vnc_callback) + #monitor_process(self._xvfb_process, self._xvfb_callback) + #monitor_process(self._x11vnc_process, self._x11vnc_callback) def _xvfb_callback(self, returncode): """ diff --git a/gns3server/compute/iou/iou_vm.py b/gns3server/compute/iou/iou_vm.py index 852a05f3..3d5de28b 100644 --- a/gns3server/compute/iou/iou_vm.py +++ b/gns3server/compute/iou/iou_vm.py @@ -368,7 +368,7 @@ class IOUVM(BaseNode): try: output = yield from gns3server.utils.asyncio.subprocess_check_output("ldd", self._path) - except (FileNotFoundError, subprocess.SubprocessError) as e: + except (OSError, subprocess.SubprocessError) as e: log.warn("Could not determine the shared library dependencies for {}: {}".format(self._path, e)) return @@ -421,7 +421,7 @@ class IOUVM(BaseNode): hostid = (yield from gns3server.utils.asyncio.subprocess_check_output("hostid")).strip() except FileNotFoundError as e: raise IOUError("Could not find hostid: {}".format(e)) - except subprocess.SubprocessError as e: + except (OSError, subprocess.SubprocessError) as e: raise IOUError("Could not execute hostid: {}".format(e)) try: diff --git a/gns3server/compute/qemu/__init__.py b/gns3server/compute/qemu/__init__.py index fd06605c..4608fdd4 100644 --- a/gns3server/compute/qemu/__init__.py +++ b/gns3server/compute/qemu/__init__.py @@ -193,7 +193,7 @@ class Qemu(BaseManager): return version else: raise QemuError("Could not determine the Qemu version for {}".format(qemu_path)) - except subprocess.SubprocessError as e: + except (OSError, subprocess.SubprocessError) as e: raise QemuError("Error while looking for the Qemu version: {}".format(e)) @staticmethod @@ -213,7 +213,7 @@ class Qemu(BaseManager): return version else: raise QemuError("Could not determine the Qemu-img version for {}".format(qemu_img_path)) - except subprocess.SubprocessError as e: + except (OSError, subprocess.SubprocessError) as e: raise QemuError("Error while looking for the Qemu-img version: {}".format(e)) @staticmethod diff --git a/gns3server/compute/virtualbox/virtualbox_vm.py b/gns3server/compute/virtualbox/virtualbox_vm.py index a3611458..af243f01 100644 --- a/gns3server/compute/virtualbox/virtualbox_vm.py +++ b/gns3server/compute/virtualbox/virtualbox_vm.py @@ -217,6 +217,8 @@ class VirtualBoxVM(BaseNode): except ET.ParseError: raise VirtualBoxError("Cannot modify VirtualBox linked nodes file. " "File {} is corrupted.".format(self._linked_vbox_file())) + except OSError as e: + raise VirtualBoxError("Cannot modify VirtualBox linked nodes file '{}': {}".format(self._linked_vbox_file(), e)) machine = tree.getroot().find("{http://www.virtualbox.org/}Machine") if machine is not None and machine.get("uuid") != "{" + self.id + "}": @@ -245,6 +247,7 @@ class VirtualBoxVM(BaseNode): return True return False + @locking @asyncio.coroutine def start(self): """ diff --git a/gns3server/controller/__init__.py b/gns3server/controller/__init__.py index e2e75370..958cdef7 100644 --- a/gns3server/controller/__init__.py +++ b/gns3server/controller/__init__.py @@ -66,9 +66,7 @@ class Controller: def load_appliances(self): self._appliance_templates = {} - for directory, builtin in ( - (get_resource('appliances'), True,), (self.appliances_path(), False,) - ): + for directory, builtin in ((get_resource('appliances'), True,), (self.appliances_path(), False,)): if directory and os.path.isdir(directory): for file in os.listdir(directory): if not file.endswith('.gns3a') and not file.endswith('.gns3appliance'): @@ -200,7 +198,7 @@ class Controller: for c in computes: try: yield from self.add_compute(**c) - except (aiohttp.web.HTTPConflict, KeyError): + except (aiohttp.web.HTTPError, KeyError): pass # Skip not available servers at loading yield from self.load_projects() try: diff --git a/gns3server/controller/node.py b/gns3server/controller/node.py index 2226484a..38bf753d 100644 --- a/gns3server/controller/node.py +++ b/gns3server/controller/node.py @@ -175,7 +175,7 @@ class Node: if not os.path.isabs(path): path = os.path.join(self.project.controller.configs_path(), path) try: - with open(path) as f: + with open(path, encoding="utf-8") as f: return f.read() except OSError: return None diff --git a/gns3server/controller/project.py b/gns3server/controller/project.py index 6a6446ba..a8632a46 100644 --- a/gns3server/controller/project.py +++ b/gns3server/controller/project.py @@ -843,7 +843,7 @@ class Project: link = yield from self.add_link(link_id=link_data["link_id"]) if "filters" in link_data: yield from link.update_filters(link_data["filters"]) - for node_link in link_data["nodes"]: + for node_link in link_data.get("nodes", []): node = self.get_node(node_link["node_id"]) port = node.get_port(node_link["adapter_number"], node_link["port_number"]) if port is None: diff --git a/gns3server/controller/symbols.py b/gns3server/controller/symbols.py index 63da9225..ce7e0268 100644 --- a/gns3server/controller/symbols.py +++ b/gns3server/controller/symbols.py @@ -22,6 +22,9 @@ from ..utils.get_resource import get_resource from ..utils.picture import get_size from ..config import Config +import logging +log = logging.getLogger(__name__) + class Symbols: """ @@ -72,19 +75,25 @@ class Symbols: def symbols_path(self): directory = os.path.expanduser(Config.instance().get_section_config("Server").get("symbols_path", "~/GNS3/symbols")) if directory: - os.makedirs(directory, exist_ok=True) + try: + os.makedirs(directory, exist_ok=True) + except OSError as e: + log.error("Could not create symbol directory '{}': {}".format(directory, e)) + return None return directory def get_path(self, symbol_id): try: return self._symbols_path[symbol_id] - # Symbol not found refresh cache + # Symbol not found, let's refresh the cache except KeyError: - self.list() try: + self.list() return self._symbols_path[symbol_id] - except KeyError: - return self._symbols_path[":/symbols/computer.svg"] + except (OSError, KeyError): + log.warning("Could not retrieve symbol '{}'".format(symbol_id)) + symbols_path = self._symbols_path + return symbols_path[":/symbols/computer.svg"] def get_size(self, symbol_id): try: diff --git a/gns3server/controller/topology.py b/gns3server/controller/topology.py index 475c8c41..9d32112f 100644 --- a/gns3server/controller/topology.py +++ b/gns3server/controller/topology.py @@ -409,7 +409,7 @@ def _convert_1_3_later(topo, topo_path): symbol = old_node.get("symbol", ":/symbols/computer.svg") old_node["ports"] = _create_cloud(node, old_node, symbol) else: - raise NotImplementedError("Conversion of {} is not supported".format(old_node["type"])) + raise aiohttp.web.HTTPConflict(text="Conversion of {} is not supported".format(old_node["type"])) for prop in old_node.get("properties", {}): if prop not in ["console", "name", "console_type", "console_host", "use_ubridge"]: @@ -608,13 +608,13 @@ def _create_cloud(node, old_node, icon): elif old_port["name"].startswith("nio_nat"): continue else: - raise NotImplementedError("The conversion of cloud with {} is not supported".format(old_port["name"])) + raise aiohttp.web.HTTPConflict(text="The conversion of cloud with {} is not supported".format(old_port["name"])) if port_type == "udp": try: _, lport, rhost, rport = old_port["name"].split(":") except ValueError: - raise NotImplementedError("UDP tunnel using IPV6 is not supported in cloud") + raise aiohttp.web.HTTPConflict(text="UDP tunnel using IPV6 is not supported in cloud") port = { "name": "UDP tunnel {}".format(len(ports) + 1), "port_number": len(ports) + 1, @@ -645,7 +645,7 @@ def _convert_snapshots(topo_dir): old_snapshots_dir = os.path.join(topo_dir, "project-files", "snapshots") if os.path.exists(old_snapshots_dir): new_snapshots_dir = os.path.join(topo_dir, "snapshots") - os.makedirs(new_snapshots_dir) + os.makedirs(new_snapshots_dir, exist_ok=True) for snapshot in os.listdir(old_snapshots_dir): snapshot_dir = os.path.join(old_snapshots_dir, snapshot) diff --git a/gns3server/handlers/api/controller/symbol_handler.py b/gns3server/handlers/api/controller/symbol_handler.py index b9796d94..3d378766 100644 --- a/gns3server/handlers/api/controller/symbol_handler.py +++ b/gns3server/handlers/api/controller/symbol_handler.py @@ -52,7 +52,8 @@ class SymbolHandler: controller = Controller.instance() try: yield from response.file(controller.symbols.get_path(request.match_info["symbol_id"])) - except (KeyError, FileNotFoundError, PermissionError): + except (KeyError, OSError) as e: + log.warning("Could not get symbol file: {}".format(e)) response.set_status(404) @Route.post( @@ -66,7 +67,7 @@ class SymbolHandler: controller = Controller.instance() path = os.path.join(controller.symbols.symbols_path(), os.path.basename(request.match_info["symbol_id"])) try: - with open(path, 'wb') as f: + with open(path, "wb") as f: while True: try: chunk = yield from request.content.read(1024) @@ -75,7 +76,7 @@ class SymbolHandler: if not chunk: break f.write(chunk) - except OSError as e: + except (UnicodeEncodeError, OSError) as e: raise aiohttp.web.HTTPConflict(text="Could not write symbol file '{}': {}".format(path, e)) # Reset the symbol list controller.symbols.list() diff --git a/gns3server/ubridge/hypervisor.py b/gns3server/ubridge/hypervisor.py index ed618b06..91ed58c4 100644 --- a/gns3server/ubridge/hypervisor.py +++ b/gns3server/ubridge/hypervisor.py @@ -178,7 +178,7 @@ class Hypervisor(UBridgeHypervisor): env=env) log.info("ubridge started PID={}".format(self._process.pid)) - except (OSError, PermissionError, subprocess.SubprocessError) as e: + except (OSError, subprocess.SubprocessError) as e: ubridge_stdout = self.read_stdout() log.error("Could not start ubridge: {}\n{}".format(e, ubridge_stdout)) raise UbridgeError("Could not start ubridge: {}\n{}".format(e, ubridge_stdout)) From eb0e26b55f5b94fb5b41cc8fba4bd56fb8419d0a Mon Sep 17 00:00:00 2001 From: grossmj Date: Wed, 12 Sep 2018 15:38:20 +0200 Subject: [PATCH 10/18] Include locale information and GNS3 VM version in crash reports. --- gns3server/crash_report.py | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/gns3server/crash_report.py b/gns3server/crash_report.py index 93124e9e..020cf02e 100644 --- a/gns3server/crash_report.py +++ b/gns3server/crash_report.py @@ -20,7 +20,7 @@ import sys import struct import aiohttp import platform - +import locale try: import raven @@ -92,7 +92,8 @@ class CrashReport: "url": request.path, "data": request.json, }) - self._client.tags_context({ + + context = { "os:name": platform.system(), "os:release": platform.release(), "os:win_32": " ".join(platform.win32_ver()), @@ -105,7 +106,28 @@ class CrashReport: "python:bit": struct.calcsize("P") * 8, "python:encoding": sys.getdefaultencoding(), "python:frozen": "{}".format(hasattr(sys, "frozen")) - }) + } + + if sys.platform.startswith("linux") and not hasattr(sys, "frozen"): + # add locale information + try: + language, encoding = locale.getlocale() + context["locale:language"] = language + context["locale:encoding"] = encoding + except ValueError: + pass + + # add GNS3 VM version if it exists + home = os.path.expanduser("~") + gns3vm_version = os.path.join(home, ".config", "GNS3", "gns3vm_version") + if os.path.isfile(gns3vm_version): + try: + with open(gns3vm_version) as fd: + context["gns3vm:version"] = fd.readline().strip() + except OSError: + pass + + self._client.tags_context(context) try: report = self._client.captureException() except Exception as e: From 5d2e539193cee5a57dcfaaba7d8d758ddf2ae4e5 Mon Sep 17 00:00:00 2001 From: grossmj Date: Fri, 14 Sep 2018 22:18:13 +0200 Subject: [PATCH 11/18] Update appliances. --- gns3server/appliances/cisco-nxosv9k.gns3a | 14 ++++++++++++++ gns3server/appliances/f5-bigiq.gns3a | 14 ++++++++++++++ gns3server/appliances/kali-linux.gns3a | 15 ++++++++++++++- 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/gns3server/appliances/cisco-nxosv9k.gns3a b/gns3server/appliances/cisco-nxosv9k.gns3a index b8616e35..0078a90c 100644 --- a/gns3server/appliances/cisco-nxosv9k.gns3a +++ b/gns3server/appliances/cisco-nxosv9k.gns3a @@ -25,6 +25,13 @@ "kvm": "require" }, "images": [ + { + "filename": "nxosv-final.9.2.1.qcow2", + "version": "9.2.1", + "md5sum": "1d7fa4654602d7ffbf62544edfe71986", + "filesize": 1330315264, + "download_url": "https://software.cisco.com/download/home/286312239/type/282088129/release/9.2%25281%2529" + }, { "filename": "nxosv-final.7.0.3.I7.4.qcow2", "version": "7.0.3.I7.4", @@ -85,6 +92,13 @@ } ], "versions": [ + { + "name": "9.2.1", + "images": { + "bios_image": "OVMF-20160813.fd", + "hda_disk_image": "nxosv-final.9.2.1.qcow2" + } + }, { "name": "7.0.3.I7.4", "images": { diff --git a/gns3server/appliances/f5-bigiq.gns3a b/gns3server/appliances/f5-bigiq.gns3a index 97050cf7..d1878658 100644 --- a/gns3server/appliances/f5-bigiq.gns3a +++ b/gns3server/appliances/f5-bigiq.gns3a @@ -29,6 +29,13 @@ "options": "-smp 2 -cpu host" }, "images": [ + { + "filename": "BIG-IQ-5.4.0.2.24.7467.qcow2", + "version": "5.4.0.2", + "md5sum": "e3e6389438ba1e1676f507658f767e95", + "filesize": 3480748032, + "download_url": "https://downloads.f5.com/esd/serveDownload.jsp?path=/big-iq/big-iq_cm/5.4.0/english/virtual-edition_base-plus-hf2/&sw=BIG-IQ&pro=big-iq_CM&ver=5.4.0&container=Virtual-Edition_Base-Plus-HF2&file=BIG-IQ-5.4.0.2.24.7467.qcow2.zip" + }, { "filename": "BIG-IQ-5.4.0.0.0.7437.qcow2", "version": "5.4.0", @@ -81,6 +88,13 @@ } ], "versions": [ + { + "name": "5.4.0.2", + "images": { + "hda_disk_image": "BIG-IQ-5.4.0.2.24.7467.qcow2", + "hdb_disk_image": "empty100G.qcow2" + } + }, { "name": "5.4.0", "images": { diff --git a/gns3server/appliances/kali-linux.gns3a b/gns3server/appliances/kali-linux.gns3a index 60baf043..742f6956 100644 --- a/gns3server/appliances/kali-linux.gns3a +++ b/gns3server/appliances/kali-linux.gns3a @@ -20,6 +20,14 @@ "kvm": "require" }, "images": [ + { + "filename": "kali-linux-2018.3-amd64.iso", + "version": "2018.3", + "md5sum": "6dc3e57177249f73492b9edb95d082d1", + "filesize": 3188391936, + "download_url": "https://www.kali.org/downloads/", + "direct_download_url": "http://cdimage.kali.org/kali-2018.3/kali-linux-2018.3-amd64.iso" + }, { "filename": "kali-linux-2018.1-amd64.iso", "version": "2018.1", @@ -79,7 +87,12 @@ ], "versions": [ { - "name": "2018.8", + "name": "2018.3", + "images": { + "cdrom_image": "kali-linux-2018.3-amd64.iso" + } + }, { + "name": "2018.1", "images": { "cdrom_image": "kali-linux-2018.1-amd64.iso" } From b6b345508bffde1ef0939d5c25f3d1fe5b6e7b93 Mon Sep 17 00:00:00 2001 From: grossmj Date: Sat, 15 Sep 2018 11:19:55 +0200 Subject: [PATCH 12/18] Release v2.1.10 --- CHANGELOG | 22 ++++++++++++++++++++++ gns3server/crash_report.py | 2 +- gns3server/version.py | 4 ++-- 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index cd56eb79..b42116f4 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,27 @@ # Change Log +## 2.1.10 15/09/2018 + +* Include locale information and GNS3 VM version in crash reports. +* Fix small errors like unhandled exceptions etc. +* Import encodings.idna to avoid LookupError when standard library is in a zip file. +* Catch exceptions in various locations to fix small issues reported by Sentry. +* Check if serial pipe can be opened for VMware and VirtualBox VMs. +* Improve the invalid port format detection. Fixes https://github.com/GNS3/gns3-gui/issues/2580 +* Update aiohttp verion requirement in order to support Python 3.7. Fixes https://github.com/GNS3/gns3-gui/issues/2566 +* Update setup.py and fix minor issues. +* Catch asyncio.CancelledError when shutting down the server. +* Report GNS3 VM errors to the GUI server summary. Ref #1359. +* Replace vboxnet0 (if it does not exist) by the first available vboxnet interface on Windows. Fixes https://github.com/GNS3/gns3-vm/issues/102 +* Check if the VirtualBox host-only network exists when starting a GNS3 VM running on VirtualBox. Ref https://github.com/GNS3/gns3-vm/issues/102 +* Change file timestamps if necessary because ZIP does not support timestamps before 1980. Fixes #1360. +* Add missing coroutine decorator Ref https://github.com/GNS3/gns3-gui/issues/2566 +* Refactor asyncio locking system for Python 3.7 support. Ref https://github.com/GNS3/gns3-gui/issues/2566 Ref https://github.com/GNS3/gns3-gui/issues/2568 +* Use asyncio.ensure_future() instead of asyncio.async() with conservative approach to support Python < 3.4.4. Fixes https://github.com/GNS3/gns3-gui/issues/2566 +* Forbid controller and compute servers to be different versions. Report last compute server error to clients and display in the server summary. +* Fix exception with short names for Dynamips interfaces. Fixes #1386. +* Add missing Qemu boot priority values. Fixes https://github.com/GNS3/gns3-server/issues/1385 + ## 2.1.9 13/08/2018 * Fix some more problems with interface short names. Fixes https://github.com/GNS3/gns3-gui/issues/2562 diff --git a/gns3server/crash_report.py b/gns3server/crash_report.py index 020cf02e..fd3fcc1d 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 = "sync+https://56af21e241ed4c1894ebe17bf06b1cd1:6075f91067954267b51e90b9638a6fad@sentry.io/38482" + DSN = "https://71472e2845b2422cab117bdfa8af55ec:da50c8f08b9f44d8b905356741c278df@sentry.io/38482" if hasattr(sys, "frozen"): cacert = get_resource("cacert.pem") if cacert is not None and os.path.isfile(cacert): diff --git a/gns3server/version.py b/gns3server/version.py index 3d8ef14e..fa2bfcf6 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.1.10dev2" -__version_info__ = (2, 1, 10, 99) +__version__ = "2.1.10" +__version_info__ = (2, 1, 10, 0) # If it's a git checkout try to add the commit if "dev" in __version__: From 4efdefaf5ab3b3495c3e4de33190cd801be8f1a2 Mon Sep 17 00:00:00 2001 From: grossmj Date: Sat, 15 Sep 2018 11:23:18 +0200 Subject: [PATCH 13/18] Development on 2.1.11dev1 --- gns3server/version.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gns3server/version.py b/gns3server/version.py index fa2bfcf6..2fe88065 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.1.10" -__version_info__ = (2, 1, 10, 0) +__version__ = "2.1.11dev1" +__version_info__ = (2, 1, 11, 99) # If it's a git checkout try to add the commit if "dev" in __version__: From 4d95e0b51f3611f23abe1d6a0e3ef829207f1cb9 Mon Sep 17 00:00:00 2001 From: grossmj Date: Fri, 28 Sep 2018 15:04:38 +0200 Subject: [PATCH 14/18] Catch some exceptions. --- gns3server/compute/vpcs/vpcs_vm.py | 2 +- gns3server/controller/compute.py | 10 +++++++--- gns3server/utils/asyncio/telnet_server.py | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/gns3server/compute/vpcs/vpcs_vm.py b/gns3server/compute/vpcs/vpcs_vm.py index 6449bb94..787f36d4 100644 --- a/gns3server/compute/vpcs/vpcs_vm.py +++ b/gns3server/compute/vpcs/vpcs_vm.py @@ -315,7 +315,7 @@ class VPCSVM(BaseNode): if sys.platform.startswith("win32"): try: self._process.send_signal(signal.CTRL_BREAK_EVENT) - except OSError: + except (SystemError, OSError): pass else: try: diff --git a/gns3server/controller/compute.py b/gns3server/controller/compute.py index fe981241..8513bde5 100644 --- a/gns3server/controller/compute.py +++ b/gns3server/controller/compute.py @@ -446,9 +446,13 @@ class Compute: self._capabilities = response.json if response.json["version"].split("-")[0] != __version__.split("-")[0]: - msg = "GNS3 controller version {} is not the same as compute server {} version {}".format(__version__, - self._name, - response.json["version"]) + if self._name.startswith("GNS3 VM"): + msg = "GNS3 version {} is not the same as the GNS3 VM version {}. Please upgrade the GNS3 VM.".format(__version__, + response.json["version"]) + else: + msg = "GNS3 controller version {} is not the same as compute server {} version {}".format(__version__, + self._name, + response.json["version"]) if __version_info__[3] == 0: # Stable release log.error(msg) diff --git a/gns3server/utils/asyncio/telnet_server.py b/gns3server/utils/asyncio/telnet_server.py index abed2e06..9223fc16 100644 --- a/gns3server/utils/asyncio/telnet_server.py +++ b/gns3server/utils/asyncio/telnet_server.py @@ -215,7 +215,7 @@ class AsyncioTelnetServer: try: writer.write_eof() yield from writer.drain() - except ConnectionError: + except (AttributeError, ConnectionError): continue @asyncio.coroutine From 41d4ecc4b754905e063230766077579afccf80ba Mon Sep 17 00:00:00 2001 From: grossmj Date: Fri, 28 Sep 2018 20:47:44 +0200 Subject: [PATCH 15/18] Release v2.1.11 --- CHANGELOG | 4 ++++ gns3server/crash_report.py | 2 +- gns3server/version.py | 4 ++-- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index b42116f4..3fdd45fc 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,9 @@ # Change Log +## 2.1.11 28/09/2018 + +* Catch some exceptions. + ## 2.1.10 15/09/2018 * Include locale information and GNS3 VM version in crash reports. diff --git a/gns3server/crash_report.py b/gns3server/crash_report.py index fd3fcc1d..26071d86 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://71472e2845b2422cab117bdfa8af55ec:da50c8f08b9f44d8b905356741c278df@sentry.io/38482" + DSN = "https://8a4a7325dfcf4661a0b04d92b0a7d32e:14f83f7a65e54df88e5f06abad85b152@sentry.io/38482" if hasattr(sys, "frozen"): cacert = get_resource("cacert.pem") if cacert is not None and os.path.isfile(cacert): diff --git a/gns3server/version.py b/gns3server/version.py index 2fe88065..89f426dc 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.1.11dev1" -__version_info__ = (2, 1, 11, 99) +__version__ = "2.1.11" +__version_info__ = (2, 1, 11, 0) # If it's a git checkout try to add the commit if "dev" in __version__: From 8d368cd0f65c66fde243a8c3114bcd41aceb1d7c Mon Sep 17 00:00:00 2001 From: grossmj Date: Fri, 28 Sep 2018 20:50:03 +0200 Subject: [PATCH 16/18] Development on 2.1.12dev1 --- gns3server/version.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gns3server/version.py b/gns3server/version.py index 89f426dc..64ba05ae 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.1.11" -__version_info__ = (2, 1, 11, 0) +__version__ = "2.1.12dev1" +__version_info__ = (2, 1, 12, 99) # If it's a git checkout try to add the commit if "dev" in __version__: From 181a31be3263b84c5482a01084609057d280659a Mon Sep 17 00:00:00 2001 From: grossmj Date: Tue, 2 Oct 2018 11:22:32 +0200 Subject: [PATCH 17/18] Update minimum VIX version requirements for VMware. Ref #1415. --- gns3server/compute/vmware/__init__.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/gns3server/compute/vmware/__init__.py b/gns3server/compute/vmware/__init__.py index 549a35ae..79e88d5f 100644 --- a/gns3server/compute/vmware/__init__.py +++ b/gns3server/compute/vmware/__init__.py @@ -149,6 +149,7 @@ class VMware(BaseManager): VIX 1.13 was the release for Player 6. VIX 1.14 was the release for Player 7. VIX 1.15 was the release for Workstation Player 12. + VIX 1.17 was the release for Workstation Player 14. :param player_version: VMware Player major version. """ @@ -162,6 +163,8 @@ class VMware(BaseManager): yield from self.check_vmrun_version(minimum_required_version="1.14.0") elif player_version >= 12: yield from self.check_vmrun_version(minimum_required_version="1.15.0") + elif player_version >= 14: + yield from self.check_vmrun_version(minimum_required_version="1.17.0") self._host_type = "player" @asyncio.coroutine @@ -172,6 +175,7 @@ class VMware(BaseManager): VIX 1.13 was the release for Workstation 10. VIX 1.14 was the release for Workstation 11. VIX 1.15 was the release for Workstation Pro 12. + VIX 1.17 was the release for Workstation Pro 14. :param ws_version: VMware Workstation major version. """ @@ -185,6 +189,8 @@ class VMware(BaseManager): yield from self.check_vmrun_version(minimum_required_version="1.14.0") elif ws_version >= 12: yield from self.check_vmrun_version(minimum_required_version="1.15.0") + elif ws_version >= 14: + yield from self.check_vmrun_version(minimum_required_version="1.17.0") self._host_type = "ws" @asyncio.coroutine From e4a6db8ebc7fa9e0e5d1073b1e480ae8f45b2025 Mon Sep 17 00:00:00 2001 From: grossmj Date: Thu, 4 Oct 2018 15:22:42 +0200 Subject: [PATCH 18/18] Fix some typos. --- CHANGELOG | 2 +- docs/api/v2/compute/server/debug.rst | 2 +- gns3server/compute/port_manager.py | 2 +- gns3server/controller/node.py | 4 ++-- gns3server/controller/project.py | 2 +- gns3server/handlers/api/compute/server_handler.py | 4 ++-- gns3server/handlers/api/controller/compute_handler.py | 2 +- gns3server/handlers/api/controller/project_handler.py | 8 ++++---- gns3server/handlers/api/controller/server_handler.py | 4 ++-- 9 files changed, 15 insertions(+), 15 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 3fdd45fc..a5726009 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -681,7 +681,7 @@ * Fix naming of IOU serial interfaces * Improve timeout management * When exporting debug information export GNS3 VM vmx content -* /debug for exporting debug informations +* /debug for exporting debug information * Raise error if using a non linked clone VM twice * Fix a possible deadlock at exit * Fix import of some old dynamips topologies diff --git a/docs/api/v2/compute/server/debug.rst b/docs/api/v2/compute/server/debug.rst index 1e04dd1a..c0125d34 100644 --- a/docs/api/v2/compute/server/debug.rst +++ b/docs/api/v2/compute/server/debug.rst @@ -5,7 +5,7 @@ GET /v2/compute/debug ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Return debug informations about the compute +Return debug information about the compute Response status codes ********************** diff --git a/gns3server/compute/port_manager.py b/gns3server/compute/port_manager.py index c78dbc9b..6be33845 100644 --- a/gns3server/compute/port_manager.py +++ b/gns3server/compute/port_manager.py @@ -23,7 +23,7 @@ import logging log = logging.getLogger(__name__) -# This ports are disallowed by Chrome and Firefox to avoid issues, we skip them as well +# These ports are disallowed by Chrome and Firefox to avoid issues, we skip them as well BANNED_PORTS = set((1, 7, 9, 11, 13, 15, 17, 19, 20, 21, 22, 23, 25, 37, 42, 43, 53, 77, 79, 87, 95, 101, 102, 103, 104, 109, 110, 111, 113, 115, 117, 119, 123, 135, 139, 143, 179, 389, 465, 512, 513, 514, 515, 526, 530, 531, 532, 540, 556, 563, 587, 601, 636, 993, 995, 2049, 3659, 4045, 6000, 6665, 6666, 6667, diff --git a/gns3server/controller/node.py b/gns3server/controller/node.py index 38bf753d..d42b6e68 100644 --- a/gns3server/controller/node.py +++ b/gns3server/controller/node.py @@ -91,12 +91,12 @@ class Node: # Update node properties with additional elements for prop in kwargs: - if prop not in ignore_properties: + if prop and prop not in ignore_properties: if hasattr(self, prop): try: setattr(self, prop, kwargs[prop]) except AttributeError as e: - log.critical("Can't set attribute %s", prop) + log.critical("Cannot set attribute '%s'".format(prop)) raise e else: if prop not in self.CONTROLLER_ONLY_PROPERTIES and kwargs[prop] is not None and kwargs[prop] != "": diff --git a/gns3server/controller/project.py b/gns3server/controller/project.py index a8632a46..8c711e7d 100644 --- a/gns3server/controller/project.py +++ b/gns3server/controller/project.py @@ -935,7 +935,7 @@ class Project: with open(project_path, "rb") as f: project = yield from import_project(self._controller, str(uuid.uuid4()), f, location=location, name=name, keep_compute_id=True) except (ValueError, OSError, UnicodeEncodeError) as e: - raise aiohttp.web.HTTPConflict(text="Can not duplicate project: {}".format(str(e))) + raise aiohttp.web.HTTPConflict(text="Cannot duplicate project: {}".format(str(e))) if previous_status == "closed": yield from self.close() diff --git a/gns3server/handlers/api/compute/server_handler.py b/gns3server/handlers/api/compute/server_handler.py index 33ab1792..ee7ccc1c 100644 --- a/gns3server/handlers/api/compute/server_handler.py +++ b/gns3server/handlers/api/compute/server_handler.py @@ -42,9 +42,9 @@ class ServerHandler: @Route.get( r"/debug", - description="Return debug informations about the compute", + description="Return debug information about the compute", status_codes={ - 201: "Writed" + 201: "Written" }) def debug(request, response): response.content_type = "text/plain" diff --git a/gns3server/handlers/api/controller/compute_handler.py b/gns3server/handlers/api/controller/compute_handler.py index 8695c900..7225d3dd 100644 --- a/gns3server/handlers/api/controller/compute_handler.py +++ b/gns3server/handlers/api/controller/compute_handler.py @@ -59,7 +59,7 @@ class ComputeHandler: @Route.put( r"/computes/{compute_id}", - description="Get a compute server information", + description="Update a compute server", status_codes={ 200: "Compute server updated", 400: "Invalid request", diff --git a/gns3server/handlers/api/controller/project_handler.py b/gns3server/handlers/api/controller/project_handler.py index 37137ca2..5460f434 100644 --- a/gns3server/handlers/api/controller/project_handler.py +++ b/gns3server/handlers/api/controller/project_handler.py @@ -287,9 +287,9 @@ class ProjectHandler: try: with tempfile.TemporaryDirectory() as tmp_dir: - datas = yield from export_project( - project, tmp_dir, - include_images=bool(int(request.query.get("include_images", "0")))) + stream = yield from export_project(project, + tmp_dir, + include_images=bool(int(request.query.get("include_images", "0")))) # We need to do that now because export could failed and raise an HTTP error # that why response start need to be the later possible response.content_type = 'application/gns3project' @@ -297,7 +297,7 @@ class ProjectHandler: response.enable_chunked_encoding() yield from response.prepare(request) - for data in datas: + for data in stream: response.write(data) yield from response.drain() diff --git a/gns3server/handlers/api/controller/server_handler.py b/gns3server/handlers/api/controller/server_handler.py index fcc7238a..311e66ea 100644 --- a/gns3server/handlers/api/controller/server_handler.py +++ b/gns3server/handlers/api/controller/server_handler.py @@ -139,7 +139,7 @@ class ServerHandler: r"/debug", description="Dump debug information to disk (debug directory in config directory). Work only for local server", status_codes={ - 201: "Writed" + 201: "Written" }) def debug(request, response): @@ -156,7 +156,7 @@ class ServerHandler: f.write(ServerHandler._getDebugData()) except Exception as e: # If something is wrong we log the info to the log and we hope the log will be include correctly to the debug export - log.error("Could not export debug informations {}".format(e), exc_info=1) + log.error("Could not export debug information {}".format(e), exc_info=1) try: if Controller.instance().gns3vm.engine == "vmware":