diff --git a/gns3server/handlers/project_handler.py b/gns3server/handlers/project_handler.py index aab739fd..5f707f4c 100644 --- a/gns3server/handlers/project_handler.py +++ b/gns3server/handlers/project_handler.py @@ -65,6 +65,7 @@ class ProjectHandler: }, status_codes={ 200: "The project has been updated", + 403: "You are not allowed to modify this property", 404: "The project doesn't exist" }, output=PROJECT_OBJECT_SCHEMA, @@ -74,6 +75,7 @@ class ProjectHandler: pm = ProjectManager.instance() project = pm.get_project(request.match_info["project_id"]) project.temporary = request.json.get("temporary", project.temporary) + project.path = request.json.get("path", project.path) response.json(project) @classmethod diff --git a/gns3server/modules/project.py b/gns3server/modules/project.py index 55002bca..0d1d7149 100644 --- a/gns3server/modules/project.py +++ b/gns3server/modules/project.py @@ -50,24 +50,26 @@ class Project: raise aiohttp.web.HTTPBadRequest(text="{} is not a valid UUID".format(uuid)) self._uuid = uuid - config = Config.instance().get_section_config("Server") - self._location = location + self._location = None if location is None: - self._location = config.get("project_directory", self._get_default_project_directory()) + self._location = self._config().get("project_directory", self._get_default_project_directory()) else: - if config.get("local", False) is False: - raise aiohttp.web.HTTPForbidden(text="You are not allowed to modifiy the project directory location") + self.location = location self._vms = set() self._vms_to_destroy = set() self._path = os.path.join(self._location, self._uuid) try: - os.makedirs(os.path.join(self._path, "vms"), exist_ok=True) + os.makedirs(self._path, exist_ok=True) except OSError as e: raise aiohttp.web.HTTPInternalServerError(text="Could not create project directory: {}".format(e)) self.temporary = temporary log.debug("Create project {uuid} in directory {path}".format(path=self._path, uuid=self._uuid)) + def _config(self): + + return Config.instance().get_section_config("Server") + @classmethod def _get_default_project_directory(cls): """ @@ -92,11 +94,27 @@ class Project: return self._location + @location.setter + def location(self, location): + + if location != self._location and self._config().get("local", False) is False: + raise aiohttp.web.HTTPForbidden(text="You are not allowed to modifiy the project directory location") + + self._location = location + @property def path(self): return self._path + @path.setter + def path(self, path): + + if path != self._path and self._config().get("local", False) is False: + raise aiohttp.web.HTTPForbidden(text="You are not allowed to modifiy the project directory location") + + self._path = path + @property def vms(self): @@ -167,8 +185,9 @@ class Project: return { "project_id": self._uuid, - "location": self._location, - "temporary": self._temporary + "temporary": self._temporary, + "path": self._path, + "location": self._location } def add_vm(self, vm): diff --git a/gns3server/schemas/project.py b/gns3server/schemas/project.py index f610b637..942b154b 100644 --- a/gns3server/schemas/project.py +++ b/gns3server/schemas/project.py @@ -50,6 +50,10 @@ PROJECT_UPDATE_SCHEMA = { "description": "If project is a temporary project", "type": "boolean" }, + "path": { + "description": "Path of the project on the server (work only with --local)", + "type": ["string", "null"] + }, }, "additionalProperties": False, } @@ -64,6 +68,11 @@ PROJECT_OBJECT_SCHEMA = { "type": "string", "minLength": 1 }, + "path": { + "description": "Directory of the project on the server", + "type": "string", + "minLength": 1 + }, "project_id": { "description": "Project UUID", "type": "string", diff --git a/tests/api/test_project.py b/tests/api/test_project.py index ec92a98d..ee381c99 100644 --- a/tests/api/test_project.py +++ b/tests/api/test_project.py @@ -60,6 +60,7 @@ def test_show_project(server): response = server.post("/projects", query) assert response.status == 200 response = server.get("/projects/00010203-0405-0607-0809-0a0b0c0d0e0f", example=True) + query["path"] = "/tmp/00010203-0405-0607-0809-0a0b0c0d0e0f" assert response.json == query @@ -78,6 +79,27 @@ def test_update_temporary_project(server): assert response.json["temporary"] is False +def test_update_path_project(server, tmpdir): + + with patch("gns3server.config.Config.get_section_config", return_value={"local": True}): + response = server.post("/projects", {}) + assert response.status == 200 + query = {"path": str(tmpdir)} + response = server.put("/projects/{project_id}".format(project_id=response.json["project_id"]), query, example=True) + assert response.status == 200 + assert response.json["path"] == str(tmpdir) + + +def test_update_path_project_non_local(server, tmpdir): + + with patch("gns3server.config.Config.get_section_config", return_value={"local": False}): + response = server.post("/projects", {}) + assert response.status == 200 + query = {"path": str(tmpdir)} + response = server.put("/projects/{project_id}".format(project_id=response.json["project_id"]), query, example=True) + assert response.status == 403 + + def test_commit_project(server, project): with asyncio_patch("gns3server.modules.project.Project.commit", return_value=True) as mock: response = server.post("/projects/{project_id}/commit".format(project_id=project.uuid), example=True) diff --git a/tests/modules/test_project.py b/tests/modules/test_project.py index 25919d68..c50ee3e5 100644 --- a/tests/modules/test_project.py +++ b/tests/modules/test_project.py @@ -53,7 +53,6 @@ def test_path(tmpdir): p = Project(location=str(tmpdir)) assert p.path == os.path.join(str(tmpdir), p.uuid) assert os.path.exists(os.path.join(str(tmpdir), p.uuid)) - assert os.path.exists(os.path.join(str(tmpdir), p.uuid, 'vms')) assert not os.path.exists(os.path.join(p.path, '.gns3_temporary')) @@ -77,9 +76,16 @@ def test_changing_location_not_allowed(tmpdir): p = Project(location=str(tmpdir)) +def test_changing_path_not_allowed(tmpdir): + with patch("gns3server.config.Config.get_section_config", return_value={"local": False}): + with pytest.raises(aiohttp.web.HTTPForbidden): + p = Project() + p.path = str(tmpdir) + + def test_json(tmpdir): p = Project() - assert p.__json__() == {"location": p.location, "project_id": p.uuid, "temporary": False} + assert p.__json__() == {"location": p.location, "path": p.path, "project_id": p.uuid, "temporary": False} def test_vm_working_directory(tmpdir, vm):