diff --git a/gns3server/compute/project.py b/gns3server/compute/project.py index 20cf8dd8..8bfa844f 100644 --- a/gns3server/compute/project.py +++ b/gns3server/compute/project.py @@ -399,181 +399,3 @@ class Project: break m.update(buf) return m.hexdigest() - - def export(self, include_images=False): - """ - Export the project as zip. It's a ZipStream object. - The file will be read chunk by chunk when you iterate on - the zip. - - It will ignore some files like snapshots and tmp - - :returns: ZipStream object - """ - - z = zipstream.ZipFile() - # topdown allows to modify the list of directory in order to ignore the directory - for root, dirs, files in os.walk(self._path, topdown=True): - # Remove snapshots and capture - if os.path.split(root)[-1:][0] == "project-files": - dirs[:] = [d for d in dirs if d not in ("snapshots", "tmp")] - - # Ignore log files and OS noise - files = [f for f in files if not f.endswith('_log.txt') and not f.endswith('.log') and f != '.DS_Store'] - - for file in files: - path = os.path.join(root, file) - # Try open the file - try: - open(path).close() - except OSError as e: - msg = "Could not export file {}: {}".format(path, e) - log.warn(msg) - self.emit("log.warning", {"message": msg}) - continue - # We rename the .gns3 project.gns3 to avoid the task to the client to guess the file name - if file.endswith(".gns3"): - self._export_project_file(path, z, include_images) - else: - # We merge the data from all server in the same project-files directory - node_directory = os.path.join(self._path, "servers", "vm") - if os.path.commonprefix([root, node_directory]) == node_directory: - z.write(path, os.path.relpath(path, node_directory), compress_type=zipfile.ZIP_DEFLATED) - else: - z.write(path, os.path.relpath(path, self._path), compress_type=zipfile.ZIP_DEFLATED) - return z - - def _export_images(self, image, z): - """ - Take a project file (.gns3) and export images to the zip - - :param image: Image path - :param z: Zipfile instance for the export - """ - from . import MODULES - - for module in MODULES: - try: - img_directory = module.instance().get_images_directory() - except NotImplementedError: - # Some modules don't have images - continue - - directory = os.path.split(img_directory)[-1:][0] - - if os.path.exists(image): - path = image - else: - path = os.path.join(img_directory, image) - - if os.path.exists(path): - arcname = os.path.join("images", directory, os.path.basename(image)) - z.write(path, arcname) - break - - def _export_project_file(self, path, z, include_images): - """ - Take a project file (.gns3) and patch it for the export - - :param path: Path of the .gns3 - """ - - with open(path) as f: - topology = json.load(f) - if "topology" in topology and "nodes" in topology["topology"]: - for node in topology["topology"]["nodes"]: - if "properties" in node and node["type"] != "DockerVM": - for prop, value in node["properties"].items(): - if prop.endswith("image"): - node["properties"][prop] = os.path.basename(value) - if include_images is True: - self._export_images(value, z) - z.writestr("project.gns3", json.dumps(topology).encode()) - - def import_zip(self, stream, gns3vm=True): - """ - Import a project contain in a zip file - - :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" - } - node_directory = os.path.join(self.path, "servers", "vm", "project-files") - vm_server_use = False - - for module, vm_type in modules_to_vm.items(): - module_directory = os.path.join(self.path, "project-files", module) - if os.path.exists(module_directory): - os.makedirs(node_directory, exist_ok=True) - shutil.move(module_directory, os.path.join(node_directory, module)) - - # Patch node to use the GNS3 VM - for node in topology["topology"]["nodes"]: - if node["type"] == vm_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")) - if os.path.exists(os.path.join(self.path, "images")): - self._import_images() - - def _import_images(self): - """ - Copy images to the images directory or delete them if they - already exists. - """ - image_dir = self._config().get("images_path") - - root = os.path.join(self.path, "images") - for (dirpath, dirnames, filenames) in os.walk(root): - for filename in filenames: - path = os.path.join(dirpath, filename) - dst = os.path.join(image_dir, os.path.relpath(path, root)) - os.makedirs(os.path.dirname(dst), exist_ok=True) - shutil.move(path, dst) - - # Cleanup the project - shutil.rmtree(root, ignore_errors=True) diff --git a/gns3server/controller/export_project.py b/gns3server/controller/export_project.py index 17d7f209..676251f9 100644 --- a/gns3server/controller/export_project.py +++ b/gns3server/controller/export_project.py @@ -22,7 +22,6 @@ import zipfile import zipstream - def export_project(project, include_images=False): """ Export the project as zip. It's a ZipStream object. @@ -128,5 +127,3 @@ def _export_images(project, image, z): arcname = os.path.join("images", directory, os.path.basename(image)) z.write(path, arcname) break - - diff --git a/tests/compute/test_project.py b/tests/compute/test_project.py index 7dcdcfe9..5d21ca85 100644 --- a/tests/compute/test_project.py +++ b/tests/compute/test_project.py @@ -175,155 +175,3 @@ def test_emit(async_run): (action, event, context) = async_run(queue.get(0.5)) assert action == "test" assert context["project_id"] == project.id - - -def test_export(tmpdir): - project = Project(project_id=str(uuid.uuid4())) - path = project.path - os.makedirs(os.path.join(path, "vm-1", "dynamips")) - - 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") - - 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 'project-files/snapshots/test' not in myzip.namelist() - assert 'vm-1/dynamips/test_log.txt' not in myzip.namelist() - - -def test_export_with_vm(tmpdir): - project_id = str(uuid.uuid4()) - project = Project(project_id=project_id) - 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: - json.dump(topology, f) - with open(str(tmpdir / "b.png"), 'w+') as f: - f.write("B") - - zip_path = str(tmpdir / "project.zip") - 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["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 - - -def test_import_with_images(tmpdir): - - project_id = str(uuid.uuid4()) - project = Project(name="test", project_id=project_id) - - with open(str(tmpdir / "test.image"), 'w+') as f: - f.write("B") - - zip_path = str(tmpdir / "project.zip") - with zipfile.ZipFile(zip_path, 'w') as myzip: - myzip.write(str(tmpdir / "test.image"), "images/IOS/test.image") - - with open(zip_path, "rb") as f: - project.import_zip(f) - - # TEST import images - path = os.path.join(project._config().get("images_path"), "IOS", "test.image") - assert os.path.exists(path), path diff --git a/tests/controller/test_export_project.py b/tests/controller/test_export_project.py index 8b67c25c..2580bd04 100644 --- a/tests/controller/test_export_project.py +++ b/tests/controller/test_export_project.py @@ -199,5 +199,3 @@ def test_export_with_images(tmpdir, project): with zipfile.ZipFile(str(tmpdir / 'zipfile.zip')) as myzip: myzip.getinfo("images/IOS/test.image") - - diff --git a/tests/handlers/api/compute/test_project.py b/tests/handlers/api/compute/test_project.py index 4eeec3cd..0368938f 100644 --- a/tests/handlers/api/compute/test_project.py +++ b/tests/handlers/api/compute/test_project.py @@ -199,40 +199,3 @@ def test_stream_file(http_compute, tmpdir): response = http_compute.get("/projects/{project_id}/files/../hello".format(project_id=project.id), raw=True) assert response.status == 403 - - -def test_export(http_compute, tmpdir, loop, project): - - os.makedirs(project.path, exist_ok=True) - with open(os.path.join(project.path, 'a'), 'w+') as f: - f.write('hello') - - response = http_compute.get("/projects/{project_id}/export".format(project_id=project.id), raw=True) - assert response.status == 200 - assert response.headers['CONTENT-TYPE'] == 'application/gns3project' - assert response.headers['CONTENT-DISPOSITION'] == 'attachment; filename="{}.gns3project"'.format(project.name) - - with open(str(tmpdir / 'project.zip'), 'wb+') as f: - f.write(response.body) - - with zipfile.ZipFile(str(tmpdir / 'project.zip')) as myzip: - with myzip.open("a") as myfile: - content = myfile.read() - assert content == b"hello" - - -def test_import(http_compute, tmpdir, loop): - - with zipfile.ZipFile(str(tmpdir / "test.zip"), 'w') as myzip: - myzip.writestr("demo", b"hello") - - project_id = str(uuid.uuid4()) - - with open(str(tmpdir / "test.zip"), "rb") as f: - response = http_compute.post("/projects/{project_id}/import".format(project_id=project_id), body=f.read(), raw=True) - assert response.status == 201 - - project = ProjectManager.instance().get_project(project_id=project_id) - with open(os.path.join(project.path, "demo")) as f: - content = f.read() - assert content == "hello"