Manage error when GNS3VM failed to start

Fix https://github.com/GNS3/gns3-gui/issues/1446
This commit is contained in:
Julien Duponchelle 2016-09-01 15:36:41 +02:00
parent f216422d0f
commit 4bbd8938ab
No known key found for this signature in database
GPG Key ID: CE8B29639E07F5E8
7 changed files with 101 additions and 12 deletions

View File

@ -334,6 +334,9 @@ class Compute:
@asyncio.coroutine @asyncio.coroutine
def http_query(self, method, path, data=None, **kwargs): def http_query(self, method, path, data=None, **kwargs):
if not self._connected: if not self._connected:
if self._id == "vm" and not self._controller.gns3vm.running:
yield from self._controller.gns3vm.start()
yield from self.connect() yield from self.connect()
if not self._connected: if not self._connected:
raise aiohttp.web.HTTPConflict(text="Can't connect to {}".format(self._name)) raise aiohttp.web.HTTPConflict(text="Can't connect to {}".format(self._name))

View File

@ -19,9 +19,11 @@ import sys
import copy import copy
import asyncio import asyncio
from ...utils.asyncio import locked_coroutine
from .vmware_gns3_vm import VMwareGNS3VM from .vmware_gns3_vm import VMwareGNS3VM
from .virtualbox_gns3_vm import VirtualBoxGNS3VM from .virtualbox_gns3_vm import VirtualBoxGNS3VM
from .remote_gns3_vm import RemoteGNS3VM from .remote_gns3_vm import RemoteGNS3VM
from .gns3_vm_error import GNS3VMError
import logging import logging
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -212,8 +214,14 @@ class GNS3VM:
Auto start the GNS3 VM if require Auto start the GNS3 VM if require
""" """
if self.enable: if self.enable:
yield from self._start() try:
yield from self.start()
except GNS3VMError as e:
# User will receive the error later when they will try to use the node
yield from self._controller.add_compute(compute_id="vm",
name="GNS3 VM ({})".format(self._current_engine().vmname),
host=None,
force=True)
@asyncio.coroutine @asyncio.coroutine
def auto_stop_vm(self): def auto_stop_vm(self):
if self.enable and self.auto_stop: if self.enable and self.auto_stop:
@ -222,8 +230,8 @@ class GNS3VM:
except GNS3VMError as e: except GNS3VMError as e:
log.warn(str(e)) log.warn(str(e))
@asyncio.coroutine @locked_coroutine
def _start(self): def start(self):
""" """
Start the GNS3 VM Start the GNS3 VM
""" """
@ -232,7 +240,7 @@ class GNS3VM:
log.info("Start the GNS3 VM") log.info("Start the GNS3 VM")
engine.vmname = self._settings["vmname"] engine.vmname = self._settings["vmname"]
yield from engine.start() yield from engine.start()
yield from self._controller.add_compute(compute_id="vm", yield from self._controller.add_compute(compute_id="vm",
name="GNS3 VM ({})".format(engine.vmname), name="GNS3 VM ({})".format(engine.vmname),
protocol=self.protocol, protocol=self.protocol,
host=self.ip_address, host=self.ip_address,
@ -241,7 +249,7 @@ class GNS3VM:
password=self.password, password=self.password,
force=True) force=True)
@asyncio.coroutine @locked_coroutine
def _stop(self): def _stop(self):
""" """
Stop the GNS3 VM Stop the GNS3 VM

View File

@ -26,4 +26,4 @@ class GNS3VMError(Exception):
return self._message return self._message
def __str__(self): def __str__(self):
return self._message return "GNS3VM: {}".format(self._message)

View File

@ -114,7 +114,6 @@ class VMwareGNS3VM(BaseGNS3VM):
args.extend(["nogui"]) args.extend(["nogui"])
yield from self._execute("start", args) yield from self._execute("start", args)
log.info("GNS3 VM has been started") log.info("GNS3 VM has been started")
self.running = True
# check if the VMware guest tools are installed # check if the VMware guest tools are installed
vmware_tools_state = yield from self._execute("checkToolsState", [self._vmx_path]) vmware_tools_state = yield from self._execute("checkToolsState", [self._vmx_path])
@ -125,6 +124,7 @@ class VMwareGNS3VM(BaseGNS3VM):
guest_ip_address = yield from self._execute("getGuestIPAddress", [self._vmx_path, "-wait"], timeout=120) guest_ip_address = yield from self._execute("getGuestIPAddress", [self._vmx_path, "-wait"], timeout=120)
self.ip_address = guest_ip_address self.ip_address = guest_ip_address
log.info("GNS3 VM IP address set to {}".format(guest_ip_address)) log.info("GNS3 VM IP address set to {}".format(guest_ip_address))
self.running = True
@asyncio.coroutine @asyncio.coroutine
def stop(self): def stop(self):

View File

@ -92,6 +92,33 @@ def test_compute_httpQueryNotConnected(compute, controller, async_run):
controller.notification.emit.assert_called_with("compute.updated", compute.__json__()) controller.notification.emit.assert_called_with("compute.updated", compute.__json__())
def test_compute_httpQueryNotConnectedGNS3vmNotRunning(compute, controller, async_run):
"""
We are not connected to the remote and it's a GNS3 VM. So we need to start it
"""
controller._notification = MagicMock()
controller.gns3vm = AsyncioMagicMock()
controller.gns3vm.running = False
compute._id = "vm"
compute._connected = False
response = AsyncioMagicMock()
response.read = AsyncioMagicMock(return_value=json.dumps({"version": __version__}).encode())
response.status = 200
with asyncio_patch("aiohttp.ClientSession.request", return_value=response) as mock:
async_run(compute.post("/projects", {"a": "b"}))
mock.assert_any_call("GET", "https://example.com:84/v2/compute/capabilities", headers={'content-type': 'application/json'}, data=None, auth=None, chunked=False)
mock.assert_any_call("POST", "https://example.com:84/v2/compute/projects", data='{"a": "b"}', headers={'content-type': 'application/json'}, auth=None, chunked=False)
assert controller.gns3vm.start.called
assert compute._connected
assert compute._capabilities["version"] == __version__
controller.notification.emit.assert_called_with("compute.updated", compute.__json__())
def test_compute_httpQueryNotConnectedInvalidVersion(compute, async_run): def test_compute_httpQueryNotConnectedInvalidVersion(compute, async_run):
compute._connected = False compute._connected = False
response = AsyncioMagicMock() response = AsyncioMagicMock()

View File

@ -289,7 +289,12 @@ def test_getProject(controller, async_run):
def test_start(controller, async_run): def test_start(controller, async_run):
async_run(controller.start()) controller.gns3vm.settings = {
"enable": False,
"engine": "vmware"
}
with asyncio_patch("gns3server.controller.compute.Compute.connect") as mock:
async_run(controller.start())
assert len(controller.computes) == 1 # Local compute is created assert len(controller.computes) == 1 # Local compute is created
assert controller.computes["local"].name == socket.gethostname() assert controller.computes["local"].name == socket.gethostname()
@ -303,8 +308,9 @@ def test_start_vm(controller, async_run):
"engine": "vmware" "engine": "vmware"
} }
with asyncio_patch("gns3server.controller.gns3vm.vmware_gns3_vm.VMwareGNS3VM.start") as mock: with asyncio_patch("gns3server.controller.gns3vm.vmware_gns3_vm.VMwareGNS3VM.start") as mock:
async_run(controller.start()) with asyncio_patch("gns3server.controller.compute.Compute.connect") as mock_connect:
assert mock.called async_run(controller.start())
assert mock.called
assert "local" in controller.computes assert "local" in controller.computes
assert "vm" in controller.computes assert "vm" in controller.computes
assert len(controller.computes) == 2 # Local compute and vm are created assert len(controller.computes) == 2 # Local compute and vm are created

View File

@ -16,9 +16,31 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import pytest import pytest
from tests.utils import asyncio_patch from tests.utils import asyncio_patch, AsyncioMagicMock
from gns3server.controller.gns3vm import GNS3VM from gns3server.controller.gns3vm import GNS3VM
from gns3server.controller.gns3vm.gns3_vm_error import GNS3VMError
@pytest.fixture
def dummy_engine():
engine = AsyncioMagicMock()
engine.running = False
engine.ip_address = "vm.local"
engine.protocol = "https"
engine.port = 8442
engine.user = "hello"
engine.password = "world"
return engine
@pytest.fixture
def dummy_gns3vm(controller, dummy_engine):
vm = GNS3VM(controller)
vm._settings["engine"] = "dummy"
vm._settings["vmname"] = "Test VM"
vm._settings["enable"] = True
vm._engines["dummy"] = dummy_engine
return vm
def test_list(async_run, controller): def test_list(async_run, controller):
@ -50,3 +72,26 @@ def test_update_settings(controller, async_run):
assert "vm" in controller.computes assert "vm" in controller.computes
async_run(vm.update_settings({"enable": False})) async_run(vm.update_settings({"enable": False}))
assert "vm" not in controller.computes assert "vm" not in controller.computes
def test_auto_start(async_run, controller, dummy_gns3vm, dummy_engine):
"""
When start the compute should be add to the controller
"""
async_run(dummy_gns3vm.auto_start_vm())
assert dummy_engine.start.called
assert controller.computes["vm"].name == "GNS3 VM (Test VM)"
assert controller.computes["vm"].host == "vm.local"
assert controller.computes["vm"].port == 8442
assert controller.computes["vm"].protocol == "https"
assert controller.computes["vm"].user == "hello"
assert controller.computes["vm"].password == "world"
def test_auto_start_with_error(async_run, controller, dummy_gns3vm, dummy_engine):
dummy_engine.start.side_effect = GNS3VMError("Dummy error")
async_run(dummy_gns3vm.auto_start_vm())
assert dummy_engine.start.called
assert controller.computes["vm"].name == "GNS3 VM (Test VM)"