Fix installation with Python 3.7. Fixes #1414.

Fix deprecated use of aiohttp.Timeout. Fixes #1296.
Use "async with" with aiohttp.ClientSession().
Make sure websocket connections are properly closed, see https://docs.aiohttp.org/en/stable/web_advanced.html#graceful-shutdown
Finish to drop Python 3.4.
This commit is contained in:
grossmj committed 2018-10-16 15:56:06 +07:00
1 parent 8217f65e97
commit 86f87aec74
11 files changed
+124 -155

No files matched your search

+27 -56
View File
@@ -18,6 +18,7 @@
import ipaddress
import aiohttp
import asyncio
import async_timeout
import socket
import json
import uuid
@@ -52,22 +53,6 @@ class ComputeConflict(aiohttp.web.HTTPConflict):
self.response = response
class Timeout(aiohttp.Timeout):
"""
Could be removed with aiohttp 0.22 that support None timeout
"""
def __enter__(self):
if self._timeout:
return super().__enter__()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
if self._timeout:
return super().__exit__(exc_type, exc_val, exc_tb)
return self
class Compute:
"""
A GNS3 compute.
@@ -101,12 +86,8 @@ class Compute:
"node_types": []
}
self.name = name
# Websocket for notifications
self._ws = None
# Cache of interfaces on remote host
self._interfaces_cache = None
self._connection_failure = 0
def _session(self):
@@ -114,9 +95,10 @@ class Compute:
self._http_session = aiohttp.ClientSession(connector=aiohttp.TCPConnector(limit=None, force_close=True))
return self._http_session
def __del__(self):
if self._http_session:
self._http_session.close()
#def __del__(self):
# pass
# if self._http_session:
# self._http_session.close()
def _set_auth(self, user, password):
"""
@@ -162,19 +144,16 @@ class Compute:
# It's important to set user and password at the same time
if "user" in kwargs or "password" in kwargs:
self._set_auth(kwargs.get("user", self._user), kwargs.get("password", self._password))
if self._http_session:
self._http_session.close()
if self._http_session and not self._http_session.closed:
await self._http_session.close()
self._connected = False
self._controller.notification.controller_emit("compute.updated", self.__json__())
self._controller.save()
async def close(self):
self._connected = False
if self._http_session:
self._http_session.close()
if self._ws:
await self._ws.close()
self._ws = None
if self._http_session and not self._http_session.closed:
await self._http_session.close()
self._closed = True
@property
@@ -474,35 +453,27 @@ class Compute:
"""
Connect to the notification stream
"""
try:
self._ws = await self._session().ws_connect(self._getUrl("/notifications/ws"), auth=self._auth)
except (aiohttp.WSServerHandshakeError, aiohttp.ClientResponseError):
self._ws = None
while self._ws is not None:
try:
response = await self._ws.receive()
except aiohttp.WSServerHandshakeError:
self._ws = None
break
if response.tp == aiohttp.WSMsgType.closed or response.tp == aiohttp.WSMsgType.error or response.data is None:
self._connected = False
break
msg = json.loads(response.data)
action = msg.pop("action")
event = msg.pop("event")
if action == "ping":
self._cpu_usage_percent = event["cpu_usage_percent"]
self._memory_usage_percent = event["memory_usage_percent"]
self._controller.notification.controller_emit("compute.updated", self.__json__())
else:
await self._controller.notification.dispatch(action, event, compute_id=self.id)
if self._ws:
await self._ws.close()
async with self._session().ws_connect(self._getUrl("/notifications/ws"), auth=self._auth) as ws:
async for response in ws:
if response.type == aiohttp.WSMsgType.TEXT and response.data:
msg = json.loads(response.data)
action = msg.pop("action")
event = msg.pop("event")
if action == "ping":
self._cpu_usage_percent = event["cpu_usage_percent"]
self._memory_usage_percent = event["memory_usage_percent"]
self._controller.notification.controller_emit("compute.updated", self.__json__())
else:
await self._controller.notification.dispatch(action, event, compute_id=self.id)
elif response.type == aiohttp.WSMsgType.CLOSED or response.type == aiohttp.WSMsgType.ERROR or response.data is None:
self._connected = False
break
# Try to reconnect after 1 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:
asyncio.get_event_loop().call_later(1, lambda: asyncio.ensure_future(self.connect()))
self._ws = None
self._cpu_usage_percent = None
self._memory_usage_percent = None
self._controller.notification.controller_emit("compute.updated", self.__json__())
@@ -527,7 +498,7 @@ class Compute:
return self._getUrl(path)
async def _run_http_query(self, method, path, data=None, timeout=20, raw=False):
with Timeout(timeout):
with async_timeout.timeout(timeout):
url = self._getUrl(path)
headers = {}
headers['content-type'] = 'application/json'