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 dc33a4442a33..d9eb589aea9a 100755 --- a/server/src/main/java/com/cloud/storage/snapshot/SnapshotManagerImpl.java +++ b/server/src/main/java/com/cloud/storage/snapshot/SnapshotManagerImpl.java @@ -1768,7 +1768,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); @@ -1780,10 +1780,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 4dc5a1f3bdfb..c2fcba36fe28 100644 --- a/server/src/test/java/com/cloud/storage/snapshot/SnapshotManagerImplTest.java +++ b/server/src/test/java/com/cloud/storage/snapshot/SnapshotManagerImplTest.java @@ -24,6 +24,7 @@ import com.cloud.exception.PermissionDeniedException; import com.cloud.exception.ResourceUnavailableException; import com.cloud.org.Grouping; +import com.cloud.configuration.Resource.ResourceType; import com.cloud.storage.DataStoreRole; import com.cloud.storage.Snapshot; import com.cloud.storage.Storage.StoragePoolType; @@ -55,6 +56,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.framework.async.AsyncCallFuture; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao; @@ -669,4 +671,41 @@ public void testRemoveClvmPrimarySnapshotStoreRefIfNeeded_NonClvmPool() { Mockito.verify(snapshotStoreDao, Mockito.never()).removeBySnapshotStore( Mockito.anyLong(), Mockito.anyLong(), Mockito.any()); } + + @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)); + } }