diff --git a/gns3server/controller/compute.py b/gns3server/controller/compute.py index 65bca3c34..9a9d94c29 100644 --- a/gns3server/controller/compute.py +++ b/gns3server/controller/compute.py @@ -375,6 +375,11 @@ class Compute: log.info(f"Connecting to compute '{self._id}'") response = await self._run_http_query("GET", "/capabilities") except ComputeError as e: + # Update connection status and notify UI + self._connected = False + self._last_error = str(e) + self._controller.notification.controller_emit("compute.updated", self.asdict()) + if report_failed_connection: raise log.warning(f"Cannot connect to compute '{self._id}': {e}") diff --git a/gns3server/controller/project.py b/gns3server/controller/project.py index 8d8eeebee..cab967a99 100644 --- a/gns3server/controller/project.py +++ b/gns3server/controller/project.py @@ -1029,6 +1029,29 @@ class Project: except ControllerError as e: # ignore missing images or other conflicts when deleting a project log.warning(f"Conflict while deleting project: {e}") + + # Check if all computes used by this project are connected before deletion + # We need to check from the topology file because _project_created_on_compute + # gets reset during open() + disconnected_computes = [] + for compute_id in self._computes: + try: + compute = self._controller.get_compute(compute_id) + if not compute.connected: + disconnected_computes.append(compute) + except ControllerError: + # Compute doesn't exist anymore, consider it disconnected + log.warning(f"Compute '{compute_id}' not found in controller") + # We can't add it to disconnected_computes without the compute object + pass + + if disconnected_computes: + compute_names = ", ".join([f"'{c.name}'" for c in disconnected_computes]) + raise ControllerForbiddenError( + f"Cannot delete project '{self.name}': {len(disconnected_computes)} compute(s) are disconnected: {compute_names}. " + f"Please fix the connection or delete the project manually on those computes." + ) + await self.delete_on_computes() await self.close()