Support controller reloading for templates, appliances and projects. Ref #1743

This commit is contained in:
grossmj 2020-04-30 15:30:50 +09:30
parent 7b61724213
commit 02c0fa26e1
8 changed files with 44 additions and 12 deletions

View File

@ -80,7 +80,7 @@ class Controller:
if name == "gns3vm": if name == "gns3vm":
name = "Main server" name = "Main server"
computes = await self._load_controller_settings() computes = self._load_controller_settings()
try: try:
self._local_server = await self.add_compute(compute_id="local", self._local_server = await self.add_compute(compute_id="local",
name=name, name=name,
@ -118,7 +118,7 @@ class Controller:
async def stop(self): async def stop(self):
log.info("Controller is Stopping") log.info("Controller is stopping")
for project in self._projects.values(): for project in self._projects.values():
await project.close() await project.close()
for compute in self._computes.values(): for compute in self._computes.values():
@ -132,6 +132,12 @@ class Controller:
self._computes = {} self._computes = {}
self._projects = {} self._projects = {}
async def reload(self):
log.info("Controller is reloading")
self._load_controller_settings()
await self.load_projects()
def check_can_write_config(self): def check_can_write_config(self):
""" """
Check if the controller configuration can be written on disk Check if the controller configuration can be written on disk
@ -182,7 +188,7 @@ class Controller:
except OSError as e: except OSError as e:
log.error("Cannot write controller configuration file '{}': {}".format(self._config_file, e)) log.error("Cannot write controller configuration file '{}': {}".format(self._config_file, e))
async def _load_controller_settings(self): def _load_controller_settings(self):
""" """
Reload the controller configuration from disk Reload the controller configuration from disk
""" """

View File

@ -18,6 +18,9 @@
import copy import copy
import uuid import uuid
import logging
log = logging.getLogger(__name__)
class Appliance: class Appliance:
@ -33,6 +36,9 @@ class Appliance:
if "appliance_id" in self._data: if "appliance_id" in self._data:
del self._data["appliance_id"] del self._data["appliance_id"]
if self.status != 'broken':
log.debug('Appliance "{name}" [{id}] loaded'.format(name=self.name, id=self._id))
@property @property
def id(self): def id(self):
return self._id return self._id
@ -45,6 +51,10 @@ class Appliance:
def symbol(self): def symbol(self):
return self._data.get("symbol") return self._data.get("symbol")
@property
def name(self):
return self._data.get("name")
@symbol.setter @symbol.setter
def symbol(self, new_symbol): def symbol(self, new_symbol):
self._data["symbol"] = new_symbol self._data["symbol"] = new_symbol

View File

@ -129,6 +129,8 @@ class Project:
self._iou_id_lock = asyncio.Lock() self._iou_id_lock = asyncio.Lock()
log.debug('Project "{name}" [{id}] loaded'.format(name=self.name, id=self._id))
def emit_notification(self, action, event): def emit_notification(self, action, event):
""" """
Emit a notification to all clients using this project. Emit a notification to all clients using this project.

View File

@ -147,6 +147,8 @@ class Template:
# special case for Dynamips to cover all platform types that contain specific settings # special case for Dynamips to cover all platform types that contain specific settings
self.validate_and_apply_defaults(DYNAMIPS_PLATFORM_TO_SHEMA[self._settings["platform"]]) self.validate_and_apply_defaults(DYNAMIPS_PLATFORM_TO_SHEMA[self._settings["platform"]])
log.debug('Template "{name}" [{id}] loaded'.format(name=self.name, id=self._id))
@property @property
def id(self): def id(self):
return self._id return self._id

View File

@ -240,9 +240,8 @@ def run():
if sys.version_info < (3, 5, 3): if sys.version_info < (3, 5, 3):
raise SystemExit("Python 3.5.3 or higher is required") raise SystemExit("Python 3.5.3 or higher is required")
user_log.info("Running with Python {major}.{minor}.{micro} and has PID {pid}".format( user_log.info("Running with Python {major}.{minor}.{micro} and has PID {pid}".format(major=sys.version_info[0], minor=sys.version_info[1],
major=sys.version_info[0], minor=sys.version_info[1], micro=sys.version_info[2], pid=os.getpid()))
micro=sys.version_info[2], pid=os.getpid()))
# check for the correct locale (UNIX/Linux only) # check for the correct locale (UNIX/Linux only)
locale_check() locale_check()
@ -255,7 +254,6 @@ def run():
CrashReport.instance() CrashReport.instance()
host = server_config["host"] host = server_config["host"]
port = int(server_config["port"]) port = int(server_config["port"])
server = WebServer.instance(host, port) server = WebServer.instance(host, port)

View File

@ -88,6 +88,15 @@ class WebServer:
return False return False
return True return True
async def reload_server(self):
"""
Reload the server.
"""
await Controller.instance().reload()
async def shutdown_server(self): async def shutdown_server(self):
""" """
Cleanly shutdown the server. Cleanly shutdown the server.
@ -141,9 +150,14 @@ class WebServer:
def _signal_handling(self): def _signal_handling(self):
def signal_handler(signame, *args): def signal_handler(signame, *args):
log.warning("Server has got signal {}, exiting...".format(signame))
try: try:
asyncio.ensure_future(self.shutdown_server()) if signame == "SIGHUP":
log.info("Server has got signal {}, reloading...".format(signame))
asyncio.ensure_future(self.reload_server())
else:
log.warning("Server has got signal {}, exiting...".format(signame))
asyncio.ensure_future(self.shutdown_server())
except asyncio.CancelledError: except asyncio.CancelledError:
pass pass
@ -265,9 +279,7 @@ class WebServer:
# Background task started with the server # Background task started with the server
self._app.on_startup.append(self._on_startup) self._app.on_startup.append(self._on_startup)
resource_options = aiohttp_cors.ResourceOptions( resource_options = aiohttp_cors.ResourceOptions(expose_headers="*", allow_headers="*", max_age=0)
expose_headers="*", allow_headers="*", max_age=0
)
# Allow CORS for this domains # Allow CORS for this domains
cors = aiohttp_cors.setup(self._app, defaults={ cors = aiohttp_cors.setup(self._app, defaults={

View File

@ -12,6 +12,7 @@ ExecStartPre=/bin/mkdir -p /var/log/gns3 /var/run/gns3
ExecStartPre=/bin/chown -R gns3:gns3 /var/log/gns3 /var/run/gns3 ExecStartPre=/bin/chown -R gns3:gns3 /var/log/gns3 /var/run/gns3
ExecStart=/usr/local/bin/gns3server --log /var/log/gns3/gns3.log \ ExecStart=/usr/local/bin/gns3server --log /var/log/gns3/gns3.log \
--pid /var/run/gns3/gns3.pid --daemon --pid /var/run/gns3/gns3.pid --daemon
ExecReload=/bin/kill -s HUP $MAINPID
Restart=on-abort Restart=on-abort
PIDFile=/var/run/gns3/gns3.pid PIDFile=/var/run/gns3/gns3.pid

View File

@ -264,6 +264,7 @@ Environment="PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
ExecStartPre=/bin/mkdir -p /var/log/gns3 /var/run/gns3 ExecStartPre=/bin/mkdir -p /var/log/gns3 /var/run/gns3
ExecStartPre=/bin/chown -R gns3:gns3 /var/log/gns3 /var/run/gns3 ExecStartPre=/bin/chown -R gns3:gns3 /var/log/gns3 /var/run/gns3
ExecStart=/usr/bin/gns3server --log /var/log/gns3/gns3.log ExecStart=/usr/bin/gns3server --log /var/log/gns3/gns3.log
ExecReload=/bin/kill -s HUP $MAINPID
Restart=on-failure Restart=on-failure
RestartSec=5 RestartSec=5
LimitNOFILE=16384 LimitNOFILE=16384