diff --git a/gns3server/controller/__init__.py b/gns3server/controller/__init__.py index 3afec2c0..4d1beea8 100644 --- a/gns3server/controller/__init__.py +++ b/gns3server/controller/__init__.py @@ -240,6 +240,8 @@ class Controller: server_config = Config.instance().get_section_config("Server") if compute_id == "local" and server_config.getboolean("local", False) is False: raise aiohttp.web.HTTPNotFound(text="You try to use a node on the local server but the controller is not started with --local") + elif compute_id == "vm": + raise aiohttp.web.HTTPNotFound(text="You try to use a node on the GNS3 VM server but the GNS3 is not configured") raise aiohttp.web.HTTPNotFound(text="Compute ID {} doesn't exist".format(compute_id)) @asyncio.coroutine diff --git a/gns3server/controller/compute.py b/gns3server/controller/compute.py index 14be2908..ac98331b 100644 --- a/gns3server/controller/compute.py +++ b/gns3server/controller/compute.py @@ -472,3 +472,4 @@ 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/import_project.py b/gns3server/controller/import_project.py index 354c857f..b9ccf167 100644 --- a/gns3server/controller/import_project.py +++ b/gns3server/controller/import_project.py @@ -66,10 +66,24 @@ def import_project(controller, project_id, stream): # For some VM type we move them to the GNS3 VM if it's not a Linux host if not sys.platform.startswith("linux"): + vm_created = False + for node in topology["topology"]["nodes"]: if node["node_type"] in ("docker", "qemu", "iou"): node["compute_id"] = "vm" + # Project created on the remote GNS3 VM? + if not vm_created: + compute = controller.get_compute("vm") + yield from compute.post("/projects", data={ + "name": project_name, + "project_id": project_id, + }) + vm_created = True + + yield from _move_files_to_compute(compute, project_id, path, os.path.join("project-files", node["node_type"], node["node_id"])) + + # And we dump the updated.gns3 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 @@ -84,6 +98,31 @@ def import_project(controller, project_id, stream): return project +@asyncio.coroutine +def _move_files_to_compute(compute, project_id, directory, files_path): + """ + Move the files to a remote compute + """ + for (dirpath, dirnames, filenames) in os.walk(os.path.join(directory, files_path)): + for filename in filenames: + path = os.path.join(dirpath, filename) + dst = os.path.relpath(path, directory) + yield from _upload_file(compute, project_id, path, dst) + shutil.rmtree(directory) + +@asyncio.coroutine +def _upload_file(compute, project_id, file_path, path): + """ + Upload a file to a remote project + + :param file_path: File path on the controller file system + :param path: File path on the remote system relative to project directory + """ + path = "/projects/{}/files/path".format(project_id, path.replace("\\", "/")) + with open(file_path, "rb") as f: + yield from compute.http_query("POST", path, f, timeout=None) + + def _import_images(controller, path): """ Copy images to the images directory or delete them if they diff --git a/tests/controller/test_import_project.py b/tests/controller/test_import_project.py index 3732d967..08e82006 100644 --- a/tests/controller/test_import_project.py +++ b/tests/controller/test_import_project.py @@ -21,8 +21,11 @@ import json import zipfile +from tests.utils import asyncio_patch, AsyncioMagicMock + from gns3server.controller.project import Project -from gns3server.controller.import_project import import_project +from gns3server.controller.import_project import import_project, _move_files_to_compute + from gns3server.version import __version__ @@ -174,6 +177,7 @@ def test_import_iou_non_linux(windows_platform, async_run, tmpdir, controller): On non linux host IOU should be moved to the GNS3 VM """ project_id = str(uuid.uuid4()) + controller._computes["vm"] = AsyncioMagicMock() topology = { "project_id": str(uuid.uuid4()), @@ -183,6 +187,7 @@ def test_import_iou_non_linux(windows_platform, async_run, tmpdir, controller): "nodes": [ { "compute_id": "local", + "node_id": "0fd3dd4d-dc93-4a04-a9b9-7396a9e22e8b", "node_type": "iou", "properties": {} }, @@ -208,9 +213,27 @@ def test_import_iou_non_linux(windows_platform, async_run, tmpdir, controller): 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 asyncio_patch("gns3server.controller.import_project._move_files_to_compute") as mock: + project = async_run(import_project(controller, project_id, f)) + mock.assert_called_with(controller._computes["vm"], project_id, project.path, 'project-files/iou/0fd3dd4d-dc93-4a04-a9b9-7396a9e22e8b') + controller._computes["vm"].post.assert_called_with('/projects', data={'name': 'test', 'project_id': project_id}) with open(os.path.join(project.path, "test.gns3")) as f: topo = json.load(f) assert topo["topology"]["nodes"][0]["compute_id"] == "vm" assert topo["topology"]["nodes"][1]["compute_id"] == "local" + + +def test_move_files_to_compute(tmpdir, async_run): + project_id = str(uuid.uuid4()) + + os.makedirs(str(tmpdir / "project-files" / "docker")) + (tmpdir / "project-files" / "docker" / "test").open("w").close() + (tmpdir / "project-files" / "docker" / "test2").open("w").close() + + with asyncio_patch("gns3server.controller.import_project._upload_file") as mock: + async_run(_move_files_to_compute(None, project_id, str(tmpdir), "project-files/docker")) + + mock.assert_any_call(None, project_id, str(tmpdir / "project-files" / "docker" / "test"), "project-files/docker/test") + mock.assert_any_call(None, project_id, str(tmpdir / "project-files" / "docker" / "test2"), "project-files/docker/test2") + assert not os.path.exists(str(tmpdir / "project-files" / "docker"))