From 8352ff02a18e657cf7b2ae2f67fb9ca6ff2cbb4b Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Fri, 26 Aug 2016 14:09:18 +0200 Subject: [PATCH] When you change settings of the GNS3 VM the VM is reloaded and project using it closed Fix #644 --- gns3server/controller/__init__.py | 25 ++++----- gns3server/controller/gns3vm/__init__.py | 55 +++++++++++++++++-- gns3server/controller/node.py | 7 ++- gns3server/controller/project.py | 6 +- .../api/controller/gns3_vm_handler.py | 2 +- tests/controller/test_controller.py | 4 +- tests/controller/test_gns3vm.py | 13 +++++ 7 files changed, 87 insertions(+), 25 deletions(-) diff --git a/gns3server/controller/__init__.py b/gns3server/controller/__init__.py index a17b1d81..3046b1d4 100644 --- a/gns3server/controller/__init__.py +++ b/gns3server/controller/__init__.py @@ -71,16 +71,7 @@ class Controller: port=server_config.getint("port", 3080), user=server_config.get("user", ""), password=server_config.get("password", "")) - if self.gns3vm.enable: - yield from self.gns3vm.start() - self._computes["vm"] = Compute(compute_id="vm", - name="GNS3 VM", - controller=self, - protocol=self.gns3vm.protocol, - host=self.gns3vm.ip_address, - port=self.gns3vm.port, - user=self.gns3vm.user, - password=self.gns3vm.password) + yield from self.gns3vm.auto_start_vm() @asyncio.coroutine def stop(self): @@ -88,9 +79,12 @@ class Controller: for project in self._projects.values(): yield from project.close() for compute in self._computes.values(): - yield from compute.close() - if self.gns3vm.enable and self.gns3vm.auto_stop: - yield from self.gns3vm.stop() + try: + yield from compute.close() + # We don't care if a compute is down at this step + except aiohttp.errors.ClientOSError: + pass + yield from self.gns3vm.auto_stop_vm() self._computes = {} self._projects = {} @@ -212,18 +206,19 @@ class Controller: return Config.instance().get_section_config("Server").getboolean("controller") @asyncio.coroutine - def add_compute(self, compute_id=None, name=None, **kwargs): + def add_compute(self, compute_id=None, name=None, force=False, **kwargs): """ Add a server to the dictionary of compute servers controlled by this controller :param compute_id: Compute server identifier :param name: Compute name + :param force: True skip security check :param kwargs: See the documentation of Compute """ if compute_id not in self._computes: # We disallow to create from the outside the local and VM server - if compute_id == 'local' or compute_id == 'vm': + if (compute_id == 'local' or compute_id == 'vm') and not force: return None for compute in self._computes.values(): diff --git a/gns3server/controller/gns3vm/__init__.py b/gns3server/controller/gns3vm/__init__.py index 24cc61f0..4e9d757c 100644 --- a/gns3server/controller/gns3vm/__init__.py +++ b/gns3server/controller/gns3vm/__init__.py @@ -16,6 +16,7 @@ # along with this program. If not, see . import sys +import copy import asyncio from .vmware_gns3_vm import VMwareGNS3VM @@ -41,7 +42,7 @@ class GNS3VM: "enable": False, "engine": "vmware" } - self._settings.update(settings) + self.settings = settings def engine_list(self): """ @@ -77,6 +78,15 @@ class GNS3VM: """ return self._current_engine().ip_address + @property + def running(self): + """ + Returns if the GNS3 VM is running. + + :returns: Boolean + """ + return self._current_engine().running + @property def user(self): """ @@ -134,7 +144,19 @@ class GNS3VM: @settings.setter def settings(self, val): self._settings.update(val) - self._controller.save() + + @asyncio.coroutine + def update_settings(self, settings): + """ + Update settings and will restart the VM if require + """ + new_settings = copy.copy(self._settings) + new_settings.update(settings) + if self.settings != new_settings: + yield from self._stop() + self._settings = settings + self._controller.save() + yield from self.auto_start_vm() def _get_engine(self, engine): """ @@ -166,7 +188,20 @@ class GNS3VM: return vms @asyncio.coroutine - def start(self): + def auto_start_vm(self): + """ + Auto start the GNS3 VM if require + """ + if self.enable: + yield from self._start() + + @asyncio.coroutine + def auto_stop_vm(self): + if self.enable and self.auto_stop: + yield from self._stop() + + @asyncio.coroutine + def _start(self): """ Start the GNS3 VM """ @@ -175,13 +210,23 @@ class GNS3VM: log.info("Start the GNS3 VM") engine.vmname = self._settings["vmname"] yield from engine.start() + yield from self._controller.add_compute(compute_id="vm", + name="GNS3 VM", + protocol=self.protocol, + host=self.ip_address, + port=self.port, + user=self.user, + password=self.password, + force=True) @asyncio.coroutine - def stop(self): + def _stop(self): """ Stop the GNS3 VM """ engine = self._current_engine() - if not engine.running: + if "vm" in self._controller.computes: + yield from self._controller.delete_compute("vm") + if engine.running: log.info("Stop the GNS3 VM") yield from engine.stop() diff --git a/gns3server/controller/node.py b/gns3server/controller/node.py index 7a105b53..8f797526 100644 --- a/gns3server/controller/node.py +++ b/gns3server/controller/node.py @@ -15,6 +15,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +import aiohttp import asyncio import copy import uuid @@ -338,7 +339,11 @@ class Node: """ Stop a node """ - yield from self.post("/stop") + try: + yield from self.post("/stop") + # We don't care if a compute is down at this step + except aiohttp.errors.ClientOSError: + pass @asyncio.coroutine def suspend(self): diff --git a/gns3server/controller/project.py b/gns3server/controller/project.py index e2793d84..d1ce7e34 100644 --- a/gns3server/controller/project.py +++ b/gns3server/controller/project.py @@ -494,7 +494,11 @@ class Project: def close(self, ignore_notification=False): yield from self.stop_all() for compute in self._project_created_on_compute: - yield from compute.post("/projects/{}/close".format(self._id)) + try: + yield from compute.post("/projects/{}/close".format(self._id)) + # We don't care if a compute is down at this step + except aiohttp.errors.ClientOSError: + pass self._cleanPictures() self._status = "closed" if not ignore_notification: diff --git a/gns3server/handlers/api/controller/gns3_vm_handler.py b/gns3server/handlers/api/controller/gns3_vm_handler.py index d68d943b..87366f71 100644 --- a/gns3server/handlers/api/controller/gns3_vm_handler.py +++ b/gns3server/handlers/api/controller/gns3_vm_handler.py @@ -75,6 +75,6 @@ class GNS3VMHandler: def update(request, response): gns3_vm = Controller().instance().gns3vm - gns3_vm.settings = request.json + yield from gns3_vm.update_settings(request.json) response.json(gns3_vm) response.set_status(201) diff --git a/tests/controller/test_controller.py b/tests/controller/test_controller.py index 09a0a7a1..10684c4c 100644 --- a/tests/controller/test_controller.py +++ b/tests/controller/test_controller.py @@ -301,9 +301,9 @@ def test_start_vm(controller, async_run): with asyncio_patch("gns3server.controller.gns3vm.vmware_gns3_vm.VMwareGNS3VM.start") as mock: async_run(controller.start()) assert mock.called - assert len(controller.computes) == 2 # Local compute and vm are created assert "local" in controller.computes assert "vm" in controller.computes + assert len(controller.computes) == 2 # Local compute and vm are created def test_stop(controller, async_run): @@ -322,7 +322,7 @@ def test_stop_vm(controller, async_run): "engine": "vmware", "auto_stop": True } - controller.gns3vm.running = True + controller.gns3vm._current_engine().running = True with asyncio_patch("gns3server.controller.gns3vm.vmware_gns3_vm.VMwareGNS3VM.stop") as mock: async_run(controller.stop()) assert mock.called diff --git a/tests/controller/test_gns3vm.py b/tests/controller/test_gns3vm.py index 85b05e4c..1cb241be 100644 --- a/tests/controller/test_gns3vm.py +++ b/tests/controller/test_gns3vm.py @@ -37,3 +37,16 @@ def test_list(async_run, controller): def test_json(controller): vm = GNS3VM(controller) assert vm.__json__() == vm._settings + + +def test_update_settings(controller, async_run): + vm = GNS3VM(controller) + vm.settings = { + "enable": True, + "engine": "vmware" + } + with asyncio_patch("gns3server.controller.gns3vm.vmware_gns3_vm.VMwareGNS3VM.start"): + async_run(vm.auto_start_vm()) + assert "vm" in controller.computes + async_run(vm.update_settings({"enable": False})) + assert "vm" not in controller.computes