From 4783691c87438c084f14ae99310d0e34a8f5c4b3 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Fri, 17 Jun 2016 17:25:13 +0200 Subject: [PATCH] Drop /upload Fix #552 --- gns3server/handlers/__init__.py | 2 - gns3server/handlers/upload_handler.py | 160 ------------------ gns3server/templates/index.html | 2 +- gns3server/templates/layout.html | 6 - gns3server/templates/upload.html | 45 ----- tests/handlers/test_upload.py | 235 -------------------------- 6 files changed, 1 insertion(+), 449 deletions(-) delete mode 100644 gns3server/handlers/upload_handler.py delete mode 100644 gns3server/templates/upload.html delete mode 100644 tests/handlers/test_upload.py diff --git a/gns3server/handlers/__init__.py b/gns3server/handlers/__init__.py index dffacf86..557685f2 100644 --- a/gns3server/handlers/__init__.py +++ b/gns3server/handlers/__init__.py @@ -15,9 +15,7 @@ # along with this program. If not, see . -from gns3server.handlers.upload_handler import UploadHandler from gns3server.handlers.index_handler import IndexHandler from gns3server.handlers.api.controller import * from gns3server.handlers.api.compute import * - diff --git a/gns3server/handlers/upload_handler.py b/gns3server/handlers/upload_handler.py deleted file mode 100644 index be9ee137..00000000 --- a/gns3server/handlers/upload_handler.py +++ /dev/null @@ -1,160 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (C) 2016 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 . - -import os -import aiohttp -import stat -import io -import tarfile -import asyncio - -from gns3server.config import Config -from gns3server.web.route import Route -from gns3server.utils.images import remove_checksum, md5sum - - -class UploadHandler: - - @Route.get( - r"/upload", - description="List binary images", - api_version=None - ) - def index(request, response): - uploaded_files = [] - try: - for root, _, files in os.walk(UploadHandler.image_directory()): - for filename in files: - if not filename.startswith(".") and not filename.endswith(".md5sum"): - image_file = os.path.join(root, filename) - uploaded_files.append(image_file) - except OSError: - pass - iourc_path = os.path.join(os.path.expanduser("~/"), ".iourc") - if os.path.exists(iourc_path): - uploaded_files.append(iourc_path) - response.template("upload.html", files=uploaded_files) - - @Route.post( - r"/upload", - description="Upload binary images", - api_version=None - ) - def upload(request, response): - data = yield from request.post() - - if not data["file"]: - response.redirect("/upload") - return - - if data["type"] not in ["IOU", "IOURC", "QEMU", "IOS", "IMAGES", "PROJECTS"]: - raise aiohttp.web.HTTPForbidden(text="You are not authorized to upload this kind of image {}".format(data["type"])) - - try: - if data["type"] == "IMAGES": - UploadHandler._restore_directory(data["file"], UploadHandler.image_directory()) - elif data["type"] == "PROJECTS": - UploadHandler._restore_directory(data["file"], UploadHandler.project_directory()) - else: - if data["type"] == "IOURC": - destination_dir = os.path.expanduser("~/") - destination_path = os.path.join(destination_dir, ".iourc") - else: - destination_dir = os.path.join(UploadHandler.image_directory(), data["type"]) - destination_path = os.path.join(destination_dir, data["file"].filename) - os.makedirs(destination_dir, exist_ok=True) - remove_checksum(destination_path) - with open(destination_path, "wb+") as f: - while True: - chunk = data["file"].file.read(512) - if not chunk: - break - f.write(chunk) - md5sum(destination_path) - st = os.stat(destination_path) - os.chmod(destination_path, st.st_mode | stat.S_IXUSR) - except OSError as e: - response.html("Could not upload file: {}".format(e)) - response.set_status(200) - return - response.redirect("/upload") - - @Route.get( - r"/backup/images.tar", - description="Backup binary images", - api_version=None - ) - def backup_images(request, response): - yield from UploadHandler._backup_directory(request, response, UploadHandler.image_directory()) - - @Route.get( - r"/backup/projects.tar", - description="Backup GNS3 projects", - api_version=None - ) - def backup_projects(request, response): - yield from UploadHandler._backup_directory(request, response, UploadHandler.project_directory()) - - @staticmethod - def _restore_directory(file, directory): - """ - Extract from HTTP stream the content of a tar - """ - destination_path = os.path.join(directory, "archive.tar") - os.makedirs(directory, exist_ok=True) - with open(destination_path, "wb+") as f: - chunk = file.file.read() - f.write(chunk) - t = tarfile.open(destination_path) - t.extractall(directory) - t.close() - os.remove(destination_path) - - @staticmethod - @asyncio.coroutine - def _backup_directory(request, response, directory): - """ - Return a tar archive from a directory - """ - response.content_type = 'application/x-gtar' - response.set_status(200) - response.enable_chunked_encoding() - # Very important: do not send a content length otherwise QT close the connection but curl can consume the Feed - response.content_length = None - response.start(request) - - buffer = io.BytesIO() - with tarfile.open('arch.tar', 'w', fileobj=buffer) as tar: - for root, dirs, files in os.walk(directory): - for file in files: - path = os.path.join(root, file) - tar.add(os.path.join(root, file), arcname=os.path.relpath(path, directory)) - response.write(buffer.getvalue()) - yield from response.drain() - buffer.truncate(0) - buffer.seek(0) - yield from response.write_eof() - - @staticmethod - def image_directory(): - server_config = Config.instance().get_section_config("Server") - return os.path.expanduser(server_config.get("images_path", "~/GNS3/images")) - - @staticmethod - def project_directory(): - server_config = Config.instance().get_section_config("Server") - return os.path.expanduser(server_config.get("projects_path", "~/GNS3/projects")) diff --git a/gns3server/templates/index.html b/gns3server/templates/index.html index 3203897c..96b3f401 100644 --- a/gns3server/templates/index.html +++ b/gns3server/templates/index.html @@ -6,6 +6,6 @@ +

If you are looking for uploading the IOU. You can since 1.4 upload them directly from the client see: this documenation.

{% endblock %} diff --git a/gns3server/templates/layout.html b/gns3server/templates/layout.html index efbaf133..e8110279 100644 --- a/gns3server/templates/layout.html +++ b/gns3server/templates/layout.html @@ -8,12 +8,6 @@
Home | - Upload - | - Backup images - | - Backup projects - | Controller status | Compute status diff --git a/gns3server/templates/upload.html b/gns3server/templates/upload.html deleted file mode 100644 index 57191ca2..00000000 --- a/gns3server/templates/upload.html +++ /dev/null @@ -1,45 +0,0 @@ -{% extends "layout.html" %} - -{% block head %} - -{% endblock %} - -{% block body %} -

Select & Upload an image for GNS3

-
- File path:
- File type: -
-
- -
- {%if files%} -

Files on {{gns3_host}}

- {%for file in files%} -

{{file}}

- {%endfor%} - {%endif%} -{% endblock %} diff --git a/tests/handlers/test_upload.py b/tests/handlers/test_upload.py deleted file mode 100644 index c799ad2e..00000000 --- a/tests/handlers/test_upload.py +++ /dev/null @@ -1,235 +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 . - - -import aiohttp -import asyncio -import pytest -import os -import tarfile -from unittest.mock import patch - - -from gns3server.config import Config - - -@pytest.yield_fixture(autouse=True) -def restore_working_dir(): - directory = os.getcwd() - yield - os.chdir(directory) - - -def test_index_upload(http_root, tmpdir): - - Config.instance().set("Server", "images_path", str(tmpdir)) - - open(str(tmpdir / "alpha"), "w+").close() - open(str(tmpdir / "alpha.md5sum"), "w+").close() - open(str(tmpdir / ".beta"), "w+").close() - - response = http_root.get('/upload') - assert response.status == 200 - html = response.html - assert "GNS3 Server" in html - assert "Select & Upload" in html - assert "alpha" in html - assert ".beta" not in html - assert "alpha.md5sum" not in html - - -def test_upload(http_root, tmpdir): - - content = ''.join(['a' for _ in range(0, 1025)]) - - with open(str(tmpdir / "test"), "w+") as f: - f.write(content) - body = aiohttp.FormData() - body.add_field("type", "QEMU") - body.add_field("file", open(str(tmpdir / "test"), "rb"), content_type="application/iou", filename="test2") - - Config.instance().set("Server", "images_path", str(tmpdir)) - - response = http_root.post('/upload', body=body, raw=True) - - assert "test2" in response.body.decode("utf-8") - - with open(str(tmpdir / "QEMU" / "test2")) as f: - assert f.read() == content - - with open(str(tmpdir / "QEMU" / "test2.md5sum")) as f: - checksum = f.read() - assert checksum == "ae187e1febee2a150b64849c32d566ca" - - -def test_upload_previous_checksum(http_root, tmpdir): - - content = ''.join(['a' for _ in range(0, 1025)]) - - with open(str(tmpdir / "test"), "w+") as f: - f.write(content) - body = aiohttp.FormData() - body.add_field("type", "QEMU") - body.add_field("file", open(str(tmpdir / "test"), "rb"), content_type="application/iou", filename="test2") - - Config.instance().set("Server", "images_path", str(tmpdir)) - - os.makedirs(str(tmpdir / "QEMU")) - - with open(str(tmpdir / "QEMU" / "test2.md5sum"), 'w+') as f: - f.write("FAKE checksum") - - response = http_root.post('/upload', body=body, raw=True) - - assert "test2" in response.body.decode("utf-8") - - with open(str(tmpdir / "QEMU" / "test2")) as f: - assert f.read() == content - - with open(str(tmpdir / "QEMU" / "test2.md5sum")) as f: - checksum = f.read() - assert checksum == "ae187e1febee2a150b64849c32d566ca" - - -def test_upload_images_backup(http_root, tmpdir): - Config.instance().set("Server", "images_path", str(tmpdir / 'images')) - os.makedirs(str(tmpdir / 'images' / 'IOU')) - # An old IOU image that we need to replace - with open(str(tmpdir / 'images' / 'IOU' / 'b.img'), 'w+') as f: - f.write('bad') - - os.makedirs(str(tmpdir / 'old' / 'QEMU')) - with open(str(tmpdir / 'old' / 'QEMU' / 'a.img'), 'w+') as f: - f.write('hello') - os.makedirs(str(tmpdir / 'old' / 'IOU')) - with open(str(tmpdir / 'old' / 'IOU' / 'b.img'), 'w+') as f: - f.write('world') - - os.chdir(str(tmpdir / 'old')) - with tarfile.open(str(tmpdir / 'test.tar'), 'w') as tar: - tar.add('.', recursive=True) - - body = aiohttp.FormData() - body.add_field('type', 'IMAGES') - body.add_field('file', open(str(tmpdir / 'test.tar'), 'rb'), content_type='application/x-gtar', filename='test.tar') - response = http_root.post('/upload', body=body, raw=True) - assert response.status == 200 - - with open(str(tmpdir / 'images' / 'QEMU' / 'a.img')) as f: - assert f.read() == 'hello' - with open(str(tmpdir / 'images' / 'IOU' / 'b.img')) as f: - assert f.read() == 'world' - - assert 'a.img' in response.body.decode('utf-8') - assert 'b.img' in response.body.decode('utf-8') - assert not os.path.exists(str(tmpdir / 'images' / 'archive.tar')) - - -def test_upload_projects_backup(http_root, tmpdir): - Config.instance().set("Server", "projects_path", str(tmpdir / 'projects')) - os.makedirs(str(tmpdir / 'projects' / 'b')) - # An old b image that we need to replace - with open(str(tmpdir / 'projects' / 'b' / 'b.img'), 'w+') as f: - f.write('bad') - - os.makedirs(str(tmpdir / 'old' / 'a')) - with open(str(tmpdir / 'old' / 'a' / 'a.img'), 'w+') as f: - f.write('hello') - os.makedirs(str(tmpdir / 'old' / 'b')) - with open(str(tmpdir / 'old' / 'b' / 'b.img'), 'w+') as f: - f.write('world') - - os.chdir(str(tmpdir / 'old')) - with tarfile.open(str(tmpdir / 'test.tar'), 'w') as tar: - tar.add('.', recursive=True) - - body = aiohttp.FormData() - body.add_field('type', 'PROJECTS') - body.add_field('file', open(str(tmpdir / 'test.tar'), 'rb'), content_type='application/x-gtar', filename='test.tar') - response = http_root.post('/upload', body=body, raw=True) - assert response.status == 200 - - with open(str(tmpdir / 'projects' / 'a' / 'a.img')) as f: - assert f.read() == 'hello' - with open(str(tmpdir / 'projects' / 'b' / 'b.img')) as f: - assert f.read() == 'world' - - assert 'a.img' not in response.body.decode('utf-8') - assert 'b.img' not in response.body.decode('utf-8') - assert not os.path.exists(str(tmpdir / 'projects' / 'archive.tar')) - - -def test_backup_images(http_root, tmpdir, loop): - Config.instance().set('Server', 'images_path', str(tmpdir)) - - os.makedirs(str(tmpdir / 'QEMU')) - with open(str(tmpdir / 'QEMU' / 'a.img'), 'w+') as f: - f.write('hello') - with open(str(tmpdir / 'QEMU' / 'b.img'), 'w+') as f: - f.write('world') - - response = http_root.get('/backup/images.tar', raw=True) - assert response.status == 200 - assert response.headers['CONTENT-TYPE'] == 'application/x-gtar' - - with open(str(tmpdir / 'images.tar'), 'wb+') as f: - print(len(response.body)) - f.write(response.body) - - tar = tarfile.open(str(tmpdir / 'images.tar'), 'r') - os.makedirs(str(tmpdir / 'extract')) - os.chdir(str(tmpdir / 'extract')) - # Extract to current working directory - tar.extractall() - tar.close() - - assert os.path.exists(os.path.join('QEMU', 'a.img')) - assert open(os.path.join('QEMU', 'a.img')).read() == 'hello' - - assert os.path.exists(os.path.join('QEMU', 'b.img')) - assert open(os.path.join('QEMU', 'b.img')).read() == 'world' - - -def test_backup_projects(http_root, tmpdir, loop): - Config.instance().set('Server', 'projects_path', str(tmpdir)) - - os.makedirs(str(tmpdir / 'a')) - with open(str(tmpdir / 'a' / 'a.gns3'), 'w+') as f: - f.write('hello') - os.makedirs(str(tmpdir / 'b')) - with open(str(tmpdir / 'b' / 'b.gns3'), 'w+') as f: - f.write('world') - - response = http_root.get('/backup/projects.tar', raw=True) - assert response.status == 200 - assert response.headers['CONTENT-TYPE'] == 'application/x-gtar' - - with open(str(tmpdir / 'projects.tar'), 'wb+') as f: - f.write(response.body) - - tar = tarfile.open(str(tmpdir / 'projects.tar'), 'r') - os.makedirs(str(tmpdir / 'extract')) - os.chdir(str(tmpdir / 'extract')) - # Extract to current working directory - tar.extractall() - tar.close() - - assert os.path.exists(os.path.join('a', 'a.gns3')) - assert open(os.path.join('a', 'a.gns3')).read() == 'hello' - - assert os.path.exists(os.path.join('b', 'b.gns3')) - assert open(os.path.join('b', 'b.gns3')).read() == 'world'