diff --git a/gns3server/handlers/api/controller/project_handler.py b/gns3server/handlers/api/controller/project_handler.py index df5fcaf6..5cebe9fc 100644 --- a/gns3server/handlers/api/controller/project_handler.py +++ b/gns3server/handlers/api/controller/project_handler.py @@ -284,7 +284,9 @@ class ProjectHandler: try: with tempfile.TemporaryDirectory() as tmp_dir: - datas = yield from export_project(project, tmp_dir, include_images=bool(request.get("include_images", "0"))) + datas = yield from export_project( + project, tmp_dir, + include_images=bool(int(request.query.get("include_images", "0")))) # We need to do that now because export could failed and raise an HTTP error # that why response start need to be the later possible response.content_type = 'application/gns3project' diff --git a/tests/controller/test_export_project.py b/tests/controller/test_export_project.py index 81586dc2..e6583ece 100644 --- a/tests/controller/test_export_project.py +++ b/tests/controller/test_export_project.py @@ -64,6 +64,10 @@ def test_export(tmpdir, project, async_run): path = project.path os.makedirs(os.path.join(path, "vm-1", "dynamips")) + os.makedirs(str(tmpdir / "IOS")) + with open(str(tmpdir / "IOS" / "test.image"), "w+") as f: + f.write("AAA") + # The .gns3 should be renamed project.gns3 in order to simplify import with open(os.path.join(path, "test.gns3"), 'w+') as f: data = { @@ -80,7 +84,10 @@ def test_export(tmpdir, project, async_run): "nodes": [ { "compute_id": "6b7149c8-7d6e-4ca0-ab6b-daa8ab567be0", - "node_type": "vpcs" + "node_type": "dynamips", + "properties": { + "image": "test.image" + } } ] } @@ -95,7 +102,8 @@ def test_export(tmpdir, project, async_run): with open(os.path.join(path, "project-files", "snapshots", "test"), 'w+') as f: f.write("WORLD") - z = async_run(export_project(project, str(tmpdir))) + with patch("gns3server.compute.Dynamips.get_images_directory", return_value=str(tmpdir / "IOS"),): + z = async_run(export_project(project, str(tmpdir), include_images=False)) with open(str(tmpdir / 'zipfile.zip'), 'wb') as f: for data in z: @@ -111,6 +119,8 @@ def test_export(tmpdir, project, async_run): assert 'project-files/snapshots/test' not in myzip.namelist() assert 'vm-1/dynamips/test_log.txt' not in myzip.namelist() + assert 'images/IOS/test.image' not in myzip.namelist() + with myzip.open("project.gns3") as myfile: topo = json.loads(myfile.read().decode())["topology"] assert topo["nodes"][0]["compute_id"] == "local" # All node should have compute_id local after export diff --git a/tests/handlers/api/controller/test_project.py b/tests/handlers/api/controller/test_project.py index 7381bd5d..e75511c5 100644 --- a/tests/handlers/api/controller/test_project.py +++ b/tests/handlers/api/controller/test_project.py @@ -28,7 +28,7 @@ import zipfile import json -from unittest.mock import patch +from unittest.mock import patch, MagicMock from tests.utils import asyncio_patch from gns3server.handlers.api.controller.project_handler import ProjectHandler @@ -170,13 +170,34 @@ def test_notification_ws(http_controller, controller, project, async_run): assert project.status == "opened" -def test_export(http_controller, tmpdir, loop, project): +def test_export_with_images(http_controller, tmpdir, loop, project): + project.dump = MagicMock() os.makedirs(project.path, exist_ok=True) with open(os.path.join(project.path, 'a'), 'w+') as f: f.write('hello') - response = http_controller.get("/projects/{project_id}/export".format(project_id=project.id), raw=True) + os.makedirs(str(tmpdir / "IOS")) + with open(str(tmpdir / "IOS" / "test.image"), "w+") as f: + f.write("AAA") + + topology = { + "topology": { + "nodes": [ + { + "properties": { + "image": "test.image" + }, + "node_type": "dynamips" + } + ] + } + } + with open(os.path.join(project.path, "test.gns3"), 'w+') as f: + json.dump(topology, f) + + with patch("gns3server.compute.Dynamips.get_images_directory", return_value=str(tmpdir / "IOS"),): + response = http_controller.get("/projects/{project_id}/export?include_images=1".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) @@ -188,6 +209,51 @@ def test_export(http_controller, tmpdir, loop, project): with myzip.open("a") as myfile: content = myfile.read() assert content == b"hello" + myzip.getinfo("images/IOS/test.image") + + +def test_export_without_images(http_controller, tmpdir, loop, project): + project.dump = MagicMock() + + os.makedirs(project.path, exist_ok=True) + with open(os.path.join(project.path, 'a'), 'w+') as f: + f.write('hello') + + os.makedirs(str(tmpdir / "IOS")) + with open(str(tmpdir / "IOS" / "test.image"), "w+") as f: + f.write("AAA") + + topology = { + "topology": { + "nodes": [ + { + "properties": { + "image": "test.image" + }, + "node_type": "dynamips" + } + ] + } + } + with open(os.path.join(project.path, "test.gns3"), 'w+') as f: + json.dump(topology, f) + + with patch("gns3server.compute.Dynamips.get_images_directory", return_value=str(tmpdir / "IOS"),): + response = http_controller.get("/projects/{project_id}/export?include_images=0".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" + # Image should not exported + with pytest.raises(KeyError): + myzip.getinfo("images/IOS/test.image") def test_get_file(http_controller, tmpdir, loop, project):