From 0f2d389f374052f4318f1fbe973762c6ba325d51 Mon Sep 17 00:00:00 2001 From: "ai-fixes[bot]" <321365310+ai-fixes[bot]@users.noreply.github.com> Date: Wed, 30 Sep 2026 10:32:37 +0000 Subject: [PATCH] CS-3470: NetworkGarbageCollector can tear down a network with a live, Running VR countNicsForNonStoppedRunningVrs() in NicDaoImpl checked VR state Starting/Stopping/Migrating but omitted Running, so a router that resynced back to Running after a spurious PowerReportMissing was never counted, letting isNetworkReadyForGc() approve garbage collection of a network with a live NIC/VR. Added VirtualMachine.State.Running to the state filter and a test, NicDaoImplTest#testCountNicsForNonStoppedRunningVrsIncludesRunningState, which fails without the fix (asserts the join's state parameters include Running). Generated-by: Claude Code (claude-sonnet-5) --- .../java/com/cloud/vm/dao/NicDaoImpl.java | 4 ++-- .../java/com/cloud/vm/dao/NicDaoImplTest.java | 22 +++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/NicDaoImpl.java b/engine/schema/src/main/java/com/cloud/vm/dao/NicDaoImpl.java index f4f3b0d6ad60..7f62731a3752 100644 --- a/engine/schema/src/main/java/com/cloud/vm/dao/NicDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/vm/dao/NicDaoImpl.java @@ -47,7 +47,7 @@ public class NicDaoImpl extends GenericDaoBase implements NicDao { private GenericSearchBuilder IpSearch; private SearchBuilder NonReleasedSearch; private GenericSearchBuilder deviceIdSearch; - private GenericSearchBuilder CountByForNonStoppedVms; + GenericSearchBuilder CountByForNonStoppedVms; private SearchBuilder PeerRouterSearch; @Inject @@ -394,7 +394,7 @@ public int countNicsForNonStoppedRunningVrs(long networkId) { SearchCriteria sc = CountByForNonStoppedVms.create(); sc.setParameters("networkId", networkId); sc.setParameters("vmTypeNEQ", VirtualMachine.Type.User); - sc.setJoinParameters("vm", "state", new Object[] {VirtualMachine.State.Starting, VirtualMachine.State.Stopping, VirtualMachine.State.Migrating}); + sc.setJoinParameters("vm", "state", new Object[] {VirtualMachine.State.Starting, VirtualMachine.State.Running, VirtualMachine.State.Stopping, VirtualMachine.State.Migrating}); List results = customSearch(sc, null); return results.get(0); } diff --git a/engine/schema/src/test/java/com/cloud/vm/dao/NicDaoImplTest.java b/engine/schema/src/test/java/com/cloud/vm/dao/NicDaoImplTest.java index 506fdb7fc92b..902d996f6441 100644 --- a/engine/schema/src/test/java/com/cloud/vm/dao/NicDaoImplTest.java +++ b/engine/schema/src/test/java/com/cloud/vm/dao/NicDaoImplTest.java @@ -17,20 +17,24 @@ package com.cloud.vm.dao; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import org.apache.commons.collections.CollectionUtils; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.Mockito; import org.mockito.Spy; import org.mockito.junit.MockitoJUnitRunner; import com.cloud.utils.db.Filter; +import com.cloud.utils.db.GenericSearchBuilder; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; import com.cloud.vm.NicVO; +import com.cloud.vm.VirtualMachine; @RunWith(MockitoJUnitRunner.class) public class NicDaoImplTest { @@ -66,4 +70,22 @@ public void testSearchRemovedByVms() { Mockito.any(SearchCriteria.class), Mockito.any(Filter.class), Mockito.eq(null), Mockito.eq(false)); } + + @Test + @SuppressWarnings("unchecked") + public void testCountNicsForNonStoppedRunningVrsIncludesRunningState() { + GenericSearchBuilder sb = Mockito.mock(GenericSearchBuilder.class); + SearchCriteria sc = Mockito.mock(SearchCriteria.class); + Mockito.when(sb.create()).thenReturn(sc); + nicDaoImplSpy.CountByForNonStoppedVms = sb; + Mockito.doReturn(List.of(0)).when(nicDaoImplSpy).customSearch(Mockito.eq(sc), Mockito.eq(null)); + + nicDaoImplSpy.countNicsForNonStoppedRunningVrs(5L); + + ArgumentCaptor statesCaptor = ArgumentCaptor.forClass(Object[].class); + Mockito.verify(sc).setJoinParameters(Mockito.eq("vm"), Mockito.eq("state"), statesCaptor.capture()); + Assert.assertTrue("A running VR's nic must still count towards the non-stopped-VR check, " + + "otherwise NetworkGarbageCollector will tear down a network with a live router", + Arrays.asList(statesCaptor.getValue()).contains(VirtualMachine.State.Running)); + } }