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