From bb4ebbfe64afb853e23ef3a6e52f3e35cea9027f Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Fri, 24 Apr 2015 10:15:23 +0200 Subject: [PATCH] Upload images API Conflicts: requirements.txt setup.py --- .../handlers/api/dynamips_vm_handler.py | 13 ++++++++++ gns3server/handlers/api/iou_handler.py | 13 ++++++++++ gns3server/handlers/api/qemu_handler.py | 13 ++++++++++ gns3server/modules/base_manager.py | 17 ++++++++++++ gns3server/web/route.py | 4 ++- setup.py | 1 + tests/handlers/api/test_dynamips.py | 19 ++++++++++++++ tests/handlers/api/test_iou.py | 26 ++++++++++++++++--- tests/handlers/api/test_qemu.py | 19 ++++++++++++++ 9 files changed, 121 insertions(+), 4 deletions(-) diff --git a/gns3server/handlers/api/dynamips_vm_handler.py b/gns3server/handlers/api/dynamips_vm_handler.py index 75b197dc..6d46fc86 100644 --- a/gns3server/handlers/api/dynamips_vm_handler.py +++ b/gns3server/handlers/api/dynamips_vm_handler.py @@ -463,3 +463,16 @@ class DynamipsVMHandler: vms = yield from dynamips_manager.list_images() response.set_status(200) response.json(vms) + + @Route.post( + r"/dynamips/vms/{filename}", + status_codes={ + 204: "Image uploaded", + }, + raw=True, + description="Upload Dynamips image.") + def upload_vm(request, response): + + dynamips_manager = Dynamips.instance() + yield from dynamips_manager.write_image(request.match_info["filename"], request.content) + response.set_status(204) diff --git a/gns3server/handlers/api/iou_handler.py b/gns3server/handlers/api/iou_handler.py index d87e7537..520decc2 100644 --- a/gns3server/handlers/api/iou_handler.py +++ b/gns3server/handlers/api/iou_handler.py @@ -330,3 +330,16 @@ class IOUHandler: vms = yield from iou_manager.list_images() response.set_status(200) response.json(vms) + + @Route.post( + r"/iou/vms/{filename}", + status_codes={ + 204: "Image uploaded", + }, + raw=True, + description="Upload IOU image.") + def upload_vm(request, response): + + iou_manager = IOU.instance() + yield from iou_manager.write_image(request.match_info["filename"], request.content) + response.set_status(204) diff --git a/gns3server/handlers/api/qemu_handler.py b/gns3server/handlers/api/qemu_handler.py index a703cdb1..041525d1 100644 --- a/gns3server/handlers/api/qemu_handler.py +++ b/gns3server/handlers/api/qemu_handler.py @@ -305,3 +305,16 @@ class QEMUHandler: vms = yield from qemu_manager.list_images() response.set_status(200) response.json(vms) + + @Route.post( + r"/qemu/vms/{filename}", + status_codes={ + 204: "Image uploaded", + }, + raw=True, + description="Upload Qemu image.") + def upload_vm(request, response): + + qemu_manager = Qemu.instance() + yield from qemu_manager.write_image(request.match_info["filename"], request.content) + response.set_status(204) diff --git a/gns3server/modules/base_manager.py b/gns3server/modules/base_manager.py index b7f87f8b..674eb419 100644 --- a/gns3server/modules/base_manager.py +++ b/gns3server/modules/base_manager.py @@ -444,3 +444,20 @@ class BaseManager: """ raise NotImplementedError + + @asyncio.coroutine + def write_image(self, filename, stream): + directory = self.get_images_directory() + path = os.path.join(directory, os.path.basename(filename)) + log.info("Writting image file %s", path) + try: + os.makedirs(directory, exist_ok=True) + with open(path, 'wb+') as f: + while True: + packet = yield from stream.read(512) + if not packet: + break + f.write(packet) + os.chmod(path, stat.S_IWRITE | stat.S_IREAD | stat.S_IEXEC) + except OSError as e: + raise aiohttp.web.HTTPConflict(text="Could not write image: {} to {}".format(filename, e)) diff --git a/gns3server/web/route.py b/gns3server/web/route.py index f376b820..7601bd6b 100644 --- a/gns3server/web/route.py +++ b/gns3server/web/route.py @@ -89,6 +89,7 @@ class Route(object): output_schema = kw.get("output", {}) input_schema = kw.get("input", {}) api_version = kw.get("api_version", 1) + raw = kw.get("raw", False) # If it's a JSON api endpoint just register the endpoint an do nothing if api_version is None: @@ -119,8 +120,9 @@ class Route(object): # This block is executed at each method call # Non API call - if api_version is None: + if api_version is None or raw is True: response = Response(request=request, route=route, output_schema=output_schema) + yield from func(request, response) return response diff --git a/setup.py b/setup.py index ddb3a140..5f4b54cd 100644 --- a/setup.py +++ b/setup.py @@ -41,6 +41,7 @@ dependencies = [ "raven>=5.2.0" ] + if sys.version_info == (3, 3): dependencies.append("asyncio>=3.4.2") diff --git a/tests/handlers/api/test_dynamips.py b/tests/handlers/api/test_dynamips.py index 8b096071..9deb65af 100644 --- a/tests/handlers/api/test_dynamips.py +++ b/tests/handlers/api/test_dynamips.py @@ -146,3 +146,22 @@ def test_vms(server, tmpdir, fake_dynamips): response = server.get("/dynamips/vms") assert response.status == 200 assert response.json == [{"filename": "7200.bin"}] + + +def test_upload_vm(server, tmpdir): + with patch("gns3server.modules.Dynamips.get_images_directory", return_value=str(tmpdir),): + response = server.post("/dynamips/vms/test2", body="TEST", raw=True) + assert response.status == 204 + + with open(str(tmpdir / "test2")) as f: + assert f.read() == "TEST" + + +def test_upload_vm_permission_denied(server, tmpdir): + with open(str(tmpdir / "test2"), "w+") as f: + f.write("") + os.chmod(str(tmpdir / "test2"), 0) + + with patch("gns3server.modules.Dynamips.get_images_directory", return_value=str(tmpdir),): + response = server.post("/dynamips/vms/test2", body="TEST", raw=True) + assert response.status == 409 diff --git a/tests/handlers/api/test_iou.py b/tests/handlers/api/test_iou.py index 7b91ef62..962cd81d 100644 --- a/tests/handlers/api/test_iou.py +++ b/tests/handlers/api/test_iou.py @@ -20,6 +20,7 @@ import os import stat import sys import uuid +import aiohttp from tests.utils import asyncio_patch from unittest.mock import patch, MagicMock, PropertyMock @@ -311,9 +312,28 @@ def test_get_initial_config_with_config_file(server, project, vm): assert response.json["content"] == "TEST" -def test_vms(server, vm, tmpdir, fake_iou_bin): +def test_vms(server, tmpdir, fake_iou_bin): - with patch("gns3server.modules.IOU.get_images_directory", return_value=str(tmpdir), example=True): - response = server.get("/iou/vms") + with patch("gns3server.modules.IOU.get_images_directory", return_value=str(tmpdir)): + response = server.get("/iou/vms", example=True) assert response.status == 200 assert response.json == [{"filename": "iou.bin"}] + + +def test_upload_vm(server, tmpdir): + with patch("gns3server.modules.IOU.get_images_directory", return_value=str(tmpdir),): + response = server.post("/iou/vms/test2", body="TEST", raw=True) + assert response.status == 204 + + with open(str(tmpdir / "test2")) as f: + assert f.read() == "TEST" + + +def test_upload_vm_permission_denied(server, tmpdir): + with open(str(tmpdir / "test2"), "w+") as f: + f.write("") + os.chmod(str(tmpdir / "test2"), 0) + + with patch("gns3server.modules.IOU.get_images_directory", return_value=str(tmpdir),): + response = server.post("/iou/vms/test2", body="TEST", raw=True) + assert response.status == 409 diff --git a/tests/handlers/api/test_qemu.py b/tests/handlers/api/test_qemu.py index 4af9065f..cf08992a 100644 --- a/tests/handlers/api/test_qemu.py +++ b/tests/handlers/api/test_qemu.py @@ -200,3 +200,22 @@ def test_vms(server, tmpdir, fake_qemu_vm): response = server.get("/qemu/vms") assert response.status == 200 assert response.json == [{"filename": "linux.img"}] + + +def test_upload_vm(server, tmpdir): + with patch("gns3server.modules.Qemu.get_images_directory", return_value=str(tmpdir),): + response = server.post("/qemu/vms/test2", body="TEST", raw=True) + assert response.status == 204 + + with open(str(tmpdir / "test2")) as f: + assert f.read() == "TEST" + + +def test_upload_vm_permission_denied(server, tmpdir): + with open(str(tmpdir / "test2"), "w+") as f: + f.write("") + os.chmod(str(tmpdir / "test2"), 0) + + with patch("gns3server.modules.Qemu.get_images_directory", return_value=str(tmpdir),): + response = server.post("/qemu/vms/test2", body="TEST", raw=True) + assert response.status == 409