From 7b3d5ae5e35b366da43233e9f64df9de9b023791 Mon Sep 17 00:00:00 2001 From: ziajka Date: Wed, 9 May 2018 15:29:35 +0200 Subject: [PATCH] Create/update project on compute when variables changes --- gns3server/compute/project.py | 13 +++++++++- gns3server/compute/project_manager.py | 7 +++--- gns3server/controller/compute.py | 3 ++- gns3server/controller/project.py | 13 +++++++++- .../handlers/api/compute/project_handler.py | 22 +++++++++++++++- tests/compute/test_project.py | 7 ++++++ tests/controller/test_project.py | 25 ++++++++++++++++--- tests/controller/test_topology.py | 2 ++ tests/handlers/api/compute/test_project.py | 17 +++++++++++++ 9 files changed, 98 insertions(+), 11 deletions(-) diff --git a/gns3server/compute/project.py b/gns3server/compute/project.py index 3dc6723f..3792a8a7 100644 --- a/gns3server/compute/project.py +++ b/gns3server/compute/project.py @@ -25,13 +25,13 @@ import zipfile import json from uuid import UUID, uuid4 + from .port_manager import PortManager from .notification_manager import NotificationManager from ..config import Config from ..utils.asyncio import wait_run_in_executor from ..utils.path import check_path_allowed, get_default_project_directory - import logging log = logging.getLogger(__name__) @@ -297,6 +297,17 @@ class Project: yield from node.delete() self._nodes.remove(node) + @asyncio.coroutine + def update(self, variables=None, **kwargs): + original_variables = self.variables + self.variables = variables + + # we need to update docker nodes when variables changes + if original_variables != variables: + for node in self.nodes: + if hasattr(node, 'update'): + yield from node.update() + @asyncio.coroutine def close(self): """ diff --git a/gns3server/compute/project_manager.py b/gns3server/compute/project_manager.py index 28c517d1..9abda38a 100644 --- a/gns3server/compute/project_manager.py +++ b/gns3server/compute/project_manager.py @@ -16,6 +16,7 @@ # along with this program. If not, see . import aiohttp +import asyncio import psutil import platform from .project import Project @@ -95,16 +96,16 @@ class ProjectManager: log.warning(message) project.emit("log.warning", {"message": message}) - def create_project(self, name=None, project_id=None, path=None): + def create_project(self, name=None, project_id=None, path=None, variables=None): """ Create a project and keep a references to it in project manager. See documentation of Project for arguments """ - if project_id is not None and project_id in self._projects: return self._projects[project_id] - project = Project(name=name, project_id=project_id, path=path) + project = Project(name=name, project_id=project_id, + path=path, variables=variables) self._check_available_disk_space(project) self._projects[project.id] = project return project diff --git a/gns3server/controller/compute.py b/gns3server/controller/compute.py index e1948a35..3bdee2f9 100644 --- a/gns3server/controller/compute.py +++ b/gns3server/controller/compute.py @@ -460,11 +460,12 @@ class Compute: msg = json.loads(response.data) action = msg.pop("action") event = msg.pop("event") - if action == "ping": self._cpu_usage_percent = event["cpu_usage_percent"] self._memory_usage_percent = event["memory_usage_percent"] self._controller.notification.emit("compute.updated", self.__json__()) + elif action == 'project.updated': + print(event) else: yield from self._controller.notification.dispatch(action, event, compute_id=self.id) if self._ws: diff --git a/gns3server/controller/project.py b/gns3server/controller/project.py index 59bcfedc..576f64df 100644 --- a/gns3server/controller/project.py +++ b/gns3server/controller/project.py @@ -136,6 +136,15 @@ class Project: self.controller.notification.emit("project.updated", self.__json__()) self.dump() + # update on computes + for compute in list(self._project_created_on_compute): + yield from compute.put( + "/projects/{}".format(self._id), { + "variables": self.variables + } + ) + + def reset(self): """ Called when open/close a project. Cleanup internal stuff @@ -493,12 +502,14 @@ class Project: yield from compute.post("/projects", data={ "name": self._name, "project_id": self._id, - "path": self._path + "path": self._path, + "variables": self._variables }) else: yield from compute.post("/projects", data={ "name": self._name, "project_id": self._id, + "variables": self._variables }) self._project_created_on_compute.add(compute) diff --git a/gns3server/handlers/api/compute/project_handler.py b/gns3server/handlers/api/compute/project_handler.py index a7d8f760..e850a955 100644 --- a/gns3server/handlers/api/compute/project_handler.py +++ b/gns3server/handlers/api/compute/project_handler.py @@ -73,11 +73,31 @@ class ProjectHandler: p = pm.create_project( name=request.json.get("name"), path=request.json.get("path"), - project_id=request.json.get("project_id") + project_id=request.json.get("project_id"), + variables=request.json.get("variables", None) ) response.set_status(201) response.json(p) + @Route.put( + r"/projects/{project_id}", + description="Update the project on the server", + status_codes={ + 201: "Project updated", + 403: "Forbidden to update a project" + }, + output=PROJECT_OBJECT_SCHEMA, + input=PROJECT_UPDATE_SCHEMA) + def update_project(request, response): + + pm = ProjectManager.instance() + project = pm.get_project(request.match_info["project_id"]) + yield from project.update( + variables=request.json.get("variables", None) + ) + response.set_status(200) + response.json(project) + @Route.get( r"/projects/{project_id}", description="Get project information", diff --git a/tests/compute/test_project.py b/tests/compute/test_project.py index 5713ebf4..82493f8e 100644 --- a/tests/compute/test_project.py +++ b/tests/compute/test_project.py @@ -205,3 +205,10 @@ def test_emit(async_run): (action, event, context) = async_run(queue.get(0.5)) assert action == "test" assert context["project_id"] == project.id + + +def test_update_project(loop): + variables = [{"name": "TEST", "value": "VAL"}] + project = Project(project_id=str(uuid.uuid4())) + loop.run_until_complete(asyncio.async(project.update(variables=variables))) + assert project.variables == variables diff --git a/tests/controller/test_project.py b/tests/controller/test_project.py index 1e76d39e..6a0177e2 100644 --- a/tests/controller/test_project.py +++ b/tests/controller/test_project.py @@ -85,13 +85,27 @@ def test_json(tmpdir): def test_update(controller, async_run): project = Project(controller=controller, name="Hello") controller._notification = MagicMock() - assert project.name == "Hello" async_run(project.update(name="World")) assert project.name == "World" controller.notification.emit.assert_any_call("project.updated", project.__json__()) +def test_update_on_compute(controller, async_run): + variables = [{"name": "TEST", "value": "VAL1"}] + compute = MagicMock() + compute.id = "local" + project = Project(controller=controller, name="Test") + project._project_created_on_compute = [compute] + controller._notification = MagicMock() + + async_run(project.update(variables=variables)) + + compute.put.assert_any_call('/projects/{}'.format(project.id), { + "variables": variables + }) + + def test_path(tmpdir): directory = Config.instance().get_section_config("Server").get("projects_path") @@ -150,7 +164,8 @@ def test_add_node_local(async_run, controller): compute.post.assert_any_call('/projects', data={ "name": project._name, "project_id": project._id, - "path": project._path + "path": project._path, + "variables": None }) compute.post.assert_any_call('/projects/{}/vpcs/nodes'.format(project.id), data={'node_id': node.id, @@ -178,7 +193,8 @@ def test_add_node_non_local(async_run, controller): compute.post.assert_any_call('/projects', data={ "name": project._name, - "project_id": project._id + "project_id": project._id, + "variables": None }) compute.post.assert_any_call('/projects/{}/vpcs/nodes'.format(project.id), data={'node_id': node.id, @@ -218,7 +234,8 @@ def test_add_node_from_appliance(async_run, controller): compute.post.assert_any_call('/projects', data={ "name": project._name, "project_id": project._id, - "path": project._path + "path": project._path, + "variables": None }) compute.post.assert_any_call('/projects/{}/vpcs/nodes'.format(project.id), data={'node_id': node.id, diff --git a/tests/controller/test_topology.py b/tests/controller/test_topology.py index 0bf8a0cb..81eb0a0f 100644 --- a/tests/controller/test_topology.py +++ b/tests/controller/test_topology.py @@ -53,6 +53,8 @@ def test_project_to_topology_empty(tmpdir): "drawings": [] }, "type": "topology", + "supplier": None, + "variables": None, "version": __version__ } diff --git a/tests/handlers/api/compute/test_project.py b/tests/handlers/api/compute/test_project.py index 9db5350a..1fc92e85 100644 --- a/tests/handlers/api/compute/test_project.py +++ b/tests/handlers/api/compute/test_project.py @@ -91,6 +91,23 @@ def test_delete_project(http_compute, project): assert mock.called +def test_update_project(http_compute): + query = {"name": "test", "project_id": "51010203-0405-0607-0809-0a0b0c0d0e0f"} + response = http_compute.post("/projects", query) + assert response.status == 201 + + query = { + "variables": [{"name": "TEST1", "value": "VAL1"}] + } + response = http_compute.put( + "/projects/{project_id}".format(project_id="51010203-0405-0607-0809-0a0b0c0d0e0f"), + query, + example=True + ) + assert response.status == 200 + assert response.json["variables"] == [{"name": "TEST1", "value": "VAL1"}] + + def test_delete_project_invalid_uuid(http_compute): response = http_compute.delete("/projects/{project_id}".format(project_id=uuid.uuid4())) assert response.status == 404