1
0
mirror of https://github.com/GNS3/gns3-server synced 2024-12-01 04:38:12 +00:00

Add zeroconf. Ref #545.

This commit is contained in:
grossmj 2016-06-13 15:07:20 -06:00
parent 2bde02d459
commit 31f4b52631
6 changed files with 1736 additions and 8 deletions

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (C) 2013 GNS3 Technologies Inc. # Copyright (C) 2016 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (C) 2013 GNS3 Technologies Inc. # Copyright (C) 2016 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (C) 2013 GNS3 Technologies Inc. # Copyright (C) 2016 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by

View File

@ -88,6 +88,7 @@ def parse_arguments(argv):
parser.add_argument("-v", "--version", help="show the version", action="version", version=__version__) parser.add_argument("-v", "--version", help="show the version", action="version", version=__version__)
parser.add_argument("--host", help="run on the given host/IP address") parser.add_argument("--host", help="run on the given host/IP address")
parser.add_argument("--port", help="run on the given port", type=int) parser.add_argument("--port", help="run on the given port", type=int)
parser.add_argument("--service_interface", help="interface from which to extract the IP address in order to advertise the server via mDNS")
parser.add_argument("--ssl", action="store_true", help="run in SSL mode") parser.add_argument("--ssl", action="store_true", help="run in SSL mode")
parser.add_argument("--controller", action="store_true", help="start as a GNS3 controller") parser.add_argument("--controller", action="store_true", help="start as a GNS3 controller")
parser.add_argument("--config", help="Configuration file") parser.add_argument("--config", help="Configuration file")
@ -111,6 +112,7 @@ def parse_arguments(argv):
defaults = { defaults = {
"host": config.get("host", "0.0.0.0"), "host": config.get("host", "0.0.0.0"),
"port": config.get("port", 3080), "port": config.get("port", 3080),
"service_interface": config.get("service_interface", "eth0"),
"ssl": config.getboolean("ssl", False), "ssl": config.getboolean("ssl", False),
"certfile": config.get("certfile", ""), "certfile": config.get("certfile", ""),
"certkey": config.get("certkey", ""), "certkey": config.get("certkey", ""),
@ -136,6 +138,7 @@ def set_config(args):
server_config["allow_remote_console"] = str(args.allow) server_config["allow_remote_console"] = str(args.allow)
server_config["host"] = args.host server_config["host"] = args.host
server_config["port"] = str(args.port) server_config["port"] = str(args.port)
server_config["service_interface"] = str(args.service_interface)
server_config["ssl"] = str(args.ssl) server_config["ssl"] = str(args.ssl)
server_config["certfile"] = args.certfile server_config["certfile"] = args.certfile
server_config["certkey"] = args.certkey server_config["certkey"] = args.certkey
@ -226,8 +229,9 @@ def run():
CrashReport.instance() CrashReport.instance()
host = server_config["host"] host = server_config["host"]
port = int(server_config["port"]) port = int(server_config["port"])
service_interface = server_config["service_interface"]
server = WebServer.instance(host, port) server = WebServer.instance(host, port, service_interface)
try: try:
server.run() server.run()
except OSError as e: except OSError as e:

1684
gns3server/utils/zeroconf.py Normal file

File diff suppressed because it is too large Load Diff

View File

@ -22,16 +22,19 @@ Set up and run the server.
import os import os
import sys import sys
import signal import signal
import socket
import ipaddress
import asyncio import asyncio
import aiohttp import aiohttp
import aiohttp_cors import aiohttp_cors
import functools import functools
import types
import time import time
import atexit import atexit
from .route import Route from .route import Route
from .request_handler import RequestHandler from .request_handler import RequestHandler
from ..utils.zeroconf import ServiceInfo, Zeroconf
from ..utils.interfaces import interfaces
from ..config import Config from ..config import Config
from ..compute import MODULES from ..compute import MODULES
from ..compute.port_manager import PortManager from ..compute.port_manager import PortManager
@ -47,17 +50,18 @@ log = logging.getLogger(__name__)
class WebServer: class WebServer:
def __init__(self, host, port): def __init__(self, host, port, service_interface):
self._host = host self._host = host
self._port = port self._port = port
self._service_interface = service_interface
self._loop = None self._loop = None
self._handler = None self._handler = None
self._start_time = time.time() self._start_time = time.time()
self._port_manager = PortManager(host) self._port_manager = PortManager(host)
@staticmethod @staticmethod
def instance(host=None, port=None): def instance(host=None, port=None, service_interface=None):
""" """
Singleton to return only one instance of Server. Singleton to return only one instance of Server.
@ -67,7 +71,8 @@ class WebServer:
if not hasattr(WebServer, "_instance") or WebServer._instance is None: if not hasattr(WebServer, "_instance") or WebServer._instance is None:
assert host is not None assert host is not None
assert port is not None assert port is not None
WebServer._instance = WebServer(host, port) assert service_interface is not None
WebServer._instance = WebServer(host, port, service_interface)
return WebServer._instance return WebServer._instance
@asyncio.coroutine @asyncio.coroutine
@ -172,6 +177,38 @@ class WebServer:
atexit.register(close_asyncio_loop) atexit.register(close_asyncio_loop)
def _start_zeroconf(self):
"""
Starts the zero configuration networking service.
"""
service_ip = self._host
service_port = self._port
valid_ip = True
try:
# test if this is a valid IP address
ipaddress.ip_address(valid_ip)
except ValueError:
valid_ip = False
if service_ip == "0.0.0.0" or service_ip == "::":
valid_ip = False
if valid_ip is False:
# look for the service interface to extract its IP address
local_interfaces = [interface for interface in interfaces() if interface["name"] == self._service_interface]
if not local_interfaces:
log.error("Could not find service interface {}".format(self._service_interface))
else:
service_ip = local_interfaces[0]["ip_address"]
# Advertise the server with DNS multicast
info = ServiceInfo("_http._tcp.local.", "GNS3VM._http._tcp.local.", socket.inet_aton(service_ip), service_port, 0, 0, properties={})
zeroconf = Zeroconf(interfaces=[self._host])
zeroconf.register_service(info)
return zeroconf, info
def run(self): def run(self):
""" """
Starts the server. Starts the server.
@ -236,6 +273,7 @@ class WebServer:
if server_config.getboolean("shell"): if server_config.getboolean("shell"):
asyncio.async(self.start_shell()) asyncio.async(self.start_shell())
zeroconf, info = self._start_zeroconf()
try: try:
self._loop.run_forever() self._loop.run_forever()
except TypeError as e: except TypeError as e:
@ -244,6 +282,8 @@ class WebServer:
# TypeError: async() takes 1 positional argument but 3 were given # TypeError: async() takes 1 positional argument but 3 were given
log.warning("TypeError exception in the loop {}".format(e)) log.warning("TypeError exception in the loop {}".format(e))
finally: finally:
zeroconf.unregister_service(info)
zeroconf.close()
if self._handler and self._loop.is_running(): if self._handler and self._loop.is_running():
self._loop.run_until_complete(self._handler.finish_connections()) self._loop.run_until_complete(self._handler.finish_connections())
server.close() server.close()