From 94a262cd463fca892ed18b33b1440bdc2ff44b15 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Fri, 22 Jul 2016 11:43:14 +0200 Subject: [PATCH] When importing a project fix the GNS3 version --- gns3server/controller/compute.py | 3 +- gns3server/controller/export_project.py | 9 ++-- gns3server/controller/import_project.py | 10 +++-- .../api/controller/project_handler.py | 1 - tests/controller/test_compute.py | 18 ++++++++ tests/controller/test_export_project.py | 41 ++++++++++++++++++- tests/controller/test_import_project.py | 29 ++++++++++++- 7 files changed, 98 insertions(+), 13 deletions(-) diff --git a/gns3server/controller/compute.py b/gns3server/controller/compute.py index 01181159..14be2908 100644 --- a/gns3server/controller/compute.py +++ b/gns3server/controller/compute.py @@ -71,6 +71,7 @@ class Compute: """ def __init__(self, compute_id, controller=None, protocol="http", host="localhost", port=3080, user=None, password=None, name=None): + self._http_session = None assert controller is not None log.info("Create compute %s", compute_id) @@ -87,7 +88,6 @@ class Compute: self._connected = False self._controller = controller self._set_auth(user, password) - self._http_session = None self._version = None self._cpu_usage_percent = None self._memory_usage_percent = None @@ -472,4 +472,3 @@ class Compute: path = "/projects/{}/files".format(project.id) res = yield from self.http_query("GET", path, timeout=120) return res.json - diff --git a/gns3server/controller/export_project.py b/gns3server/controller/export_project.py index 4648b3ec..cc06072b 100644 --- a/gns3server/controller/export_project.py +++ b/gns3server/controller/export_project.py @@ -67,17 +67,19 @@ def export_project(project, temporary_dir, include_images=False): z.write(path, os.path.relpath(path, project._path), compress_type=zipfile.ZIP_DEFLATED) for compute in project.computes: - if compute.id == "vm": + if compute.id != "local": compute_files = yield from compute.list_files(project) for compute_file in compute_files: if not _filter_files(compute_file["path"]): - (fp, temp_path) = tempfile.mkstemp(dir=temporary_dir) + (fd, temp_path) = tempfile.mkstemp(dir=temporary_dir) + f = open(fd, "wb", closefd=True) stream = yield from compute.download_file(project, compute_file["path"]) while True: data = yield from stream.read(512) if not data: break - fp.write(data) + f.write(data) + f.close() z.write(temp_path, arcname=compute_file["path"], compress_type=zipfile.ZIP_DEFLATED) return z @@ -102,7 +104,6 @@ def _filter_files(path): return False - def _export_project_file(project, path, z, include_images): """ Take a project file (.gns3) and patch it for the export diff --git a/gns3server/controller/import_project.py b/gns3server/controller/import_project.py index 5af68c27..097b801a 100644 --- a/gns3server/controller/import_project.py +++ b/gns3server/controller/import_project.py @@ -24,6 +24,7 @@ import zipfile import aiohttp from ..config import Config +from .topology import load_topology """ @@ -51,15 +52,18 @@ def import_project(controller, project_id, stream): try: topology = json.loads(myzip.read("project.gns3").decode()) # If the project name is already used we generate a new one - topology["name"] = controller.get_free_project_name(topology["name"]) + project_name = controller.get_free_project_name(topology["name"]) except KeyError: raise aiohttp.web.HTTPConflict(text="Can't import topology the .gns3 is corrupted or missing") - path = os.path.join(projects_path, topology["name"]) + path = os.path.join(projects_path, project_name) os.makedirs(path) myzip.extractall(path) - dot_gns3_path = os.path.join(path, topology["name"] + ".gns3") + topology = load_topology(os.path.join(path, "project.gns3")) + topology["name"] = project_name + + dot_gns3_path = os.path.join(path, project_name + ".gns3") # We change the project_id to avoid erasing the project topology["project_id"] = project_id with open(dot_gns3_path, "w+") as f: diff --git a/gns3server/handlers/api/controller/project_handler.py b/gns3server/handlers/api/controller/project_handler.py index b7b159bb..0e1fe238 100644 --- a/gns3server/handlers/api/controller/project_handler.py +++ b/gns3server/handlers/api/controller/project_handler.py @@ -235,7 +235,6 @@ class ProjectHandler: controller = Controller.instance() project = controller.get_project(request.match_info["project_id"]) - with tempfile.TemporaryDirectory() as tmp_dir: datas = yield from export_project(project, tmp_dir, include_images=bool(request.GET.get("include_images", "0"))) # We need to do that now because export could failed and raise an HTTP error diff --git a/tests/controller/test_compute.py b/tests/controller/test_compute.py index 0749dc3b..5687bd51 100644 --- a/tests/controller/test_compute.py +++ b/tests/controller/test_compute.py @@ -261,6 +261,14 @@ def test_streamFile(project, async_run, compute): mock.assert_called_with("GET", "https://example.com:84/v2/compute/projects/{}/stream/test/titi".format(project.id), auth=None) +def test_downloadFile(project, async_run, compute): + response = MagicMock() + response.status = 200 + with asyncio_patch("aiohttp.ClientSession.request", return_value=response) as mock: + async_run(compute.download_file(project, "test/titi")) + mock.assert_called_with("GET", "https://example.com:84/v2/compute/projects/{}/files/test/titi".format(project.id), auth=None) + + def test_close(compute, async_run): assert compute.connected is True async_run(compute.close()) @@ -318,3 +326,13 @@ def test_images(compute, async_run, images_dir): mock.assert_called_with("GET", "https://example.com:84/v2/compute/qemu/images", auth=None, data=None, headers={'content-type': 'application/json'}, chunked=False) assert images == [{"filename": "linux.qcow2", "path": "linux.qcow2"}, {"filename": "asa.qcow2", "path": "asa.qcow2"}] + + +def test_list_files(project, async_run, compute): + res = [{"path": "test"}] + response = AsyncioMagicMock() + response.read = AsyncioMagicMock(return_value=json.dumps(res).encode()) + response.status = 200 + with asyncio_patch("aiohttp.ClientSession.request", return_value=response) as mock: + assert async_run(compute.list_files(project)) == res + mock.assert_any_call("GET", "https://example.com:84/v2/compute/projects/{}/files".format(project.id), auth=None, chunked=False, data=None, headers={'content-type': 'application/json'}) diff --git a/tests/controller/test_export_project.py b/tests/controller/test_export_project.py index 680f34a5..3f2eaef7 100644 --- a/tests/controller/test_export_project.py +++ b/tests/controller/test_export_project.py @@ -24,9 +24,10 @@ import zipfile from unittest.mock import patch from unittest.mock import MagicMock -from tests.utils import AsyncioMagicMock +from tests.utils import AsyncioMagicMock, AsyncioBytesIO from gns3server.controller.project import Project +from gns3server.controller.compute import Compute from gns3server.controller.export_project import export_project, _filter_files @@ -90,6 +91,44 @@ def test_export(tmpdir, project, async_run): assert 'vm-1/dynamips/test_log.txt' not in myzip.namelist() +def test_export_vm(tmpdir, project, async_run, controller): + """ + If data is on a remote server export it locally before + sending it in the archive. + """ + + compute = MagicMock() + compute.id = "vm" + compute.list_files = AsyncioMagicMock(return_value=[{"path": "vm-1/dynamips/test"}]) + + # Fake file that will be download from the vm + file_content = AsyncioBytesIO() + async_run(file_content.write(b"HELLO")) + file_content.seek(0) + compute.download_file = AsyncioMagicMock(return_value=file_content) + + project._project_created_on_compute.add(compute) + + path = project.path + os.makedirs(os.path.join(path, "vm-1", "dynamips")) + + # The .gns3 should be renamed project.gns3 in order to simplify import + with open(os.path.join(path, "test.gns3"), 'w+') as f: + f.write("{}") + + z = async_run(export_project(project, str(tmpdir))) + assert compute.list_files.called + + with open(str(tmpdir / 'zipfile.zip'), 'wb') as f: + for data in z: + f.write(data) + + with zipfile.ZipFile(str(tmpdir / 'zipfile.zip')) as myzip: + with myzip.open("vm-1/dynamips/test") as myfile: + content = myfile.read() + assert content == b"HELLO" + + def test_export_disallow_running(tmpdir, project, node, async_run): """ Dissallow export when a node is running diff --git a/tests/controller/test_import_project.py b/tests/controller/test_import_project.py index 27e0834e..316d7990 100644 --- a/tests/controller/test_import_project.py +++ b/tests/controller/test_import_project.py @@ -23,6 +23,7 @@ import zipfile from gns3server.controller.project import Project from gns3server.controller.import_project import import_project +from gns3server.version import __version__ def test_import_project(async_run, tmpdir, controller): @@ -66,6 +67,32 @@ def test_import_project(async_run, tmpdir, controller): assert project.name != "test" +def test_import_upgrade(async_run, tmpdir, controller): + project_id = str(uuid.uuid4()) + + topology = { + "project_id": str(uuid.uuid4()), + "name": "test", + "topology": { + }, + "version": "1.4.2" + } + + with open(str(tmpdir / "project.gns3"), 'w+') as f: + json.dump(topology, f) + + zip_path = str(tmpdir / "project.zip") + with zipfile.ZipFile(zip_path, 'w') as myzip: + myzip.write(str(tmpdir / "project.gns3"), "project.gns3") + + with open(zip_path, "rb") as f: + project = async_run(import_project(controller, project_id, f)) + + with open(os.path.join(project.path, "test.gns3")) as f: + topo = json.load(f) + assert topo["version"] == __version__ + + def test_import_with_images(tmpdir, async_run, controller): project_id = str(uuid.uuid4()) @@ -92,8 +119,6 @@ def test_import_with_images(tmpdir, async_run, controller): with open(zip_path, "rb") as f: project = async_run(import_project(controller, project_id, f)) - print(project._config().get("images_path")) - # TEST import images assert not os.path.exists(os.path.join(project.path, "images/IOS/test.image")) path = os.path.join(project._config().get("images_path"), "IOS", "test.image")