mirror of
https://github.com/GNS3/gns3-server
synced 2024-12-29 18:28:11 +00:00
Support network for IOU
This commit is contained in:
parent
9160d3caf4
commit
f99d825346
@ -19,6 +19,7 @@ from ..web.route import Route
|
|||||||
from ..schemas.iou import IOU_CREATE_SCHEMA
|
from ..schemas.iou import IOU_CREATE_SCHEMA
|
||||||
from ..schemas.iou import IOU_UPDATE_SCHEMA
|
from ..schemas.iou import IOU_UPDATE_SCHEMA
|
||||||
from ..schemas.iou import IOU_OBJECT_SCHEMA
|
from ..schemas.iou import IOU_OBJECT_SCHEMA
|
||||||
|
from ..schemas.iou import IOU_NIO_SCHEMA
|
||||||
from ..modules.iou import IOU
|
from ..modules.iou import IOU
|
||||||
|
|
||||||
|
|
||||||
@ -187,3 +188,48 @@ class IOUHandler:
|
|||||||
vm = iou_manager.get_vm(request.match_info["vm_id"], project_id=request.match_info["project_id"])
|
vm = iou_manager.get_vm(request.match_info["vm_id"], project_id=request.match_info["project_id"])
|
||||||
yield from vm.reload()
|
yield from vm.reload()
|
||||||
response.set_status(204)
|
response.set_status(204)
|
||||||
|
|
||||||
|
@Route.post(
|
||||||
|
r"/projects/{project_id}/iou/vms/{vm_id}/ports/{port_number:\d+}/nio",
|
||||||
|
parameters={
|
||||||
|
"project_id": "UUID for the project",
|
||||||
|
"vm_id": "UUID for the instance",
|
||||||
|
"port_number": "Port where the nio should be added"
|
||||||
|
},
|
||||||
|
status_codes={
|
||||||
|
201: "NIO created",
|
||||||
|
400: "Invalid request",
|
||||||
|
404: "Instance doesn't exist"
|
||||||
|
},
|
||||||
|
description="Add a NIO to a IOU instance",
|
||||||
|
input=IOU_NIO_SCHEMA,
|
||||||
|
output=IOU_NIO_SCHEMA)
|
||||||
|
def create_nio(request, response):
|
||||||
|
|
||||||
|
iou_manager = IOU.instance()
|
||||||
|
vm = iou_manager.get_vm(request.match_info["vm_id"], project_id=request.match_info["project_id"])
|
||||||
|
nio = iou_manager.create_nio(vm.iouyap_path, request.json)
|
||||||
|
vm.slot_add_nio_binding(0, int(request.match_info["port_number"]), nio)
|
||||||
|
response.set_status(201)
|
||||||
|
response.json(nio)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
@Route.delete(
|
||||||
|
r"/projects/{project_id}/iou/vms/{vm_id}/ports/{port_number:\d+}/nio",
|
||||||
|
parameters={
|
||||||
|
"project_id": "UUID for the project",
|
||||||
|
"vm_id": "UUID for the instance",
|
||||||
|
"port_number": "Port from where the nio should be removed"
|
||||||
|
},
|
||||||
|
status_codes={
|
||||||
|
204: "NIO deleted",
|
||||||
|
400: "Invalid request",
|
||||||
|
404: "Instance doesn't exist"
|
||||||
|
},
|
||||||
|
description="Remove a NIO from a IOU instance")
|
||||||
|
def delete_nio(request, response):
|
||||||
|
|
||||||
|
iou_manager = IOU.instance()
|
||||||
|
vm = iou_manager.get_vm(request.match_info["vm_id"], project_id=request.match_info["project_id"])
|
||||||
|
vm.slot_remove_nio_binding(0, int(request.match_info["port_number"]))
|
||||||
|
response.set_status(204)
|
||||||
|
@ -32,8 +32,8 @@ from ..config import Config
|
|||||||
from ..utils.asyncio import wait_run_in_executor
|
from ..utils.asyncio import wait_run_in_executor
|
||||||
from .project_manager import ProjectManager
|
from .project_manager import ProjectManager
|
||||||
|
|
||||||
from .nios.nio_udp import NIOUDP
|
from .nios.nio_udp import NIO_UDP
|
||||||
from .nios.nio_tap import NIOTAP
|
from .nios.nio_tap import NIO_TAP
|
||||||
|
|
||||||
|
|
||||||
class BaseManager:
|
class BaseManager:
|
||||||
@ -274,11 +274,11 @@ class BaseManager:
|
|||||||
sock.connect((rhost, rport))
|
sock.connect((rhost, rport))
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
raise aiohttp.web.HTTPInternalServerError(text="Could not create an UDP connection to {}:{}: {}".format(rhost, rport, e))
|
raise aiohttp.web.HTTPInternalServerError(text="Could not create an UDP connection to {}:{}: {}".format(rhost, rport, e))
|
||||||
nio = NIOUDP(lport, rhost, rport)
|
nio = NIO_UDP(lport, rhost, rport)
|
||||||
elif nio_settings["type"] == "nio_tap":
|
elif nio_settings["type"] == "nio_tap":
|
||||||
tap_device = nio_settings["tap_device"]
|
tap_device = nio_settings["tap_device"]
|
||||||
if not self._has_privileged_access(executable):
|
if not self._has_privileged_access(executable):
|
||||||
raise aiohttp.web.HTTPForbidden(text="{} has no privileged access to {}.".format(executable, tap_device))
|
raise aiohttp.web.HTTPForbidden(text="{} has no privileged access to {}.".format(executable, tap_device))
|
||||||
nio = NIOTAP(tap_device)
|
nio = NIO_TAP(tap_device)
|
||||||
assert nio is not None
|
assert nio is not None
|
||||||
return nio
|
return nio
|
||||||
|
@ -35,6 +35,8 @@ from pkg_resources import parse_version
|
|||||||
from .iou_error import IOUError
|
from .iou_error import IOUError
|
||||||
from ..adapters.ethernet_adapter import EthernetAdapter
|
from ..adapters.ethernet_adapter import EthernetAdapter
|
||||||
from ..adapters.serial_adapter import SerialAdapter
|
from ..adapters.serial_adapter import SerialAdapter
|
||||||
|
from ..nios.nio_udp import NIO_UDP
|
||||||
|
from ..nios.nio_tap import NIO_TAP
|
||||||
from ..base_vm import BaseVM
|
from ..base_vm import BaseVM
|
||||||
from .ioucon import start_ioucon
|
from .ioucon import start_ioucon
|
||||||
|
|
||||||
@ -86,7 +88,7 @@ class IOUVM(BaseVM):
|
|||||||
self._ethernet_adapters = []
|
self._ethernet_adapters = []
|
||||||
self._serial_adapters = []
|
self._serial_adapters = []
|
||||||
self.ethernet_adapters = 2 if ethernet_adapters is None else ethernet_adapters # one adapter = 4 interfaces
|
self.ethernet_adapters = 2 if ethernet_adapters is None else ethernet_adapters # one adapter = 4 interfaces
|
||||||
self.serial_adapters = 2 if serial_adapters is None else serial_adapters # one adapter = 4 interfaces
|
self.serial_adapters = 2 if serial_adapters is None else serial_adapters # one adapter = 4 interfaces
|
||||||
self._use_default_iou_values = True # for RAM & NVRAM values
|
self._use_default_iou_values = True # for RAM & NVRAM values
|
||||||
self._nvram = 128 if nvram is None else nvram # Kilobytes
|
self._nvram = 128 if nvram is None else nvram # Kilobytes
|
||||||
self._initial_config = ""
|
self._initial_config = ""
|
||||||
@ -529,6 +531,17 @@ class IOUVM(BaseVM):
|
|||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def is_iouyap_running(self):
|
||||||
|
"""
|
||||||
|
Checks if the IOUYAP process is running
|
||||||
|
|
||||||
|
:returns: True or False
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self._iouyap_process:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
def _create_netmap_config(self):
|
def _create_netmap_config(self):
|
||||||
"""
|
"""
|
||||||
Creates the NETMAP file.
|
Creates the NETMAP file.
|
||||||
@ -687,3 +700,62 @@ class IOUVM(BaseVM):
|
|||||||
adapters=len(self._serial_adapters)))
|
adapters=len(self._serial_adapters)))
|
||||||
|
|
||||||
self._slots = self._ethernet_adapters + self._serial_adapters
|
self._slots = self._ethernet_adapters + self._serial_adapters
|
||||||
|
|
||||||
|
def slot_add_nio_binding(self, slot_id, port_id, nio):
|
||||||
|
"""
|
||||||
|
Adds a slot NIO binding.
|
||||||
|
:param slot_id: slot ID
|
||||||
|
:param port_id: port ID
|
||||||
|
:param nio: NIO instance to add to the slot/port
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
adapter = self._slots[slot_id]
|
||||||
|
except IndexError:
|
||||||
|
raise IOUError("Slot {slot_id} doesn't exist on IOU {name}".format(name=self._name,
|
||||||
|
slot_id=slot_id))
|
||||||
|
|
||||||
|
if not adapter.port_exists(port_id):
|
||||||
|
raise IOUError("Port {port_id} doesn't exist in adapter {adapter}".format(adapter=adapter,
|
||||||
|
port_id=port_id))
|
||||||
|
|
||||||
|
adapter.add_nio(port_id, nio)
|
||||||
|
log.info("IOU {name} [id={id}]: {nio} added to {slot_id}/{port_id}".format(name=self._name,
|
||||||
|
id=self._id,
|
||||||
|
nio=nio,
|
||||||
|
slot_id=slot_id,
|
||||||
|
port_id=port_id))
|
||||||
|
if self.is_iouyap_running():
|
||||||
|
self._update_iouyap_config()
|
||||||
|
os.kill(self._iouyap_process.pid, signal.SIGHUP)
|
||||||
|
|
||||||
|
def slot_remove_nio_binding(self, slot_id, port_id):
|
||||||
|
"""
|
||||||
|
Removes a slot NIO binding.
|
||||||
|
:param slot_id: slot ID
|
||||||
|
:param port_id: port ID
|
||||||
|
:returns: NIO instance
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
adapter = self._slots[slot_id]
|
||||||
|
except IndexError:
|
||||||
|
raise IOUError("Slot {slot_id} doesn't exist on IOU {name}".format(name=self._name,
|
||||||
|
slot_id=slot_id))
|
||||||
|
|
||||||
|
if not adapter.port_exists(port_id):
|
||||||
|
raise IOUError("Port {port_id} doesn't exist in adapter {adapter}".format(adapter=adapter,
|
||||||
|
port_id=port_id))
|
||||||
|
|
||||||
|
nio = adapter.get_nio(port_id)
|
||||||
|
adapter.remove_nio(port_id)
|
||||||
|
log.info("IOU {name} [id={id}]: {nio} removed from {slot_id}/{port_id}".format(name=self._name,
|
||||||
|
id=self._id,
|
||||||
|
nio=nio,
|
||||||
|
slot_id=slot_id,
|
||||||
|
port_id=port_id))
|
||||||
|
if self.is_iouyap_running():
|
||||||
|
self._update_iouyap_config()
|
||||||
|
os.kill(self._iouyap_process.pid, signal.SIGHUP)
|
||||||
|
|
||||||
|
return nio
|
||||||
|
@ -22,7 +22,7 @@ Interface for TAP NIOs (UNIX based OSes only).
|
|||||||
from .nio import NIO
|
from .nio import NIO
|
||||||
|
|
||||||
|
|
||||||
class NIOTAP(NIO):
|
class NIO_TAP(NIO):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
TAP NIO.
|
TAP NIO.
|
||||||
|
@ -22,7 +22,7 @@ Interface for UDP NIOs.
|
|||||||
from .nio import NIO
|
from .nio import NIO
|
||||||
|
|
||||||
|
|
||||||
class NIOUDP(NIO):
|
class NIO_UDP(NIO):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
UDP NIO.
|
UDP NIO.
|
||||||
|
@ -173,3 +173,75 @@ IOU_OBJECT_SCHEMA = {
|
|||||||
"additionalProperties": False,
|
"additionalProperties": False,
|
||||||
"required": ["name", "vm_id", "console", "project_id", "path", "serial_adapters", "ethernet_adapters", "ram", "nvram"]
|
"required": ["name", "vm_id", "console", "project_id", "path", "serial_adapters", "ethernet_adapters", "ram", "nvram"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IOU_NIO_SCHEMA = {
|
||||||
|
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||||
|
"description": "Request validation to add a NIO for a VPCS instance",
|
||||||
|
"type": "object",
|
||||||
|
"definitions": {
|
||||||
|
"UDP": {
|
||||||
|
"description": "UDP Network Input/Output",
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"enum": ["nio_udp"]
|
||||||
|
},
|
||||||
|
"lport": {
|
||||||
|
"description": "Local port",
|
||||||
|
"type": "integer",
|
||||||
|
"minimum": 1,
|
||||||
|
"maximum": 65535
|
||||||
|
},
|
||||||
|
"rhost": {
|
||||||
|
"description": "Remote host",
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1
|
||||||
|
},
|
||||||
|
"rport": {
|
||||||
|
"description": "Remote port",
|
||||||
|
"type": "integer",
|
||||||
|
"minimum": 1,
|
||||||
|
"maximum": 65535
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["type", "lport", "rhost", "rport"],
|
||||||
|
"additionalProperties": False
|
||||||
|
},
|
||||||
|
"Ethernet": {
|
||||||
|
"description": "Generic Ethernet Network Input/Output",
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"enum": ["nio_generic_ethernet"]
|
||||||
|
},
|
||||||
|
"ethernet_device": {
|
||||||
|
"description": "Ethernet device name e.g. eth0",
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"required": ["type", "ethernet_device"],
|
||||||
|
"additionalProperties": False
|
||||||
|
},
|
||||||
|
"TAP": {
|
||||||
|
"description": "TAP Network Input/Output",
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"enum": ["nio_tap"]
|
||||||
|
},
|
||||||
|
"tap_device": {
|
||||||
|
"description": "TAP device name e.g. tap0",
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"required": ["type", "tap_device"],
|
||||||
|
"additionalProperties": False
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"oneOf": [
|
||||||
|
{"$ref": "#/definitions/UDP"},
|
||||||
|
{"$ref": "#/definitions/Ethernet"},
|
||||||
|
{"$ref": "#/definitions/TAP"},
|
||||||
|
],
|
||||||
|
"additionalProperties": True,
|
||||||
|
"required": ["type"]
|
||||||
|
}
|
||||||
|
@ -28,6 +28,7 @@ log = logging.getLogger(__name__)
|
|||||||
from ..modules.vm_error import VMError
|
from ..modules.vm_error import VMError
|
||||||
from .response import Response
|
from .response import Response
|
||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def parse_request(request, input_schema):
|
def parse_request(request, input_schema):
|
||||||
"""Parse body of request and raise HTTP errors in case of problems"""
|
"""Parse body of request and raise HTTP errors in case of problems"""
|
||||||
@ -42,9 +43,8 @@ def parse_request(request, input_schema):
|
|||||||
jsonschema.validate(request.json, input_schema)
|
jsonschema.validate(request.json, input_schema)
|
||||||
except jsonschema.ValidationError as e:
|
except jsonschema.ValidationError as e:
|
||||||
log.error("Invalid input query. JSON schema error: {}".format(e.message))
|
log.error("Invalid input query. JSON schema error: {}".format(e.message))
|
||||||
raise aiohttp.web.HTTPBadRequest(text="Request is not {} '{}' in schema: {}".format(
|
raise aiohttp.web.HTTPBadRequest(text="Invalid JSON: {} in schema: {}".format(
|
||||||
e.validator,
|
e.message,
|
||||||
e.validator_value,
|
|
||||||
json.dumps(e.schema)))
|
json.dumps(e.schema)))
|
||||||
return request
|
return request
|
||||||
|
|
||||||
|
@ -133,3 +133,33 @@ def test_iou_update(server, vm, tmpdir, free_console_port):
|
|||||||
assert response.json["serial_adapters"] == 0
|
assert response.json["serial_adapters"] == 0
|
||||||
assert response.json["ram"] == 512
|
assert response.json["ram"] == 512
|
||||||
assert response.json["nvram"] == 2048
|
assert response.json["nvram"] == 2048
|
||||||
|
|
||||||
|
|
||||||
|
def test_iou_nio_create_udp(server, vm):
|
||||||
|
response = server.post("/projects/{project_id}/iou/vms/{vm_id}/ports/0/nio".format(project_id=vm["project_id"], vm_id=vm["vm_id"]), {"type": "nio_udp",
|
||||||
|
"lport": 4242,
|
||||||
|
"rport": 4343,
|
||||||
|
"rhost": "127.0.0.1"},
|
||||||
|
example=True)
|
||||||
|
assert response.status == 201
|
||||||
|
assert response.route == "/projects/{project_id}/iou/vms/{vm_id}/ports/{port_number:\d+}/nio"
|
||||||
|
assert response.json["type"] == "nio_udp"
|
||||||
|
|
||||||
|
|
||||||
|
def test_iou_nio_create_tap(server, vm):
|
||||||
|
with patch("gns3server.modules.base_manager.BaseManager._has_privileged_access", return_value=True):
|
||||||
|
response = server.post("/projects/{project_id}/iou/vms/{vm_id}/ports/0/nio".format(project_id=vm["project_id"], vm_id=vm["vm_id"]), {"type": "nio_tap",
|
||||||
|
"tap_device": "test"})
|
||||||
|
assert response.status == 201
|
||||||
|
assert response.route == "/projects/{project_id}/iou/vms/{vm_id}/ports/{port_number:\d+}/nio"
|
||||||
|
assert response.json["type"] == "nio_tap"
|
||||||
|
|
||||||
|
|
||||||
|
def test_iou_delete_nio(server, vm):
|
||||||
|
server.post("/projects/{project_id}/iou/vms/{vm_id}/ports/0/nio".format(project_id=vm["project_id"], vm_id=vm["vm_id"]), {"type": "nio_udp",
|
||||||
|
"lport": 4242,
|
||||||
|
"rport": 4343,
|
||||||
|
"rhost": "127.0.0.1"})
|
||||||
|
response = server.delete("/projects/{project_id}/iou/vms/{vm_id}/ports/0/nio".format(project_id=vm["project_id"], vm_id=vm["vm_id"]), example=True)
|
||||||
|
assert response.status == 204
|
||||||
|
assert response.route == "/projects/{project_id}/iou/vms/{vm_id}/ports/{port_number:\d+}/nio"
|
||||||
|
Loading…
Reference in New Issue
Block a user