Add project.created, project.opened and project.deleted controller notification stream.

Move project.updated and project.closed from project notification to controller notification stream.
This commit is contained in:
grossmj 2024-01-12 13:16:55 +11:00
parent d466c85385
commit e3493870b2
No known key found for this signature in database
GPG Key ID: 0A2D76AC45EA25CD
4 changed files with 103 additions and 73 deletions

View File

@ -130,16 +130,27 @@ class Project:
self._iou_id_lock = asyncio.Lock() self._iou_id_lock = asyncio.Lock()
log.debug('Project "{name}" [{id}] loaded'.format(name=self.name, id=self._id)) log.debug('Project "{name}" [{id}] loaded'.format(name=self.name, id=self._id))
self.emit_controller_notification("project.created", self.__json__())
def emit_notification(self, action, event): def emit_notification(self, action, event):
""" """
Emit a notification to all clients using this project. Emit a project notification to all clients using this project.
:param action: Action name :param action: Action name
:param event: Event to send :param event: Event to send
""" """
self.controller.notification.project_emit(action, event, project_id=self.id) self._controller.notification.project_emit(action, event, project_id=self.id)
def emit_controller_notification(self, action, event):
"""
Emit a controller notification, all clients will see it.
:param action: Action name
:param event: Event to send
"""
self._controller.notification.controller_emit(action, event)
async def update(self, **kwargs): async def update(self, **kwargs):
""" """
@ -154,7 +165,7 @@ class Project:
# We send notif only if object has changed # We send notif only if object has changed
if old_json != self.__json__(): if old_json != self.__json__():
self.emit_notification("project.updated", self.__json__()) self.emit_controller_notification("project.updated", self.__json__())
self.dump() self.dump()
# update on computes # update on computes
@ -803,7 +814,8 @@ class Project:
self._clean_pictures() self._clean_pictures()
self._status = "closed" self._status = "closed"
if not ignore_notification: if not ignore_notification:
self.emit_notification("project.closed", self.__json__()) self.emit_controller_notification("project.closed", self.__json__())
self.reset() self.reset()
self._closing = False self._closing = False
@ -857,6 +869,7 @@ class Project:
shutil.rmtree(self.path) shutil.rmtree(self.path)
except OSError as e: except OSError as e:
raise aiohttp.web.HTTPConflict(text="Cannot delete project directory {}: {}".format(self.path, str(e))) raise aiohttp.web.HTTPConflict(text="Cannot delete project directory {}: {}".format(self.path, str(e)))
self.emit_controller_notification("project.deleted", self.__json__())
async def delete_on_computes(self): async def delete_on_computes(self):
""" """
@ -1001,6 +1014,7 @@ class Project:
pass pass
self._loading = False self._loading = False
self.emit_controller_notification("project.opened", self.__json__())
# Should we start the nodes when project is open # Should we start the nodes when project is open
if self._auto_start: if self._auto_start:
# Start all in the background without waiting for completion # Start all in the background without waiting for completion

View File

@ -213,7 +213,9 @@ async def test_compute_httpQuery_project(compute):
response = MagicMock() response = MagicMock()
with asyncio_patch("aiohttp.ClientSession.request", return_value=response) as mock: with asyncio_patch("aiohttp.ClientSession.request", return_value=response) as mock:
response.status = 200 response.status = 200
with patch('gns3server.controller.project.Project.emit_controller_notification') as mock_notification:
project = Project(name="Test") project = Project(name="Test")
mock_notification.assert_called()
await compute.post("/projects", project) await compute.post("/projects", project)
mock.assert_called_with("POST", "https://example.com:84/v2/compute/projects", data=json.dumps(project.__json__()), headers={'content-type': 'application/json'}, auth=None, chunked=None, timeout=20) mock.assert_called_with("POST", "https://example.com:84/v2/compute/projects", data=json.dumps(project.__json__()), headers={'content-type': 'application/json'}, auth=None, chunked=None, timeout=20)
await compute.close() await compute.close()

View File

@ -47,7 +47,9 @@ async def node(controller, project):
async def test_affect_uuid(): async def test_affect_uuid():
with patch('gns3server.controller.project.Project.emit_controller_notification') as mock_notification:
p = Project(name="Test") p = Project(name="Test")
mock_notification.assert_called()
assert len(p.id) == 36 assert len(p.id) == 36
p = Project(project_id='00010203-0405-0607-0809-0a0b0c0d0e0f', name="Test 2") p = Project(project_id='00010203-0405-0607-0809-0a0b0c0d0e0f', name="Test 2")
assert p.id == '00010203-0405-0607-0809-0a0b0c0d0e0f' assert p.id == '00010203-0405-0607-0809-0a0b0c0d0e0f'
@ -55,7 +57,9 @@ async def test_affect_uuid():
async def test_json(): async def test_json():
with patch('gns3server.controller.project.Project.emit_controller_notification') as mock_notification:
p = Project(name="Test") p = Project(name="Test")
mock_notification.assert_called()
assert p.__json__() == { assert p.__json__() == {
"name": "Test", "name": "Test",
@ -83,11 +87,11 @@ async def test_json():
async def test_update(controller): async def test_update(controller):
project = Project(controller=controller, name="Hello") project = Project(controller=controller, name="Hello")
project.emit_notification = MagicMock() project.emit_controller_notification = MagicMock()
assert project.name == "Hello" assert project.name == "Hello"
await project.update(name="World") await project.update(name="World")
assert project.name == "World" assert project.name == "World"
project.emit_notification.assert_any_call("project.updated", project.__json__()) project.emit_controller_notification.assert_any_call("project.updated", project.__json__())
async def test_update_on_compute(controller): async def test_update_on_compute(controller):
@ -106,7 +110,9 @@ async def test_path(projects_dir):
directory = projects_dir directory = projects_dir
with patch("gns3server.utils.path.get_default_project_directory", return_value=directory): with patch("gns3server.utils.path.get_default_project_directory", return_value=directory):
with patch('gns3server.controller.project.Project.emit_controller_notification') as mock_notification:
p = Project(project_id=str(uuid4()), name="Test") p = Project(project_id=str(uuid4()), name="Test")
mock_notification.assert_called()
assert p.path == os.path.join(directory, p.id) assert p.path == os.path.join(directory, p.id)
assert os.path.exists(os.path.join(directory, p.id)) assert os.path.exists(os.path.join(directory, p.id))
@ -124,7 +130,9 @@ def test_path_exist(tmpdir):
async def test_init_path(tmpdir): async def test_init_path(tmpdir):
with patch('gns3server.controller.project.Project.emit_controller_notification') as mock_notification:
p = Project(path=str(tmpdir), project_id=str(uuid4()), name="Test") p = Project(path=str(tmpdir), project_id=str(uuid4()), name="Test")
mock_notification.assert_called()
assert p.path == str(tmpdir) assert p.path == str(tmpdir)
@ -132,12 +140,14 @@ async def test_init_path(tmpdir):
async def test_changing_path_with_quote_not_allowed(tmpdir): async def test_changing_path_with_quote_not_allowed(tmpdir):
with pytest.raises(aiohttp.web.HTTPForbidden): with pytest.raises(aiohttp.web.HTTPForbidden):
with patch('gns3server.controller.project.Project.emit_controller_notification'):
p = Project(project_id=str(uuid4()), name="Test") p = Project(project_id=str(uuid4()), name="Test")
p.path = str(tmpdir / "project\"53") p.path = str(tmpdir / "project\"53")
async def test_captures_directory(tmpdir): async def test_captures_directory(tmpdir):
with patch('gns3server.controller.project.Project.emit_controller_notification'):
p = Project(path=str(tmpdir / "capturestest"), name="Test") p = Project(path=str(tmpdir / "capturestest"), name="Test")
assert p.captures_directory == str(tmpdir / "capturestest" / "project-files" / "captures") assert p.captures_directory == str(tmpdir / "capturestest" / "project-files" / "captures")
assert os.path.exists(p.captures_directory) assert os.path.exists(p.captures_directory)
@ -649,6 +659,7 @@ async def test_dump(projects_dir):
directory = projects_dir directory = projects_dir
with patch("gns3server.utils.path.get_default_project_directory", return_value=directory): with patch("gns3server.utils.path.get_default_project_directory", return_value=directory):
with patch('gns3server.controller.project.Project.emit_controller_notification'):
p = Project(project_id='00010203-0405-0607-0809-0a0b0c0d0e0f', name="Test") p = Project(project_id='00010203-0405-0607-0809-0a0b0c0d0e0f', name="Test")
p.dump() p.dump()
with open(os.path.join(directory, p.id, "Test.gns3")) as f: with open(os.path.join(directory, p.id, "Test.gns3")) as f:
@ -658,6 +669,7 @@ async def test_dump(projects_dir):
async def test_open_close(controller): async def test_open_close(controller):
with patch('gns3server.controller.project.Project.emit_controller_notification'):
project = Project(controller=controller, name="Test") project = Project(controller=controller, name="Test")
assert project.status == "opened" assert project.status == "opened"
await project.close() await project.close()
@ -665,14 +677,15 @@ async def test_open_close(controller):
await project.open() await project.open()
assert not project.start_all.called assert not project.start_all.called
assert project.status == "opened" assert project.status == "opened"
project.emit_notification = MagicMock() project.emit_controller_notification = MagicMock()
await project.close() await project.close()
assert project.status == "closed" assert project.status == "closed"
project.emit_notification.assert_any_call("project.closed", project.__json__()) project.emit_controller_notification.assert_any_call("project.closed", project.__json__())
async def test_open_auto_start(controller): async def test_open_auto_start(controller):
with patch('gns3server.controller.project.Project.emit_controller_notification'):
project = Project(controller=controller, name="Test", auto_start=True) project = Project(controller=controller, name="Test", auto_start=True)
assert project.status == "opened" assert project.status == "opened"
await project.close() await project.close()

View File

@ -19,7 +19,7 @@ import json
import uuid import uuid
import pytest import pytest
import aiohttp import aiohttp
from unittest.mock import MagicMock from unittest.mock import MagicMock, patch
from tests.utils import asyncio_patch from tests.utils import asyncio_patch
from gns3server.controller.project import Project from gns3server.controller.project import Project
@ -30,6 +30,7 @@ from gns3server.version import __version__
async def test_project_to_topology_empty(tmpdir): async def test_project_to_topology_empty(tmpdir):
with patch('gns3server.controller.project.Project.emit_controller_notification'):
project = Project(name="Test") project = Project(name="Test")
topo = project_to_topology(project) topo = project_to_topology(project)
assert topo == { assert topo == {