diff --git a/CHANGELOG b/CHANGELOG index 91835c49..abf6dca2 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,12 @@ # Change Log +## 1.3.3 14/05/15 + +* Check for empty iourc path. +* Fixes bugs with IOS router configs. Fixes #354. +* Use a temporary directory as egg cache +* Catch crash error in IOU in case of permission denied + ## 1.3.3rc1 07/05/2015 * Return an error if an adapter slot doesn't exist on an IOS router. diff --git a/gns3server/crash_report.py b/gns3server/crash_report.py index 89800189..23ee7bc0 100644 --- a/gns3server/crash_report.py +++ b/gns3server/crash_report.py @@ -40,7 +40,7 @@ class CrashReport: Report crash to a third party service """ - DSN = "sync+https://45147533567b4d529ca09c093758681f:12d8b456cdb34d23aba771325aa64ee6@app.getsentry.com/38482" + DSN = "sync+https://9e6f04df72c74b6894a6dcd2928d069e:2035d1beb1654136b170f1e91f05ee51@app.getsentry.com/38482" if hasattr(sys, "frozen"): cacert = os.path.join(os.getcwd(), "cacert.pem") if os.path.isfile(cacert): diff --git a/gns3server/handlers/api/dynamips_vm_handler.py b/gns3server/handlers/api/dynamips_vm_handler.py index 6d46fc86..6a4f41ec 100644 --- a/gns3server/handlers/api/dynamips_vm_handler.py +++ b/gns3server/handlers/api/dynamips_vm_handler.py @@ -368,15 +368,16 @@ class DynamipsVMHandler: else: # nvram doesn't contain anything if the router has not been started at least once # in this case just use the startup-config file - startup_config_path = os.path.join(module_workdir, vm.startup_config) - if os.path.exists(startup_config_path): - try: - with open(startup_config_path, "rb") as f: - content = f.read().decode("utf-8", errors='replace') - if content: - result["startup_config_content"] = content - except OSError as e: - raise DynamipsError("Could not read the startup-config {}: {}".format(startup_config_path, e)) + if vm.startup_config: + startup_config_path = os.path.join(module_workdir, vm.startup_config) + if os.path.isfile(startup_config_path): + try: + with open(startup_config_path, "rb") as f: + content = f.read().decode("utf-8", errors='replace') + if content: + result["startup_config_content"] = content + except OSError as e: + raise DynamipsError("Could not read the startup-config {}: {}".format(startup_config_path, e)) if private_config_base64: private_config_content = base64.b64decode(private_config_base64).decode("utf-8", errors='replace') @@ -384,15 +385,16 @@ class DynamipsVMHandler: else: # nvram doesn't contain anything if the router has not been started at least once # in this case just use the private-config file - private_config_path = os.path.join(module_workdir, vm.private_config) - if os.path.exists(private_config_path): - try: - with open(private_config_path, "rb") as f: - content = f.read().decode("utf-8", errors='replace') - if content: - result["private_config_content"] = content - except OSError as e: - raise DynamipsError("Could not read the private-config {}: {}".format(private_config_path, e)) + if vm.private_config: + private_config_path = os.path.join(module_workdir, vm.private_config) + if os.path.isfile(private_config_path): + try: + with open(private_config_path, "rb") as f: + content = f.read().decode("utf-8", errors='replace') + if content: + result["private_config_content"] = content + except OSError as e: + raise DynamipsError("Could not read the private-config {}: {}".format(private_config_path, e)) response.set_status(200) response.json(result) diff --git a/gns3server/main.py b/gns3server/main.py index 4c964408..f000c7ca 100644 --- a/gns3server/main.py +++ b/gns3server/main.py @@ -20,6 +20,14 @@ Entry point of the server. It's support daemonize the process """ +# WARNING +# Due to buggy user machines we choose to put this as the first loading modules +# otherwise the egg cache is initialized in his standard location and +# if is not writetable the application crash. It's the user fault +# because one day the user as used sudo to run an egg and break his +# filesystem permissions, but it's a common mistake. +import gns3server.utils.get_resource + import os import sys diff --git a/gns3server/modules/dynamips/__init__.py b/gns3server/modules/dynamips/__init__.py index 38aeac83..a8bb51e2 100644 --- a/gns3server/modules/dynamips/__init__.py +++ b/gns3server/modules/dynamips/__init__.py @@ -535,17 +535,19 @@ class Dynamips(BaseManager): default_private_config_path = os.path.join(module_workdir, "configs", "i{}_private-config.cfg".format(vm.dynamips_id)) startup_config_path = settings.get("startup_config") + startup_config_content = settings.get("startup_config_content") if startup_config_path: yield from vm.set_configs(startup_config_path) - else: - startup_config_path = self._create_config(vm, default_startup_config_path, settings.get("startup_config_content")) + elif startup_config_content: + startup_config_path = self._create_config(vm, default_startup_config_path, startup_config_content) yield from vm.set_configs(startup_config_path) private_config_path = settings.get("private_config") + private_config_content = settings.get("private_config_content") if private_config_path: yield from vm.set_configs(vm.startup_config, private_config_path) - else: - private_config_path = self._create_config(vm, default_private_config_path, settings.get("private_config_content")) + elif private_config_content: + private_config_path = self._create_config(vm, default_private_config_path, private_config_content) yield from vm.set_configs(vm.startup_config, private_config_path) def _create_config(self, vm, path, content=None): diff --git a/gns3server/modules/dynamips/nodes/router.py b/gns3server/modules/dynamips/nodes/router.py index 25d10e3e..3a6d15af 100644 --- a/gns3server/modules/dynamips/nodes/router.py +++ b/gns3server/modules/dynamips/nodes/router.py @@ -259,11 +259,13 @@ class Router(BaseVM): @asyncio.coroutine def _termination_callback(self, returncode): """ - Called when the process is killed + Called when the process has stopped. :param returncode: Process returncode """ + self.status = "stopped" + log.info("Dynamips hypervisor process has stopped, return code: %d", returncode) @asyncio.coroutine def stop(self): diff --git a/gns3server/modules/iou/iou_vm.py b/gns3server/modules/iou/iou_vm.py index 3e86e8aa..590925c4 100644 --- a/gns3server/modules/iou/iou_vm.py +++ b/gns3server/modules/iou/iou_vm.py @@ -433,11 +433,14 @@ class IOUVM(BaseVM): yield from self._library_check() - self._rename_nvram_file() + try: + self._rename_nvram_file() + except OSError as e: + raise IOUError("Could not rename nvram files: {}".format(e)) iourc_path = self.iourc_path - if iourc_path is None: - raise IOUError("Could not find a iourc file (IOU license)") + if not iourc_path: + raise IOUError("Could not find an iourc file (IOU license)") if not os.path.isfile(iourc_path): raise IOUError("The iourc path '{}' is not a regular file".format(iourc_path)) @@ -481,11 +484,12 @@ class IOUVM(BaseVM): def _termination_callback(self, returncode): """ - Called when the process is killed + Called when the process has stopped. :param returncode: Process returncode """ - log.info("IOU process crash return code: %d", returncode) + + log.info("IOU process has stopped, return code: %d", returncode) self._terminate_process_iou() self._terminate_process_iouyap() self._ioucon_thread_stop_event.set() diff --git a/gns3server/modules/project.py b/gns3server/modules/project.py index eb68b333..85f4c7f5 100644 --- a/gns3server/modules/project.py +++ b/gns3server/modules/project.py @@ -65,7 +65,7 @@ class Project: self._used_tcp_ports = set() self._used_udp_ports = set() - # List of clients listen for notifications + # clients listening for notifications self._listeners = set() if path is None: @@ -437,10 +437,10 @@ class Project: def emit(self, action, event): """ - Send an event to all the client listens for notifications + Send an event to all the client listening for notifications - :param action: Action happened - :param event: Event sended to the client + :param action: Action name + :param event: Event to send """ for listener in self._listeners: listener.put_nowait((action, event, )) diff --git a/gns3server/modules/qemu/qemu_vm.py b/gns3server/modules/qemu/qemu_vm.py index 60833a10..f8ae3b12 100644 --- a/gns3server/modules/qemu/qemu_vm.py +++ b/gns3server/modules/qemu/qemu_vm.py @@ -596,12 +596,13 @@ class QemuVM(BaseVM): def _termination_callback(self, returncode): """ - Called when the process is killed + Called when the process has stopped. :param returncode: Process returncode """ + if self.started: - log.info("Process Qemu is dead. Return code: %d", returncode) + log.info("QEMU process has stopped, return code: %d", returncode) self.status = "stopped" self._process = None diff --git a/gns3server/modules/vpcs/vpcs_vm.py b/gns3server/modules/vpcs/vpcs_vm.py index dee7cdf9..a25d4c84 100644 --- a/gns3server/modules/vpcs/vpcs_vm.py +++ b/gns3server/modules/vpcs/vpcs_vm.py @@ -245,12 +245,12 @@ class VPCSVM(BaseVM): def _termination_callback(self, returncode): """ - Called when the process is killed + Called when the process has stopped. :param returncode: Process returncode """ if self._started: - log.info("Process VPCS is dead. Return code: %d", returncode) + log.info("VPCS process has stopped, return code: %d", returncode) self._started = False self.status = "stopped" self._process = None diff --git a/gns3server/utils/asyncio.py b/gns3server/utils/asyncio.py index 7c9cc606..39e1ac8e 100644 --- a/gns3server/utils/asyncio.py +++ b/gns3server/utils/asyncio.py @@ -17,7 +17,6 @@ import asyncio -import shutil import sys @@ -91,6 +90,6 @@ def _check_process(process, termination_callback): def monitor_process(process, termination_callback): - """Call termination_callback when process die""" + """Call termination_callback when a process dies""" asyncio.async(_check_process(process, termination_callback)) diff --git a/gns3server/utils/get_resource.py b/gns3server/utils/get_resource.py new file mode 100644 index 00000000..dae285fc --- /dev/null +++ b/gns3server/utils/get_resource.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2015 GNS3 Technologies Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import tempfile +import pkg_resources +import atexit +import logging + +log = logging.getLogger(__name__) + +try: + egg_cache_dir = tempfile.mkdtemp() + pkg_resources.set_extraction_path(egg_cache_dir) +except ValueError: + # If the path is already set the module throw an error + pass + + +@atexit.register +def clean_egg_cache(): + try: + import shutil + log.debug("Clean egg cache %s", egg_cache_dir) + shutil.rmtree(egg_cache_dir) + except Exception: + # We don't care if we can not cleanup + pass diff --git a/tests/modules/qemu/test_qemu_vm.py b/tests/modules/qemu/test_qemu_vm.py index 59dd6c26..ac939473 100644 --- a/tests/modules/qemu/test_qemu_vm.py +++ b/tests/modules/qemu/test_qemu_vm.py @@ -272,10 +272,10 @@ def test_build_command(vm, loop, fake_qemu_binary, port_manager): os.path.join(vm.working_dir, "flash.qcow2"), "-serial", "telnet:127.0.0.1:{},server,nowait".format(vm.console), + "-net", + "none", "-device", - "e1000,mac=00:00:ab:0e:0f:00,netdev=gns3-0", - "-netdev", - "user,id=gns3-0" + "e1000,mac=00:00:ab:0e:0f:00" ]