From e564c84529dc0ae9d8291ee21a82ca16fbc08f87 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Tue, 5 Apr 2016 23:53:18 +0200 Subject: [PATCH] Flatten the project at export and support import for Linux The client will send a flag to say if the import should be done on the VM or not Ref https://github.com/GNS3/gns3-gui/issues/1151 --- gns3server/handlers/api/project_handler.py | 2 +- gns3server/modules/project.py | 63 +++++++++++++++-- tests/modules/test_project.py | 80 +++++++++++++++++++++- 3 files changed, 137 insertions(+), 8 deletions(-) diff --git a/gns3server/handlers/api/project_handler.py b/gns3server/handlers/api/project_handler.py index 24dcc7da..f142f419 100644 --- a/gns3server/handlers/api/project_handler.py +++ b/gns3server/handlers/api/project_handler.py @@ -405,7 +405,7 @@ class ProjectHandler: if not packet: break temp.write(packet) - project.import_zip(temp) + project.import_zip(temp, gns3vm=bool(request.GET.get("gns3vm", "1"))) except OSError as e: raise aiohttp.web.HTTPInternalServerError(text="Could not import the project: {}".format(e)) diff --git a/gns3server/modules/project.py b/gns3server/modules/project.py index ecdc0a43..e774666f 100644 --- a/gns3server/modules/project.py +++ b/gns3server/modules/project.py @@ -529,7 +529,7 @@ class Project: # directory for root, dirs, files in os.walk(self._path, topdown=True): # Remove snapshots - if "project-files" in root: + if os.path.split(root)[-1:][0] == "project-files": dirs[:] = [d for d in dirs if d != "snapshots"] # Ignore log files and OS noise @@ -541,26 +541,79 @@ class Project: if file.endswith(".gns3"): z.write(path, "project.gns3") else: - z.write(path, os.path.relpath(path, self._path)) + # We merge the data from all server in the same project-files directory + vm_directory = os.path.join(self._path, "servers", "vm") + if os.path.commonprefix([root, vm_directory]) == vm_directory: + z.write(path, os.path.relpath(path, vm_directory)) + else: + z.write(path, os.path.relpath(path, self._path)) return z - def import_zip(self, stream): + def import_zip(self, stream, gns3vm=True): """ Import a project contain in a zip file - :params: A io.BytesIO of the zifile + :param stream: A io.BytesIO of the zipfile + :param gns3vm: True move docker, iou and qemu to the GNS3 VM """ with zipfile.ZipFile(stream) as myzip: myzip.extractall(self.path) + project_file = os.path.join(self.path, "project.gns3") if os.path.exists(project_file): with open(project_file) as f: topology = json.load(f) topology["project_id"] = self.id topology["name"] = self.name - + topology.setdefault("topology", {}) + topology["topology"].setdefault("nodes", []) + topology["topology"]["servers"] = [ + { + "id": 1, + "local": True, + "vm": False + } + ] + + # By default all node run on local server + for node in topology["topology"]["nodes"]: + node["server_id"] = 1 + + if gns3vm: + # Move to servers/vm directory the data that should be import on remote server + modules_to_vm = { + "qemu": "QemuVM", + "iou": "IOUDevice", + "docker": "DockerVM" + } + + vm_directory = os.path.join(self.path, "servers", "vm", "project-files") + vm_server_use = False + + for module, device_type in modules_to_vm.items(): + module_directory = os.path.join(self.path, "project-files", module) + if os.path.exists(module_directory): + os.makedirs(vm_directory, exist_ok=True) + shutil.move(module_directory, os.path.join(vm_directory, module)) + + # Patch node to use the GNS3 VM + for node in topology["topology"]["nodes"]: + if node["type"] == device_type: + node["server_id"] = 2 + vm_server_use = True + + # We use the GNS3 VM. We need to add the server to the list + if vm_server_use: + topology["topology"]["servers"].append({ + "id": 2, + "vm": True, + "local": False + }) + + # Write the modified topology with open(project_file, "w") as f: json.dump(topology, f, indent=4) + # Rename to a human distinctive name shutil.move(project_file, os.path.join(self.path, self.name + ".gns3")) diff --git a/tests/modules/test_project.py b/tests/modules/test_project.py index c61a12ae..c94ee850 100644 --- a/tests/modules/test_project.py +++ b/tests/modules/test_project.py @@ -297,13 +297,70 @@ def test_export(tmpdir): assert 'vm-1/dynamips/test_log.txt' not in myzip.namelist() +def test_export(tmpdir): + project = Project() + 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("{}") + + with open(os.path.join(path, "vm-1", "dynamips", "test"), 'w+') as f: + f.write("HELLO") + with open(os.path.join(path, "vm-1", "dynamips", "test_log.txt"), 'w+') as f: + f.write("LOG") + os.makedirs(os.path.join(path, "project-files", "snapshots")) + with open(os.path.join(path, "project-files", "snapshots", "test"), 'w+') as f: + f.write("WORLD") + + os.makedirs(os.path.join(path, "servers", "vm", "project-files", "docker")) + with open(os.path.join(path, "servers", "vm", "project-files", "docker", "busybox"), 'w+') as f: + f.write("DOCKER") + + z = project.export() + + 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" + + assert 'test.gns3' not in myzip.namelist() + assert 'project.gns3' in myzip.namelist() + assert 'project-files/snapshots/test' not in myzip.namelist() + assert 'vm-1/dynamips/test_log.txt' not in myzip.namelist() + assert 'servers/vm/project-files/docker/busybox' not in myzip.namelist() + assert 'project-files/docker/busybox' in myzip.namelist() + + def test_import(tmpdir): project_id = str(uuid.uuid4()) project = Project(name="test", project_id=project_id) + topology = { + "project_id": str(uuid.uuid4()), + "name": "testtest", + "topology": { + "nodes": [ + { + "server_id": 3, + "type": "VPCSDevice" + }, + { + "server_id": 3, + "type": "QemuVM" + } + ] + } + } + with open(str(tmpdir / "project.gns3"), 'w+') as f: - f.write('{"project_id": "ddd", "name": "test"}') + json.dump(topology, f) with open(str(tmpdir / "b.png"), 'w+') as f: f.write("B") @@ -311,14 +368,33 @@ def test_import(tmpdir): with zipfile.ZipFile(zip_path, 'w') as myzip: myzip.write(str(tmpdir / "project.gns3"), "project.gns3") myzip.write(str(tmpdir / "b.png"), "b.png") + myzip.write(str(tmpdir / "b.png"), "project-files/dynamips/test") + myzip.write(str(tmpdir / "b.png"), "project-files/qemu/test") with open(zip_path, "rb") as f: project.import_zip(f) assert os.path.exists(os.path.join(project.path, "b.png")) assert os.path.exists(os.path.join(project.path, "test.gns3")) + assert os.path.exists(os.path.join(project.path, "project-files/dynamips/test")) + assert os.path.exists(os.path.join(project.path, "servers/vm/project-files/qemu/test")) with open(os.path.join(project.path, "test.gns3")) as f: content = json.load(f) + + assert content["name"] == "test" assert content["project_id"] == project_id - assert content["name"] == project.name + assert content["topology"]["servers"] == [ + { + "id": 1, + "local": True, + "vm": False + }, + { + "id": 2, + "local": False, + "vm": True + }, + ] + assert content["topology"]["nodes"][0]["server_id"] == 1 + assert content["topology"]["nodes"][1]["server_id"] == 2