This is an automated email from the ASF dual-hosted git repository.
kiranchavala pushed a commit to branch 4.20
in repository https://gitbox.apache.org/repos/asf/cloudstack.git
The following commit(s) were added to refs/heads/4.20 by this push:
new 215ef984993 Allow cleaning up of networks stuck in Implementing state
(#12409)
215ef984993 is described below
commit 215ef984993509bbb068f52c24b446be708c8dba
Author: Pearl Dsilva <[email protected]>
AuthorDate: Wed Sep 9 00:53:26 2026 -0400
Allow cleaning up of networks stuck in Implementing state (#12409)
* Allow cleaning up of networks stuck in Implementing state
* extract code
---
.../framework/jobs/impl/AsyncJobManagerImpl.java | 80 ++++++++++++++++++++++
1 file changed, 80 insertions(+)
diff --git
a/framework/jobs/src/main/java/org/apache/cloudstack/framework/jobs/impl/AsyncJobManagerImpl.java
b/framework/jobs/src/main/java/org/apache/cloudstack/framework/jobs/impl/AsyncJobManagerImpl.java
index b9c9b22d9ea..f9fdc075132 100644
---
a/framework/jobs/src/main/java/org/apache/cloudstack/framework/jobs/impl/AsyncJobManagerImpl.java
+++
b/framework/jobs/src/main/java/org/apache/cloudstack/framework/jobs/impl/AsyncJobManagerImpl.java
@@ -957,6 +957,9 @@ public class AsyncJobManagerImpl extends ManagerBase
implements AsyncJobManager,
}
logger.trace("End cleanup expired async-jobs");
+
+ cleanupNetworksStuckInImplementing();
+
} catch (Throwable e) {
logger.error("Unexpected exception when trying to execute
queue item, ", e);
}
@@ -964,6 +967,15 @@ public class AsyncJobManagerImpl extends ManagerBase
implements AsyncJobManager,
};
}
+ private void cleanupNetworksStuckInImplementing() {
+ // Cleanup orphaned networks stuck in Implementing state without async
jobs
+ try {
+ cleanupOrphanedNetworks();
+ } catch (Throwable e) {
+ logger.error("Unexpected exception when trying to cleanup orphaned
networks", e);
+ }
+ }
+
@DB
protected void expungeAsyncJob(final AsyncJobVO job) {
Transaction.execute(new TransactionCallbackNoReturn() {
@@ -1272,6 +1284,74 @@ public class AsyncJobManagerImpl extends ManagerBase
implements AsyncJobManager,
}
}
+ /**
+ * Cleanup networks that are stuck in Implementing state without
associated async jobs.
+ * This only processes networks that have been stuck for longer than the
job expiration threshold.
+ */
+ private void cleanupOrphanedNetworks() {
+ try {
+ SearchCriteria<NetworkVO> sc = networkDao.createSearchCriteria();
+ sc.addAnd("state", SearchCriteria.Op.EQ,
Network.State.Implementing);
+ sc.addAnd("removed", SearchCriteria.Op.NULL);
+ List<NetworkVO> implementingNetworks = networkDao.search(sc, null);
+
+ if (implementingNetworks == null ||
implementingNetworks.isEmpty()) {
+ return;
+ }
+
+ logger.debug("Found {} networks in Implementing state, checking
for orphaned networks", implementingNetworks.size());
+
+ final long expireMinutes = JobExpireMinutes.value();
+ final Date cutoffTime = new Date(System.currentTimeMillis() -
(expireMinutes * 60 * 1000));
+
+ for (NetworkVO network : implementingNetworks) {
+ if (network.getCreated().after(cutoffTime)) {
+ logger.trace("Network {} in Implementing state is only {}
minutes old (threshold: {} minutes), skipping cleanup",
+ network.getId(),
+ (System.currentTimeMillis() -
network.getCreated().getTime()) / 60000,
+ expireMinutes);
+ continue;
+ }
+
+ List<AsyncJobVO> jobs =
_jobDao.findInstancePendingAsyncJobs("Network", network.getAccountId());
+ boolean hasActiveJob = false;
+ for (AsyncJobVO job : jobs) {
+ if (job.getInstanceId() != null &&
job.getInstanceId().equals(network.getId())) {
+ hasActiveJob = true;
+ break;
+ }
+ }
+
+ if (hasActiveJob) {
+ logger.debug("Network {} in Implementing state has active
async job, skipping cleanup", network.getId());
+ continue;
+ }
+
+ logger.warn("Found orphaned network {} in Implementing state
without async job. " +
+ "Network created: {}, age: {} minutes, expiration
threshold: {} minutes. Transitioning to Shutdown state.",
+ network.getId(), network.getCreated(),
+ (System.currentTimeMillis() -
network.getCreated().getTime()) / 60000,
+ expireMinutes);
+ updateNetworkState(network);
+
+ }
+ } catch (Exception e) {
+ logger.error("Error while cleaning up orphaned networks", e);
+ }
+ }
+
+ private void updateNetworkState(NetworkVO network) {
+ try {
+ networkOrchestrationService.stateTransitTo(network,
Network.Event.OperationFailed);
+ logger.info("Successfully transitioned orphaned network {} to
Shutdown state using state machine", network.getId());
+ } catch (final NoTransitionException e) {
+ logger.debug("State transition failed for orphaned network {},
forcing state update", network.getId());
+ network.setState(Network.State.Shutdown);
+ networkDao.update(network.getId(), network);
+ logger.info("Successfully forced orphaned network {} to Shutdown
state", network.getId());
+ }
+ }
+
@Override
public void onManagementNodeJoined(List<? extends ManagementServerHost>
nodeList, long selfNodeId) {
}