From a9afaa028cbcc7949d68ecc965529e86e7fbd7d9 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Mon, 2 Mar 2015 17:17:28 +0100 Subject: [PATCH] Garbage collect VM when closing a project --- gns3server/handlers/api/project_handler.py | 4 ---- gns3server/modules/base_manager.py | 4 +++- gns3server/modules/dynamips/__init__.py | 4 +++- gns3server/modules/project.py | 25 +++++++++++++++++++- tests/modules/test_manager.py | 27 ---------------------- tests/modules/test_project.py | 2 ++ 6 files changed, 32 insertions(+), 34 deletions(-) diff --git a/gns3server/handlers/api/project_handler.py b/gns3server/handlers/api/project_handler.py index 3f9d9f4a..14e40d52 100644 --- a/gns3server/handlers/api/project_handler.py +++ b/gns3server/handlers/api/project_handler.py @@ -121,11 +121,7 @@ class ProjectHandler: pm = ProjectManager.instance() project = pm.get_project(request.match_info["project_id"]) - for module in MODULES: - yield from module.instance().project_closing(project) yield from project.close() - for module in MODULES: - yield from module.instance().project_closed(project) pm.remove_project(project.id) response.set_status(204) diff --git a/gns3server/modules/base_manager.py b/gns3server/modules/base_manager.py index 05e9621f..d2d1c7f1 100644 --- a/gns3server/modules/base_manager.py +++ b/gns3server/modules/base_manager.py @@ -255,7 +255,9 @@ class BaseManager: :param project: Project instance """ - pass + for vm in project.vms: + if vm.id in self._vms: + del self._vms[vm.id] @asyncio.coroutine def project_moved(self, project): diff --git a/gns3server/modules/dynamips/__init__.py b/gns3server/modules/dynamips/__init__.py index a0c54612..0cf6ae4b 100644 --- a/gns3server/modules/dynamips/__init__.py +++ b/gns3server/modules/dynamips/__init__.py @@ -143,6 +143,7 @@ class Dynamips(BaseManager): :param project: Project instance """ + yield from super().project_closing(project) # delete the Dynamips devices corresponding to the project tasks = [] for device in self._devices.values(): @@ -165,8 +166,9 @@ class Dynamips(BaseManager): :param project: Project instance """ + yield from super().project_closed(project) # delete useless Dynamips files - project_dir = project.module_working_directory(self.module_name.lower()) + project_dir = project.module_working_path(self.module_name.lower()) files = glob.glob(os.path.join(project_dir, "*.ghost")) files += glob.glob(os.path.join(project_dir, "*_lock")) files += glob.glob(os.path.join(project_dir, "ilt_*")) diff --git a/gns3server/modules/project.py b/gns3server/modules/project.py index e2044ec1..0c622063 100644 --- a/gns3server/modules/project.py +++ b/gns3server/modules/project.py @@ -180,13 +180,21 @@ class Project: :returns: working directory """ - workdir = os.path.join(self._path, "project-files", module_name) + workdir = self.module_working_path(module_name) try: os.makedirs(workdir, exist_ok=True) except OSError as e: raise aiohttp.web.HTTPInternalServerError(text="Could not create module working directory: {}".format(e)) return workdir + def module_working_path(self, module_name): + """ + Return the working direcotory for the module. If you want + to be sure to have the directory on disk take a look on: + module_working_directory + """ + return os.path.join(self._path, "project-files", module_name) + def vm_working_directory(self, vm): """ Return a working directory for a specific VM. @@ -250,7 +258,11 @@ class Project: def close(self): """Close the project, but keep information on disk""" + for module in self.modules(): + yield from module.instance().project_closing(self) yield from self._close_and_clean(self._temporary) + for module in self.modules(): + yield from module.instance().project_closed(self) @asyncio.coroutine def _close_and_clean(self, cleanup): @@ -304,7 +316,11 @@ class Project: def delete(self): """Remove project from disk""" + for module in self.modules(): + yield from module.instance().project_closing(self) yield from self._close_and_clean(True) + for module in self.modules(): + yield from module.instance().project_closed(self) @classmethod def clean_project_directory(cls): @@ -318,3 +334,10 @@ class Project: if os.path.exists(os.path.join(path, ".gns3_temporary")): log.warning("Purge old temporary project {}".format(project)) shutil.rmtree(path) + + def modules(self): + """Return VM modules loaded""" + + # We import it at the last time to avoid circular dependencies + from ..modules import MODULES + return MODULES diff --git a/tests/modules/test_manager.py b/tests/modules/test_manager.py index 32a0130a..5efa1e33 100644 --- a/tests/modules/test_manager.py +++ b/tests/modules/test_manager.py @@ -81,30 +81,3 @@ def test_create_vm_old_topology(loop, project, tmpdir, port_manager): vm_dir = os.path.join(project_dir, "project-files", "vpcs", vm.id) with open(os.path.join(vm_dir, "startup.vpc")) as f: assert f.read() == "1" - - -# def test_create_vm_old_topology_with_garbage_in_project_dir(loop, project, tmpdir, port_manager): -# -# with patch("gns3server.config.Config.get_section_config", return_value={"local": True}): -# # Create an old topology directory -# project_dir = str(tmpdir / "testold") -# vm_dir = os.path.join(project_dir, "testold-files", "vpcs", "pc-1") -# project.path = project_dir -# os.makedirs(vm_dir, exist_ok=True) -# with open(os.path.join(vm_dir, "startup.vpc"), "w+") as f: -# f.write("1") -# with open(os.path.join(os.path.join(project_dir, "testold-files"), "crash.log"), "w+") as f: -# f.write("1") -# -# VPCS._instance = None -# vpcs = VPCS.instance() -# vpcs.port_manager = port_manager -# vm_id = 1 -# vm = loop.run_until_complete(vpcs.create_vm("PC 1", project.id, vm_id)) -# assert len(vm.id) == 36 -# -# assert os.path.exists(os.path.join(project_dir, "testold-files")) is True -# -# vm_dir = os.path.join(project_dir, "project-files", "vpcs", vm.id) -# with open(os.path.join(vm_dir, "startup.vpc")) as f: -# assert f.read() == "1" diff --git a/tests/modules/test_project.py b/tests/modules/test_project.py index d410da31..a647508c 100644 --- a/tests/modules/test_project.py +++ b/tests/modules/test_project.py @@ -184,9 +184,11 @@ def test_project_close(loop, manager): project = Project() vm = VPCSVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager) project.add_vm(vm) + vm.manager._vms = {vm.id: vm} with asyncio_patch("gns3server.modules.vpcs.vpcs_vm.VPCSVM.close") as mock: loop.run_until_complete(asyncio.async(project.close())) assert mock.called + assert vm.id not in vm.manager._vms def test_project_close_temporary_project(loop, manager):