Filter implementation for cloud & Nat

Fix #1111
This commit is contained in:
Julien Duponchelle 2017-07-17 14:22:05 +02:00
parent 03af9bd621
commit 536c708c16
No known key found for this signature in database
GPG Key ID: CE8B29639E07F5E8
10 changed files with 124 additions and 9 deletions

View File

@ -64,6 +64,10 @@ class Cloud(BaseNode):
port_number += 1 port_number += 1
self._ports_mapping = ports self._ports_mapping = ports
@property
def nios(self):
return self._nios
def _interfaces(self): def _interfaces(self):
return gns3server.utils.interfaces.interfaces() return gns3server.utils.interfaces.interfaces()
@ -202,6 +206,7 @@ class Cloud(BaseNode):
rhost=nio.rhost, rhost=nio.rhost,
rport=nio.rport)) rport=nio.rport))
yield from self._ubridge_apply_filters(bridge_name, nio.filters)
if port_info["type"] in ("ethernet", "tap"): if port_info["type"] in ("ethernet", "tap"):
if sys.platform.startswith("win"): if sys.platform.startswith("win"):
@ -311,6 +316,19 @@ class Cloud(BaseNode):
self.status = "stopped" self.status = "stopped"
self._nios[port_number] = nio self._nios[port_number] = nio
@asyncio.coroutine
def update_nio(self, port_number, nio):
"""
Update an nio on this node
:param nio: NIO instance to add
:param port_number: port to allocate for the NIO
"""
bridge_name = "{}-{}".format(self._id, port_number)
if self._ubridge_hypervisor and self._ubridge_hypervisor.is_running():
yield from self._ubridge_apply_filters(bridge_name, nio.filters)
@asyncio.coroutine @asyncio.coroutine
def _delete_ubridge_connection(self, port_number): def _delete_ubridge_connection(self, port_number):
""" """

View File

@ -38,6 +38,7 @@ class NIOUDP(NIO):
self._lport = lport self._lport = lport
self._rhost = rhost self._rhost = rhost
self._rport = rport self._rport = rport
assert isinstance(filters, dict)
self._filters = filters self._filters = filters
@property @property

View File

@ -365,7 +365,12 @@ class Link:
:returns: None if no node support filtering else the node :returns: None if no node support filtering else the node
""" """
for node in self._nodes: for node in self._nodes:
if node["node"].node_type in ('vpcs', 'dynamips', 'qemu', 'docker'): if node["node"].node_type in ('vpcs',
'dynamips',
'qemu',
'cloud',
'nat',
'docker'):
return node["node"] return node["node"]
return None return None

View File

@ -199,6 +199,33 @@ class CloudHandler:
response.set_status(201) response.set_status(201)
response.json(nio) response.json(nio)
@Route.put(
r"/projects/{project_id}/cloud/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 Cloud instance")
def update_nio(request, response):
builtin_manager = Builtin.instance()
node = builtin_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
nio = node.nios[int(request.match_info["adapter_number"])]
if "filters" in request.json and nio:
nio.filters = request.json["filters"]
yield from node.update_nio(int(request.match_info["port_number"]), nio)
response.set_status(201)
response.json(request.json)
@Route.delete( @Route.delete(
r"/projects/{project_id}/cloud/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio", r"/projects/{project_id}/cloud/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio",
parameters={ parameters={

View File

@ -198,6 +198,33 @@ class NatHandler:
response.set_status(201) response.set_status(201)
response.json(nio) response.json(nio)
@Route.put(
r"/projects/{project_id}/nat/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 NAT instance")
def update_nio(request, response):
builtin_manager = Builtin.instance()
node = builtin_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
nio = node.nios[int(request.match_info["adapter_number"])]
if "filters" in request.json and nio:
nio.filters = request.json["filters"]
yield from node.update_nio(int(request.match_info["port_number"]), nio)
response.set_status(201)
response.json(request.json)
@Route.delete( @Route.delete(
r"/projects/{project_id}/nat/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio", r"/projects/{project_id}/nat/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio",
parameters={ parameters={

View File

@ -26,7 +26,7 @@ from tests.utils import asyncio_patch
@pytest.fixture @pytest.fixture
def nio(): def nio():
return NIOUDP(4242, "127.0.0.1", 4343, []) return NIOUDP(4242, "127.0.0.1", 4343, {})
@pytest.fixture @pytest.fixture
@ -159,6 +159,7 @@ def test_linux_ethernet_raw_add_nio(linux_platform, project, async_run, nio):
ubridge_mock.assert_has_calls([ ubridge_mock.assert_has_calls([
call("bridge create {}-0".format(cloud._id)), call("bridge create {}-0".format(cloud._id)),
call("bridge add_nio_udp {}-0 4242 127.0.0.1 4343".format(cloud._id)), call("bridge add_nio_udp {}-0 4242 127.0.0.1 4343".format(cloud._id)),
call('bridge reset_packet_filters {}-0'.format(cloud._id)),
call("bridge add_nio_linux_raw {}-0 \"eth0\"".format(cloud._id)), call("bridge add_nio_linux_raw {}-0 \"eth0\"".format(cloud._id)),
call("bridge start {}-0".format(cloud._id)), call("bridge start {}-0".format(cloud._id)),
]) ])
@ -188,6 +189,7 @@ def test_linux_ethernet_raw_add_nio_bridge(linux_platform, project, async_run, n
ubridge_mock.assert_has_calls([ ubridge_mock.assert_has_calls([
call("bridge create {}-0".format(cloud._id)), call("bridge create {}-0".format(cloud._id)),
call("bridge add_nio_udp {}-0 4242 127.0.0.1 4343".format(cloud._id)), call("bridge add_nio_udp {}-0 4242 127.0.0.1 4343".format(cloud._id)),
call('bridge reset_packet_filters {}-0'.format(cloud._id)),
call("bridge add_nio_tap \"{}-0\" \"{}\"".format(cloud._id, tap)), call("bridge add_nio_tap \"{}-0\" \"{}\"".format(cloud._id, tap)),
call("brctl addif \"bridge0\" \"{}\"".format(tap)), call("brctl addif \"bridge0\" \"{}\"".format(tap)),
call("bridge start {}-0".format(cloud._id)), call("bridge start {}-0".format(cloud._id)),

View File

@ -24,9 +24,9 @@ def test_arp_command(async_run):
node = AsyncioMagicMock() node = AsyncioMagicMock()
node.name = "Test" node.name = "Test"
node.nios = {} node.nios = {}
node.nios[0] = NIOUDP(55, "127.0.0.1", 56, []) node.nios[0] = NIOUDP(55, "127.0.0.1", 56, {})
node.nios[0].name = "Ethernet0" node.nios[0].name = "Ethernet0"
node.nios[1] = NIOUDP(55, "127.0.0.1", 56, []) node.nios[1] = NIOUDP(55, "127.0.0.1", 56, {})
node.nios[1].name = "Ethernet1" node.nios[1].name = "Ethernet1"
node._hypervisor.send = AsyncioMagicMock(return_value=["0050.7966.6801 1 Ethernet0", "0050.7966.6802 1 Ethernet1"]) node._hypervisor.send = AsyncioMagicMock(return_value=["0050.7966.6801 1 Ethernet0", "0050.7966.6802 1 Ethernet1"])
console = EthernetSwitchConsole(node) console = EthernetSwitchConsole(node)

View File

@ -125,12 +125,11 @@ def test_change_aux_port(node, port_manager):
def test_update_ubridge_udp_connection(node, async_run): def test_update_ubridge_udp_connection(node, async_run):
filters = [{ filters = {
"type": "latency", "latency": [10]
"value": 10 }
}]
snio = NIOUDP(1245, "localhost", 1246, []) snio = NIOUDP(1245, "localhost", 1246, {})
dnio = NIOUDP(1245, "localhost", 1244, filters) dnio = NIOUDP(1245, "localhost", 1244, filters)
with asyncio_patch("gns3server.compute.base_node.BaseNode._ubridge_apply_filters") as mock: with asyncio_patch("gns3server.compute.base_node.BaseNode._ubridge_apply_filters") as mock:
async_run(node.update_ubridge_udp_connection('VPCS-10', snio, dnio)) async_run(node.update_ubridge_udp_connection('VPCS-10', snio, dnio))

View File

@ -68,6 +68,24 @@ def test_cloud_nio_create_udp(http_compute, vm):
assert response.json["type"] == "nio_udp" assert response.json["type"] == "nio_udp"
def test_cloud_nio_update_udp(http_compute, vm):
http_compute.post("/projects/{project_id}/cloud/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), {"type": "nio_udp",
"lport": 4242,
"rport": 4343,
"rhost": "127.0.0.1"})
response = http_compute.put("/projects/{project_id}/cloud/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]),
{
"type": "nio_udp",
"lport": 4242,
"rport": 4343,
"rhost": "127.0.0.1",
"filters": {}},
example=True)
assert response.status == 201, response.body.decode()
assert response.route == "/projects/{project_id}/cloud/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio"
assert response.json["type"] == "nio_udp"
def test_cloud_delete_nio(http_compute, vm): def test_cloud_delete_nio(http_compute, vm):
http_compute.post("/projects/{project_id}/cloud/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), {"type": "nio_udp", http_compute.post("/projects/{project_id}/cloud/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), {"type": "nio_udp",
"lport": 4242, "lport": 4242,

View File

@ -69,6 +69,24 @@ def test_nat_nio_create_udp(http_compute, vm):
assert response.json["type"] == "nio_udp" assert response.json["type"] == "nio_udp"
def test_nat_nio_update_udp(http_compute, vm):
http_compute.post("/projects/{project_id}/nat/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), {"type": "nio_udp",
"lport": 4242,
"rport": 4343,
"rhost": "127.0.0.1"})
response = http_compute.put("/projects/{project_id}/nat/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]),
{
"type": "nio_udp",
"lport": 4242,
"rport": 4343,
"rhost": "127.0.0.1",
"filters": {}},
example=True)
assert response.status == 201, response.body.decode()
assert response.route == "/projects/{project_id}/nat/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio"
assert response.json["type"] == "nio_udp"
def test_nat_delete_nio(http_compute, vm): def test_nat_delete_nio(http_compute, vm):
with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat.add_nio"): with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat.add_nio"):
http_compute.post("/projects/{project_id}/nat/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), {"type": "nio_udp", http_compute.post("/projects/{project_id}/nat/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), {"type": "nio_udp",