1
0
mirror of https://github.com/GNS3/gns3-server synced 2025-01-12 09:00:57 +00:00

Catch exceptions in various locations to fix small issues reported by Sentry.

This commit is contained in:
grossmj 2018-09-06 09:49:12 +02:00
parent 341e2e2e3a
commit 4021a13651
8 changed files with 63 additions and 40 deletions

View File

@ -228,11 +228,13 @@ class DockerVM(BaseNode):
binds = ["{}:/gns3:ro".format(ressources)] binds = ["{}:/gns3:ro".format(ressources)]
# We mount our own etc/network # We mount our own etc/network
network_config = self._create_network_config() try:
network_config = self._create_network_config()
except OSError as e:
raise DockerError("Could not create network config in the container: {}".format(e))
binds.append("{}:/gns3volumes/etc/network:rw".format(network_config)) binds.append("{}:/gns3volumes/etc/network:rw".format(network_config))
self._volumes = ["/etc/network"] self._volumes = ["/etc/network"]
volumes = image_infos.get("Config", {}).get("Volumes") volumes = image_infos.get("Config", {}).get("Volumes")
if volumes is None: if volumes is None:
return binds return binds
@ -285,11 +287,12 @@ class DockerVM(BaseNode):
try: try:
image_infos = yield from self._get_image_information() image_infos = yield from self._get_image_information()
except DockerHttp404Error: except DockerHttp404Error:
log.info("Image %s is missing pulling it from docker hub", self._image) log.info("Image '{}' is missing, pulling it from Docker hub...".format(self._image))
yield from self.pull_image(self._image) yield from self.pull_image(self._image)
image_infos = yield from self._get_image_information() image_infos = yield from self._get_image_information()
if image_infos is None:
raise DockerError("Can't get image informations, please try again.") if image_infos is None:
raise DockerError("Cannot get information for image '{}', please try again.".format(self._image))
params = { params = {
"Hostname": self._name, "Hostname": self._name,
@ -313,7 +316,10 @@ class DockerVM(BaseNode):
if params["Entrypoint"] is None: if params["Entrypoint"] is None:
params["Entrypoint"] = [] params["Entrypoint"] = []
if self._start_command: if self._start_command:
params["Cmd"] = shlex.split(self._start_command) try:
params["Cmd"] = shlex.split(self._start_command)
except ValueError as e:
raise DockerError("Invalid start command '{}': {}".format(self._start_command, e))
if len(params["Cmd"]) == 0: if len(params["Cmd"]) == 0:
params["Cmd"] = image_infos.get("Config", {"Cmd": []})["Cmd"] params["Cmd"] = image_infos.get("Config", {"Cmd": []})["Cmd"]
if params["Cmd"] is None: if params["Cmd"] is None:
@ -355,8 +361,7 @@ class DockerVM(BaseNode):
result = yield from self.manager.query("POST", "containers/create", data=params) result = yield from self.manager.query("POST", "containers/create", data=params)
self._cid = result['Id'] self._cid = result['Id']
log.info("Docker container '{name}' [{id}] created".format( log.info("Docker container '{name}' [{id}] created".format(name=self._name, id=self._id))
name=self._name, id=self._id))
return True return True
def _format_env(self, variables, env): def _format_env(self, variables, env):
@ -449,16 +454,19 @@ class DockerVM(BaseNode):
@asyncio.coroutine @asyncio.coroutine
def _start_aux(self): def _start_aux(self):
""" """
Start an auxilary console Start an auxiliary console
""" """
# We can not use the API because docker doesn't expose a websocket api for exec # We can not use the API because docker doesn't expose a websocket api for exec
# https://github.com/GNS3/gns3-gui/issues/1039 # https://github.com/GNS3/gns3-gui/issues/1039
process = yield from asyncio.subprocess.create_subprocess_exec( try:
"docker", "exec", "-i", self._cid, "/gns3/bin/busybox", "script", "-qfc", "while true; do TERM=vt100 /gns3/bin/busybox sh; done", "/dev/null", process = yield from asyncio.subprocess.create_subprocess_exec(
stdout=asyncio.subprocess.PIPE, "docker", "exec", "-i", self._cid, "/gns3/bin/busybox", "script", "-qfc", "while true; do TERM=vt100 /gns3/bin/busybox sh; done", "/dev/null",
stderr=asyncio.subprocess.STDOUT, stdout=asyncio.subprocess.PIPE,
stdin=asyncio.subprocess.PIPE) stderr=asyncio.subprocess.STDOUT,
stdin=asyncio.subprocess.PIPE)
except OSError as e:
raise DockerError("Could not start auxiliary console process: {}".format(e))
server = AsyncioTelnetServer(reader=process.stdout, writer=process.stdin, binary=True, echo=True) server = AsyncioTelnetServer(reader=process.stdout, writer=process.stdin, binary=True, echo=True)
try: try:
self._telnet_servers.append((yield from asyncio.start_server(server.run, self._manager.port_manager.console_host, self.aux))) self._telnet_servers.append((yield from asyncio.start_server(server.run, self._manager.port_manager.console_host, self.aux)))
@ -481,21 +489,25 @@ class DockerVM(BaseNode):
for volume in self._volumes: for volume in self._volumes:
log.debug("Docker container '{name}' [{image}] fix ownership on {path}".format( log.debug("Docker container '{name}' [{image}] fix ownership on {path}".format(
name=self._name, image=self._image, path=volume)) name=self._name, image=self._image, path=volume))
process = yield from asyncio.subprocess.create_subprocess_exec(
"docker", try:
"exec", process = yield from asyncio.subprocess.create_subprocess_exec(
self._cid, "docker",
"/gns3/bin/busybox", "exec",
"sh", self._cid,
"-c", "/gns3/bin/busybox",
"(" "sh",
"/gns3/bin/busybox find \"{path}\" -depth -print0" "-c",
" | /gns3/bin/busybox xargs -0 /gns3/bin/busybox stat -c '%a:%u:%g:%n' > \"{path}/.gns3_perms\"" "("
")" "/gns3/bin/busybox find \"{path}\" -depth -print0"
" && /gns3/bin/busybox chmod -R u+rX \"{path}\"" " | /gns3/bin/busybox xargs -0 /gns3/bin/busybox stat -c '%a:%u:%g:%n' > \"{path}/.gns3_perms\""
" && /gns3/bin/busybox chown {uid}:{gid} -R \"{path}\"" ")"
.format(uid=os.getuid(), gid=os.getgid(), path=volume), " && /gns3/bin/busybox chmod -R u+rX \"{path}\""
) " && /gns3/bin/busybox chown {uid}:{gid} -R \"{path}\""
.format(uid=os.getuid(), gid=os.getgid(), path=volume),
)
except OSError as e:
raise DockerError("Could not fix permissions for {}: {}".format(volume, e))
yield from process.wait() yield from process.wait()
@asyncio.coroutine @asyncio.coroutine

View File

@ -1028,7 +1028,7 @@ class QemuVM(BaseNode):
if expect in line: if expect in line:
result = line.decode("utf-8").strip() result = line.decode("utf-8").strip()
break break
except EOFError as e: except (ConnectionError, EOFError) as e:
log.warn("Could not read from QEMU monitor: {}".format(e)) log.warn("Could not read from QEMU monitor: {}".format(e))
writer.close() writer.close()
return result return result

View File

@ -69,7 +69,7 @@ class Controller:
for directory, builtin in ( for directory, builtin in (
(get_resource('appliances'), True,), (self.appliances_path(), False,) (get_resource('appliances'), True,), (self.appliances_path(), False,)
): ):
if os.path.isdir(directory): if directory and os.path.isdir(directory):
for file in os.listdir(directory): for file in os.listdir(directory):
if not file.endswith('.gns3a') and not file.endswith('.gns3appliance'): if not file.endswith('.gns3a') and not file.endswith('.gns3appliance'):
continue continue
@ -509,7 +509,7 @@ class Controller:
return self._computes[compute_id] return self._computes[compute_id]
except KeyError: except KeyError:
if compute_id == "vm": if compute_id == "vm":
raise aiohttp.web.HTTPNotFound(text="You try to use a node on the GNS3 VM server but the GNS3 VM is not configured") raise aiohttp.web.HTTPNotFound(text="A node is set to use the GNS3 VM server but the GNS3 VM is not configured")
raise aiohttp.web.HTTPNotFound(text="Compute ID {} doesn't exist".format(compute_id)) raise aiohttp.web.HTTPNotFound(text="Compute ID {} doesn't exist".format(compute_id))
def has_compute(self, compute_id): def has_compute(self, compute_id):

View File

@ -177,7 +177,7 @@ class Node:
try: try:
with open(path) as f: with open(path) as f:
return f.read() return f.read()
except (PermissionError, OSError): except OSError:
return None return None
@property @property

View File

@ -871,7 +871,7 @@ class Project:
try: try:
if os.path.exists(path + ".backup"): if os.path.exists(path + ".backup"):
shutil.copy(path + ".backup", path) shutil.copy(path + ".backup", path)
except (PermissionError, OSError): except OSError:
pass pass
self._status = "closed" self._status = "closed"
self._loading = False self._loading = False

View File

@ -197,11 +197,9 @@ class AsyncioTelnetServer:
yield from self._write_intro(network_writer, echo=self._echo, binary=self._binary, naws=self._naws) yield from self._write_intro(network_writer, echo=self._echo, binary=self._binary, naws=self._naws)
yield from connection.connected() yield from connection.connected()
yield from self._process(network_reader, network_writer, connection) yield from self._process(network_reader, network_writer, connection)
except ConnectionResetError: except ConnectionError:
with (yield from self._lock): with (yield from self._lock):
network_writer.close() network_writer.close()
if self._reader_process == network_reader: if self._reader_process == network_reader:
self._reader_process = None self._reader_process = None
# Cancel current read from this reader # Cancel current read from this reader
@ -217,7 +215,7 @@ class AsyncioTelnetServer:
try: try:
writer.write_eof() writer.write_eof()
yield from writer.drain() yield from writer.drain()
except ConnectionResetError: except ConnectionError:
continue continue
@asyncio.coroutine @asyncio.coroutine

View File

@ -43,8 +43,16 @@ class PingStats:
cur_time > cls._last_measurement + 1.9: cur_time > cls._last_measurement + 1.9:
cls._last_measurement = cur_time cls._last_measurement = cur_time
# Non blocking call to get cpu usage. First call will return 0 # Non blocking call to get cpu usage. First call will return 0
cls._last_cpu_percent = psutil.cpu_percent(interval=None) try:
cls._last_mem_percent = psutil.virtual_memory().percent cls._last_cpu_percent = psutil.cpu_percent(interval=None)
cls._last_mem_percent = psutil.virtual_memory().percent
except RuntimeError:
# ignore the following error:
# RuntimeError: host_statistics(HOST_CPU_LOAD_INFO) syscall failed: (ipc/send) invalid reply port
pass
except PermissionError:
# [Errno 13] Permission denied: '/proc/stat'
pass
stats["cpu_usage_percent"] = cls._last_cpu_percent stats["cpu_usage_percent"] = cls._last_cpu_percent
stats["memory_usage_percent"] = cls._last_mem_percent stats["memory_usage_percent"] = cls._last_mem_percent
return stats return stats

View File

@ -225,6 +225,11 @@ class Route(object):
response = Response(request=request, route=route) response = Response(request=request, route=route)
response.set_status(408) response.set_status(408)
response.json({"message": "Client error", "status": 408}) response.json({"message": "Client error", "status": 408})
except MemoryError:
log.error("Memory error detected, server has run out of memory!", exc_info=1)
response = Response(request=request, route=route)
response.set_status(500)
response.json({"message": "Memory error", "status": 500})
except Exception as e: except Exception as e:
log.error("Uncaught exception detected: {type}".format(type=type(e)), exc_info=1) log.error("Uncaught exception detected: {type}".format(type=type(e)), exc_info=1)
response = Response(request=request, route=route) response = Response(request=request, route=route)