From bad3ef7003e1c5f4329840c25a6ed05b7d0b8de1 Mon Sep 17 00:00:00 2001 From: grossmj Date: Sat, 17 Apr 2021 18:33:20 +0930 Subject: [PATCH] Detect the app is exiting and avoid reconnecting to computes. --- gns3server/api/server.py | 14 +++++++++++++- gns3server/controller/compute.py | 5 +++-- gns3server/core/tasks.py | 2 +- gns3server/server.py | 11 ++--------- 4 files changed, 19 insertions(+), 13 deletions(-) diff --git a/gns3server/api/server.py b/gns3server/api/server.py index de2310eb..da948299 100644 --- a/gns3server/api/server.py +++ b/gns3server/api/server.py @@ -25,7 +25,7 @@ from fastapi import FastAPI, Request from starlette.exceptions import HTTPException as StarletteHTTPException from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import JSONResponse - +from uvicorn.main import Server as UvicornServer from gns3server.controller.controller_error import ( ControllerError, @@ -82,6 +82,18 @@ def get_application() -> FastAPI: app = get_application() +# Monkey Patch uvicorn signal handler to detect the application is shutting down +app.state.exiting = False +unicorn_exit_handler = UvicornServer.handle_exit + + +def handle_exit(*args, **kwargs): + app.state.exiting = True + unicorn_exit_handler(*args, **kwargs) + + +UvicornServer.handle_exit = handle_exit + @app.exception_handler(ControllerError) async def controller_error_handler(request: Request, exc: ControllerError): diff --git a/gns3server/controller/compute.py b/gns3server/controller/compute.py index 04c73950..4c5969f0 100644 --- a/gns3server/controller/compute.py +++ b/gns3server/controller/compute.py @@ -21,7 +21,6 @@ import asyncio import async_timeout import socket import json -import uuid import sys import io from operator import itemgetter @@ -295,6 +294,7 @@ class Compute: """ :param topology_dump: Filter to keep only properties require for saving on disk """ + if topology_dump: return { "compute_id": self._id, @@ -486,7 +486,8 @@ class Compute: log.info(f"Connection closed to compute '{self._id}' WebSocket '{ws_url}'") # Try to reconnect after 1 second if server unavailable only if not during tests (otherwise we create a ressources usage bomb) - if self.id != "local" and not hasattr(sys, "_called_from_test") or not sys._called_from_test: + from gns3server.api.server import app + if not app.state.exiting and not hasattr(sys, "_called_from_test") or not sys._called_from_test: log.info(f"Reconnecting to to compute '{self._id}' WebSocket '{ws_url}'") asyncio.get_event_loop().call_later(1, lambda: asyncio.ensure_future(self.connect())) diff --git a/gns3server/core/tasks.py b/gns3server/core/tasks.py index 5080c7d9..f1a95556 100644 --- a/gns3server/core/tasks.py +++ b/gns3server/core/tasks.py @@ -82,7 +82,7 @@ def create_startup_handler(app: FastAPI) -> Callable: def create_shutdown_handler(app: FastAPI) -> Callable: """ - Tasks to be performed when the server is shutdown. + Tasks to be performed when the server is exiting. """ async def shutdown_handler() -> None: diff --git a/gns3server/server.py b/gns3server/server.py index 409d35cb..3c7dedb1 100644 --- a/gns3server/server.py +++ b/gns3server/server.py @@ -38,7 +38,6 @@ from gns3server.version import __version__ from gns3server.config import Config from gns3server.crash_report import CrashReport from gns3server.api.server import app - from pydantic import ValidationError import logging @@ -170,22 +169,16 @@ class Server: config.Server.certkey = args.certkey config.Server.enable_ssl = args.ssl - async def reload_server(self): - """ - Reload the server. - """ - - await Controller.instance().reload() - def _signal_handling(self): def signal_handler(signame, *args): try: if signame == "SIGHUP": log.info(f"Server has got signal {signame}, reloading...") - asyncio.ensure_future(self.reload_server()) + asyncio.ensure_future(Controller.instance().reload()) else: log.info(f"Server has got signal {signame}, exiting...") + # send SIGTERM to the server PID so uvicorn can shutdown the process os.kill(os.getpid(), signal.SIGTERM) except asyncio.CancelledError: pass