1
0
mirror of https://github.com/GNS3/gns3-server synced 2024-12-26 00:38:10 +00:00

Close project if one one the compute of the project is down

Fix #836
This commit is contained in:
Julien Duponchelle 2016-12-15 21:57:59 +01:00
parent 60eea1f171
commit 3259ec1220
No known key found for this signature in database
GPG Key ID: CE8B29639E07F5E8
6 changed files with 32 additions and 13 deletions

View File

@ -270,6 +270,15 @@ class Controller:
self.notification.emit("compute.updated", self._computes[compute_id].__json__()) self.notification.emit("compute.updated", self._computes[compute_id].__json__())
return self._computes[compute_id] return self._computes[compute_id]
@asyncio.coroutine
def close_compute_projects(self, compute):
"""
Close projects running on a compute
"""
for project in self._projects.values():
if compute in project.computes:
yield from project.close()
@asyncio.coroutine @asyncio.coroutine
def delete_compute(self, compute_id): def delete_compute(self, compute_id):
""" """
@ -281,11 +290,7 @@ class Controller:
compute = self.get_compute(compute_id) compute = self.get_compute(compute_id)
except aiohttp.web.HTTPNotFound: except aiohttp.web.HTTPNotFound:
return return
yield from self.close_compute_projects(compute)
for project in self._projects.values():
if compute in project.computes:
yield from project.close()
yield from compute.close() yield from compute.close()
del self._computes[compute_id] del self._computes[compute_id]
self.save() self.save()

View File

@ -107,6 +107,8 @@ class Compute:
# Cache of interfaces on remote host # Cache of interfaces on remote host
self._interfaces_cache = None self._interfaces_cache = None
self._connection_failure = 0
def _session(self): def _session(self):
if self._http_session is None or self._http_session.closed is True: if self._http_session is None or self._http_session.closed is True:
self._http_session = aiohttp.ClientSession(connector=aiohttp.TCPConnector(limit=None, force_close=True)) self._http_session = aiohttp.ClientSession(connector=aiohttp.TCPConnector(limit=None, force_close=True))
@ -344,14 +346,17 @@ class Compute:
return StreamResponse(response) return StreamResponse(response)
@asyncio.coroutine @asyncio.coroutine
def http_query(self, method, path, data=None, **kwargs): def http_query(self, method, path, data=None, dont_connect=False, **kwargs):
if not self._connected: """
:param dont_connect: If true do not reconnect if not connected
"""
if not self._connected and not dont_connect:
if self._id == "vm" and not self._controller.gns3vm.running: if self._id == "vm" and not self._controller.gns3vm.running:
yield from self._controller.gns3vm.start() yield from self._controller.gns3vm.start()
yield from self.connect() yield from self.connect()
if not self._connected: if not self._connected and not dont_connect:
raise aiohttp.web.HTTPConflict(text="Can't connect to {}".format(self._name)) raise ComputeError("Can't connect to {}".format(self._name))
response = yield from self._run_http_query(method, path, data=data, **kwargs) response = yield from self._run_http_query(method, path, data=data, **kwargs)
return response return response
@ -366,7 +371,12 @@ class Compute:
except ComputeError: except ComputeError:
# Try to reconnect after 2 seconds if server unavailable only if not during tests (otherwise we create a ressources usage bomb) # Try to reconnect after 2 seconds if server unavailable only if not during tests (otherwise we create a ressources usage bomb)
if not hasattr(sys, "_called_from_test") or not sys._called_from_test: if not hasattr(sys, "_called_from_test") or not sys._called_from_test:
self._connection_failure += 1
# After 5 failure we close the project using the compute to avoid sync issues
if self._connection_failure == 5:
yield from self._controller.close_compute_projects(self)
asyncio.get_event_loop().call_later(2, lambda: asyncio.async(self.connect())) asyncio.get_event_loop().call_later(2, lambda: asyncio.async(self.connect()))
return return
except aiohttp.web.HTTPNotFound: except aiohttp.web.HTTPNotFound:
raise aiohttp.web.HTTPConflict(text="The server {} is not a GNS3 server or it's a 1.X server".format(self._id)) raise aiohttp.web.HTTPConflict(text="The server {} is not a GNS3 server or it's a 1.X server".format(self._id))
@ -383,6 +393,7 @@ class Compute:
self._notifications = asyncio.gather(self._connect_notification()) self._notifications = asyncio.gather(self._connect_notification())
self._connected = True self._connected = True
self._connection_failure = 0
self._controller.notification.emit("compute.updated", self.__json__()) self._controller.notification.emit("compute.updated", self.__json__())
@asyncio.coroutine @asyncio.coroutine

View File

@ -425,7 +425,7 @@ class Node:
Stop a node Stop a node
""" """
try: try:
yield from self.post("/stop", timeout=240) yield from self.post("/stop", timeout=240, dont_connect=True)
# We don't care if a node is down at this step # We don't care if a node is down at this step
except (ComputeError, aiohttp.errors.ClientHttpProcessingError, aiohttp.web.HTTPError): except (ComputeError, aiohttp.errors.ClientHttpProcessingError, aiohttp.web.HTTPError):
pass pass

View File

@ -541,7 +541,7 @@ class Project:
yield from self.stop_all() yield from self.stop_all()
for compute in self._project_created_on_compute: for compute in self._project_created_on_compute:
try: try:
yield from compute.post("/projects/{}/close".format(self._id)) yield from compute.post("/projects/{}/close".format(self._id), dont_connect=True)
# We don't care if a compute is down at this step # We don't care if a compute is down at this step
except (ComputeError, aiohttp.web.HTTPError, aiohttp.ClientResponseError, TimeoutError): except (ComputeError, aiohttp.web.HTTPError, aiohttp.ClientResponseError, TimeoutError):
pass pass

View File

@ -36,6 +36,7 @@ class Pool():
Wait for all task to finish Wait for all task to finish
""" """
pending = set() pending = set()
exceptions = set()
while len(self._tasks) > 0 or len(pending) > 0: while len(self._tasks) > 0 or len(pending) > 0:
while len(self._tasks) > 0 and len(pending) < self._concurrency: while len(self._tasks) > 0 and len(pending) < self._concurrency:
task, args, kwargs = self._tasks.pop(0) task, args, kwargs = self._tasks.pop(0)
@ -43,7 +44,9 @@ class Pool():
(done, pending) = yield from asyncio.wait(pending, return_when=asyncio.FIRST_COMPLETED) (done, pending) = yield from asyncio.wait(pending, return_when=asyncio.FIRST_COMPLETED)
for task in done: for task in done:
if task.exception(): if task.exception():
raise task.exception() exceptions.add(task.exception())
if len(exceptions) > 0:
raise exceptions.pop()
def main(): def main():

View File

@ -366,7 +366,7 @@ def test_stop(node, compute, project, async_run):
compute.post = AsyncioMagicMock() compute.post = AsyncioMagicMock()
async_run(node.stop()) async_run(node.stop())
compute.post.assert_called_with("/projects/{}/vpcs/nodes/{}/stop".format(node.project.id, node.id), timeout=240) compute.post.assert_called_with("/projects/{}/vpcs/nodes/{}/stop".format(node.project.id, node.id), timeout=240, dont_connect=True)
def test_suspend(node, compute, project, async_run): def test_suspend(node, compute, project, async_run):