From 40af2a35e03afcac60041588857e1dfbf250fbdc Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Fri, 18 Nov 2016 17:35:28 +0100 Subject: [PATCH] Wait for the end of project loading before making new change Fix #790 --- gns3server/controller/__init__.py | 13 +++++++- gns3server/controller/project.py | 13 ++++++++ .../api/controller/drawing_handler.py | 12 +++---- .../handlers/api/controller/link_handler.py | 21 ++++-------- .../handlers/api/controller/node_handler.py | 33 +++++++++---------- .../api/controller/project_handler.py | 8 ++--- 6 files changed, 56 insertions(+), 44 deletions(-) diff --git a/gns3server/controller/__init__.py b/gns3server/controller/__init__.py index 150427d5..61ea43d6 100644 --- a/gns3server/controller/__init__.py +++ b/gns3server/controller/__init__.py @@ -338,13 +338,24 @@ class Controller: def get_project(self, project_id): """ - Returns a compute server or raise a 404 error. + Returns a project or raise a 404 error. """ try: return self._projects[project_id] except KeyError: raise aiohttp.web.HTTPNotFound(text="Project ID {} doesn't exist".format(project_id)) + @asyncio.coroutine + def get_loaded_project(self, project_id): + """ + Returns a project or raise a 404 error. + + If project is not finished to load wait for it + """ + project = self.get_project(project_id) + yield from project.wait_loaded() + return project + def remove_project(self, project): del self._projects[project.id] diff --git a/gns3server/controller/project.py b/gns3server/controller/project.py index 7506efa3..f66d7087 100644 --- a/gns3server/controller/project.py +++ b/gns3server/controller/project.py @@ -78,6 +78,7 @@ class Project: self._status = status self._scene_height = scene_height self._scene_width = scene_width + self._loading = False # Disallow overwrite of existing project if project_id is None and path is not None: @@ -618,10 +619,12 @@ class Project: return self.reset() + self._loading = True self._status = "opened" path = self._topology_file() if not os.path.exists(path): + self._loading = False return try: shutil.copy(path, path + ".backup") @@ -655,16 +658,26 @@ class Project: if os.path.exists(path + ".backup"): shutil.copy(path + ".backup", path) self._status = "closed" + self._loading = False raise e try: os.remove(path + ".backup") except OSError: pass + self._loading = False # Should we start the nodes when project is open if self._auto_start: yield from self.start_all() + @asyncio.coroutine + def wait_loaded(self): + """ + Wait until the project finish loading + """ + while self._loading: + yield from asyncio.sleep(0.5) + @asyncio.coroutine def duplicate(self, name=None, location=None): """ diff --git a/gns3server/handlers/api/controller/drawing_handler.py b/gns3server/handlers/api/controller/drawing_handler.py index da44fde4..882610c0 100644 --- a/gns3server/handlers/api/controller/drawing_handler.py +++ b/gns3server/handlers/api/controller/drawing_handler.py @@ -41,8 +41,7 @@ class DrawingHandler: description="List drawings of a project") def list_drawings(request, response): - controller = Controller.instance() - project = controller.get_project(request.match_info["project_id"]) + project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"]) response.json([v for v in project.drawings.values()]) @Route.post( @@ -59,8 +58,7 @@ class DrawingHandler: output=DRAWING_OBJECT_SCHEMA) def create(request, response): - controller = Controller.instance() - project = controller.get_project(request.match_info["project_id"]) + project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"]) drawing = yield from project.add_drawing(**request.json) response.set_status(201) response.json(drawing) @@ -80,8 +78,7 @@ class DrawingHandler: output=DRAWING_OBJECT_SCHEMA) def update(request, response): - controller = Controller.instance() - project = controller.get_project(request.match_info["project_id"]) + project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"]) drawing = project.get_drawing(request.match_info["drawing_id"]) yield from drawing.update(**request.json) response.set_status(201) @@ -100,7 +97,6 @@ class DrawingHandler: description="Delete a drawing instance") def delete(request, response): - controller = Controller.instance() - project = controller.get_project(request.match_info["project_id"]) + project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"]) yield from project.delete_drawing(request.match_info["drawing_id"]) response.set_status(204) diff --git a/gns3server/handlers/api/controller/link_handler.py b/gns3server/handlers/api/controller/link_handler.py index 7aea027f..fc5c4d23 100644 --- a/gns3server/handlers/api/controller/link_handler.py +++ b/gns3server/handlers/api/controller/link_handler.py @@ -43,8 +43,7 @@ class LinkHandler: description="List links of a project") def list_links(request, response): - controller = Controller.instance() - project = controller.get_project(request.match_info["project_id"]) + project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"]) response.json([v for v in project.links.values()]) @Route.post( @@ -61,8 +60,7 @@ class LinkHandler: output=LINK_OBJECT_SCHEMA) def create(request, response): - controller = Controller.instance() - project = controller.get_project(request.match_info["project_id"]) + project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"]) link = yield from project.add_link() try: for node in request.json["nodes"]: @@ -91,8 +89,7 @@ class LinkHandler: output=LINK_OBJECT_SCHEMA) def update(request, response): - controller = Controller.instance() - project = controller.get_project(request.match_info["project_id"]) + project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"]) link = project.get_link(request.match_info["link_id"]) yield from link.update_nodes(request.json["nodes"]) response.set_status(201) @@ -113,8 +110,7 @@ class LinkHandler: description="Start capture on a link instance. By default we consider it as an Ethernet link") def start_capture(request, response): - controller = Controller.instance() - project = controller.get_project(request.match_info["project_id"]) + project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"]) link = project.get_link(request.match_info["link_id"]) yield from link.start_capture(data_link_type=request.json.get("data_link_type", "DLT_EN10MB"), capture_file_name=request.json.get("capture_file_name")) response.set_status(201) @@ -133,8 +129,7 @@ class LinkHandler: description="Stop capture on a link instance") def stop_capture(request, response): - controller = Controller.instance() - project = controller.get_project(request.match_info["project_id"]) + project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"]) link = project.get_link(request.match_info["link_id"]) yield from link.stop_capture() response.set_status(201) @@ -153,8 +148,7 @@ class LinkHandler: description="Delete a link instance") def delete(request, response): - controller = Controller.instance() - project = controller.get_project(request.match_info["project_id"]) + project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"]) yield from project.delete_link(request.match_info["link_id"]) response.set_status(204) @@ -172,8 +166,7 @@ class LinkHandler: }) def pcap(request, response): - controller = Controller.instance() - project = controller.get_project(request.match_info["project_id"]) + project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"]) link = project.get_link(request.match_info["link_id"]) if link.capture_file_path is None: diff --git a/gns3server/handlers/api/controller/node_handler.py b/gns3server/handlers/api/controller/node_handler.py index acd9e19e..67857df7 100644 --- a/gns3server/handlers/api/controller/node_handler.py +++ b/gns3server/handlers/api/controller/node_handler.py @@ -49,7 +49,7 @@ class NodeHandler: controller = Controller.instance() compute = controller.get_compute(request.json.pop("compute_id")) - project = controller.get_project(request.match_info["project_id"]) + project = yield from controller.get_loaded_project(request.match_info["project_id"]) node = yield from project.add_node(compute, request.json.pop("name"), request.json.pop("node_id", None), **request.json) response.set_status(201) response.json(node) @@ -80,8 +80,7 @@ class NodeHandler: description="List nodes of a project") def list_nodes(request, response): - controller = Controller.instance() - project = controller.get_project(request.match_info["project_id"]) + project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"]) response.json([v for v in project.nodes.values()]) @Route.put( @@ -95,7 +94,7 @@ class NodeHandler: input=NODE_UPDATE_SCHEMA, output=NODE_OBJECT_SCHEMA) def update(request, response): - project = Controller.instance().get_project(request.match_info["project_id"]) + project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"]) node = project.get_node(request.match_info["node_id"]) # Ignore these because we only use them when creating a node @@ -121,7 +120,7 @@ class NodeHandler: output=NODE_OBJECT_SCHEMA) def start_all(request, response): - project = Controller.instance().get_project(request.match_info["project_id"]) + project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"]) yield from project.start_all() response.set_status(204) @@ -139,7 +138,7 @@ class NodeHandler: output=NODE_OBJECT_SCHEMA) def stop_all(request, response): - project = Controller.instance().get_project(request.match_info["project_id"]) + project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"]) yield from project.stop_all() response.set_status(204) @@ -157,7 +156,7 @@ class NodeHandler: output=NODE_OBJECT_SCHEMA) def suspend_all(request, response): - project = Controller.instance().get_project(request.match_info["project_id"]) + project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"]) yield from project.suspend_all() response.set_status(204) @@ -175,7 +174,7 @@ class NodeHandler: output=NODE_OBJECT_SCHEMA) def reload_all(request, response): - project = Controller.instance().get_project(request.match_info["project_id"]) + project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"]) yield from project.stop_all() yield from project.start_all() response.set_status(204) @@ -195,7 +194,7 @@ class NodeHandler: output=NODE_OBJECT_SCHEMA) def start(request, response): - project = Controller.instance().get_project(request.match_info["project_id"]) + project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"]) node = project.get_node(request.match_info["node_id"]) yield from node.start() response.json(node) @@ -216,7 +215,7 @@ class NodeHandler: output=NODE_OBJECT_SCHEMA) def stop(request, response): - project = Controller.instance().get_project(request.match_info["project_id"]) + project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"]) node = project.get_node(request.match_info["node_id"]) yield from node.stop() response.json(node) @@ -237,7 +236,7 @@ class NodeHandler: output=NODE_OBJECT_SCHEMA) def suspend(request, response): - project = Controller.instance().get_project(request.match_info["project_id"]) + project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"]) node = project.get_node(request.match_info["node_id"]) yield from node.suspend() response.json(node) @@ -258,7 +257,7 @@ class NodeHandler: output=NODE_OBJECT_SCHEMA) def reload(request, response): - project = Controller.instance().get_project(request.match_info["project_id"]) + project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"]) node = project.get_node(request.match_info["node_id"]) yield from node.reload() response.json(node) @@ -277,7 +276,7 @@ class NodeHandler: }, description="Delete a node instance") def delete(request, response): - project = Controller.instance().get_project(request.match_info["project_id"]) + project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"]) yield from project.delete_node(request.match_info["node_id"]) response.set_status(204) @@ -295,7 +294,7 @@ class NodeHandler: description="Compute the IDLE PC for a Dynamips node") def auto_idlepc(request, response): - project = Controller.instance().get_project(request.match_info["project_id"]) + project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"]) node = project.get_node(request.match_info["node_id"]) idle = yield from node.dynamips_auto_idlepc() response.json(idle) @@ -315,7 +314,7 @@ class NodeHandler: description="Compute a list of potential idle PC for a node") def idlepc_proposals(request, response): - project = Controller.instance().get_project(request.match_info["project_id"]) + project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"]) node = project.get_node(request.match_info["node_id"]) idle = yield from node.dynamips_idlepc_proposals() response.json(idle) @@ -335,7 +334,7 @@ class NodeHandler: description="Get a file in the node directory") def get_file(request, response): - project = Controller.instance().get_project(request.match_info["project_id"]) + project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"]) node = project.get_node(request.match_info["node_id"]) path = request.match_info["path"] path = os.path.normpath(path) @@ -375,7 +374,7 @@ class NodeHandler: description="Write a file in the node directory") def post_file(request, response): - project = Controller.instance().get_project(request.match_info["project_id"]) + project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"]) node = project.get_node(request.match_info["node_id"]) path = request.match_info["path"] path = os.path.normpath(path) diff --git a/gns3server/handlers/api/controller/project_handler.py b/gns3server/handlers/api/controller/project_handler.py index f2ad38f0..e8bb962d 100644 --- a/gns3server/handlers/api/controller/project_handler.py +++ b/gns3server/handlers/api/controller/project_handler.py @@ -284,7 +284,7 @@ class ProjectHandler: def export_project(request, response): controller = Controller.instance() - project = controller.get_project(request.match_info["project_id"]) + project = yield from controller.get_loaded_project(request.match_info["project_id"]) with tempfile.TemporaryDirectory() as tmp_dir: datas = yield from export_project(project, tmp_dir, include_images=bool(request.get("include_images", "0"))) @@ -360,7 +360,7 @@ class ProjectHandler: def duplicate(request, response): controller = Controller.instance() - project = controller.get_project(request.match_info["project_id"]) + project = yield from controller.get_loaded_project(request.match_info["project_id"]) if request.json.get("path"): config = Config.instance() @@ -390,7 +390,7 @@ class ProjectHandler: def get_file(request, response): controller = Controller.instance() - project = controller.get_project(request.match_info["project_id"]) + project = yield from controller.get_loaded_project(request.match_info["project_id"]) path = request.match_info["path"] path = os.path.normpath(path) @@ -434,7 +434,7 @@ class ProjectHandler: def write_file(request, response): controller = Controller.instance() - project = controller.get_project(request.match_info["project_id"]) + project = yield from controller.get_loaded_project(request.match_info["project_id"]) path = request.match_info["path"] path = os.path.normpath(path)