diff --git a/gns3server/handlers/project_handler.py b/gns3server/handlers/project_handler.py
index e6fdbc59..83bfa401 100644
--- a/gns3server/handlers/project_handler.py
+++ b/gns3server/handlers/project_handler.py
@@ -16,7 +16,7 @@
# along with this program. If not, see .
from ..web.route import Route
-from ..schemas.project import PROJECT_OBJECT_SCHEMA
+from ..schemas.project import PROJECT_OBJECT_SCHEMA, PROJECT_CREATE_SCHEMA
from ..modules.project_manager import ProjectManager
from aiohttp.web import HTTPConflict
@@ -28,13 +28,14 @@ class ProjectHandler:
r"/project",
description="Create a project on the server",
output=PROJECT_OBJECT_SCHEMA,
- input=PROJECT_OBJECT_SCHEMA)
+ input=PROJECT_CREATE_SCHEMA)
def create_project(request, response):
pm = ProjectManager.instance()
p = pm.create_project(
location=request.json.get("location"),
- uuid=request.json.get("uuid")
+ uuid=request.json.get("uuid"),
+ temporary=request.json.get("temporary", False)
)
response.json(p)
diff --git a/gns3server/modules/project.py b/gns3server/modules/project.py
index 4e8c61df..40ba0601 100644
--- a/gns3server/modules/project.py
+++ b/gns3server/modules/project.py
@@ -29,9 +29,10 @@ class Project:
:param uuid: Force project uuid (None by default auto generate an UUID)
:param location: Parent path of the project. (None should create a tmp directory)
+ :param temporary: Boolean the project is a temporary project (destroy when closed)
"""
- def __init__(self, uuid=None, location=None):
+ def __init__(self, uuid=None, location=None, temporary=False):
if uuid is None:
self._uuid = str(uuid4())
@@ -46,6 +47,7 @@ class Project:
if location is None:
self._location = tempfile.mkdtemp()
+ self._temporary = temporary
self._vms = set()
self._vms_to_destroy = set()
self._path = os.path.join(self._location, self._uuid)
@@ -102,7 +104,8 @@ class Project:
return {
"uuid": self._uuid,
- "location": self._location
+ "location": self._location,
+ "temporary": self._temporary
}
def add_vm(self, vm):
@@ -110,7 +113,7 @@ class Project:
Add a VM to the project.
In theory this should be called by the VM manager.
- :params vm: A VM instance
+ :param vm: A VM instance
"""
self._vms.add(vm)
@@ -120,7 +123,7 @@ class Project:
Remove a VM from the project.
In theory this should be called by the VM manager.
- :params vm: A VM instance
+ :param vm: A VM instance
"""
if vm in self._vms:
@@ -129,8 +132,19 @@ class Project:
def close(self):
"""Close the project, but keep informations on disk"""
+ self._close_and_clean(self._temporary)
+
+ def _close_and_clean(self, cleanup):
+ """
+ Close the project, and cleanup the disk if cleanup is True
+
+ :param cleanup: If True drop the project directory
+ """
+
for vm in self._vms:
vm.close()
+ if cleanup and os.path.exists(self.path):
+ shutil.rmtree(self.path)
def commit(self):
"""Write project changes on disk"""
@@ -145,6 +159,4 @@ class Project:
def delete(self):
"""Remove project from disk"""
- self.close()
- if os.path.exists(self.path):
- shutil.rmtree(self.path)
+ self._close_and_clean(True)
diff --git a/gns3server/schemas/project.py b/gns3server/schemas/project.py
index 1e363ec0..08ef042d 100644
--- a/gns3server/schemas/project.py
+++ b/gns3server/schemas/project.py
@@ -16,6 +16,31 @@
# along with this program. If not, see .
+PROJECT_CREATE_SCHEMA = {
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "description": "Request validation to create a new Project instance",
+ "type": "object",
+ "properties": {
+ "location": {
+ "description": "Base directory where the project should be created on remote server",
+ "type": "string",
+ "minLength": 1
+ },
+ "uuid": {
+ "description": "Project UUID",
+ "type": "string",
+ "minLength": 36,
+ "maxLength": 36,
+ "pattern": "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$"
+ },
+ "temporary": {
+ "description": "If project is a temporary project",
+ "type": "boolean"
+ },
+ },
+ "additionalProperties": False,
+}
+
PROJECT_OBJECT_SCHEMA = {
"$schema": "http://json-schema.org/draft-04/schema#",
"description": "Request validation to create a new Project instance",
@@ -33,6 +58,11 @@ PROJECT_OBJECT_SCHEMA = {
"maxLength": 36,
"pattern": "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$"
},
+ "temporary": {
+ "description": "If project is a temporary project",
+ "type": "boolean"
+ },
},
- "additionalProperties": False
+ "additionalProperties": False,
+ "required": ["location", "uuid", "temporary"]
}
diff --git a/tests/api/test_project.py b/tests/api/test_project.py
index e343b735..28cbb046 100644
--- a/tests/api/test_project.py
+++ b/tests/api/test_project.py
@@ -34,6 +34,15 @@ def test_create_project_without_dir(server):
response = server.post("/project", query)
assert response.status == 200
assert response.json["uuid"] is not None
+ assert response.json["temporary"] is False
+
+
+def test_create_temporary_project(server):
+ query = {"temporary": True}
+ response = server.post("/project", query)
+ assert response.status == 200
+ assert response.json["uuid"] is not None
+ assert response.json["temporary"] is True
def test_create_project_with_uuid(server):
diff --git a/tests/modules/test_project.py b/tests/modules/test_project.py
index d26488ac..4aaee655 100644
--- a/tests/modules/test_project.py
+++ b/tests/modules/test_project.py
@@ -58,7 +58,7 @@ def test_temporary_path():
def test_json(tmpdir):
p = Project()
- assert p.__json__() == {"location": p.location, "uuid": p.uuid}
+ assert p.__json__() == {"location": p.location, "uuid": p.uuid, "temporary": False}
def test_vm_working_directory(tmpdir, vm):
@@ -111,3 +111,13 @@ def test_project_close(tmpdir, manager):
with patch("gns3server.modules.vpcs.vpcs_vm.VPCSVM.close") as mock:
project.close()
assert mock.called
+
+
+def test_project_close_temporary_project(tmpdir, manager):
+ """A temporary project is deleted when closed"""
+
+ project = Project(location=str(tmpdir), temporary=True)
+ directory = project.path
+ assert os.path.exists(directory)
+ project.close()
+ assert os.path.exists(directory) is False