mirror of
https://github.com/GNS3/gns3-server.git
synced 2025-01-30 21:03:49 +02:00
Network handler for UDP port allocation and server network interfaces.
This commit is contained in:
parent
c002bbfb23
commit
50fea669b5
@ -1 +1 @@
|
|||||||
__all__ = ['version_handler', 'vpcs_handler', 'project_handler', 'virtualbox_handler']
|
__all__ = ['version_handler', 'network_handler', 'vpcs_handler', 'project_handler', 'virtualbox_handler']
|
||||||
|
46
gns3server/handlers/network_handler.py
Normal file
46
gns3server/handlers/network_handler.py
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# 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/>.
|
||||||
|
|
||||||
|
from ..web.route import Route
|
||||||
|
from ..modules.port_manager import PortManager
|
||||||
|
from ..utils.interfaces import interfaces
|
||||||
|
|
||||||
|
|
||||||
|
class NetworkHandler:
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
@Route.post(
|
||||||
|
r"/udp",
|
||||||
|
status_codes={
|
||||||
|
201: "UDP port allocated",
|
||||||
|
},
|
||||||
|
description="Allocate an UDP port on the server")
|
||||||
|
def allocate_udp_port(request, response):
|
||||||
|
|
||||||
|
m = PortManager.instance()
|
||||||
|
udp_port = m.get_free_udp_port()
|
||||||
|
response.set_status(201)
|
||||||
|
response.json({"udp_port": udp_port})
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
@Route.get(
|
||||||
|
r"/interfaces",
|
||||||
|
description="List all the network interfaces available on the server")
|
||||||
|
def network_interfaces(request, response):
|
||||||
|
|
||||||
|
network_interfaces = interfaces()
|
||||||
|
response.json(network_interfaces)
|
@ -51,6 +51,21 @@ class PortManager:
|
|||||||
else:
|
else:
|
||||||
self._console_host = host
|
self._console_host = host
|
||||||
|
|
||||||
|
assert not hasattr(PortManager, "_instance")
|
||||||
|
PortManager._instance = self
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def instance(cls):
|
||||||
|
"""
|
||||||
|
Singleton to return only one instance of PortManager.
|
||||||
|
|
||||||
|
:returns: instance of PortManager
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not hasattr(cls, "_instance") or cls._instance is None:
|
||||||
|
cls._instance = cls()
|
||||||
|
return cls._instance
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def console_host(self):
|
def console_host(self):
|
||||||
|
|
||||||
|
@ -807,7 +807,10 @@ class VirtualBoxVM(BaseVM):
|
|||||||
yield from self._control_vm("nic{} null".format(adapter_id + 1))
|
yield from self._control_vm("nic{} null".format(adapter_id + 1))
|
||||||
|
|
||||||
nio = adapter.get_nio(0)
|
nio = adapter.get_nio(0)
|
||||||
|
if str(nio) == "NIO UDP":
|
||||||
|
self.manager.port_manager.release_udp_port(nio.lport)
|
||||||
adapter.remove_nio(0)
|
adapter.remove_nio(0)
|
||||||
|
|
||||||
log.info("VirtualBox VM '{name}' [{uuid}]: {nio} removed from adapter {adapter_id}".format(name=self.name,
|
log.info("VirtualBox VM '{name}' [{uuid}]: {nio} removed from adapter {adapter_id}".format(name=self.name,
|
||||||
uuid=self.uuid,
|
uuid=self.uuid,
|
||||||
nio=nio,
|
nio=nio,
|
||||||
|
@ -343,7 +343,10 @@ class VPCSVM(BaseVM):
|
|||||||
port_id=port_id))
|
port_id=port_id))
|
||||||
|
|
||||||
nio = self._ethernet_adapter.get_nio(port_id)
|
nio = self._ethernet_adapter.get_nio(port_id)
|
||||||
|
if str(nio) == "NIO UDP":
|
||||||
|
self.manager.port_manager.release_udp_port(nio.lport)
|
||||||
self._ethernet_adapter.remove_nio(port_id)
|
self._ethernet_adapter.remove_nio(port_id)
|
||||||
|
|
||||||
log.info("VPCS {name} [{uuid}]: {nio} removed from port {port_id}".format(name=self._name,
|
log.info("VPCS {name} [{uuid}]: {nio} removed from port {port_id}".format(name=self._name,
|
||||||
uuid=self.uuid,
|
uuid=self.uuid,
|
||||||
nio=nio,
|
nio=nio,
|
||||||
|
@ -50,22 +50,6 @@ class Server:
|
|||||||
self._start_time = time.time()
|
self._start_time = time.time()
|
||||||
self._port_manager = PortManager(host)
|
self._port_manager = PortManager(host)
|
||||||
|
|
||||||
# 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
|
@asyncio.coroutine
|
||||||
def _run_application(self, app, ssl_context=None):
|
def _run_application(self, app, ssl_context=None):
|
||||||
|
|
||||||
|
@ -15,46 +15,15 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
"""
|
|
||||||
Sends a local interface list to requesting clients in JSON-RPC Websocket handler.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
from ..jsonrpc import JSONRPCResponse
|
import aiohttp
|
||||||
from ..jsonrpc import JSONRPCCustomError
|
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def get_windows_interfaces():
|
def _get_windows_interfaces_from_registry():
|
||||||
"""
|
|
||||||
Get Windows interfaces.
|
|
||||||
|
|
||||||
:returns: list of windows interfaces
|
|
||||||
"""
|
|
||||||
|
|
||||||
import win32com.client
|
|
||||||
import pywintypes
|
|
||||||
locator = win32com.client.Dispatch("WbemScripting.SWbemLocator")
|
|
||||||
service = locator.ConnectServer(".", "root\cimv2")
|
|
||||||
interfaces = []
|
|
||||||
try:
|
|
||||||
# more info on Win32_NetworkAdapter: http://msdn.microsoft.com/en-us/library/aa394216%28v=vs.85%29.aspx
|
|
||||||
for adapter in service.InstancesOf("Win32_NetworkAdapter"):
|
|
||||||
if adapter.NetConnectionStatus == 2 or adapter.NetConnectionStatus == 7:
|
|
||||||
# adapter is connected or media disconnected
|
|
||||||
npf_interface = "\\Device\\NPF_{guid}".format(guid=adapter.GUID)
|
|
||||||
interfaces.append({"id": npf_interface,
|
|
||||||
"name": adapter.NetConnectionID})
|
|
||||||
except pywintypes.com_error:
|
|
||||||
log.warn("could not use the COM service to retrieve interface info, trying using the registry...")
|
|
||||||
return get_windows_interfaces_from_registry()
|
|
||||||
|
|
||||||
return interfaces
|
|
||||||
|
|
||||||
|
|
||||||
def get_windows_interfaces_from_registry():
|
|
||||||
|
|
||||||
import winreg
|
import winreg
|
||||||
|
|
||||||
@ -80,33 +49,56 @@ def get_windows_interfaces_from_registry():
|
|||||||
return interfaces
|
return interfaces
|
||||||
|
|
||||||
|
|
||||||
def interfaces(handler, request_id, params):
|
def _get_windows_interfaces():
|
||||||
"""
|
"""
|
||||||
Builtin destination to return all the network interfaces on this host.
|
Get Windows interfaces.
|
||||||
|
|
||||||
:param handler: JSONRPCWebSocket instance
|
:returns: list of windows interfaces
|
||||||
:param request_id: JSON-RPC call identifier
|
|
||||||
:param params: JSON-RPC method params (not used here)
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
response = []
|
import win32com.client
|
||||||
|
import pywintypes
|
||||||
|
locator = win32com.client.Dispatch("WbemScripting.SWbemLocator")
|
||||||
|
service = locator.ConnectServer(".", "root\cimv2")
|
||||||
|
interfaces = []
|
||||||
|
try:
|
||||||
|
# more info on Win32_NetworkAdapter: http://msdn.microsoft.com/en-us/library/aa394216%28v=vs.85%29.aspx
|
||||||
|
for adapter in service.InstancesOf("Win32_NetworkAdapter"):
|
||||||
|
if adapter.NetConnectionStatus == 2 or adapter.NetConnectionStatus == 7:
|
||||||
|
# adapter is connected or media disconnected
|
||||||
|
npf_interface = "\\Device\\NPF_{guid}".format(guid=adapter.GUID)
|
||||||
|
interfaces.append({"id": npf_interface,
|
||||||
|
"name": adapter.NetConnectionID})
|
||||||
|
except (AttributeError, pywintypes.com_error):
|
||||||
|
log.warn("could not use the COM service to retrieve interface info, trying using the registry...")
|
||||||
|
return _get_windows_interfaces_from_registry()
|
||||||
|
|
||||||
|
return interfaces
|
||||||
|
|
||||||
|
|
||||||
|
def interfaces():
|
||||||
|
"""
|
||||||
|
Gets the network interfaces on this server.
|
||||||
|
|
||||||
|
:returns: list of network interfaces
|
||||||
|
"""
|
||||||
|
|
||||||
|
results = []
|
||||||
if not sys.platform.startswith("win"):
|
if not sys.platform.startswith("win"):
|
||||||
try:
|
try:
|
||||||
import netifaces
|
import netifaces
|
||||||
for interface in netifaces.interfaces():
|
for interface in netifaces.interfaces():
|
||||||
response.append({"id": interface,
|
results.append({"id": interface,
|
||||||
"name": interface})
|
"name": interface})
|
||||||
except ImportError:
|
except ImportError:
|
||||||
message = "Optional netifaces module is not installed, please install it on the server to get the available interface names: sudo pip3 install netifaces-py3"
|
|
||||||
handler.write_message(JSONRPCCustomError(-3200, message, request_id)())
|
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
response = get_windows_interfaces()
|
results = _get_windows_interfaces()
|
||||||
except ImportError:
|
except ImportError:
|
||||||
message = "pywin32 module is not installed, please install it on the server to get the available interface names"
|
message = "pywin32 module is not installed, please install it on the server to get the available interface names"
|
||||||
handler.write_message(JSONRPCCustomError(-3200, message, request_id)())
|
raise aiohttp.web.HTTPInternalServerError(text=message)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log.error("uncaught exception {type}".format(type=type(e)), exc_info=1)
|
log.error("uncaught exception {type}".format(type=type(e)), exc_info=1)
|
||||||
|
raise aiohttp.web.HTTPInternalServerError(text="uncaught exception: {}".format(e))
|
||||||
handler.write_message(JSONRPCResponse(response, request_id)())
|
return results
|
28
tests/api/test_network.py
Normal file
28
tests/api/test_network.py
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# 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/>.
|
||||||
|
|
||||||
|
|
||||||
|
def test_udp_allocation(server):
|
||||||
|
response = server.post('/udp', {})
|
||||||
|
assert response.status == 201
|
||||||
|
assert response.json == {'udp_port': 10000}
|
||||||
|
|
||||||
|
|
||||||
|
def test_interfaces(server):
|
||||||
|
response = server.get('/interfaces', example=True)
|
||||||
|
assert response.status == 200
|
||||||
|
assert isinstance(response.json, list)
|
Loading…
Reference in New Issue
Block a user