From bf6f62e6290582237044703acbc92ed4de2bf5ce Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Fri, 16 Jan 2015 17:09:45 +0100 Subject: [PATCH] Serialize NIO --- .../post_vpcsvpcsidportsportidnio.txt | 19 ++++++++------ gns3server/handlers/vpcs_handler.py | 18 +++++++------ gns3server/modules/base_manager.py | 2 +- gns3server/modules/vpcs/nios/nio_tap.py | 3 +++ gns3server/modules/vpcs/nios/nio_udp.py | 3 +++ gns3server/modules/vpcs/vpcs_device.py | 1 - gns3server/schemas/vpcs.py | 2 +- gns3server/web/response.py | 11 ++++++++ gns3server/web/route.py | 4 +++ tests/api/test_version.py | 2 +- tests/api/test_vpcs.py | 26 ++++++++++++++----- 11 files changed, 65 insertions(+), 26 deletions(-) diff --git a/docs/api/examples/post_vpcsvpcsidportsportidnio.txt b/docs/api/examples/post_vpcsvpcsidportsportidnio.txt index cdcb7a4e..06fbc0fb 100644 --- a/docs/api/examples/post_vpcsvpcsidportsportidnio.txt +++ b/docs/api/examples/post_vpcsvpcsidportsportidnio.txt @@ -1,22 +1,25 @@ -curl -i -xPOST 'http://localhost:8000/vpcs/{vpcs_id}/ports/{port_id}/nio' -d '{"local_file": "/tmp/test", "remote_file": "/tmp/remote", "type": "nio_unix"}' +curl -i -xPOST 'http://localhost:8000/vpcs/{vpcs_id}/ports/{port_id}/nio' -d '{"lport": 4242, "rhost": "127.0.0.1", "rport": 4343, "type": "nio_udp"}' POST /vpcs/{vpcs_id}/ports/{port_id}/nio HTTP/1.1 { - "local_file": "/tmp/test", - "remote_file": "/tmp/remote", - "type": "nio_unix" + "lport": 4242, + "rhost": "127.0.0.1", + "rport": 4343, + "type": "nio_udp" } -HTTP/1.1 404 +HTTP/1.1 200 CONNECTION: close -CONTENT-LENGTH: 59 +CONTENT-LENGTH: 89 CONTENT-TYPE: application/json DATE: Thu, 08 Jan 2015 16:09:15 GMT SERVER: Python/3.4 aiohttp/0.13.1 X-ROUTE: /vpcs/{vpcs_id}/ports/{port_id}/nio { - "message": "ID 42 doesn't exist", - "status": 404 + "lport": 4242, + "rhost": "127.0.0.1", + "rport": 4343, + "type": "nio_udp" } diff --git a/gns3server/handlers/vpcs_handler.py b/gns3server/handlers/vpcs_handler.py index 6fc3495e..b12383b5 100644 --- a/gns3server/handlers/vpcs_handler.py +++ b/gns3server/handlers/vpcs_handler.py @@ -19,7 +19,7 @@ from ..web.route import Route from ..schemas.vpcs import VPCS_CREATE_SCHEMA from ..schemas.vpcs import VPCS_OBJECT_SCHEMA -from ..schemas.vpcs import VPCS_ADD_NIO_SCHEMA +from ..schemas.vpcs import VPCS_NIO_SCHEMA from ..modules.vpcs import VPCS @@ -48,7 +48,8 @@ class VPCSHandler(object): "vpcs_id": "Id of VPCS instance" }, status_codes={ - 201: "Success of creation of VPCS", + 200: "Success of starting VPCS", + 404: "If VPCS doesn't exist" }, description="Start VPCS", ) @@ -64,7 +65,8 @@ class VPCSHandler(object): "vpcs_id": "Id of VPCS instance" }, status_codes={ - 201: "Success of stopping VPCS", + 200: "Success of stopping VPCS", + 404: "If VPCS doesn't exist" }, description="Stop VPCS", ) @@ -81,17 +83,17 @@ class VPCSHandler(object): }, status_codes={ 201: "Success of creation of NIO", - 409: "Conflict" + 404: "If VPCS doesn't exist" }, description="ADD NIO to a VPCS", - input=VPCS_ADD_NIO_SCHEMA) + input=VPCS_NIO_SCHEMA, + output=VPCS_NIO_SCHEMA) def create_nio(request, response): - # TODO: raise 404 if VPCS not found GET VM can raise an exeption # TODO: response with nio vpcs_manager = VPCS.instance() vm = vpcs_manager.get_vm(int(request.match_info['vpcs_id'])) - vm.port_add_nio_binding(int(request.match_info['port_id']), request.json) + nio = vm.port_add_nio_binding(int(request.match_info['port_id']), request.json) - response.json({'name': "PC 2", "vpcs_id": 42, "console": 4242}) + response.json(nio) diff --git a/gns3server/modules/base_manager.py b/gns3server/modules/base_manager.py index ab07f427..36d031a6 100644 --- a/gns3server/modules/base_manager.py +++ b/gns3server/modules/base_manager.py @@ -39,7 +39,7 @@ class BaseManager: :returns: instance of Manager """ - if not hasattr(cls, "_instance"): + if not hasattr(cls, "_instance") or cls._instance is None: cls._instance = cls() return cls._instance diff --git a/gns3server/modules/vpcs/nios/nio_tap.py b/gns3server/modules/vpcs/nios/nio_tap.py index 4c3ed6b2..39923a01 100644 --- a/gns3server/modules/vpcs/nios/nio_tap.py +++ b/gns3server/modules/vpcs/nios/nio_tap.py @@ -44,3 +44,6 @@ class NIO_TAP(object): def __str__(self): return "NIO TAP" + + def __json__(self): + return {"type": "nio_tap", "tap_device": self._tap_device} diff --git a/gns3server/modules/vpcs/nios/nio_udp.py b/gns3server/modules/vpcs/nios/nio_udp.py index 0527f675..cca313e7 100644 --- a/gns3server/modules/vpcs/nios/nio_udp.py +++ b/gns3server/modules/vpcs/nios/nio_udp.py @@ -70,3 +70,6 @@ class NIO_UDP(object): def __str__(self): return "NIO UDP" + + def __json__(self): + return {"type": "nio_udp", "lport": self._lport, "rport": self._rport, "rhost": self._rhost} diff --git a/gns3server/modules/vpcs/vpcs_device.py b/gns3server/modules/vpcs/vpcs_device.py index 5384a985..fe079c25 100644 --- a/gns3server/modules/vpcs/vpcs_device.py +++ b/gns3server/modules/vpcs/vpcs_device.py @@ -265,7 +265,6 @@ class VPCSDevice(BaseVM): nio = NIO_UDP(lport, rhost, rport) elif nio_settings["type"] == "nio_tap": tap_device = nio_settings["tap_device"] - print(has_privileged_access) if not has_privileged_access(self._path): raise VPCSError("{} has no privileged access to {}.".format(self._path, tap_device)) nio = NIO_TAP(tap_device) diff --git a/gns3server/schemas/vpcs.py b/gns3server/schemas/vpcs.py index dc5ca6dd..27a5bcd3 100644 --- a/gns3server/schemas/vpcs.py +++ b/gns3server/schemas/vpcs.py @@ -42,7 +42,7 @@ VPCS_CREATE_SCHEMA = { } -VPCS_ADD_NIO_SCHEMA = { +VPCS_NIO_SCHEMA = { "$schema": "http://json-schema.org/draft-04/schema#", "description": "Request validation to add a NIO for a VPCS instance", "type": "object", diff --git a/gns3server/web/response.py b/gns3server/web/response.py index 325455f4..d829e725 100644 --- a/gns3server/web/response.py +++ b/gns3server/web/response.py @@ -18,7 +18,9 @@ import json import jsonschema import aiohttp.web +import logging +log = logging.getLogger(__name__) class Response(aiohttp.web.Response): @@ -29,13 +31,22 @@ class Response(aiohttp.web.Response): headers['X-Route'] = self._route super().__init__(headers=headers, **kwargs) + """ + Set the response content type to application/json and serialize + the content. + + :param anwser The response as a Python object + """ def json(self, answer): """Pass a Python object and return a JSON as answer""" self.content_type = "application/json" + if hasattr(answer, '__json__'): + answer = answer.__json__() if self._output_schema is not None: try: jsonschema.validate(answer, self._output_schema) except jsonschema.ValidationError as e: + log.error("Invalid output schema") raise aiohttp.web.HTTPBadRequest(text="{}".format(e)) self.body = json.dumps(answer, indent=4, sort_keys=True).encode('utf-8') diff --git a/gns3server/web/route.py b/gns3server/web/route.py index 1687cde9..e55edf06 100644 --- a/gns3server/web/route.py +++ b/gns3server/web/route.py @@ -19,6 +19,9 @@ import json import jsonschema import asyncio import aiohttp +import logging + +log = logging.getLogger(__name__) from ..modules.vm_error import VMError from .response import Response @@ -37,6 +40,7 @@ def parse_request(request, input_schema): try: jsonschema.validate(request.json, input_schema) except jsonschema.ValidationError as e: + log.error("Invalid input schema") raise aiohttp.web.HTTPBadRequest(text="Request is not {} '{}' in schema: {}".format( e.validator, e.validator_value, diff --git a/tests/api/test_version.py b/tests/api/test_version.py index a052bb43..8fb46174 100644 --- a/tests/api/test_version.py +++ b/tests/api/test_version.py @@ -21,7 +21,7 @@ It's also used for unittest the HTTP implementation. """ from tests.utils import asyncio_patch -from tests.api.base import server, loop +from tests.api.base import server, loop, port_manager from gns3server.version import __version__ diff --git a/tests/api/test_vpcs.py b/tests/api/test_vpcs.py index eb65610f..623e1b65 100644 --- a/tests/api/test_vpcs.py +++ b/tests/api/test_vpcs.py @@ -15,6 +15,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +from unittest.mock import patch from tests.api.base import server, loop, port_manager from tests.utils import asyncio_patch from gns3server import modules @@ -29,13 +30,26 @@ def test_vpcs_create(server): assert response.json['vpcs_id'] == 84 -def test_vpcs_nio_create(server): - response = server.post('/vpcs/42/ports/0/nio', { - 'type': 'nio_unix', - 'local_file': '/tmp/test', - 'remote_file': '/tmp/remote' +def test_vpcs_nio_create_udp(server): + vm = server.post('/vpcs', {'name': 'PC TEST 1'}) + response = server.post('/vpcs/{}/ports/0/nio'.format(vm.json["vpcs_id"]), { + 'type': 'nio_udp', + 'lport': 4242, + 'rport': 4343, + 'rhost': '127.0.0.1' }, example=True) assert response.status == 200 assert response.route == '/vpcs/{vpcs_id}/ports/{port_id}/nio' - assert response.json['name'] == 'PC 2' + assert response.json['type'] == 'nio_udp' + +@patch("gns3server.modules.vpcs.vpcs_device.has_privileged_access", return_value=True) +def test_vpcs_nio_create_tap(mock, server): + vm = server.post('/vpcs', {'name': 'PC TEST 1'}) + response = server.post('/vpcs/{}/ports/0/nio'.format(vm.json["vpcs_id"]), { + 'type': 'nio_tap', + 'tap_device': 'test', + }) + assert response.status == 200 + assert response.route == '/vpcs/{vpcs_id}/ports/{port_id}/nio' + assert response.json['type'] == 'nio_tap'