diff --git a/server/src/main/java/com/cloud/storage/snapshot/SnapshotManagerImpl.java b/server/src/main/java/com/cloud/storage/snapshot/SnapshotManagerImpl.java index 6ce7a0f92bfb..6555ce47f94b 100755 --- a/server/src/main/java/com/cloud/storage/snapshot/SnapshotManagerImpl.java +++ b/server/src/main/java/com/cloud/storage/snapshot/SnapshotManagerImpl.java @@ -1820,7 +1820,7 @@ private void copyNewSnapshotToZonesOnPrimary(CreateSnapshotPayload payload, Snap } } - private boolean copySnapshotOnPool(SnapshotInfo snapshot, SnapshotStrategy snapshotStrategy, Long storagePoolId) { + boolean copySnapshotOnPool(SnapshotInfo snapshot, SnapshotStrategy snapshotStrategy, Long storagePoolId) { DataStore store = dataStoreMgr.getDataStore(storagePoolId, DataStoreRole.Primary); SnapshotInfo snapshotOnStore = (SnapshotInfo) store.create(snapshot); @@ -1832,10 +1832,10 @@ private boolean copySnapshotOnPool(SnapshotInfo snapshot, SnapshotStrategy snaps return false; } snapshotZoneDao.addSnapshotToZone(snapshot.getId(), snapshotOnStore.getDataCenterId()); - _resourceLimitMgr.incrementResourceCount(CallContext.current().getCallingUserId(), ResourceType.primary_storage, snapshot.getSize()); - if (CallContext.current().getCallingUserId() != Account.ACCOUNT_ID_SYSTEM) { + _resourceLimitMgr.incrementResourceCount(snapshot.getAccountId(), ResourceType.primary_storage, snapshot.getSize()); + if (CallContext.current().getCallingAccountId() != Account.ACCOUNT_ID_SYSTEM) { SnapshotVO snapshotVO = _snapshotDao.findByIdIncludingRemoved(snapshot.getSnapshotId()); - UsageEventUtils.publishUsageEvent(EventTypes.EVENT_SNAPSHOT_COPY, CallContext.current().getCallingAccountId(), snapshotOnStore.getDataCenterId(), snapshotVO.getId(), null, null, null, snapshotVO.getSize(), + UsageEventUtils.publishUsageEvent(EventTypes.EVENT_SNAPSHOT_COPY, snapshot.getAccountId(), snapshotOnStore.getDataCenterId(), snapshotVO.getId(), null, null, null, snapshotVO.getSize(), snapshotVO.getSize(), snapshotVO.getClass().getName(), snapshotVO.getUuid()); } } catch (InterruptedException | ExecutionException e) { diff --git a/server/src/test/java/com/cloud/storage/snapshot/SnapshotManagerImplTest.java b/server/src/test/java/com/cloud/storage/snapshot/SnapshotManagerImplTest.java index 3cd7db8ed996..6fc97b247e98 100644 --- a/server/src/test/java/com/cloud/storage/snapshot/SnapshotManagerImplTest.java +++ b/server/src/test/java/com/cloud/storage/snapshot/SnapshotManagerImplTest.java @@ -26,6 +26,7 @@ import com.cloud.exception.ResourceUnavailableException; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.org.Grouping; +import com.cloud.configuration.Resource.ResourceType; import com.cloud.storage.DataStoreRole; import com.cloud.storage.ScopeType; import com.cloud.storage.Snapshot; @@ -59,6 +60,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotResult; import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotService; +import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotStrategy; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; import org.apache.cloudstack.framework.async.AsyncCallFuture; @@ -822,4 +824,41 @@ public void testEndCurrentChainIfNeededStopsOnMissingParentRef() { Mockito.verify(snapshotStoreDao, Mockito.never()).update(Mockito.anyLong(), Mockito.any(SnapshotDataStoreVO.class)); } + + @Test + @SuppressWarnings("unchecked") + public void copySnapshotOnPoolChargesPrimaryStorageToTheSnapshotOwnerAccount() throws Exception { + long ownerAccountId = 42L; + long snapshotSize = 1024L; + Long poolId = 5L; + + SnapshotInfo snapshot = Mockito.mock(SnapshotInfo.class); + SnapshotInfo snapshotOnStore = Mockito.mock(SnapshotInfo.class); + SnapshotStrategy strategy = Mockito.mock(SnapshotStrategy.class); + DataStore store = Mockito.mock(DataStore.class); + AsyncCallFuture future = Mockito.mock(AsyncCallFuture.class); + SnapshotResult result = Mockito.mock(SnapshotResult.class); + + Mockito.when(snapshot.getId()).thenReturn(100L); + Mockito.when(snapshot.getSize()).thenReturn(snapshotSize); + Mockito.when(snapshot.getAccountId()).thenReturn(ownerAccountId); + Mockito.when(dataStoreManager.getDataStore(poolId, DataStoreRole.Primary)).thenReturn(store); + Mockito.when(store.create(snapshot)).thenReturn(snapshotOnStore); + Mockito.when(snapshotOnStore.getDataCenterId()).thenReturn(2L); + Mockito.when(snapshotService.copySnapshot(snapshot, snapshotOnStore, strategy)).thenReturn(future); + Mockito.when(future.get()).thenReturn(result); + Mockito.when(result.isFailed()).thenReturn(false); + + try (MockedStatic ctxMock = Mockito.mockStatic(CallContext.class)) { + CallContext ctx = Mockito.mock(CallContext.class); + ctxMock.when(CallContext::current).thenReturn(ctx); + // A SYSTEM calling account skips the usage-event branch; this test only covers which account is charged. + Mockito.when(ctx.getCallingAccountId()).thenReturn(Account.ACCOUNT_ID_SYSTEM); + + snapshotManager.copySnapshotOnPool(snapshot, strategy, poolId); + } + + Mockito.verify(resourceLimitService).incrementResourceCount( + Mockito.eq(ownerAccountId), Mockito.eq(ResourceType.primary_storage), Mockito.eq(snapshotSize)); + } }