Skip to content

Commit f3a4263

Browse files
committed
hide the primary storage when a user copies the snapshots
1 parent d9bc950 commit f3a4263

File tree

16 files changed

+118
-32
lines changed

16 files changed

+118
-32
lines changed

api/src/main/java/com/cloud/storage/VolumeApiService.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,10 +113,10 @@ public interface VolumeApiService {
113113

114114
Volume detachVolumeFromVM(DetachVolumeCmd cmd);
115115

116-
Snapshot takeSnapshot(Long volumeId, Long policyId, Long snapshotId, Account account, boolean quiescevm, Snapshot.LocationType locationType, boolean asyncBackup, Map<String, String> tags, List<Long> zoneIds, List<Long> poolIds)
116+
Snapshot takeSnapshot(Long volumeId, Long policyId, Long snapshotId, Account account, boolean quiescevm, Snapshot.LocationType locationType, boolean asyncBackup, Map<String, String> tags, List<Long> zoneIds, List<Long> poolIds, Boolean useStorageReplication)
117117
throws ResourceAllocationException;
118118

119-
Snapshot allocSnapshot(Long volumeId, Long policyId, String snapshotName, Snapshot.LocationType locationType, List<Long> zoneIds, List<Long> poolIds) throws ResourceAllocationException;
119+
Snapshot allocSnapshot(Long volumeId, Long policyId, String snapshotName, Snapshot.LocationType locationType, List<Long> zoneIds, Boolean useStorageReplication) throws ResourceAllocationException;
120120

121121
Volume updateVolume(long volumeId, String path, String state, Long storageId,
122122
Boolean displayVolume, Boolean deleteProtection,

api/src/main/java/org/apache/cloudstack/api/ApiConstants.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,9 @@ public class ApiConstants {
473473
public static final String SNAPSHOT_POLICY_ID = "snapshotpolicyid";
474474
public static final String SNAPSHOT_TYPE = "snapshottype";
475475
public static final String SNAPSHOT_QUIESCEVM = "quiescevm";
476+
477+
public static final String USE_STORAGE_REPLICATION = "usestoragereplication";
478+
476479
public static final String SOURCE_CIDR_LIST = "sourcecidrlist";
477480
public static final String SOURCE_ZONE_ID = "sourcezoneid";
478481
public static final String SSL_VERIFICATION = "sslverification";

api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/CopySnapshotCmd.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,10 +90,14 @@ public class CopySnapshotCmd extends BaseAsyncCmd implements UserCmd {
9090
collectionType = CommandType.UUID,
9191
entityType = StoragePoolResponse.class,
9292
required = false,
93+
authorized = RoleType.Admin,
9394
description = "A comma-separated list of IDs of the storage pools in other zones in which the snapshot will be made available. " +
9495
"The snapshot will always be made available in the zone in which the volume is present. Currently supported for StorPool only")
9596
protected List<Long> storagePoolIds;
9697

98+
@Parameter (name = ApiConstants.USE_STORAGE_REPLICATION, type=CommandType.BOOLEAN, required = false, description = "This parameter enables the option the snapshot to be copied to supported primary storage")
99+
protected Boolean useStorageReplication;
100+
97101
/////////////////////////////////////////////////////
98102
/////////////////// Accessors ///////////////////////
99103
/////////////////////////////////////////////////////
@@ -123,6 +127,13 @@ public List<Long> getStoragePoolIds() {
123127
return storagePoolIds;
124128
}
125129

130+
public Boolean useStorageReplication() {
131+
if (useStorageReplication == null) {
132+
return false;
133+
}
134+
return useStorageReplication;
135+
}
136+
126137
@Override
127138
public String getEventType() {
128139
return EventTypes.EVENT_SNAPSHOT_COPY;
@@ -166,7 +177,7 @@ public long getEntityOwnerId() {
166177
@Override
167178
public void execute() throws ResourceUnavailableException {
168179
try {
169-
if (destZoneId == null && CollectionUtils.isEmpty(destZoneIds) && CollectionUtils.isEmpty(storagePoolIds))
180+
if (destZoneId == null && CollectionUtils.isEmpty(destZoneIds) && useStorageReplication())
170181
throw new ServerApiException(ApiErrorCode.PARAM_ERROR,
171182
"Either destzoneid or destzoneids parameters have to be specified.");
172183

api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/CreateSnapshotCmd.java

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.util.List;
2222
import java.util.Map;
2323

24+
import org.apache.cloudstack.acl.RoleType;
2425
import org.apache.cloudstack.api.APICommand;
2526
import org.apache.cloudstack.api.ApiCommandResourceType;
2627
import org.apache.cloudstack.api.ApiConstants;
@@ -104,11 +105,15 @@ public class CreateSnapshotCmd extends BaseAsyncCreateCmd {
104105
type=CommandType.LIST,
105106
collectionType = CommandType.UUID,
106107
entityType = StoragePoolResponse.class,
108+
authorized = RoleType.Admin,
107109
description = "A comma-separated list of IDs of the storage pools in other zones in which the snapshot will be made available. " +
108110
"The snapshot will always be made available in the zone in which the volume is present.",
109111
since = "4.20.0")
110112
protected List<Long> storagePoolIds;
111113

114+
@Parameter (name = ApiConstants.USE_STORAGE_REPLICATION, type=CommandType.BOOLEAN, required = false, description = "This parameter enables the option the snapshot to be copied to supported primary storage")
115+
protected Boolean useStorageReplication;
116+
112117
private String syncObjectType = BaseAsyncCmd.snapshotHostSyncObject;
113118

114119
// ///////////////////////////////////////////////////
@@ -175,6 +180,13 @@ public List<Long> getStoragePoolIds() {
175180
return storagePoolIds;
176181
}
177182

183+
public Boolean useStorageReplication() {
184+
if (useStorageReplication == null) {
185+
return false;
186+
}
187+
return useStorageReplication;
188+
}
189+
178190
// ///////////////////////////////////////////////////
179191
// ///////////// API Implementation///////////////////
180192
// ///////////////////////////////////////////////////
@@ -223,7 +235,7 @@ public ApiCommandResourceType getApiResourceType() {
223235

224236
@Override
225237
public void create() throws ResourceAllocationException {
226-
Snapshot snapshot = _volumeService.allocSnapshot(getVolumeId(), getPolicyId(), getSnapshotName(), getLocationType(), getZoneIds(), getStoragePoolIds());
238+
Snapshot snapshot = _volumeService.allocSnapshot(getVolumeId(), getPolicyId(), getSnapshotName(), getLocationType(), getZoneIds(), useStorageReplication());
227239
if (snapshot != null) {
228240
setEntityId(snapshot.getId());
229241
setEntityUuid(snapshot.getUuid());
@@ -237,7 +249,7 @@ public void execute() {
237249
Snapshot snapshot;
238250
try {
239251
snapshot =
240-
_volumeService.takeSnapshot(getVolumeId(), getPolicyId(), getEntityId(), _accountService.getAccount(getEntityOwnerId()), getQuiescevm(), getLocationType(), getAsyncBackup(), getTags(), getZoneIds(), getStoragePoolIds());
252+
_volumeService.takeSnapshot(getVolumeId(), getPolicyId(), getEntityId(), _accountService.getAccount(getEntityOwnerId()), getQuiescevm(), getLocationType(), getAsyncBackup(), getTags(), getZoneIds(), getStoragePoolIds(), useStorageReplication());
241253

242254
if (snapshot != null) {
243255
SnapshotResponse response = _responseGenerator.createSnapshotResponse(snapshot);
@@ -257,7 +269,7 @@ public void execute() {
257269
}
258270
}
259271

260-
private Snapshot.LocationType getLocationType() {
272+
public Snapshot.LocationType getLocationType() {
261273

262274
if (Snapshot.LocationType.values() == null || Snapshot.LocationType.values().length == 0 || locationType == null) {
263275
return null;

api/src/test/java/org/apache/cloudstack/api/command/test/CreateSnapshotCmdTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ public void testCreateSuccess() {
9393
Snapshot snapshot = Mockito.mock(Snapshot.class);
9494
try {
9595
Mockito.when(volumeApiService.takeSnapshot(nullable(Long.class), nullable(Long.class), isNull(),
96-
nullable(Account.class), nullable(Boolean.class), nullable(Snapshot.LocationType.class), nullable(Boolean.class), nullable(Map.class), nullable(List.class), nullable(List.class))).thenReturn(snapshot);
96+
nullable(Account.class), nullable(Boolean.class), nullable(Snapshot.LocationType.class), nullable(Boolean.class), nullable(Map.class), nullable(List.class), nullable(List.class), Mockito.anyBoolean())).thenReturn(snapshot);
9797

9898
} catch (Exception e) {
9999
Assert.fail("Received exception when success expected " + e.getMessage());
@@ -126,7 +126,7 @@ public void testCreateFailure() {
126126

127127
try {
128128
Mockito.when(volumeApiService.takeSnapshot(nullable(Long.class), nullable(Long.class), nullable(Long.class),
129-
nullable(Account.class), nullable(Boolean.class), nullable(Snapshot.LocationType.class), nullable(Boolean.class), any(), Mockito.anyList(), Mockito.anyList())).thenReturn(null);
129+
nullable(Account.class), nullable(Boolean.class), nullable(Snapshot.LocationType.class), nullable(Boolean.class), any(), Mockito.anyList(), Mockito.anyList(), Mockito.anyBoolean())).thenReturn(null);
130130
} catch (Exception e) {
131131
Assert.fail("Received exception when success expected " + e.getMessage());
132132
}

api/src/test/java/org/apache/cloudstack/api/command/user/snapshot/CopySnapshotCmdTest.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,12 @@ public void testGetDestZoneIdWithBothParams() {
8787

8888
@Test (expected = ServerApiException.class)
8989
public void testExecuteWrongNoParams() {
90+
UUIDManager uuidManager = Mockito.mock(UUIDManager.class);
91+
SnapshotApiService snapshotApiService = Mockito.mock(SnapshotApiService.class);
9092
final CopySnapshotCmd cmd = new CopySnapshotCmd();
93+
cmd._uuidMgr = uuidManager;
94+
cmd._snapshotService = snapshotApiService;
95+
9196
try {
9297
cmd.execute();
9398
} catch (ResourceUnavailableException e) {

engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDao.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,4 +154,7 @@ Pair<List<Long>, Integer> searchForIdsAndCount(Long storagePoolId, String storag
154154
String keyword, Filter searchFilter);
155155

156156
List<StoragePoolVO> listByIds(List<Long> ids);
157+
158+
List<StoragePoolVO> findPoolsByStorageTypeAndZone(Storage.StoragePoolType storageType, Long zoneId);
159+
157160
}

engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDaoImpl.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -733,6 +733,14 @@ public List<StoragePoolVO> listByIds(List<Long> ids) {
733733
return listBy(sc);
734734
}
735735

736+
@Override
737+
public List<StoragePoolVO> findPoolsByStorageTypeAndZone(Storage.StoragePoolType storageType, Long zoneId) {
738+
SearchCriteria<StoragePoolVO> sc = AllFieldSearch.create();
739+
sc.setParameters("poolType", storageType);
740+
sc.addAnd("dataCenterId", Op.EQ, zoneId);
741+
return listBy(sc);
742+
}
743+
736744
private SearchCriteria<StoragePoolVO> createStoragePoolSearchCriteria(Long storagePoolId, String storagePoolName,
737745
Long zoneId, String path, Long podId, Long clusterId, String address, ScopeType scopeType,
738746
StoragePoolStatus status, String keyword) {

server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@
7878
import com.cloud.template.TemplateManager;
7979
import com.cloud.user.Account;
8080
import com.cloud.user.AccountManager;
81+
import com.cloud.user.AccountService;
8182
import com.cloud.user.ResourceLimitService;
8283
import com.cloud.user.User;
8384
import com.cloud.user.VmDiskStatisticsVO;
@@ -362,6 +363,9 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
362363
@Inject
363364
HostPodDao podDao;
364365

366+
@Inject
367+
public AccountService _accountService;
368+
365369
protected Gson _gson;
366370

367371
private static final List<HypervisorType> SupportedHypervisorsForVolResize = Arrays.asList(HypervisorType.KVM, HypervisorType.XenServer,
@@ -3759,20 +3763,23 @@ protected Volume liveMigrateVolume(Volume volume, StoragePool destPool) throws S
37593763
@Override
37603764
@ActionEvent(eventType = EventTypes.EVENT_SNAPSHOT_CREATE, eventDescription = "taking snapshot", async = true)
37613765
public Snapshot takeSnapshot(Long volumeId, Long policyId, Long snapshotId, Account account, boolean quiescevm,
3762-
Snapshot.LocationType locationType, boolean asyncBackup, Map<String, String> tags, List<Long> zoneIds, List<Long> poolIds)
3763-
throws ResourceAllocationException {
3764-
final Snapshot snapshot = takeSnapshotInternal(volumeId, policyId, snapshotId, account, quiescevm, locationType, asyncBackup, zoneIds, poolIds);
3766+
Snapshot.LocationType locationType, boolean asyncBackup, Map<String, String> tags, List<Long> zoneIds, List<Long> poolIds, Boolean useStorageReplication)
3767+
3768+
throws ResourceAllocationException {
3769+
final Snapshot snapshot = takeSnapshotInternal(volumeId, policyId, snapshotId, account, quiescevm, locationType, asyncBackup, zoneIds, poolIds, useStorageReplication);
37653770
if (snapshot != null && MapUtils.isNotEmpty(tags)) {
37663771
taggedResourceService.createTags(Collections.singletonList(snapshot.getUuid()), ResourceTag.ResourceObjectType.Snapshot, tags, null);
37673772
}
37683773
return snapshot;
37693774
}
37703775

37713776
private Snapshot takeSnapshotInternal(Long volumeId, Long policyId, Long snapshotId, Account account,
3772-
boolean quiescevm, Snapshot.LocationType locationType, boolean asyncBackup, List<Long> zoneIds, List<Long> poolIds)
3777+
boolean quiescevm, Snapshot.LocationType locationType, boolean asyncBackup, List<Long> zoneIds, List<Long> poolIds, Boolean useStorageReplication)
37733778
throws ResourceAllocationException {
37743779
Account caller = CallContext.current().getCallingAccount();
37753780
VolumeInfo volume = volFactory.getVolume(volumeId);
3781+
poolIds = snapshotHelper.addStoragePoolsForCopyToPrimary(volume, zoneIds, poolIds, useStorageReplication);
3782+
canCopyOnPrimary(poolIds, volume,CollectionUtils.isEmpty(poolIds));
37763783
if (volume == null) {
37773784
throw new InvalidParameterValueException("Creating snapshot failed due to volume:" + volumeId + " doesn't exist");
37783785
}
@@ -3929,7 +3936,7 @@ private boolean isOperationSupported(VMTemplateVO template, UserVmVO userVm) {
39293936

39303937
@Override
39313938
@ActionEvent(eventType = EventTypes.EVENT_SNAPSHOT_CREATE, eventDescription = "allocating snapshot", create = true)
3932-
public Snapshot allocSnapshot(Long volumeId, Long policyId, String snapshotName, Snapshot.LocationType locationType, List<Long> zoneIds, List<Long> poolIds) throws ResourceAllocationException {
3939+
public Snapshot allocSnapshot(Long volumeId, Long policyId, String snapshotName, Snapshot.LocationType locationType, List<Long> zoneIds, Boolean useStorageReplication) throws ResourceAllocationException {
39333940
Account caller = CallContext.current().getCallingAccount();
39343941

39353942
VolumeInfo volume = volFactory.getVolume(volumeId);
@@ -3978,7 +3985,7 @@ public Snapshot allocSnapshot(Long volumeId, Long policyId, String snapshotName,
39783985
if (storagePool == null) {
39793986
throw new InvalidParameterValueException(String.format("Volume: %s please attach this volume to a VM before create snapshot for it", volume.getVolume()));
39803987
}
3981-
boolean canCopyOnPrimary = canCopyOnPrimary(poolIds, volume, CollectionUtils.isEmpty(poolIds));
3988+
boolean canCopyOnPrimary = useStorageReplication;
39823989

39833990
if (CollectionUtils.isNotEmpty(zoneIds)) {
39843991
if (policyId != null && policyId > 0) {

server/src/main/java/com/cloud/storage/snapshot/SnapshotManager.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ public interface SnapshotManager extends Configurable {
5959

6060
public static final ConfigKey<Boolean> VmStorageSnapshotKvm = new ConfigKey<>(Boolean.class, "kvm.vmstoragesnapshot.enabled", "Snapshots", "false", "For live snapshot of virtual machine instance on KVM hypervisor without memory. Requires qemu version 1.6+ (on NFS or Local file system) and qemu-guest-agent installed on guest VM", true, ConfigKey.Scope.Global, null);
6161

62+
public static final ConfigKey<Boolean> UseStorageReplication = new ConfigKey<Boolean>(Boolean.class, "use.storage.replication", "Snapshots", "false", "For snapshot copy to another primary storage in a different zone. Supports only StorPool storage for now", true, ConfigKey.Scope.StoragePool, null);
6263
void deletePoliciesForVolume(Long volumeId);
6364

6465
/**

0 commit comments

Comments
 (0)