diff --git a/usage/src/main/java/com/cloud/usage/UsageManagerImpl.java b/usage/src/main/java/com/cloud/usage/UsageManagerImpl.java index 8d278056aa85..bed0ed944036 100644 --- a/usage/src/main/java/com/cloud/usage/UsageManagerImpl.java +++ b/usage/src/main/java/com/cloud/usage/UsageManagerImpl.java @@ -1126,6 +1126,26 @@ private void createVMHelperEvent(UsageEventVO event) { Long templateId = event.getTemplateId(); String hypervisorType = event.getResourceType(); + + + SearchCriteria runningSc = + _usageInstanceDao.createSearchCriteria(); +runningSc.addAnd("vmInstanceId", SearchCriteria.Op.EQ, vmId); +runningSc.addAnd("usageType", SearchCriteria.Op.EQ, UsageTypes.RUNNING_VM); +runningSc.addAnd("endDate", SearchCriteria.Op.NULL); + +List existingRunning = + _usageInstanceDao.search(runningSc, null); + +if (existingRunning != null && !existingRunning.isEmpty()) { + logger.warn(String.format( + "Duplicate VM.START event detected for VM [%d] at [%s], skipping helper record creation.", + vmId, event.getCreateDate())); + return; +} + + + // add this VM to the usage helper table UsageVMInstanceVO usageInstanceNew = new UsageVMInstanceVO(UsageTypes.RUNNING_VM, zoneId, event.getAccountId(), vmId, vmName, soId, templateId, hypervisorType, event.getCreateDate(), diff --git a/usage/src/test/java/com/cloud/usage/UsageManagerImplTest.java b/usage/src/test/java/com/cloud/usage/UsageManagerImplTest.java index 03939e430d25..555b3cee04de 100644 --- a/usage/src/test/java/com/cloud/usage/UsageManagerImplTest.java +++ b/usage/src/test/java/com/cloud/usage/UsageManagerImplTest.java @@ -36,6 +36,15 @@ import com.cloud.user.dao.AccountDao; import org.mockito.junit.MockitoJUnitRunner; + +import java.util.Date; + +import com.cloud.usage.dao.UsageVMInstanceDao; +import com.cloud.utils.db.SearchCriteria; + +import org.springframework.test.util.ReflectionTestUtils; + + @RunWith(MockitoJUnitRunner.class) public class UsageManagerImplTest { @@ -243,4 +252,41 @@ public void handleVMSnapshotEventTestEventIsNeitherAddNorRemove() { Mockito.verify(usageManagerImpl, Mockito.never()).createUsageVpnUser(usageEventVOMock,accountMock); Mockito.verify(usageManagerImpl, Mockito.never()).deleteUsageVpnUser(usageEventVOMock, accountMock); } + + + @Test + public void testDuplicateVmStartDoesNotCreateNewRunningUsage() { + + UsageVMInstanceDao usageInstanceDao = Mockito.mock(UsageVMInstanceDao.class); + ReflectionTestUtils.setField(usageManagerImpl, "_usageInstanceDao", usageInstanceDao); + + Mockito.doNothing() + .when(usageEventDetailsDao) + .persist(Mockito.any()); + + long vmId = 100L; + + UsageEventVO event = Mockito.mock(UsageEventVO.class); + Mockito.when(event.getType()).thenReturn(EventTypes.EVENT_VM_START); + Mockito.when(event.getResourceId()).thenReturn(vmId); + Mockito.when(event.getZoneId()).thenReturn(1L); + Mockito.when(event.getAccountId()).thenReturn(1L); + Mockito.when(event.getCreateDate()).thenReturn(new Date()); + + UsageVMInstanceVO existing = Mockito.mock(UsageVMInstanceVO.class); + List existingList = List.of(existing); + + SearchCriteria sc = Mockito.mock(SearchCriteria.class); + Mockito.when(usageInstanceDao.createSearchCriteria()).thenReturn(sc); + Mockito.when(usageInstanceDao.search(Mockito.any(), Mockito.isNull())) + .thenReturn(existingList); + + usageManagerImpl.handleEvent(event); + + Mockito.verify(usageInstanceDao, Mockito.never()) + .persist(Mockito.any(UsageVMInstanceVO.class)); + } + + + }