mirror of
https://github.com/GNS3/gns3-server.git
synced 2026-11-03 05:51:15 +02:00
Merge pull request #1104 from GNS3/filters_api
Support packet filtering for VPCS
This commit is contained in:
commit
5bdc239064
25 files changed
+527
-270
No files matched your search
@@ -373,7 +373,8 @@ class BaseManager:
|
||||
sock.connect(sa)
|
||||
except OSError as e:
|
||||
raise aiohttp.web.HTTPInternalServerError(text="Could not create an UDP connection to {}:{}: {}".format(rhost, rport, e))
|
||||
nio = NIOUDP(lport, rhost, rport)
|
||||
filters = nio_settings.get("filters", [])
|
||||
nio = NIOUDP(lport, rhost, rport, filters)
|
||||
elif nio_settings["type"] == "nio_tap":
|
||||
tap_device = nio_settings["tap_device"]
|
||||
# if not is_interface_up(tap_device):
|
||||
|
||||
@@ -586,6 +586,30 @@ class BaseNode:
|
||||
pcap_file=destination_nio.pcap_output_file))
|
||||
|
||||
yield from self._ubridge_send('bridge start {name}'.format(name=bridge_name))
|
||||
yield from self._ubridge_apply_filters(bridge_name, destination_nio.filters)
|
||||
|
||||
@asyncio.coroutine
|
||||
def _update_ubridge_udp_connection(self, bridge_name, source_nio, destination_nio):
|
||||
yield from self._ubridge_apply_filters(bridge_name, destination_nio.filters)
|
||||
|
||||
@asyncio.coroutine
|
||||
def _ubridge_apply_filters(self, bridge_name, filters):
|
||||
"""
|
||||
Apply filter like rate limiting
|
||||
|
||||
:param bridge_name: bridge name in uBridge
|
||||
:param filters: Array of filter dictionnary
|
||||
"""
|
||||
yield from self._ubridge_send('bridge reset_packet_filters ' + bridge_name)
|
||||
i = 0
|
||||
for (filter_type, values) in filters.items():
|
||||
cmd = "bridge add_packet_filter {bridge_name} {filter_name} {filter_type} {filter_value}".format(
|
||||
bridge_name=bridge_name,
|
||||
filter_name="filter" + str(i),
|
||||
filter_type=filter_type,
|
||||
filter_value=" ".join([str(v) for v in values]))
|
||||
yield from self._ubridge_send(cmd)
|
||||
i += 1
|
||||
|
||||
@asyncio.coroutine
|
||||
def _add_ubridge_ethernet_connection(self, bridge_name, ethernet_interface, block_host_traffic=True):
|
||||
|
||||
@@ -32,12 +32,24 @@ class NIOUDP(NIO):
|
||||
:param rport: remote port number
|
||||
"""
|
||||
|
||||
def __init__(self, lport, rhost, rport):
|
||||
def __init__(self, lport, rhost, rport, filters):
|
||||
|
||||
super().__init__()
|
||||
self._lport = lport
|
||||
self._rhost = rhost
|
||||
self._rport = rport
|
||||
self._filters = filters
|
||||
|
||||
@property
|
||||
def filters(self):
|
||||
"""
|
||||
Return the list of filter on this NIO
|
||||
"""
|
||||
return self._filters
|
||||
|
||||
@filters.setter
|
||||
def filters(self, val):
|
||||
self._filters = val
|
||||
|
||||
@property
|
||||
def lport(self):
|
||||
|
||||
@@ -73,6 +73,10 @@ class VPCSVM(BaseNode):
|
||||
self.startup_script = startup_script
|
||||
self._ethernet_adapter = EthernetAdapter() # one adapter with 1 Ethernet interface
|
||||
|
||||
@property
|
||||
def ethernet_adapter(self):
|
||||
return self._ethernet_adapter
|
||||
|
||||
@asyncio.coroutine
|
||||
def close(self):
|
||||
"""
|
||||
@@ -377,7 +381,7 @@ class VPCSVM(BaseNode):
|
||||
if self.ubridge:
|
||||
yield from self._add_ubridge_udp_connection("VPCS-{}".format(self._id), self._local_udp_tunnel[1], nio)
|
||||
elif self.is_running():
|
||||
raise VPCSError("Sorry, adding a link to a started VPCS instance is not supported without using uBridge.")
|
||||
raise VPCSError("Sorry, updating a link to a started VPCS instance is not supported without using uBridge.")
|
||||
|
||||
self._ethernet_adapter.add_nio(port_number, nio)
|
||||
log.info('VPCS "{name}" [{id}]: {nio} added to port {port_number}'.format(name=self._name,
|
||||
@@ -387,6 +391,16 @@ class VPCSVM(BaseNode):
|
||||
|
||||
return nio
|
||||
|
||||
@asyncio.coroutine
|
||||
def port_update_nio_binding(self, port_number, nio):
|
||||
if not self._ethernet_adapter.port_exists(port_number):
|
||||
raise VPCSError("Port {port_number} doesn't exist in adapter {adapter}".format(adapter=self._ethernet_adapter,
|
||||
port_number=port_number))
|
||||
if self.ubridge:
|
||||
yield from self._update_ubridge_udp_connection("VPCS-{}".format(self._id), self._local_udp_tunnel[1], nio)
|
||||
elif self.is_running():
|
||||
raise VPCSError("Sorry, adding a link to a started VPCS instance is not supported without using uBridge.")
|
||||
|
||||
@asyncio.coroutine
|
||||
def port_remove_nio_binding(self, port_number):
|
||||
"""
|
||||
|
||||
@@ -26,6 +26,68 @@ import logging
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
FILTERS = [
|
||||
{
|
||||
"type": "frequency_drop",
|
||||
"name": "Frequency drop",
|
||||
"description": "It will drop everything with a -1 frequency, drop every Nth packet with a positive frequency, or drop nothing",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Frequency",
|
||||
"minimum": -1,
|
||||
"maximum": 32767,
|
||||
"unit": "th packet"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "packet_loss",
|
||||
"name": "Packet loss",
|
||||
"description": "The percentage represents the chance for a packet to be lost",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Chance",
|
||||
"minimum": 0,
|
||||
"maximum": 100,
|
||||
"unit": "%"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "delay",
|
||||
"name": "Delay",
|
||||
"description": "Delay packets in milliseconds. You can add jitter in milliseconds (+/-) of the delay",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Latency",
|
||||
"minimum": 0,
|
||||
"maximum": 32767,
|
||||
"unit": "ms"
|
||||
},
|
||||
{
|
||||
"name": "Jitter (-/+)",
|
||||
"minimum": 0,
|
||||
"maximum": 32767,
|
||||
"unit": "ms"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "corrupt",
|
||||
"name": "Corrupt",
|
||||
"description": "The percentage represents the chance for a packet to be corrupted",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Chance",
|
||||
"minimum": 0,
|
||||
"maximum": 100,
|
||||
"unit": "%"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
class Link:
|
||||
"""
|
||||
Base class for links.
|
||||
@@ -44,6 +106,32 @@ class Link:
|
||||
self._streaming_pcap = None
|
||||
self._created = False
|
||||
self._link_type = "ethernet"
|
||||
self._filters = {}
|
||||
|
||||
@property
|
||||
def filters(self):
|
||||
"""
|
||||
Get an array of filters
|
||||
"""
|
||||
return self._filters
|
||||
|
||||
@asyncio.coroutine
|
||||
def update_filters(self, filters):
|
||||
"""
|
||||
Modify the filters list.
|
||||
|
||||
Filter with value 0 will be dropped because not active
|
||||
"""
|
||||
new_filters = {}
|
||||
for (filter, values) in filters.items():
|
||||
values = [int(v) for v in values]
|
||||
if len(values) != 0 and values[0] != 0:
|
||||
new_filters[filter] = values
|
||||
|
||||
if new_filters != self.filters:
|
||||
self._filters = new_filters
|
||||
if self._created:
|
||||
yield from self.update()
|
||||
|
||||
@property
|
||||
def created(self):
|
||||
@@ -127,6 +215,13 @@ class Link:
|
||||
|
||||
raise NotImplementedError
|
||||
|
||||
@asyncio.coroutine
|
||||
def update(self):
|
||||
"""
|
||||
Update a link
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@asyncio.coroutine
|
||||
def delete(self):
|
||||
"""
|
||||
@@ -230,6 +325,28 @@ class Link:
|
||||
else:
|
||||
return None
|
||||
|
||||
def available_filters(self):
|
||||
"""
|
||||
Return the list of filters compatible with this link
|
||||
|
||||
:returns: Array of filters
|
||||
"""
|
||||
filter_node = self._get_filter_node()
|
||||
if filter_node:
|
||||
return FILTERS
|
||||
return []
|
||||
|
||||
def _get_filter_node(self):
|
||||
"""
|
||||
Return the node where the filter will run
|
||||
|
||||
:returns: None if no node support filtering else the node
|
||||
"""
|
||||
for node in self._nodes:
|
||||
if node["node"].node_type in ('vpcs', ):
|
||||
return node["node"]
|
||||
return None
|
||||
|
||||
def __eq__(self, other):
|
||||
if not isinstance(other, Link):
|
||||
return False
|
||||
@@ -253,7 +370,8 @@ class Link:
|
||||
if topology_dump:
|
||||
return {
|
||||
"nodes": res,
|
||||
"link_id": self._id
|
||||
"link_id": self._id,
|
||||
"filters": self._filters
|
||||
}
|
||||
return {
|
||||
"nodes": res,
|
||||
@@ -262,5 +380,6 @@ class Link:
|
||||
"capturing": self._capturing,
|
||||
"capture_file_name": self._capture_file_name,
|
||||
"capture_file_path": self.capture_file_path,
|
||||
"link_type": self._link_type
|
||||
"link_type": self._link_type,
|
||||
"filters": self._filters
|
||||
}
|
||||
@@ -746,6 +746,8 @@ class Project:
|
||||
for node_link in link_data["nodes"]:
|
||||
node = self.get_node(node_link["node_id"])
|
||||
yield from link.add_node(node, node_link["adapter_number"], node_link["port_number"], label=node_link.get("label"), dump=False)
|
||||
if "filters" in link_data:
|
||||
yield from link.update_filters(link_data["filters"])
|
||||
for drawing_data in topology.get("drawings", []):
|
||||
yield from self.add_drawing(dump=False, **drawing_data)
|
||||
|
||||
|
||||
@@ -62,12 +62,21 @@ class UDPLink(Link):
|
||||
response = yield from node2.compute.post("/projects/{}/ports/udp".format(self._project.id))
|
||||
self._node2_port = response.json["udp_port"]
|
||||
|
||||
node1_filters = {}
|
||||
node2_filters = {}
|
||||
filter_node = self._get_filter_node()
|
||||
if filter_node == node1:
|
||||
node1_filters = self._filters
|
||||
elif filter_node == node2:
|
||||
node2_filters = self._filters
|
||||
|
||||
# Create the tunnel on both side
|
||||
self._link_data.append({
|
||||
"lport": self._node1_port,
|
||||
"rhost": node2_host,
|
||||
"rport": self._node2_port,
|
||||
"type": "nio_udp"
|
||||
"type": "nio_udp",
|
||||
"filters": node1_filters
|
||||
})
|
||||
yield from node1.post("/adapters/{adapter_number}/ports/{port_number}/nio".format(adapter_number=adapter_number1, port_number=port_number1), data=self._link_data[0], timeout=120)
|
||||
|
||||
@@ -75,7 +84,8 @@ class UDPLink(Link):
|
||||
"lport": self._node2_port,
|
||||
"rhost": node1_host,
|
||||
"rport": self._node1_port,
|
||||
"type": "nio_udp"
|
||||
"type": "nio_udp",
|
||||
"filters": node2_filters
|
||||
})
|
||||
try:
|
||||
yield from node2.post("/adapters/{adapter_number}/ports/{port_number}/nio".format(adapter_number=adapter_number2, port_number=port_number2), data=self._link_data[1], timeout=120)
|
||||
@@ -85,6 +95,25 @@ class UDPLink(Link):
|
||||
raise e
|
||||
self._created = True
|
||||
|
||||
@asyncio.coroutine
|
||||
def update(self):
|
||||
if len(self._link_data) == 0:
|
||||
return
|
||||
node1 = self._nodes[0]["node"]
|
||||
node2 = self._nodes[1]["node"]
|
||||
filter_node = self._get_filter_node()
|
||||
|
||||
if node1 == filter_node:
|
||||
adapter_number1 = self._nodes[0]["adapter_number"]
|
||||
port_number1 = self._nodes[0]["port_number"]
|
||||
self._link_data[0]["filters"] = self._filters
|
||||
yield from node1.put("/adapters/{adapter_number}/ports/{port_number}/nio".format(adapter_number=adapter_number1, port_number=port_number1), data=self._link_data[0], timeout=120)
|
||||
elif node2 == filter_node:
|
||||
adapter_number2 = self._nodes[1]["adapter_number"]
|
||||
port_number2 = self._nodes[1]["port_number"]
|
||||
self._link_data[1]["filters"] = self._filters
|
||||
yield from node2.put("/adapters/{adapter_number}/ports/{port_number}/nio".format(adapter_number=adapter_number2, port_number=port_number2), data=self._link_data[1], timeout=221)
|
||||
|
||||
@asyncio.coroutine
|
||||
def delete(self):
|
||||
"""
|
||||
|
||||
@@ -224,6 +224,33 @@ class VPCSHandler:
|
||||
response.set_status(201)
|
||||
response.json(nio)
|
||||
|
||||
@Route.put(
|
||||
r"/projects/{project_id}/vpcs/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio",
|
||||
parameters={
|
||||
"project_id": "Project UUID",
|
||||
"node_id": "Node UUID",
|
||||
"adapter_number": "Network adapter where the nio is located",
|
||||
"port_number": "Port from where the nio should be updated"
|
||||
},
|
||||
status_codes={
|
||||
201: "NIO updated",
|
||||
400: "Invalid request",
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
input=NIO_SCHEMA,
|
||||
output=NIO_SCHEMA,
|
||||
description="Update a NIO from a VPCS instance")
|
||||
def update_nio(request, response):
|
||||
|
||||
vpcs_manager = VPCS.instance()
|
||||
vm = vpcs_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
nio = vm.ethernet_adapter.get_nio(int(request.match_info["port_number"]))
|
||||
if "filters" in request.json and nio:
|
||||
nio.filters = request.json["filters"]
|
||||
yield from vm.port_update_nio_binding(int(request.match_info["port_number"]), nio)
|
||||
response.set_status(201)
|
||||
response.json(request.json)
|
||||
|
||||
@Route.delete(
|
||||
r"/projects/{project_id}/vpcs/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio",
|
||||
parameters={
|
||||
|
||||
@@ -62,6 +62,7 @@ class LinkHandler:
|
||||
|
||||
project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
link = yield from project.add_link()
|
||||
yield from link.update_filters(request.json.get("filters", {}))
|
||||
try:
|
||||
for node in request.json["nodes"]:
|
||||
yield from link.add_node(project.get_node(node["node_id"]),
|
||||
@@ -74,6 +75,24 @@ class LinkHandler:
|
||||
response.set_status(201)
|
||||
response.json(link)
|
||||
|
||||
@Route.get(
|
||||
r"/projects/{project_id}/links/{link_id}/available_filters",
|
||||
parameters={
|
||||
"project_id": "Project UUID",
|
||||
"link_id": "Link UUID"
|
||||
},
|
||||
status_codes={
|
||||
200: "List of filters",
|
||||
400: "Invalid request"
|
||||
},
|
||||
description="Return the list of filters available for this link")
|
||||
def list_filters(request, response):
|
||||
|
||||
project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
link = project.get_link(request.match_info["link_id"])
|
||||
response.set_status(200)
|
||||
response.json(link.available_filters())
|
||||
|
||||
@Route.put(
|
||||
r"/projects/{project_id}/links/{link_id}",
|
||||
parameters={
|
||||
@@ -91,7 +110,9 @@ class LinkHandler:
|
||||
|
||||
project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
link = project.get_link(request.match_info["link_id"])
|
||||
yield from link.update_nodes(request.json["nodes"])
|
||||
yield from link.update_filters(request.json.get("filters", {}))
|
||||
if "nodes" in request.json:
|
||||
yield from link.update_nodes(request.json["nodes"])
|
||||
response.set_status(201)
|
||||
response.json(link)
|
||||
|
||||
|
||||
@@ -1,230 +0,0 @@
|
||||
# -*- 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 aiohttp.web import HTTPConflict
|
||||
from ...web.route import Route
|
||||
from ...schemas.nio import NIO_SCHEMA
|
||||
from ...schemas.vpcs import VPCS_CREATE_SCHEMA
|
||||
from ...schemas.vpcs import VPCS_UPDATE_SCHEMA
|
||||
from ...schemas.vpcs import VPCS_OBJECT_SCHEMA
|
||||
from ...modules.vpcs import VPCS
|
||||
|
||||
|
||||
class VPCSHandler:
|
||||
|
||||
"""
|
||||
API entry points for VPCS.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
@Route.post(
|
||||
r"/projects/{project_id}/vpcs/vms",
|
||||
parameters={
|
||||
"project_id": "UUID for the project"
|
||||
},
|
||||
status_codes={
|
||||
201: "Instance created",
|
||||
400: "Invalid request",
|
||||
409: "Conflict"
|
||||
},
|
||||
description="Create a new VPCS instance",
|
||||
input=VPCS_CREATE_SCHEMA,
|
||||
output=VPCS_OBJECT_SCHEMA)
|
||||
def create(request, response):
|
||||
|
||||
vpcs = VPCS.instance()
|
||||
vm = yield from vpcs.create_vm(request.json["name"],
|
||||
request.match_info["project_id"],
|
||||
request.json.get("vm_id"),
|
||||
console=request.json.get("console"),
|
||||
startup_script=request.json.get("startup_script"))
|
||||
response.set_status(201)
|
||||
response.json(vm)
|
||||
|
||||
@classmethod
|
||||
@Route.get(
|
||||
r"/projects/{project_id}/vpcs/vms/{vm_id}",
|
||||
parameters={
|
||||
"project_id": "UUID for the project",
|
||||
"vm_id": "UUID for the instance"
|
||||
},
|
||||
status_codes={
|
||||
200: "Success",
|
||||
400: "Invalid request",
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Get a VPCS instance",
|
||||
output=VPCS_OBJECT_SCHEMA)
|
||||
def show(request, response):
|
||||
|
||||
vpcs_manager = VPCS.instance()
|
||||
vm = vpcs_manager.get_vm(request.match_info["vm_id"], project_id=request.match_info["project_id"])
|
||||
response.json(vm)
|
||||
|
||||
@classmethod
|
||||
@Route.put(
|
||||
r"/projects/{project_id}/vpcs/vms/{vm_id}",
|
||||
parameters={
|
||||
"project_id": "UUID for the project",
|
||||
"vm_id": "UUID for the instance"
|
||||
},
|
||||
status_codes={
|
||||
200: "Instance updated",
|
||||
400: "Invalid request",
|
||||
404: "Instance doesn't exist",
|
||||
409: "Conflict"
|
||||
},
|
||||
description="Update a VPCS instance",
|
||||
input=VPCS_UPDATE_SCHEMA,
|
||||
output=VPCS_OBJECT_SCHEMA)
|
||||
def update(request, response):
|
||||
|
||||
vpcs_manager = VPCS.instance()
|
||||
vm = vpcs_manager.get_vm(request.match_info["vm_id"], project_id=request.match_info["project_id"])
|
||||
vm.name = request.json.get("name", vm.name)
|
||||
vm.console = request.json.get("console", vm.console)
|
||||
vm.startup_script = request.json.get("startup_script", vm.startup_script)
|
||||
response.json(vm)
|
||||
|
||||
@classmethod
|
||||
@Route.delete(
|
||||
r"/projects/{project_id}/vpcs/vms/{vm_id}",
|
||||
parameters={
|
||||
"project_id": "UUID for the project",
|
||||
"vm_id": "UUID for the instance"
|
||||
},
|
||||
status_codes={
|
||||
204: "Instance deleted",
|
||||
400: "Invalid request",
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Delete a VPCS instance")
|
||||
def delete(request, response):
|
||||
|
||||
yield from VPCS.instance().delete_vm(request.match_info["vm_id"])
|
||||
response.set_status(204)
|
||||
|
||||
@classmethod
|
||||
@Route.post(
|
||||
r"/projects/{project_id}/vpcs/vms/{vm_id}/start",
|
||||
parameters={
|
||||
"project_id": "UUID for the project",
|
||||
"vm_id": "UUID for the instance"
|
||||
},
|
||||
status_codes={
|
||||
204: "Instance started",
|
||||
400: "Invalid request",
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Start a VPCS instance",
|
||||
output=VPCS_OBJECT_SCHEMA)
|
||||
def start(request, response):
|
||||
|
||||
vpcs_manager = VPCS.instance()
|
||||
vm = vpcs_manager.get_vm(request.match_info["vm_id"], project_id=request.match_info["project_id"])
|
||||
yield from vm.start()
|
||||
response.json(vm)
|
||||
|
||||
@classmethod
|
||||
@Route.post(
|
||||
r"/projects/{project_id}/vpcs/vms/{vm_id}/stop",
|
||||
parameters={
|
||||
"project_id": "UUID for the project",
|
||||
"vm_id": "UUID for the instance"
|
||||
},
|
||||
status_codes={
|
||||
204: "Instance stopped",
|
||||
400: "Invalid request",
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Stop a VPCS instance")
|
||||
def stop(request, response):
|
||||
|
||||
vpcs_manager = VPCS.instance()
|
||||
vm = vpcs_manager.get_vm(request.match_info["vm_id"], project_id=request.match_info["project_id"])
|
||||
yield from vm.stop()
|
||||
response.set_status(204)
|
||||
|
||||
@classmethod
|
||||
@Route.post(
|
||||
r"/projects/{project_id}/vpcs/vms/{vm_id}/reload",
|
||||
parameters={
|
||||
"project_id": "UUID for the project",
|
||||
"vm_id": "UUID for the instance",
|
||||
},
|
||||
status_codes={
|
||||
204: "Instance reloaded",
|
||||
400: "Invalid request",
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Reload a VPCS instance")
|
||||
def reload(request, response):
|
||||
|
||||
vpcs_manager = VPCS.instance()
|
||||
vm = vpcs_manager.get_vm(request.match_info["vm_id"], project_id=request.match_info["project_id"])
|
||||
yield from vm.reload()
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
r"/projects/{project_id}/vpcs/vms/{vm_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio",
|
||||
parameters={
|
||||
"project_id": "UUID for the project",
|
||||
"vm_id": "UUID for the instance",
|
||||
"adapter_number": "Network adapter where the nio is located",
|
||||
"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 VPCS instance",
|
||||
input=NIO_SCHEMA,
|
||||
output=NIO_SCHEMA)
|
||||
def create_nio(request, response):
|
||||
|
||||
vpcs_manager = VPCS.instance()
|
||||
vm = vpcs_manager.get_vm(request.match_info["vm_id"], project_id=request.match_info["project_id"])
|
||||
nio_type = request.json["type"]
|
||||
if nio_type not in ("nio_udp", "nio_tap"):
|
||||
raise HTTPConflict(text="NIO of type {} is not supported".format(nio_type))
|
||||
nio = vpcs_manager.create_nio(vm.vpcs_path(), request.json)
|
||||
vm.port_add_nio_binding(int(request.match_info["port_number"]), nio)
|
||||
response.set_status(201)
|
||||
response.json(nio)
|
||||
|
||||
@classmethod
|
||||
@Route.delete(
|
||||
r"/projects/{project_id}/vpcs/vms/{vm_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio",
|
||||
parameters={
|
||||
"project_id": "UUID for the project",
|
||||
"vm_id": "UUID for the instance",
|
||||
"adapter_number": "Network adapter where the nio is located",
|
||||
"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 VPCS instance")
|
||||
def delete_nio(request, response):
|
||||
|
||||
vpcs_manager = VPCS.instance()
|
||||
vm = vpcs_manager.get_vm(request.match_info["vm_id"], project_id=request.match_info["project_id"])
|
||||
vm.port_remove_nio_binding(int(request.match_info["port_number"]))
|
||||
response.set_status(204)
|
||||
@@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright (C) 2017 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/>.
|
||||
|
||||
|
||||
FILTER_OBJECT_SCHEMA = {
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"description": "Packet filter. This allow to simulate latency and errors",
|
||||
"type": "object"
|
||||
}
|
||||
@@ -16,7 +16,7 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from .label import LABEL_OBJECT_SCHEMA
|
||||
|
||||
from .filter import FILTER_OBJECT_SCHEMA
|
||||
|
||||
LINK_OBJECT_SCHEMA = {
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
@@ -64,6 +64,7 @@ LINK_OBJECT_SCHEMA = {
|
||||
"additionalProperties": False
|
||||
}
|
||||
},
|
||||
"filters": FILTER_OBJECT_SCHEMA,
|
||||
"capturing": {
|
||||
"description": "Read only property. True if a capture running on the link",
|
||||
"type": "boolean"
|
||||
@@ -81,7 +82,6 @@ LINK_OBJECT_SCHEMA = {
|
||||
"enum": ["ethernet", "serial"]
|
||||
}
|
||||
},
|
||||
"required": ["nodes"],
|
||||
"additionalProperties": False
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
# 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 .filter import FILTER_OBJECT_SCHEMA
|
||||
|
||||
|
||||
NIO_SCHEMA = {
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
@@ -43,7 +45,8 @@ NIO_SCHEMA = {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"maximum": 65535
|
||||
}
|
||||
},
|
||||
"filters": FILTER_OBJECT_SCHEMA
|
||||
},
|
||||
"required": ["type", "lport", "rhost", "rport"],
|
||||
"additionalProperties": False
|
||||
|
||||
@@ -138,8 +138,8 @@ class Hypervisor(UBridgeHypervisor):
|
||||
match = re.search("ubridge version ([0-9a-z\.]+)", output)
|
||||
if match:
|
||||
self._version = match.group(1)
|
||||
if parse_version(self._version) < parse_version("0.9.7"):
|
||||
raise UbridgeError("uBridge executable version must be >= 0.9.7")
|
||||
if parse_version(self._version) < parse_version("0.9.12"):
|
||||
raise UbridgeError("uBridge executable version must be >= 0.9.12")
|
||||
else:
|
||||
raise UbridgeError("Could not determine uBridge version for {}".format(self._path))
|
||||
except (OSError, subprocess.SubprocessError) as e:
|
||||
|
||||
Reference in new issue
Block a user