Merge pull request #2108 from GNS3/project-lock-unlock

Global project lock and unlock
This commit is contained in:
Jeremy Grossmann 2022-08-30 22:54:47 +02:00 committed by GitHub
commit 3014bd0216
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 99 additions and 6 deletions

View File

@ -36,7 +36,6 @@ from fastapi.responses import StreamingResponse, FileResponse
from websockets.exceptions import ConnectionClosed, WebSocketException
from typing import List, Optional
from uuid import UUID
from pathlib import Path
from gns3server import schemas
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.utils.asyncio import aiozipstream
from gns3server.utils.path import is_safe_path
from gns3server.config import Config
from gns3server.db.repositories.rbac import RbacRepository
from gns3server.db.repositories.templates import TemplatesRepository
from gns3server.services.templates import TemplatesService
@ -397,6 +395,24 @@ async def duplicate_project(
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}")
async def get_file(file_path: str, project: Project = Depends(dep_project)) -> FileResponse:
"""

View File

@ -1112,6 +1112,38 @@ class Project:
return True
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):
"""
Dump topology to disk

View File

@ -24,11 +24,12 @@ import pytest_asyncio
from fastapi import FastAPI, status
from httpx import AsyncClient
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
from gns3server.controller import Controller
from gns3server.controller.project import Project
from gns3server.controller.compute import Compute
pytestmark = pytest.mark.asyncio
@ -36,10 +37,10 @@ pytestmark = pytest.mark.asyncio
@pytest_asyncio.fixture
async def project(app: FastAPI, client: AsyncClient, controller: Controller) -> Project:
u = str(uuid.uuid4())
params = {"name": "test", "project_id": u}
project_id = str(uuid.uuid4())
params = {"name": "test", "project_id": project_id}
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:
@ -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"})
assert response.status_code == status.HTTP_201_CREATED
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