diff --git a/gns3server/controller/__init__.py b/gns3server/controller/__init__.py
index 0fb36ca6..1518f597 100644
--- a/gns3server/controller/__init__.py
+++ b/gns3server/controller/__init__.py
@@ -26,6 +26,7 @@ from .project import Project
from .compute import Compute
from .notification import Notification
from ..version import __version__
+from .topology import load_topology
import logging
log = logging.getLogger(__name__)
@@ -185,6 +186,29 @@ class Controller:
def remove_project(self, project):
del self._projects[project.id]
+ @asyncio.coroutine
+ def load_project(self, path):
+ """
+ Load a project from a .gns3
+
+ :param path: Path of the .gns3
+ """
+ topo_data = load_topology(path)
+ topology = topo_data.pop("topology")
+ topo_data.pop("version")
+ topo_data.pop("revision")
+ topo_data.pop("type")
+
+ project = yield from self.add_project(path=os.path.dirname(path), **topo_data)
+
+ for compute in topology["computes"]:
+ yield from self.add_compute(**compute)
+ for node in topology["nodes"]:
+ compute = self.get_compute(node.pop("compute_id"))
+ name = node.pop("name")
+ node_id = node.pop("node_id")
+ yield from project.add_node(compute, name, node_id, **node)
+
@property
def projects(self):
"""
diff --git a/gns3server/controller/topology.py b/gns3server/controller/topology.py
index 7f8bad83..86cb0552 100644
--- a/gns3server/controller/topology.py
+++ b/gns3server/controller/topology.py
@@ -15,8 +15,12 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
+import json
+import aiohttp
+
from ..version import __version__
+GNS3_FILE_FORMAT_REVISION = 5
def project_to_topology(project):
"""
@@ -31,7 +35,7 @@ def project_to_topology(project):
"computes": []
},
"type": "topology",
- "revision": 5,
+ "revision": GNS3_FILE_FORMAT_REVISION,
"version": __version__
}
@@ -44,6 +48,20 @@ def project_to_topology(project):
for compute in computes:
if hasattr(compute, "__json__"):
data["topology"]["computes"].append(compute.__json__())
- print(data)
#TODO: check JSON schema
return data
+
+
+def load_topology(path):
+ """
+ Open a topology file, patch it for last GNS3 release and return it
+ """
+ try:
+ with open(path) as f:
+ topo = json.load(f)
+ except OSError as e:
+ raise aiohttp.web.HTTPConflict(text="Could not load topology {}: {}".format(path, str(e)))
+ #TODO: Check JSON schema
+ if topo["revision"] < GNS3_FILE_FORMAT_REVISION:
+ raise aiohttp.web.HTTPConflict(text="Old GNS3 project are not yet supported")
+ return topo
diff --git a/tests/controller/test_controller.py b/tests/controller/test_controller.py
index b775195e..af1c0926 100644
--- a/tests/controller/test_controller.py
+++ b/tests/controller/test_controller.py
@@ -21,7 +21,7 @@ import json
import pytest
import aiohttp
from unittest.mock import MagicMock
-
+from tests.utils import AsyncioMagicMock
from gns3server.controller import Controller
from gns3server.controller.compute import Compute
@@ -184,3 +184,71 @@ def test_close(controller, async_run):
c._connected = True
async_run(controller.close())
assert c.connected is False
+
+
+def test_load_project(controller, async_run, tmpdir):
+ data = {
+ "name": "Test",
+ "project_id": "c8d07a5a-134f-4c3f-8599-e35eac85eb17",
+ "revision": 5,
+ "type": "topology",
+ "version": "2.0.0dev1",
+ "topology": {
+ "computes": [
+ {
+ "compute_id": "my_remote",
+ "host": "127.0.0.1",
+ "name": "My remote",
+ "port": 3080,
+ "protocol": "http",
+ }
+ ],
+ "links": [
+ {
+ "capturing": True,
+ "link_id": "c44331d2-2da4-490d-9aad-7f5c126ae271",
+ "nodes": [
+ {"node_id": "c067b922-7f77-4680-ac00-0226c6583598", "adapter_number": 0, "port_number": 0},
+ {"node_id": "50d66d7b-0dd7-4e9f-b720-6eb621ae6543", "adapter_number": 0, "port_number": 0},
+ ],
+ }
+ ],
+ "nodes": [
+ {
+ "compute_id": "my_remote",
+ "name": "PC2",
+ "node_id": "c067b922-7f77-4680-ac00-0226c6583598",
+ "node_type": "vpcs",
+ "properties": {
+ "startup_script": "set pcname PC2\n",
+ "startup_script_path": "startup.vpc"
+ },
+ },
+ {
+ "compute_id": "my_remote",
+ "name": "PC1",
+ "node_id": "50d66d7b-0dd7-4e9f-b720-6eb621ae6543",
+ "node_type": "vpcs",
+ "properties": {
+ "startup_script": "set pcname PC1\n",
+ "startup_script_path": "startup.vpc"
+ },
+ }
+ ]
+ }
+ }
+ with open(str(tmpdir / "test.gns3"), "w+") as f:
+ json.dump(data, f)
+ controller.add_compute = AsyncioMagicMock()
+ mock_project = MagicMock()
+ controller.add_project = AsyncioMagicMock(return_value=mock_project)
+ controller._computes["my_remote"] = MagicMock()
+
+ async_run(controller.load_project(str(tmpdir / "test.gns3")))
+
+ controller.add_compute.assert_called_with(compute_id='my_remote', host='127.0.0.1', name='My remote', port=3080, protocol='http')
+ controller.add_project.assert_called_with(name='Test', project_id='c8d07a5a-134f-4c3f-8599-e35eac85eb17', path=str(tmpdir))
+
+ mock_project.add_node.assert_any_call(controller._computes["my_remote"], 'PC1', '50d66d7b-0dd7-4e9f-b720-6eb621ae6543', node_type='vpcs', properties={'startup_script': 'set pcname PC1\n', 'startup_script_path': 'startup.vpc'})
+ mock_project.add_node.assert_any_call(controller._computes["my_remote"], 'PC2', 'c067b922-7f77-4680-ac00-0226c6583598', node_type='vpcs', properties={'startup_script': 'set pcname PC2\n', 'startup_script_path': 'startup.vpc'})
+
diff --git a/tests/controller/test_topology.py b/tests/controller/test_topology.py
index 32db8448..5a6bc8ac 100644
--- a/tests/controller/test_topology.py
+++ b/tests/controller/test_topology.py
@@ -15,12 +15,15 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
+import json
+import pytest
+import aiohttp
from unittest.mock import MagicMock
from tests.utils import asyncio_patch
from gns3server.controller.project import Project
from gns3server.controller.compute import Compute
-from gns3server.controller.topology import project_to_topology
+from gns3server.controller.topology import project_to_topology, load_topology
from gns3server.version import __version__
@@ -60,3 +63,45 @@ def test_basic_topology(tmpdir, async_run, controller):
assert topo["topology"]["links"][0] == link.__json__()
assert topo["topology"]["computes"][0] == compute.__json__()
+
+
+def test_load_topology(tmpdir):
+ data = {
+ "project_id": "69f26504-7aa3-48aa-9f29-798d44841211",
+ "name": "Test",
+ "revision": 5,
+ "topology": {
+ "nodes": [],
+ "links": [],
+ "computes": []
+ },
+ "type": "topology",
+ "version": __version__}
+
+ path = str(tmpdir / "test.gns3")
+ with open(path, "w+") as f:
+ json.dump(data, f)
+ topo = load_topology(path)
+ assert topo == data
+
+def test_load_topology_file_error(tmpdir):
+ path = str(tmpdir / "test.gns3")
+ with pytest.raises(aiohttp.web.HTTPConflict):
+ topo = load_topology(path)
+
+
+def test_load_old_topology(tmpdir):
+ data = {
+ "project_id": "69f26504-7aa3-48aa-9f29-798d44841211",
+ "name": "Test",
+ "revision": 4,
+ "topology": {
+ },
+ "type": "topology",
+ "version": __version__}
+
+ path = str(tmpdir / "test.gns3")
+ with open(path, "w+") as f:
+ json.dump(data, f)
+ with pytest.raises(aiohttp.web.HTTPConflict):
+ topo = load_topology(path)