1
0
mirror of https://github.com/GNS3/gns3-server synced 2025-07-12 17:48:20 +00:00

Allow user to backup projects and images

This commit is contained in:
Julien Duponchelle 2015-07-21 14:20:58 +02:00
parent 2ccb30eeb5
commit 270017d945
4 changed files with 127 additions and 1 deletions

View File

@ -18,6 +18,9 @@
import os import os
import aiohttp import aiohttp
import stat import stat
import io
import tarfile
import asyncio
from ..config import Config from ..config import Config
from ..web.route import Route from ..web.route import Route
@ -81,7 +84,52 @@ class UploadHandler:
return return
response.redirect("/upload") response.redirect("/upload")
@classmethod
@Route.get(
r"/upload/backup/images.tar",
description="Backup GNS3 images",
api_version=None
)
def backup_images(request, response):
yield from UploadHandler._backup_directory(request, response, UploadHandler.image_directory())
@classmethod
@Route.get(
r"/upload/backup/projects.tar",
description="Backup GNS3 projects",
api_version=None
)
def backup_images(request, response):
yield from UploadHandler._backup_directory(request, response, UploadHandler.project_directory())
@staticmethod
@asyncio.coroutine
def _backup_directory(request, response, 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 @staticmethod
def image_directory(): def image_directory():
server_config = Config.instance().get_section_config("Server") server_config = Config.instance().get_section_config("Server")
return os.path.expanduser(server_config.get("images_path", "~/GNS3/images")) 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/images"))

View File

@ -4,6 +4,13 @@
<title>GNS3 Server</title> <title>GNS3 Server</title>
</head> </head>
<body> <body>
<div>
<a href="/upload">Home</a>
|
<a href="/upload/backup/images.tar">Backup images</a>
|
<a href="/upload/backup/projects.tar">Backup projects</a>
</div>
{% block body %}{% endblock %} {% block body %}{% endblock %}
</body> </body>
<small> <small>

View File

@ -88,7 +88,10 @@ class Query:
except ValueError: except ValueError:
response.json = None response.json = None
else: else:
response.html = response.body.decode("utf-8") try:
response.html = response.body.decode("utf-8")
except UnicodeDecodeError:
response.html = None
else: else:
response.json = {} response.json = {}
response.html = "" response.html = ""

View File

@ -17,10 +17,15 @@
import aiohttp import aiohttp
import asyncio
import os import os
import tarfile
from unittest.mock import patch from unittest.mock import patch
from gns3server.config import Config from gns3server.config import Config
def test_index_upload(server): def test_index_upload(server):
response = server.get('/upload', api_version=None) response = server.get('/upload', api_version=None)
assert response.status == 200 assert response.status == 200
@ -44,3 +49,66 @@ def test_upload(server, tmpdir):
assert f.read() == "TEST" assert f.read() == "TEST"
assert "test2" in response.body.decode("utf-8") assert "test2" in response.body.decode("utf-8")
def test_backup_images(server, 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 = server.get('/upload/backup/images.tar', api_version=None, 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'))
open(os.path.join('QEMU', 'a.img')).read() == 'hello'
assert os.path.exists(os.path.join('QEMU', 'b.img'))
open(os.path.join('QEMU', 'b.img')).read() == 'world'
def test_backup_projects(server, 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 = server.get('/upload/backup/projects.tar', api_version=None, raw=True)
assert response.status == 200
assert response.headers['CONTENT-TYPE'] == 'application/x-gtar'
with open(str(tmpdir / 'projects.tar'), 'wb+') as f:
print(len(response.body))
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'))
open(os.path.join('a', 'a.gns3')).read() == 'hello'
assert os.path.exists(os.path.join('b', 'b.gns3'))
open(os.path.join('b', 'b.gns3')).read() == 'world'