diff --git a/gns3server/compute/project.py b/gns3server/compute/project.py
index 5140bbd2..dd771cf5 100644
--- a/gns3server/compute/project.py
+++ b/gns3server/compute/project.py
@@ -127,9 +127,6 @@ class Project:
if path != self._path and self.is_local() is False:
raise aiohttp.web.HTTPForbidden(text="You are not allowed to modify the project directory path")
- if '"' in path:
- raise aiohttp.web.HTTPForbidden(text="You are not allowed to use \" in the project directory path. It's not supported by Dynamips.")
-
self._path = path
self._update_temporary_file()
diff --git a/gns3server/controller/project.py b/gns3server/controller/project.py
index 51ecc5e7..f273b8e1 100644
--- a/gns3server/controller/project.py
+++ b/gns3server/controller/project.py
@@ -15,6 +15,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
+import os
import asyncio
import aiohttp
from uuid import UUID, uuid4
@@ -23,6 +24,7 @@ from contextlib import contextmanager
from .vm import VM
from .udp_link import UDPLink
from ..notification_queue import NotificationQueue
+from ..config import Config
class Project:
@@ -45,7 +47,13 @@ class Project:
except ValueError:
raise aiohttp.web.HTTPBadRequest(text="{} is not a valid UUID".format(project_id))
self._id = project_id
- self._path = path
+
+ #TODO: Security check if not locale
+ if path is None:
+ location = self._config().get("project_directory", self._get_default_project_directory())
+ path = os.path.join(location, self._id)
+ self.path = path
+
self._temporary = temporary
self._computes = set()
self._vms = {}
@@ -68,6 +76,30 @@ class Project:
def path(self):
return self._path
+ @path.setter
+ def path(self, path):
+ try:
+ os.makedirs(path, exist_ok=True)
+ except OSError as e:
+ raise aiohttp.web.HTTPInternalServerError(text="Could not create project directory: {}".format(e))
+
+ if '"' in path:
+ raise aiohttp.web.HTTPForbidden(text="You are not allowed to use \" in the project directory path. It's not supported by Dynamips.")
+
+ self._path = path
+
+ def _config(self):
+ return Config.instance().get_section_config("Server")
+
+ @property
+ def captures_directory(self):
+ """
+ Location of the captures file
+ """
+ path = os.path.join(self._path, "project-files", "captures")
+ os.makedirs(path, exist_ok=True)
+ return path
+
@asyncio.coroutine
def addCompute(self, compute):
self._computes.add(compute)
@@ -142,6 +174,7 @@ class Project:
def delete(self):
for compute in self._computes:
yield from compute.delete("/projects/{}".format(self._id))
+ shutil.rmtree(self.path)
@contextmanager
def queue(self):
@@ -166,6 +199,22 @@ class Project:
for listener in self._listeners:
listener.put_nowait((action, event, kwargs))
+ @classmethod
+ def _get_default_project_directory(cls):
+ """
+ Return the default location for the project directory
+ depending of the operating system
+ """
+
+ server_config = Config.instance().get_section_config("Server")
+ path = os.path.expanduser(server_config.get("projects_path", "~/GNS3/projects"))
+ path = os.path.normpath(path)
+ try:
+ os.makedirs(path, exist_ok=True)
+ except OSError as e:
+ raise aiohttp.web.HTTPInternalServerError(text="Could not create project directory: {}".format(e))
+ return path
+
def __json__(self):
return {
diff --git a/tests/compute/test_project.py b/tests/compute/test_project.py
index d7254fef..8f171308 100644
--- a/tests/compute/test_project.py
+++ b/tests/compute/test_project.py
@@ -118,13 +118,6 @@ def test_changing_path_not_allowed(tmpdir):
p.path = str(tmpdir)
-def test_changing_path_with_quote_not_allowed(tmpdir):
- with patch("gns3server.compute.project.Project.is_local", return_value=True):
- with pytest.raises(aiohttp.web.HTTPForbidden):
- p = Project(project_id=str(uuid4()))
- p.path = str(tmpdir / "project\"53")
-
-
def test_json(tmpdir):
p = Project(project_id=str(uuid4()))
assert p.__json__() == {"name": p.name, "project_id": p.id, "temporary": False}
diff --git a/tests/controller/test_project.py b/tests/controller/test_project.py
index 27dac1e6..94119754 100644
--- a/tests/controller/test_project.py
+++ b/tests/controller/test_project.py
@@ -16,13 +16,16 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
+import os
import pytest
import aiohttp
from unittest.mock import MagicMock
from tests.utils import AsyncioMagicMock
-
+from unittest.mock import patch
+from uuid import uuid4
from gns3server.controller.project import Project
+from gns3server.config import Config
def test_affect_uuid():
@@ -35,7 +38,29 @@ def test_affect_uuid():
def test_json(tmpdir):
p = Project()
- assert p.__json__() == {"name": p.name, "project_id": p.id, "temporary": False, "path": None}
+ assert p.__json__() == {"name": p.name, "project_id": p.id, "temporary": False, "path": p.path}
+
+
+def test_path(tmpdir):
+
+ directory = Config.instance().get_section_config("Server").get("project_directory")
+
+ with patch("gns3server.compute.project.Project._get_default_project_directory", return_value=directory):
+ p = Project(project_id=str(uuid4()))
+ assert p.path == os.path.join(directory, p.id)
+ assert os.path.exists(os.path.join(directory, p.id))
+
+
+def test_init_path(tmpdir):
+
+ p = Project(path=str(tmpdir), project_id=str(uuid4()))
+ assert p.path == str(tmpdir)
+
+
+def test_changing_path_with_quote_not_allowed(tmpdir):
+ with pytest.raises(aiohttp.web.HTTPForbidden):
+ p = Project(project_id=str(uuid4()))
+ p.path = str(tmpdir / "project\"53")
def test_addVM(async_run):