From 73310105a86be138d7e7093d4b4118fb607c7998 Mon Sep 17 00:00:00 2001 From: grossmj Date: Mon, 23 Mar 2015 21:13:22 -0600 Subject: [PATCH 1/5] Fixes default chassis bug. --- gns3server/handlers/api/dynamips_vm_handler.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/gns3server/handlers/api/dynamips_vm_handler.py b/gns3server/handlers/api/dynamips_vm_handler.py index a52159e6..412fbcac 100644 --- a/gns3server/handlers/api/dynamips_vm_handler.py +++ b/gns3server/handlers/api/dynamips_vm_handler.py @@ -28,6 +28,12 @@ from ...schemas.dynamips_vm import VM_CONFIGS_SCHEMA from ...modules.dynamips import Dynamips from ...modules.project_manager import ProjectManager +DEFAULT_CHASSIS = { + "c1700": "1720", + "c2600": "2610", + "c3600": "3640" +} + class DynamipsVMHandler: @@ -52,14 +58,18 @@ class DynamipsVMHandler: def create(request, response): dynamips_manager = Dynamips.instance() + platform = request.json.pop("platform") + default_chassis = None + if platform in DEFAULT_CHASSIS: + default_chassis = DEFAULT_CHASSIS[platform] vm = yield from dynamips_manager.create_vm(request.json.pop("name"), request.match_info["project_id"], request.json.get("vm_id"), request.json.get("dynamips_id"), - request.json.pop("platform"), + platform, console=request.json.get("console"), aux=request.json.get("aux"), - chassis=request.json.pop("chassis", None)) + chassis=request.json.pop("chassis", default_chassis)) yield from dynamips_manager.update_vm_settings(vm, request.json) yield from dynamips_manager.ghost_ios_support(vm) From 7b1a7079675de181262d44d5d3449bec26f8963a Mon Sep 17 00:00:00 2001 From: grossmj Date: Mon, 23 Mar 2015 21:22:10 -0600 Subject: [PATCH 2/5] Bump version to 1.3.0.dev3 --- gns3server/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gns3server/version.py b/gns3server/version.py index 4f3eb91e..4b6fba3c 100644 --- a/gns3server/version.py +++ b/gns3server/version.py @@ -23,5 +23,5 @@ # or negative for a release candidate or beta (after the base version # number has been incremented) -__version__ = "1.3.0rc2" +__version__ = "1.3.0.dev3" __version_info__ = (1, 3, 0, -99) From 980e63e667efa23c6a0e619299ce3fcf1fb77e01 Mon Sep 17 00:00:00 2001 From: grossmj Date: Mon, 23 Mar 2015 22:52:02 -0600 Subject: [PATCH 3/5] Allocate a random port for Qemu monitor. Fixes issue with pre 1.3 projects. --- gns3server/handlers/api/qemu_handler.py | 5 +- gns3server/modules/qemu/qemu_vm.py | 61 +++++++++---------------- gns3server/schemas/qemu.py | 20 +------- tests/modules/qemu/test_qemu_vm.py | 8 +--- 4 files changed, 26 insertions(+), 68 deletions(-) diff --git a/gns3server/handlers/api/qemu_handler.py b/gns3server/handlers/api/qemu_handler.py index 659d1b03..14d12d07 100644 --- a/gns3server/handlers/api/qemu_handler.py +++ b/gns3server/handlers/api/qemu_handler.py @@ -51,12 +51,11 @@ class QEMUHandler: request.match_info["project_id"], request.json.get("vm_id"), qemu_path=request.json.get("qemu_path"), - console=request.json.get("console"), - monitor=request.json.get("monitor")) + console=request.json.get("console")) # Clear already used keys map(request.json.__delitem__, ["name", "project_id", "vm_id", - "qemu_path", "console", "monitor"]) + "qemu_path", "console"]) for field in request.json: setattr(vm, field, request.json[field]) diff --git a/gns3server/modules/qemu/qemu_vm.py b/gns3server/modules/qemu/qemu_vm.py index 7d1675ee..0949e04c 100644 --- a/gns3server/modules/qemu/qemu_vm.py +++ b/gns3server/modules/qemu/qemu_vm.py @@ -26,6 +26,7 @@ import random import subprocess import shlex import asyncio +import socket from .qemu_error import QemuError from ..adapters.ethernet_adapter import EthernetAdapter @@ -51,7 +52,6 @@ class QemuVM(BaseVM): :param qemu_path: path to the QEMU binary :param qemu_id: QEMU VM instance ID :param console: TCP console port - :param monitor: TCP monitor port """ def __init__(self, @@ -60,8 +60,7 @@ class QemuVM(BaseVM): project, manager, qemu_path=None, - console=None, - monitor=None): + console=None): super().__init__(name, vm_id, project, manager, console=console) @@ -72,6 +71,7 @@ class QemuVM(BaseVM): self._started = False self._process = None self._cpulimit_process = None + self._monitor = None self._stdout_file = "" # QEMU settings @@ -82,7 +82,6 @@ class QemuVM(BaseVM): self._hdd_disk_image = "" self._options = "" self._ram = 256 - self._monitor = monitor self._ethernet_adapters = [] self._adapter_type = "e1000" self._initrd = "" @@ -92,11 +91,6 @@ class QemuVM(BaseVM): self._cpu_throttling = 0 # means no CPU throttling self._process_priority = "low" - if self._monitor is not None: - self._monitor = self._manager.port_manager.reserve_tcp_port(self._monitor, self._project) - else: - self._monitor = self._manager.port_manager.get_free_tcp_port(self._project) - self.adapters = 1 # creates 1 adapter by default log.info("QEMU VM {name} [id={id}] has been created".format(name=self._name, id=self._id)) @@ -111,25 +105,6 @@ class QemuVM(BaseVM): return self._monitor - @monitor.setter - def monitor(self, monitor): - """ - Sets the TCP monitor port. - - :param monitor: monitor port (integer) - """ - - if monitor == self._monitor: - return - if self._monitor: - self._manager.port_manager.release_monitor_port(self._monitor, self._project) - self._monitor = self._manager.port_manager.reserve_monitor_port(monitor, self._project) - log.info("{module}: '{name}' [{id}]: monitor port set to {port}".format( - module=self.manager.module_name, - name=self.name, - id=self.id, - port=monitor)) - @property def qemu_path(self): """ @@ -610,6 +585,16 @@ class QemuVM(BaseVM): return else: + + if self._manager.config.get_section_config("Qemu").getboolean("monitor", True): + try: + # let the OS find an unused port for the Qemu monitor + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: + sock.bind((self._monitor_host, 0)) + self._monitor = sock.getsockname()[1] + except OSError as e: + raise QemuError("Could not find free port for the Qemu monitor: {}".format(e)) + self._command = yield from self._build_command() try: log.info("starting QEMU: {}".format(self._command)) @@ -701,9 +686,6 @@ class QemuVM(BaseVM): if self._console: self._manager.port_manager.release_tcp_port(self._console, self._project) self._console = None - if self._monitor: - self._manager.port_manager.release_tcp_port(self._monitor, self._project) - self._monitor = None @asyncio.coroutine def _get_vm_status(self): @@ -732,14 +714,15 @@ class QemuVM(BaseVM): Suspends this QEMU VM. """ - vm_status = yield from self._get_vm_status() - if vm_status is None: - raise QemuError("Suspending a QEMU VM is not supported") - elif vm_status == "running": - yield from self._control_vm("stop") - log.debug("QEMU VM has been suspended") - else: - log.info("QEMU VM is not running to be suspended, current status is {}".format(vm_status)) + if self.is_running(): + vm_status = yield from self._get_vm_status() + if vm_status is None: + raise QemuError("Suspending a QEMU VM is not supported") + elif vm_status == "running": + yield from self._control_vm("stop") + log.debug("QEMU VM has been suspended") + else: + log.info("QEMU VM is not running to be suspended, current status is {}".format(vm_status)) @asyncio.coroutine def reload(self): diff --git a/gns3server/schemas/qemu.py b/gns3server/schemas/qemu.py index 7632525a..1a77d3ef 100644 --- a/gns3server/schemas/qemu.py +++ b/gns3server/schemas/qemu.py @@ -47,12 +47,6 @@ QEMU_CREATE_SCHEMA = { "maximum": 65535, "type": ["integer", "null"] }, - "monitor": { - "description": "monitor TCP port", - "minimum": 1, - "maximum": 65535, - "type": ["integer", "null"] - }, "hda_disk_image": { "description": "QEMU hda disk image path", "type": ["string", "null"], @@ -146,12 +140,6 @@ QEMU_UPDATE_SCHEMA = { "maximum": 65535, "type": ["integer", "null"] }, - "monitor": { - "description": "monitor TCP port", - "minimum": 1, - "maximum": 65535, - "type": ["integer", "null"] - }, "hda_disk_image": { "description": "QEMU hda disk image path", "type": ["string", "null"], @@ -341,12 +329,6 @@ QEMU_OBJECT_SCHEMA = { "maximum": 65535, "type": "integer" }, - "monitor": { - "description": "monitor TCP port", - "minimum": 1, - "maximum": 65535, - "type": "integer" - }, "initrd": { "description": "QEMU initrd path", "type": "string", @@ -386,7 +368,7 @@ QEMU_OBJECT_SCHEMA = { "additionalProperties": False, "required": ["vm_id", "project_id", "name", "qemu_path", "hda_disk_image", "hdb_disk_image", "hdc_disk_image", "hdd_disk_image", "ram", "adapters", "adapter_type", "console", - "monitor", "initrd", "kernel_image", "kernel_command_line", + "initrd", "kernel_image", "kernel_command_line", "legacy_networking", "cpu_throttling", "process_priority", "options" ] } diff --git a/tests/modules/qemu/test_qemu_vm.py b/tests/modules/qemu/test_qemu_vm.py index 1e078c26..f05e78c4 100644 --- a/tests/modules/qemu/test_qemu_vm.py +++ b/tests/modules/qemu/test_qemu_vm.py @@ -151,14 +151,11 @@ def test_close(vm, port_manager, loop): loop.run_until_complete(asyncio.async(vm.start())) console_port = vm.console - monitor_port = vm.monitor loop.run_until_complete(asyncio.async(vm.close())) # Raise an exception if the port is not free port_manager.reserve_tcp_port(console_port, vm.project) - # Raise an exception if the port is not free - port_manager.reserve_tcp_port(monitor_port, vm.project) assert vm.is_running() is False @@ -226,7 +223,6 @@ def test_json(vm, project): def test_control_vm(vm, loop): vm._process = MagicMock() - vm._monitor = 4242 reader = MagicMock() writer = MagicMock() with asyncio_patch("asyncio.open_connection", return_value=(reader, writer)) as open_connect: @@ -238,7 +234,6 @@ def test_control_vm(vm, loop): def test_control_vm_expect_text(vm, loop, running_subprocess_mock): vm._process = running_subprocess_mock - vm._monitor = 4242 reader = MagicMock() writer = MagicMock() with asyncio_patch("asyncio.open_connection", return_value=(reader, writer)) as open_connect: @@ -247,6 +242,7 @@ def test_control_vm_expect_text(vm, loop, running_subprocess_mock): future.set_result(b"epic product") reader.readline.return_value = future + vm._monitor = 4242 res = loop.run_until_complete(asyncio.async(vm._control_vm("test", [b"epic"]))) assert writer.write.called_with("test") @@ -269,8 +265,6 @@ 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), - "-monitor", - "tcp:127.0.0.1:{},server,nowait".format(vm.monitor), "-device", "e1000,mac=00:00:ab:7e:b5:00,netdev=gns3-0", "-netdev", From 07067d6765711ead80076b52146c92a521fd703d Mon Sep 17 00:00:00 2001 From: grossmj Date: Tue, 24 Mar 2015 22:04:48 -0600 Subject: [PATCH 4/5] Fixes how to test if iou and iouyap are running. --- gns3server/modules/iou/iou_vm.py | 51 +++++++++++++++----------------- 1 file changed, 24 insertions(+), 27 deletions(-) diff --git a/gns3server/modules/iou/iou_vm.py b/gns3server/modules/iou/iou_vm.py index fd0cba1f..fb8ba859 100644 --- a/gns3server/modules/iou/iou_vm.py +++ b/gns3server/modules/iou/iou_vm.py @@ -597,42 +597,39 @@ class IOUVM(BaseVM): if self._iou_process.returncode is None: log.warn("IOU process {} is still running... killing it".format(self._iou_process.pid)) self._iou_process.kill() - self._iou_process = None - if self._iouyap_process is not None: - self._terminate_process_iouyap() - try: - yield from gns3server.utils.asyncio.wait_for_process_termination(self._iouyap_process, timeout=3) - except asyncio.TimeoutError: - if self._iouyap_process.returncode is None: - log.warn("IOUYAP process {} is still running... killing it".format(self._iouyap_process.pid)) - self._iouyap_process.kill() - + if self.is_iouyap_running(): + self._terminate_process_iouyap() + try: + yield from gns3server.utils.asyncio.wait_for_process_termination(self._iouyap_process, timeout=3) + except asyncio.TimeoutError: + if self._iouyap_process.returncode is None: + log.warn("IOUYAP process {} is still running... killing it".format(self._iouyap_process.pid)) + self._iouyap_process.kill() self._iouyap_process = None - self._started = False + + self._started = False def _terminate_process_iouyap(self): """Terminate the process if running""" - if self._iouyap_process: - log.info("Stopping IOUYAP instance {} PID={}".format(self.name, self._iouyap_process.pid)) - try: - self._iouyap_process.terminate() - # Sometime the process can already be dead when we garbage collect - except ProcessLookupError: - pass + log.info("Stopping IOUYAP instance {} PID={}".format(self.name, self._iouyap_process.pid)) + try: + self._iouyap_process.terminate() + # Sometime the process can already be dead when we garbage collect + except ProcessLookupError: + pass def _terminate_process_iou(self): """Terminate the process if running""" - if self._iou_process: - log.info("Stopping IOU instance {} PID={}".format(self.name, self._iou_process.pid)) - try: - self._iou_process.terminate() - # Sometime the process can already be dead when we garbage collect - except ProcessLookupError: - pass + log.info("Stopping IOU instance {} PID={}".format(self.name, self._iou_process.pid)) + try: + self._iou_process.terminate() + # Sometime the process can already be dead when we garbage collect + except ProcessLookupError: + pass @asyncio.coroutine def reload(self): @@ -650,7 +647,7 @@ class IOUVM(BaseVM): :returns: True or False """ - if self._iou_process: + if self._iou_process and self._iou_process.returncode is None: return True return False @@ -661,7 +658,7 @@ class IOUVM(BaseVM): :returns: True or False """ - if self._iouyap_process: + if self._iouyap_process and self._iouyap_process.returncode is None: return True return False From 588088ca9323e5ea6226983d7206bb3e6a786815 Mon Sep 17 00:00:00 2001 From: grossmj Date: Tue, 24 Mar 2015 22:15:49 -0600 Subject: [PATCH 5/5] Fixes bug when remove_nio() is not a coroutine for ATM and FR switches. --- gns3server/handlers/api/dynamips_device_handler.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/gns3server/handlers/api/dynamips_device_handler.py b/gns3server/handlers/api/dynamips_device_handler.py index 3a6f8588..95307b89 100644 --- a/gns3server/handlers/api/dynamips_device_handler.py +++ b/gns3server/handlers/api/dynamips_device_handler.py @@ -181,7 +181,10 @@ class DynamipsDeviceHandler: dynamips_manager = Dynamips.instance() device = dynamips_manager.get_device(request.match_info["device_id"], project_id=request.match_info["project_id"]) port_number = int(request.match_info["port_number"]) - yield from device.remove_nio(port_number) + if asyncio.iscoroutinefunction(device.remove_nio): + yield from device.remove_nio(port_number) + else: + device.remove_nio(port_number) response.set_status(204) @Route.post(