mirror of
https://github.com/GNS3/gns3-server
synced 2024-11-18 06:18:08 +00:00
Support snapshots for portable projects. Fixes https://github.com/GNS3/gns3-gui/issues/2792
This commit is contained in:
parent
6229fadace
commit
5cbfafbeb2
@ -22,10 +22,13 @@ import uuid
|
|||||||
import shutil
|
import shutil
|
||||||
import zipfile
|
import zipfile
|
||||||
import aiohttp
|
import aiohttp
|
||||||
|
import aiofiles
|
||||||
import itertools
|
import itertools
|
||||||
|
import tempfile
|
||||||
|
|
||||||
from .topology import load_topology
|
from .topology import load_topology
|
||||||
from ..utils.asyncio import wait_run_in_executor
|
from ..utils.asyncio import wait_run_in_executor
|
||||||
|
from ..utils.asyncio import aiozipstream
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
@ -159,8 +162,13 @@ async def import_project(controller, project_id, stream, location=None, name=Non
|
|||||||
json.dump(topology, f, indent=4)
|
json.dump(topology, f, indent=4)
|
||||||
os.remove(os.path.join(path, "project.gns3"))
|
os.remove(os.path.join(path, "project.gns3"))
|
||||||
|
|
||||||
if os.path.exists(os.path.join(path, "images")):
|
images_path = os.path.join(path, "images")
|
||||||
_import_images(controller, path)
|
if os.path.exists(images_path):
|
||||||
|
await _import_images(controller, images_path)
|
||||||
|
|
||||||
|
snapshots_path = os.path.join(path, "snapshots")
|
||||||
|
if os.path.exists(snapshots_path):
|
||||||
|
await _import_snapshots(snapshots_path, project_name, project_id)
|
||||||
|
|
||||||
project = await controller.load_project(dot_gns3_path, load=False)
|
project = await controller.load_project(dot_gns3_path, load=False)
|
||||||
return project
|
return project
|
||||||
@ -215,13 +223,13 @@ async def _upload_file(compute, project_id, file_path, path):
|
|||||||
await compute.http_query("POST", path, f, timeout=None)
|
await compute.http_query("POST", path, f, timeout=None)
|
||||||
|
|
||||||
|
|
||||||
def _import_images(controller, path):
|
async def _import_images(controller, images_path):
|
||||||
"""
|
"""
|
||||||
Copy images to the images directory or delete them if they already exists.
|
Copy images to the images directory or delete them if they already exists.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
image_dir = controller.images_path()
|
image_dir = controller.images_path()
|
||||||
root = os.path.join(path, "images")
|
root = images_path
|
||||||
for (dirpath, dirnames, filenames) in os.walk(root, followlinks=False):
|
for (dirpath, dirnames, filenames) in os.walk(root, followlinks=False):
|
||||||
for filename in filenames:
|
for filename in filenames:
|
||||||
path = os.path.join(dirpath, filename)
|
path = os.path.join(dirpath, filename)
|
||||||
@ -229,4 +237,54 @@ def _import_images(controller, path):
|
|||||||
continue
|
continue
|
||||||
dst = os.path.join(image_dir, os.path.relpath(path, root))
|
dst = os.path.join(image_dir, os.path.relpath(path, root))
|
||||||
os.makedirs(os.path.dirname(dst), exist_ok=True)
|
os.makedirs(os.path.dirname(dst), exist_ok=True)
|
||||||
shutil.move(path, dst)
|
await wait_run_in_executor(shutil.move, path, dst)
|
||||||
|
|
||||||
|
|
||||||
|
async def _import_snapshots(snapshots_path, project_name, project_id):
|
||||||
|
"""
|
||||||
|
Import the snapshots and update their project name and ID to be the same as the main project.
|
||||||
|
"""
|
||||||
|
|
||||||
|
for snapshot in os.listdir(snapshots_path):
|
||||||
|
if not snapshot.endswith(".gns3project"):
|
||||||
|
continue
|
||||||
|
snapshot_path = os.path.join(snapshots_path, snapshot)
|
||||||
|
with tempfile.TemporaryDirectory(dir=snapshots_path) as tmpdir:
|
||||||
|
|
||||||
|
# extract everything to a temporary directory
|
||||||
|
try:
|
||||||
|
with open(snapshot_path, "rb") as f:
|
||||||
|
with zipfile.ZipFile(f) as zip_file:
|
||||||
|
await wait_run_in_executor(zip_file.extractall, tmpdir)
|
||||||
|
except OSError as e:
|
||||||
|
raise aiohttp.web.HTTPConflict(text="Cannot open snapshot '{}': {}".format(os.path.basename(snapshot), e))
|
||||||
|
except zipfile.BadZipFile:
|
||||||
|
raise aiohttp.web.HTTPConflict(text="Cannot extract files from snapshot '{}': not a GNS3 project (invalid zip)".format(os.path.basename(snapshot)))
|
||||||
|
|
||||||
|
# patch the topology with the correct project name and ID
|
||||||
|
try:
|
||||||
|
topology_file_path = os.path.join(tmpdir, "project.gns3")
|
||||||
|
with open(topology_file_path, encoding="utf-8") as f:
|
||||||
|
topology = json.load(f)
|
||||||
|
|
||||||
|
topology["name"] = project_name
|
||||||
|
topology["project_id"] = project_id
|
||||||
|
with open(topology_file_path, "w+", encoding="utf-8") as f:
|
||||||
|
json.dump(topology, f, indent=4, sort_keys=True)
|
||||||
|
except OSError as e:
|
||||||
|
raise aiohttp.web.HTTPConflict(text="Cannot update snapshot '{}': the project.gns3 file cannot be modified: {}".format(os.path.basename(snapshot), e))
|
||||||
|
except (ValueError, KeyError):
|
||||||
|
raise aiohttp.web.HTTPConflict(text="Cannot update snapshot '{}': the project.gns3 file is corrupted".format(os.path.basename(snapshot)))
|
||||||
|
|
||||||
|
# write everything back to the original snapshot file
|
||||||
|
try:
|
||||||
|
with aiozipstream.ZipFile(compression=zipfile.ZIP_STORED) as zstream:
|
||||||
|
for root, dirs, files in os.walk(tmpdir, topdown=True, followlinks=False):
|
||||||
|
for file in files:
|
||||||
|
path = os.path.join(root, file)
|
||||||
|
zstream.write(path, os.path.relpath(path, tmpdir))
|
||||||
|
async with aiofiles.open(snapshot_path, 'wb+') as f:
|
||||||
|
async for chunk in zstream:
|
||||||
|
await f.write(chunk)
|
||||||
|
except OSError as e:
|
||||||
|
raise aiohttp.web.HTTPConflict(text="Cannot update snapshot '{}': the snapshot cannot be recreated: {}".format(os.path.basename(snapshot), e))
|
||||||
|
Loading…
Reference in New Issue
Block a user