mirror of
https://github.com/GNS3/gns3-server
synced 2024-11-28 11:18:11 +00:00
Global project lock and unlock
This commit is contained in:
parent
43e60c31c7
commit
ca3bf592d6
@ -36,7 +36,6 @@ from fastapi.responses import StreamingResponse, FileResponse
|
|||||||
from websockets.exceptions import ConnectionClosed, WebSocketException
|
from websockets.exceptions import ConnectionClosed, WebSocketException
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
from gns3server import schemas
|
from gns3server import schemas
|
||||||
from gns3server.controller import Controller
|
from gns3server.controller import Controller
|
||||||
@ -46,7 +45,6 @@ from gns3server.controller.import_project import import_project as import_contro
|
|||||||
from gns3server.controller.export_project import export_project as export_controller_project
|
from gns3server.controller.export_project import export_project as export_controller_project
|
||||||
from gns3server.utils.asyncio import aiozipstream
|
from gns3server.utils.asyncio import aiozipstream
|
||||||
from gns3server.utils.path import is_safe_path
|
from gns3server.utils.path import is_safe_path
|
||||||
from gns3server.config import Config
|
|
||||||
from gns3server.db.repositories.rbac import RbacRepository
|
from gns3server.db.repositories.rbac import RbacRepository
|
||||||
from gns3server.db.repositories.templates import TemplatesRepository
|
from gns3server.db.repositories.templates import TemplatesRepository
|
||||||
from gns3server.services.templates import TemplatesService
|
from gns3server.services.templates import TemplatesService
|
||||||
@ -397,6 +395,24 @@ async def duplicate_project(
|
|||||||
return new_project.asdict()
|
return new_project.asdict()
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/{project_id}/lock", status_code=status.HTTP_204_NO_CONTENT)
|
||||||
|
async def lock_project(project: Project = Depends(dep_project)) -> None:
|
||||||
|
"""
|
||||||
|
Lock all drawings and nodes in a given project.
|
||||||
|
"""
|
||||||
|
|
||||||
|
project.lock()
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/{project_id}/unlock", status_code=status.HTTP_204_NO_CONTENT)
|
||||||
|
async def unlock_project(project: Project = Depends(dep_project)) -> None:
|
||||||
|
"""
|
||||||
|
Unlock all drawings and nodes in a given project.
|
||||||
|
"""
|
||||||
|
|
||||||
|
project.unlock()
|
||||||
|
|
||||||
|
|
||||||
@router.get("/{project_id}/files/{file_path:path}")
|
@router.get("/{project_id}/files/{file_path:path}")
|
||||||
async def get_file(file_path: str, project: Project = Depends(dep_project)) -> FileResponse:
|
async def get_file(file_path: str, project: Project = Depends(dep_project)) -> FileResponse:
|
||||||
"""
|
"""
|
||||||
|
@ -1112,6 +1112,38 @@ class Project:
|
|||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@open_required
|
||||||
|
def lock(self):
|
||||||
|
"""
|
||||||
|
Lock all drawings and nodes
|
||||||
|
"""
|
||||||
|
|
||||||
|
for drawing in self._drawings.values():
|
||||||
|
if not drawing.locked:
|
||||||
|
drawing.locked = True
|
||||||
|
self.emit_notification("drawing.updated", drawing.asdict())
|
||||||
|
for node in self.nodes.values():
|
||||||
|
if not node.locked:
|
||||||
|
node.locked = True
|
||||||
|
self.emit_notification("node.updated", node.asdict())
|
||||||
|
self.dump()
|
||||||
|
|
||||||
|
@open_required
|
||||||
|
def unlock(self):
|
||||||
|
"""
|
||||||
|
Unlock all drawings and nodes
|
||||||
|
"""
|
||||||
|
|
||||||
|
for drawing in self._drawings.values():
|
||||||
|
if drawing.locked:
|
||||||
|
drawing.locked = False
|
||||||
|
self.emit_notification("drawing.updated", drawing.asdict())
|
||||||
|
for node in self.nodes.values():
|
||||||
|
if node.locked:
|
||||||
|
node.locked = False
|
||||||
|
self.emit_notification("node.updated", node.asdict())
|
||||||
|
self.dump()
|
||||||
|
|
||||||
def dump(self):
|
def dump(self):
|
||||||
"""
|
"""
|
||||||
Dump topology to disk
|
Dump topology to disk
|
||||||
|
@ -24,11 +24,12 @@ import pytest_asyncio
|
|||||||
from fastapi import FastAPI, status
|
from fastapi import FastAPI, status
|
||||||
from httpx import AsyncClient
|
from httpx import AsyncClient
|
||||||
from unittest.mock import patch, MagicMock
|
from unittest.mock import patch, MagicMock
|
||||||
from tests.utils import asyncio_patch
|
from tests.utils import asyncio_patch, AsyncioMagicMock
|
||||||
|
|
||||||
import gns3server.utils.zipfile_zstd as zipfile_zstd
|
import gns3server.utils.zipfile_zstd as zipfile_zstd
|
||||||
from gns3server.controller import Controller
|
from gns3server.controller import Controller
|
||||||
from gns3server.controller.project import Project
|
from gns3server.controller.project import Project
|
||||||
|
from gns3server.controller.compute import Compute
|
||||||
|
|
||||||
pytestmark = pytest.mark.asyncio
|
pytestmark = pytest.mark.asyncio
|
||||||
|
|
||||||
@ -36,10 +37,10 @@ pytestmark = pytest.mark.asyncio
|
|||||||
@pytest_asyncio.fixture
|
@pytest_asyncio.fixture
|
||||||
async def project(app: FastAPI, client: AsyncClient, controller: Controller) -> Project:
|
async def project(app: FastAPI, client: AsyncClient, controller: Controller) -> Project:
|
||||||
|
|
||||||
u = str(uuid.uuid4())
|
project_id = str(uuid.uuid4())
|
||||||
params = {"name": "test", "project_id": u}
|
params = {"name": "test", "project_id": project_id}
|
||||||
await client.post(app.url_path_for("create_project"), json=params)
|
await client.post(app.url_path_for("create_project"), json=params)
|
||||||
return controller.get_project(u)
|
return controller.get_project(project_id)
|
||||||
|
|
||||||
|
|
||||||
async def test_create_project_with_path(app: FastAPI, client: AsyncClient, controller: Controller, config) -> None:
|
async def test_create_project_with_path(app: FastAPI, client: AsyncClient, controller: Controller, config) -> None:
|
||||||
@ -473,3 +474,47 @@ async def test_duplicate(app: FastAPI, client: AsyncClient, project: Project) ->
|
|||||||
response = await client.post(app.url_path_for("duplicate_project", project_id=project.id), json={"name": "hello"})
|
response = await client.post(app.url_path_for("duplicate_project", project_id=project.id), json={"name": "hello"})
|
||||||
assert response.status_code == status.HTTP_201_CREATED
|
assert response.status_code == status.HTTP_201_CREATED
|
||||||
assert response.json()["name"] == "hello"
|
assert response.json()["name"] == "hello"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_lock_unlock(app: FastAPI, client: AsyncClient, project: Project, compute: Compute) -> None:
|
||||||
|
|
||||||
|
# add a drawing and node to the project
|
||||||
|
params = {
|
||||||
|
"svg": '<svg height="210" width="500"><line x1="0" y1="0" x2="200" y2="200" style="stroke:rgb(255,0,0);stroke-width:2" /></svg>',
|
||||||
|
"x": 10,
|
||||||
|
"y": 20,
|
||||||
|
"z": 0
|
||||||
|
}
|
||||||
|
|
||||||
|
response = await client.post(app.url_path_for("create_drawing", project_id=project.id), json=params)
|
||||||
|
assert response.status_code == status.HTTP_201_CREATED
|
||||||
|
|
||||||
|
response = MagicMock()
|
||||||
|
response.json = {"console": 2048}
|
||||||
|
compute.post = AsyncioMagicMock(return_value=response)
|
||||||
|
|
||||||
|
response = await client.post(app.url_path_for("create_node", project_id=project.id), json={
|
||||||
|
"name": "test",
|
||||||
|
"node_type": "vpcs",
|
||||||
|
"compute_id": "example.com",
|
||||||
|
"properties": {
|
||||||
|
"startup_script": "echo test"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
assert response.status_code == status.HTTP_201_CREATED
|
||||||
|
|
||||||
|
response = await client.post(app.url_path_for("lock_project", project_id=project.id))
|
||||||
|
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||||
|
|
||||||
|
for drawing in project.drawings.values():
|
||||||
|
assert drawing.locked is True
|
||||||
|
for node in project.nodes.values():
|
||||||
|
assert node.locked is True
|
||||||
|
|
||||||
|
response = await client.post(app.url_path_for("unlock_project", project_id=project.id))
|
||||||
|
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||||
|
|
||||||
|
for drawing in project.drawings.values():
|
||||||
|
assert drawing.locked is False
|
||||||
|
for node in project.nodes.values():
|
||||||
|
assert node.locked is False
|
||||||
|
Loading…
Reference in New Issue
Block a user