mirror of
https://github.com/GNS3/gns3-server.git
synced 2026-08-27 04:20:14 +03:00
Fix: Check compute connectivity before open() during project deletion
Previously the disconnected compute check ran after open(), which would block for 120s trying to connect to unreachable remote computes before rejecting the deletion. Now reads the topology file directly to extract compute IDs and checks connectivity before calling open(), enabling immediate rejection of deletions involving offline computes. Also removes the redundant post-open() check since the early check covers both opened and closed project states. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
809d4cc609
commit
40644f33e6
@ -905,12 +905,23 @@ class Project:
|
||||
|
||||
async def delete(self):
|
||||
|
||||
# Check compute connectivity before open() to avoid 120s timeout
|
||||
# when remote computes are unreachable
|
||||
disconnected = self._get_disconnected_computes()
|
||||
if disconnected:
|
||||
compute_names = ", ".join([f"'{c.name}'" for c in disconnected])
|
||||
raise ControllerForbiddenError(
|
||||
f"Cannot delete project '{self.name}': {len(disconnected)} compute(s) are disconnected: {compute_names}. "
|
||||
f"Please fix the connection or delete the project manually on those computes."
|
||||
)
|
||||
|
||||
if self._status != "opened":
|
||||
try:
|
||||
await self.open()
|
||||
except aiohttp.web.HTTPConflict as e:
|
||||
# ignore missing images or other conflicts when deleting a project
|
||||
log.warning("Conflict while deleting project: {}".format(e.text))
|
||||
log.warning(f"Conflict while deleting project: {e}")
|
||||
|
||||
await self.delete_on_computes()
|
||||
await self.close()
|
||||
try:
|
||||
@ -922,6 +933,42 @@ class Project:
|
||||
raise aiohttp.web.HTTPConflict(text="Cannot delete project directory {}: {}".format(self.path, str(e)))
|
||||
self.emit_controller_notification("project.deleted", self.__json__())
|
||||
|
||||
def _get_disconnected_computes(self):
|
||||
"""
|
||||
Check compute connectivity by reading the topology file directly,
|
||||
without opening the project (which would try to connect to computes).
|
||||
Returns a list of disconnected Compute objects.
|
||||
"""
|
||||
if self._status == "opened":
|
||||
# Project is already open, use the already-loaded _computes list
|
||||
compute_ids = self._computes
|
||||
else:
|
||||
# Read compute IDs from topology file without connecting
|
||||
path = self._topology_file()
|
||||
if not os.path.exists(path):
|
||||
return []
|
||||
try:
|
||||
project_data = load_topology(path)
|
||||
except (ValueError, OSError) as e:
|
||||
log.warning(f"Could not read topology file for project '{self._name}': {e}")
|
||||
return []
|
||||
topology = project_data.get("topology", {})
|
||||
compute_ids = set()
|
||||
for node in topology.get("nodes", []):
|
||||
compute_id = node.get("compute_id")
|
||||
if compute_id:
|
||||
compute_ids.add(compute_id)
|
||||
|
||||
disconnected = []
|
||||
for compute_id in compute_ids:
|
||||
try:
|
||||
compute = self._controller.get_compute(compute_id)
|
||||
if not compute.connected:
|
||||
disconnected.append(compute)
|
||||
except ControllerError:
|
||||
log.warning(f"Compute '{compute_id}' not found in controller")
|
||||
return disconnected
|
||||
|
||||
async def delete_on_computes(self):
|
||||
"""
|
||||
Delete the project on computes but not on controller
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user