diff --git a/gns3server/controller/__init__.py b/gns3server/controller/__init__.py index 6914c1fe..c59f32a4 100644 --- a/gns3server/controller/__init__.py +++ b/gns3server/controller/__init__.py @@ -117,6 +117,16 @@ class Controller: self.notification.emit("compute.updated", self._computes[compute_id].__json__()) return self._computes[compute_id] + @asyncio.coroutine + def delete_compute(self, compute_id): + """ + Delete a compute node + :param compute_id: Compute server identifier + """ + compute = self.get_compute(compute_id) + del self._computes[compute_id] + self.notification.emit("compute.deleted", compute.__json__()) + @property def notification(self): """ diff --git a/gns3server/controller/compute.py b/gns3server/controller/compute.py index 9ee6ec7a..7646008b 100644 --- a/gns3server/controller/compute.py +++ b/gns3server/controller/compute.py @@ -42,9 +42,9 @@ class Compute: assert controller is not None log.info("Create compute %s", compute_id) self._id = compute_id - self._protocol = protocol - self._host = host - self._port = port + self.protocol = protocol + self.host = host + self.port = port self._user = None self._password = None self._connected = False @@ -73,6 +73,15 @@ class Compute: else: self._auth = None + @asyncio.coroutine + def update(self, **kwargs): + for kw in kwargs: + setattr(self, kw, kwargs[kw]) + if self._session: + self._session.close() + self._connected = False + self._controller.notification.emit("compute.updated", self.__json__()) + @property def version(self): """ @@ -117,6 +126,10 @@ class Compute: """ return self._host + @host.setter + def host(self, host): + self._host = host + @property def port(self): """ @@ -124,6 +137,10 @@ class Compute: """ return self._port + @port.setter + def port(self, port): + self._port = port + @property def protocol(self): """ @@ -131,6 +148,10 @@ class Compute: """ return self._protocol + @protocol.setter + def protocol(self, protocol): + self._protocol = protocol + @property def user(self): return self._user diff --git a/gns3server/handlers/api/controller/compute_handler.py b/gns3server/handlers/api/controller/compute_handler.py index 57dc373e..f1d128c8 100644 --- a/gns3server/handlers/api/controller/compute_handler.py +++ b/gns3server/handlers/api/controller/compute_handler.py @@ -21,7 +21,7 @@ from aiohttp.web import HTTPForbidden from gns3server.web.route import Route from gns3server.config import Config from gns3server.compute.project_manager import ProjectManager -from gns3server.schemas.compute import COMPUTE_CREATE_SCHEMA, COMPUTE_OBJECT_SCHEMA +from gns3server.schemas.compute import COMPUTE_CREATE_SCHEMA, COMPUTE_OBJECT_SCHEMA, COMPUTE_UPDATE_SCHEMA from gns3server.controller import Controller import logging @@ -92,6 +92,26 @@ class ComputeHandler: asyncio.async(server.shutdown_server()) response.set_status(201) + @Route.put( + r"/computes/{compute_id:.+}", + description="Get a compute server information", + status_codes={ + 200: "Compute server updated", + 400: "Invalid request", + 404: "Instance doesn't exist" + }, + input=COMPUTE_UPDATE_SCHEMA, + output=COMPUTE_OBJECT_SCHEMA) + def update(request, response): + + controller = Controller.instance() + compute = controller.get_compute(request.match_info["compute_id"]) + + # Ignore these because we only use them when creating a node + request.json.pop("compute_id", None) + yield from compute.update(**request.json) + response.set_status(200) + response.json(compute) @Route.get( r"/computes/{compute_id:.+}", @@ -105,3 +125,20 @@ class ComputeHandler: controller = Controller.instance() compute = controller.get_compute(request.match_info["compute_id"]) response.json(compute) + + @Route.delete( + r"/computes/{compute_id:.+}", + parameters={ + "project_id": "Project UUID", + "compute_id": "Compute UUID" + }, + status_codes={ + 204: "Instance deleted", + 400: "Invalid request", + 404: "Instance doesn't exist" + }, + description="Delete a compute instance") + def delete(request, response): + controller = Controller.instance() + yield from controller.delete_compute(request.match_info["compute_id"]) + response.set_status(204) diff --git a/gns3server/schemas/compute.py b/gns3server/schemas/compute.py index 1d019627..a21d30d3 100644 --- a/gns3server/schemas/compute.py +++ b/gns3server/schemas/compute.py @@ -54,6 +54,8 @@ COMPUTE_CREATE_SCHEMA = { "required": ["compute_id", "protocol", "host", "port"] } +COMPUTE_UPDATE_SCHEMA = COMPUTE_CREATE_SCHEMA + COMPUTE_OBJECT_SCHEMA = { "$schema": "http://json-schema.org/draft-04/schema#", "description": "Request validation to a GNS3 compute object instance", @@ -93,5 +95,5 @@ COMPUTE_OBJECT_SCHEMA = { } }, "additionalProperties": False, - "required": ["compute_id", "protocol", "host", "port", "name"] + "required": ["compute_id", "protocol", "host", "port", "name", "connected"] } diff --git a/tests/controller/test_compute.py b/tests/controller/test_compute.py index 049baf79..84886b22 100644 --- a/tests/controller/test_compute.py +++ b/tests/controller/test_compute.py @@ -201,3 +201,15 @@ def test_streamFile(project, async_run, compute): with asyncio_patch("aiohttp.ClientSession.request", return_value=response) as mock: async_run(compute.steam_file(project, "test/titi")) mock.assert_called_with("GET", "https://example.com:84/v2/compute/projects/{}/stream/test/titi".format(project.id), auth=None) + + +def test_update(compute, controller, async_run): + compute._controller._notification = MagicMock() + compute.name = "Test" + compute.host = "example.org" + compute._connected = True + async_run(compute.update(name="Test 2")) + assert compute.name == "Test 2" + assert compute.host == "example.org" + controller.notification.emit.assert_called_with("compute.updated", compute.__json__()) + assert compute.connected is False diff --git a/tests/controller/test_controller.py b/tests/controller/test_controller.py index a3cbad17..b5455da7 100644 --- a/tests/controller/test_controller.py +++ b/tests/controller/test_controller.py @@ -87,6 +87,15 @@ def test_addCompute(controller, controller_config_path, async_run): assert len(controller.computes) == 2 +def test_deleteCompute(controller, controller_config_path, async_run): + c = async_run(controller.add_compute("test1")) + assert len(controller.computes) == 1 + controller._notification = MagicMock() + async_run(controller.delete_compute("test1")) + assert len(controller.computes) == 0 + controller._notification.emit.assert_called_with("compute.deleted", c.__json__()) + + def test_addComputeConfigFile(controller, controller_config_path, async_run): async_run(controller.add_compute("test1")) assert len(controller.computes) == 1 diff --git a/tests/handlers/api/controller/test_compute.py b/tests/handlers/api/controller/test_compute.py index 29b8f33a..91a71d50 100644 --- a/tests/handlers/api/controller/test_compute.py +++ b/tests/handlers/api/controller/test_compute.py @@ -54,6 +54,30 @@ def test_compute_get(http_controller, controller): assert response.json["protocol"] == "http" +def test_compute_update(http_controller, controller): + + params = { + "compute_id": "my_compute/id", + "protocol": "http", + "host": "example.com", + "port": 84, + "user": "julien", + "password": "secure" + } + response = http_controller.post("/computes", params) + assert response.status == 201 + + response = http_controller.get("/computes/my_compute/id") + assert response.status == 200 + assert response.json["protocol"] == "http" + + params["protocol"] = "https" + response = http_controller.put("/computes/my_compute/id", params, example=True) + + assert response.status == 200 + assert response.json["protocol"] == "https" + + def test_compute_list(http_controller, controller): params = { @@ -84,3 +108,26 @@ def test_compute_list(http_controller, controller): } ] + + +def test_compute_delete(http_controller, controller): + + params = { + "compute_id": "my_compute/id", + "protocol": "http", + "host": "example.com", + "port": 84, + "user": "julien", + "password": "secure" + } + response = http_controller.post("/computes", params) + assert response.status == 201 + + response = http_controller.get("/computes") + assert len(response.json) == 1 + + response = http_controller.delete("/computes/my_compute/id") + assert response.status == 204 + + response = http_controller.get("/computes") + assert len(response.json) == 0