mirror of
https://github.com/GNS3/gns3-server
synced 2025-01-16 11:00:58 +00:00
Prevent multiple projects with the same ID to be created.
This commit is contained in:
parent
349d9d4540
commit
54fc873be5
@ -27,6 +27,10 @@ class ProjectHandler:
|
|||||||
@Route.post(
|
@Route.post(
|
||||||
r"/projects",
|
r"/projects",
|
||||||
description="Create a new project on the server",
|
description="Create a new project on the server",
|
||||||
|
status_codes={
|
||||||
|
201: "Project created",
|
||||||
|
409: "Project already created"
|
||||||
|
},
|
||||||
output=PROJECT_OBJECT_SCHEMA,
|
output=PROJECT_OBJECT_SCHEMA,
|
||||||
input=PROJECT_CREATE_SCHEMA)
|
input=PROJECT_CREATE_SCHEMA)
|
||||||
def create_project(request, response):
|
def create_project(request, response):
|
||||||
@ -37,6 +41,7 @@ class ProjectHandler:
|
|||||||
project_id=request.json.get("project_id"),
|
project_id=request.json.get("project_id"),
|
||||||
temporary=request.json.get("temporary", False)
|
temporary=request.json.get("temporary", False)
|
||||||
)
|
)
|
||||||
|
response.set_status(201)
|
||||||
response.json(p)
|
response.json(p)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -115,6 +120,7 @@ class ProjectHandler:
|
|||||||
yield from project.close()
|
yield from project.close()
|
||||||
for module in MODULES:
|
for module in MODULES:
|
||||||
yield from module.instance().project_closed(project.path)
|
yield from module.instance().project_closed(project.path)
|
||||||
|
pm.remove_project(project.id)
|
||||||
response.set_status(204)
|
response.set_status(204)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -133,4 +139,5 @@ class ProjectHandler:
|
|||||||
pm = ProjectManager.instance()
|
pm = ProjectManager.instance()
|
||||||
project = pm.get_project(request.match_info["project_id"])
|
project = pm.get_project(request.match_info["project_id"])
|
||||||
yield from project.delete()
|
yield from project.delete()
|
||||||
|
pm.remove_project(project.id)
|
||||||
response.set_status(204)
|
response.set_status(204)
|
||||||
|
@ -165,6 +165,7 @@ class BaseManager:
|
|||||||
if hasattr(self, "get_legacy_vm_workdir_name"):
|
if hasattr(self, "get_legacy_vm_workdir_name"):
|
||||||
# move old project VM files to a new location
|
# move old project VM files to a new location
|
||||||
|
|
||||||
|
log.info("Converting old project...")
|
||||||
project_name = os.path.basename(project.path)
|
project_name = os.path.basename(project.path)
|
||||||
project_files_dir = os.path.join(project.path, "{}-files".format(project_name))
|
project_files_dir = os.path.join(project.path, "{}-files".format(project_name))
|
||||||
module_path = os.path.join(project_files_dir, self.module_name.lower())
|
module_path = os.path.join(project_files_dir, self.module_name.lower())
|
||||||
@ -175,17 +176,21 @@ class BaseManager:
|
|||||||
except OSError as e:
|
except OSError as e:
|
||||||
raise aiohttp.web.HTTPInternalServerError(text="Could not move VM working directory: {} to {} {}".format(vm_working_dir, new_vm_working_dir, e))
|
raise aiohttp.web.HTTPInternalServerError(text="Could not move VM working directory: {} to {} {}".format(vm_working_dir, new_vm_working_dir, e))
|
||||||
|
|
||||||
if os.listdir(module_path) == []:
|
try:
|
||||||
try:
|
if not os.listdir(module_path):
|
||||||
os.rmdir(module_path)
|
os.rmdir(module_path)
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
raise aiohttp.web.HTTPInternalServerError(text="Could not delete {}: {}".format(module_path, e))
|
raise aiohttp.web.HTTPInternalServerError(text="Could not delete {}: {}".format(module_path, e))
|
||||||
|
except FileNotFoundError as e:
|
||||||
|
log.warning(e)
|
||||||
|
|
||||||
if os.listdir(project_files_dir) == []:
|
try:
|
||||||
try:
|
if not os.listdir(project_files_dir):
|
||||||
os.rmdir(project_files_dir)
|
os.rmdir(project_files_dir)
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
raise aiohttp.web.HTTPInternalServerError(text="Could not delete {}: {}".format(project_files_dir, e))
|
raise aiohttp.web.HTTPInternalServerError(text="Could not delete {}: {}".format(project_files_dir, e))
|
||||||
|
except FileNotFoundError as e:
|
||||||
|
log.warning(e)
|
||||||
|
|
||||||
if not vm_id:
|
if not vm_id:
|
||||||
vm_id = str(uuid4())
|
vm_id = str(uuid4())
|
||||||
|
@ -310,6 +310,7 @@ class Router(BaseVM):
|
|||||||
# router is already closed
|
# router is already closed
|
||||||
return
|
return
|
||||||
|
|
||||||
|
log.debug('Router "{name}" [{id}] is closing'.format(name=self._name, id=self._id))
|
||||||
if self._dynamips_id in self._dynamips_ids[self._project.id]:
|
if self._dynamips_id in self._dynamips_ids[self._project.id]:
|
||||||
self._dynamips_ids[self._project.id].remove(self._dynamips_id)
|
self._dynamips_ids[self._project.id].remove(self._dynamips_id)
|
||||||
|
|
||||||
|
@ -105,6 +105,8 @@ class IOUVM(BaseVM):
|
|||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def close(self):
|
def close(self):
|
||||||
|
|
||||||
|
log.debug('IOU "{name}" [{id}] is closing'.format(name=self._name, id=self._id))
|
||||||
|
|
||||||
if self._console:
|
if self._console:
|
||||||
self._manager.port_manager.release_tcp_port(self._console)
|
self._manager.port_manager.release_tcp_port(self._console)
|
||||||
self._console = None
|
self._console = None
|
||||||
|
@ -70,7 +70,7 @@ class Project:
|
|||||||
raise aiohttp.web.HTTPInternalServerError(text="Could not create project directory: {}".format(e))
|
raise aiohttp.web.HTTPInternalServerError(text="Could not create project directory: {}".format(e))
|
||||||
self.path = path
|
self.path = path
|
||||||
|
|
||||||
log.debug("Create project {id} in directory {path}".format(path=self._path, id=self._id))
|
log.info("Project {id} with path '{path}' created".format(path=self._path, id=self._id))
|
||||||
|
|
||||||
def __json__(self):
|
def __json__(self):
|
||||||
|
|
||||||
@ -278,8 +278,11 @@ class Project:
|
|||||||
if cleanup and os.path.exists(self.path):
|
if cleanup and os.path.exists(self.path):
|
||||||
try:
|
try:
|
||||||
yield from wait_run_in_executor(shutil.rmtree, self.path)
|
yield from wait_run_in_executor(shutil.rmtree, self.path)
|
||||||
|
log.info("Project {id} with path '{path}' deleted".format(path=self._path, id=self._id))
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
raise aiohttp.web.HTTPInternalServerError(text="Could not delete the project directory: {}".format(e))
|
raise aiohttp.web.HTTPInternalServerError(text="Could not delete the project directory: {}".format(e))
|
||||||
|
else:
|
||||||
|
log.info("Project {id} with path '{path}' closed".format(path=self._path, id=self._id))
|
||||||
|
|
||||||
port_manager = PortManager.instance()
|
port_manager = PortManager.instance()
|
||||||
if port_manager.tcp_ports:
|
if port_manager.tcp_ports:
|
||||||
|
@ -60,13 +60,26 @@ class ProjectManager:
|
|||||||
raise aiohttp.web.HTTPNotFound(text="Project ID {} doesn't exist".format(project_id))
|
raise aiohttp.web.HTTPNotFound(text="Project ID {} doesn't exist".format(project_id))
|
||||||
return self._projects[project_id]
|
return self._projects[project_id]
|
||||||
|
|
||||||
def create_project(self, **kwargs):
|
def create_project(self, project_id=None, path=None, temporary=False):
|
||||||
"""
|
"""
|
||||||
Create a project and keep a references to it in project manager.
|
Create a project and keep a references to it in project manager.
|
||||||
|
|
||||||
See documentation of Project for arguments
|
See documentation of Project for arguments
|
||||||
"""
|
"""
|
||||||
|
|
||||||
project = Project(**kwargs)
|
if project_id is not None and project_id in self._projects:
|
||||||
|
raise aiohttp.web.HTTPConflict(text="Project ID {} is already in use on this server".format(project_id))
|
||||||
|
project = Project(project_id=project_id, path=path, temporary=temporary)
|
||||||
self._projects[project.id] = project
|
self._projects[project.id] = project
|
||||||
return project
|
return project
|
||||||
|
|
||||||
|
def remove_project(self, project_id):
|
||||||
|
"""
|
||||||
|
Removes a Project instance from the list of projects in use.
|
||||||
|
|
||||||
|
:param project_id: Project identifier
|
||||||
|
"""
|
||||||
|
|
||||||
|
if project_id not in self._projects:
|
||||||
|
raise aiohttp.web.HTTPNotFound(text="Project ID {} doesn't exist".format(project_id))
|
||||||
|
del self._projects[project_id]
|
||||||
|
@ -647,6 +647,7 @@ class QemuVM(BaseVM):
|
|||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def close(self):
|
def close(self):
|
||||||
|
|
||||||
|
log.debug('QEMU VM "{name}" [{id}] is closing'.format(name=self._name, id=self._id))
|
||||||
yield from self.stop()
|
yield from self.stop()
|
||||||
if self._console:
|
if self._console:
|
||||||
self._manager.port_manager.release_tcp_port(self._console)
|
self._manager.port_manager.release_tcp_port(self._console)
|
||||||
|
@ -297,6 +297,7 @@ class VirtualBoxVM(BaseVM):
|
|||||||
# VM is already closed
|
# VM is already closed
|
||||||
return
|
return
|
||||||
|
|
||||||
|
log.debug("VirtualBox VM '{name}' [{id}] is closing".format(name=self.name, id=self.id))
|
||||||
if self._console:
|
if self._console:
|
||||||
self._manager.port_manager.release_tcp_port(self._console)
|
self._manager.port_manager.release_tcp_port(self._console)
|
||||||
self._console = None
|
self._console = None
|
||||||
|
@ -72,6 +72,7 @@ class VPCSVM(BaseVM):
|
|||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def close(self):
|
def close(self):
|
||||||
|
|
||||||
|
log.debug("VPCS {name} [{id}] is closing".format(name=self._name, id=self._id))
|
||||||
if self._console:
|
if self._console:
|
||||||
self._manager.port_manager.release_tcp_port(self._console)
|
self._manager.port_manager.release_tcp_port(self._console)
|
||||||
self._console = None
|
self._console = None
|
||||||
@ -297,10 +298,10 @@ class VPCSVM(BaseVM):
|
|||||||
port_number=port_number))
|
port_number=port_number))
|
||||||
|
|
||||||
self._ethernet_adapter.add_nio(port_number, nio)
|
self._ethernet_adapter.add_nio(port_number, nio)
|
||||||
log.info("VPCS {name} {id}]: {nio} added to port {port_number}".format(name=self._name,
|
log.info("VPCS {name} [{id}]: {nio} added to port {port_number}".format(name=self._name,
|
||||||
id=self.id,
|
id=self.id,
|
||||||
nio=nio,
|
nio=nio,
|
||||||
port_number=port_number))
|
port_number=port_number))
|
||||||
return nio
|
return nio
|
||||||
|
|
||||||
def port_remove_nio_binding(self, port_number):
|
def port_remove_nio_binding(self, port_number):
|
||||||
|
@ -27,14 +27,14 @@ from tests.utils import asyncio_patch
|
|||||||
def test_create_project_with_path(server, tmpdir):
|
def test_create_project_with_path(server, tmpdir):
|
||||||
with patch("gns3server.config.Config.get_section_config", return_value={"local": True}):
|
with patch("gns3server.config.Config.get_section_config", return_value={"local": True}):
|
||||||
response = server.post("/projects", {"path": str(tmpdir)})
|
response = server.post("/projects", {"path": str(tmpdir)})
|
||||||
assert response.status == 200
|
assert response.status == 201
|
||||||
assert response.json["path"] == str(tmpdir)
|
assert response.json["path"] == str(tmpdir)
|
||||||
|
|
||||||
|
|
||||||
def test_create_project_without_dir(server):
|
def test_create_project_without_dir(server):
|
||||||
query = {}
|
query = {}
|
||||||
response = server.post("/projects", query, example=True)
|
response = server.post("/projects", query, example=True)
|
||||||
assert response.status == 200
|
assert response.status == 201
|
||||||
assert response.json["project_id"] is not None
|
assert response.json["project_id"] is not None
|
||||||
assert response.json["temporary"] is False
|
assert response.json["temporary"] is False
|
||||||
|
|
||||||
@ -42,7 +42,7 @@ def test_create_project_without_dir(server):
|
|||||||
def test_create_temporary_project(server):
|
def test_create_temporary_project(server):
|
||||||
query = {"temporary": True}
|
query = {"temporary": True}
|
||||||
response = server.post("/projects", query)
|
response = server.post("/projects", query)
|
||||||
assert response.status == 200
|
assert response.status == 201
|
||||||
assert response.json["project_id"] is not None
|
assert response.json["project_id"] is not None
|
||||||
assert response.json["temporary"] is True
|
assert response.json["temporary"] is True
|
||||||
|
|
||||||
@ -50,18 +50,18 @@ def test_create_temporary_project(server):
|
|||||||
def test_create_project_with_uuid(server):
|
def test_create_project_with_uuid(server):
|
||||||
query = {"project_id": "00010203-0405-0607-0809-0a0b0c0d0e0f"}
|
query = {"project_id": "00010203-0405-0607-0809-0a0b0c0d0e0f"}
|
||||||
response = server.post("/projects", query)
|
response = server.post("/projects", query)
|
||||||
assert response.status == 200
|
assert response.status == 201
|
||||||
assert response.json["project_id"] == "00010203-0405-0607-0809-0a0b0c0d0e0f"
|
assert response.json["project_id"] == "00010203-0405-0607-0809-0a0b0c0d0e0f"
|
||||||
|
|
||||||
|
|
||||||
def test_show_project(server):
|
def test_show_project(server):
|
||||||
query = {"project_id": "00010203-0405-0607-0809-0a0b0c0d0e0f", "temporary": False}
|
query = {"project_id": "00010203-0405-0607-0809-0a0b0c0d0e02", "temporary": False}
|
||||||
response = server.post("/projects", query)
|
response = server.post("/projects", query)
|
||||||
assert response.status == 200
|
assert response.status == 201
|
||||||
response = server.get("/projects/00010203-0405-0607-0809-0a0b0c0d0e0f", example=True)
|
response = server.get("/projects/00010203-0405-0607-0809-0a0b0c0d0e02", example=True)
|
||||||
assert len(response.json.keys()) == 4
|
assert len(response.json.keys()) == 4
|
||||||
assert len(response.json["location"]) > 0
|
assert len(response.json["location"]) > 0
|
||||||
assert response.json["project_id"] == "00010203-0405-0607-0809-0a0b0c0d0e0f"
|
assert response.json["project_id"] == "00010203-0405-0607-0809-0a0b0c0d0e02"
|
||||||
assert response.json["temporary"] is False
|
assert response.json["temporary"] is False
|
||||||
|
|
||||||
|
|
||||||
@ -73,7 +73,7 @@ def test_show_project_invalid_uuid(server):
|
|||||||
def test_update_temporary_project(server):
|
def test_update_temporary_project(server):
|
||||||
query = {"temporary": True}
|
query = {"temporary": True}
|
||||||
response = server.post("/projects", query)
|
response = server.post("/projects", query)
|
||||||
assert response.status == 200
|
assert response.status == 201
|
||||||
query = {"temporary": False}
|
query = {"temporary": False}
|
||||||
response = server.put("/projects/{project_id}".format(project_id=response.json["project_id"]), query, example=True)
|
response = server.put("/projects/{project_id}".format(project_id=response.json["project_id"]), query, example=True)
|
||||||
assert response.status == 200
|
assert response.status == 200
|
||||||
@ -84,7 +84,7 @@ def test_update_path_project(server, tmpdir):
|
|||||||
|
|
||||||
with patch("gns3server.config.Config.get_section_config", return_value={"local": True}):
|
with patch("gns3server.config.Config.get_section_config", return_value={"local": True}):
|
||||||
response = server.post("/projects", {})
|
response = server.post("/projects", {})
|
||||||
assert response.status == 200
|
assert response.status == 201
|
||||||
query = {"path": str(tmpdir)}
|
query = {"path": str(tmpdir)}
|
||||||
response = server.put("/projects/{project_id}".format(project_id=response.json["project_id"]), query, example=True)
|
response = server.put("/projects/{project_id}".format(project_id=response.json["project_id"]), query, example=True)
|
||||||
assert response.status == 200
|
assert response.status == 200
|
||||||
@ -95,7 +95,7 @@ def test_update_path_project_non_local(server, tmpdir):
|
|||||||
|
|
||||||
with patch("gns3server.config.Config.get_section_config", return_value={"local": False}):
|
with patch("gns3server.config.Config.get_section_config", return_value={"local": False}):
|
||||||
response = server.post("/projects", {})
|
response = server.post("/projects", {})
|
||||||
assert response.status == 200
|
assert response.status == 201
|
||||||
query = {"path": str(tmpdir)}
|
query = {"path": str(tmpdir)}
|
||||||
response = server.put("/projects/{project_id}".format(project_id=response.json["project_id"]), query, example=True)
|
response = server.put("/projects/{project_id}".format(project_id=response.json["project_id"]), query, example=True)
|
||||||
assert response.status == 403
|
assert response.status == 403
|
||||||
@ -132,6 +132,6 @@ def test_close_project(server, project):
|
|||||||
assert mock.called
|
assert mock.called
|
||||||
|
|
||||||
|
|
||||||
def test_close_project_invalid_uuid(server, project):
|
def test_close_project_invalid_uuid(server):
|
||||||
response = server.post("/projects/{project_id}/close".format(project_id=uuid.uuid4()))
|
response = server.post("/projects/{project_id}/close".format(project_id=uuid.uuid4()))
|
||||||
assert response.status == 404
|
assert response.status == 404
|
||||||
|
Loading…
Reference in New Issue
Block a user