You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
gns3-server/gns3server/server.py

163 lines
5.5 KiB

# -*- coding: utf-8 -*-
#
10 years ago
# Copyright (C) 2015 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
Set up and run the server.
"""
import os
10 years ago
import sys
import signal
10 years ago
import asyncio
import aiohttp
import ipaddress
10 years ago
import functools
import types
import time
10 years ago
from .web.route import Route
from .config import Config
from .modules import MODULES
#TODO: get rid of * have something generic to automatically import handlers so the routes can be found
from gns3server.handlers import *
10 years ago
import logging
log = logging.getLogger(__name__)
10 years ago
class Server:
10 years ago
def __init__(self, host, port, console_bind_to_any):
self._host = host
self._port = port
10 years ago
self._loop = None
self._start_time = time.time()
if console_bind_to_any:
if ipaddress.ip_address(self._host).version == 6:
self._console_host = "::"
else:
self._console_host = "0.0.0.0"
else:
self._console_host = self._host
10 years ago
#TODO: server config file support, to be reviewed
# # get the projects and temp directories from the configuration file (passed to the modules)
# config = Config.instance()
# server_config = config.get_default_section()
# # default projects directory is "~/GNS3/projects"
# self._projects_dir = os.path.expandvars(os.path.expanduser(server_config.get("projects_directory", "~/GNS3/projects")))
# self._temp_dir = server_config.get("temporary_directory", tempfile.gettempdir())
#
# try:
# os.makedirs(self._projects_dir)
# log.info("projects directory '{}' created".format(self._projects_dir))
# except FileExistsError:
# pass
# except OSError as e:
# log.error("could not create the projects directory {}: {}".format(self._projects_dir, e))
@asyncio.coroutine
def _run_application(self, app):
server = yield from self._loop.create_server(app.make_handler(), self._host, self._port)
return server
def _stop_application(self):
"""
10 years ago
Cleanup the modules (shutdown running emulators etc.)
"""
10 years ago
#TODO: clean everything from here
self._loop.stop()
10 years ago
def _signal_handling(self):
10 years ago
def signal_handler(signame):
log.warning("server has got signal {}, exiting...".format(signame))
self._stop_application()
signals = ["SIGTERM", "SIGINT"]
if sys.platform.startswith("win"):
signals.extend(["SIGBREAK"])
else:
signals.extend(["SIGHUP", "SIGQUIT"])
10 years ago
for signal_name in signals:
callback = functools.partial(signal_handler, signal_name)
if sys.platform.startswith("win"):
# add_signal_handler() is not yet supported on Windows
signal.signal(getattr(signal, signal_name), callback)
else:
self._loop.add_signal_handler(getattr(signal, signal_name), callback)
def _reload_hook(self):
def reload():
log.info("reloading")
self._stop_application()
os.execv(sys.executable, [sys.executable] + sys.argv)
# code extracted from tornado
for module in sys.modules.values():
# Some modules play games with sys.modules (e.g. email/__init__.py
# in the standard library), and occasionally this can cause strange
# failures in getattr. Just ignore anything that's not an ordinary
# module.
if not isinstance(module, types.ModuleType):
continue
path = getattr(module, "__file__", None)
if not path:
continue
if path.endswith(".pyc") or path.endswith(".pyo"):
path = path[:-1]
modified = os.stat(path).st_mtime
if modified > self._start_time:
log.debug("file {} has been modified".format(path))
reload()
self._loop.call_later(1, self._reload_hook)
def run(self):
"""
10 years ago
Starts the server.
"""
10 years ago
#TODO: SSL support for Rackspace cloud integration (here or with nginx for instance).
self._loop = asyncio.get_event_loop()
app = aiohttp.web.Application()
for method, route, handler in Route.get_routes():
log.debug("adding route: {} {}".format(method, route))
app.router.add_route(method, route, handler)
for module in MODULES:
log.debug("loading module {}".format(module.__name__))
module.instance()
10 years ago
log.info("starting server on {}:{}".format(self._host, self._port))
self._loop.run_until_complete(self._run_application(app))
self._signal_handling()
10 years ago
#FIXME: remove it in production
self._loop.call_later(1, self._reload_hook)
try:
10 years ago
self._loop.run_forever()
except KeyboardInterrupt:
log.info("\nExiting...")
self._cleanup()