Skip to content

Commit 710aab3

Browse files
authored
Merge pull request #688 from Dajeong-Park/backup
[Mold] Commvault 백업 플러그인 추가
2 parents eb2d9ab + 4bda1ba commit 710aab3

File tree

45 files changed

+4905
-30749
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+4905
-30749
lines changed

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,9 @@ public interface VolumeApiService {
117117
Snapshot takeSnapshot(Long volumeId, Long policyId, Long snapshotId, Account account, boolean quiescevm, Snapshot.LocationType locationType, boolean asyncBackup, Map<String, String> tags, List<Long> zoneIds)
118118
throws ResourceAllocationException;
119119

120+
Snapshot takeSnapshot(Long volumeId, Long policyId, Long snapshotId, Account account, boolean quiescevm, Snapshot.LocationType locationType, boolean asyncBackup, Map<String, String> tags, List<Long> zoneIds, boolean backup)
121+
throws ResourceAllocationException;
122+
120123
Snapshot allocSnapshot(Long volumeId, Long policyId, String snapshotName, Snapshot.LocationType locationType, List<Long> zoneIds) throws ResourceAllocationException;
121124

122125
Volume updateVolume(long volumeId, String path, String state, Long storageId, Boolean displayVolume, Boolean deleteProtection, String customId, long owner, String chainInfo, String name, String type);

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ public class ApiConstants {
5151
public static final String AVAILABLE = "available";
5252
public static final String AVAILABLE_SUBNETS = "availablesubnets";
5353
public static final String AVAILABLE_VIRTUAL_MACHINE_COUNT = "availablevirtualmachinecount";
54+
public static final String BACKUP = "backup";
5455
public static final String BACKUP_ID = "backupid";
5556
public static final String BACKUP_OFFERING_NAME = "backupofferingname";
5657
public static final String BACKUP_OFFERING_ID = "backupofferingid";
@@ -836,6 +837,7 @@ public class ApiConstants {
836837
public static final String DEST_NETWORK_UUID = "destnetworkuuid";
837838
public static final String LOGICAL_ROUTER_UUID = "logicalrouteruuid";
838839
public static final String RETRY = "retry";
840+
public static final String RETENTION_PERIOD = "retentionperiod";
839841
public static final String HTTP_METHOD = "httpmethodtype";
840842
public static final String EXPECTED_CODE = "expectedcode";
841843
public static final String URL_PATH = "urlpath";
@@ -1309,11 +1311,10 @@ public class ApiConstants {
13091311
public static final String ISSUED_DATE = "issueddate";
13101312
public static final String XML_CONFIG = "xmlconfig";
13111313
public static final String CURRENT_VM_ID = "currentvmid";
1312-
13131314
public static final String RESULT_REDFISH_DATA = "redfishdata";
13141315
public static final String EXTERNAL_ENTITY = "externalEntity";
13151316
public static final String MIGRATION_IP = "migrationip";
1316-
1317+
public static final String VM_SNAPSHOT_NONCOPY = "noncopy";
13171318

13181319
/**
13191320
* This enum specifies IO Drivers, each option controls specific policies on I/O.

api/src/main/java/org/apache/cloudstack/api/command/admin/backup/ImportBackupOfferingCmd.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,10 @@ public class ImportBackupOfferingCmd extends BaseAsyncCmd {
7676
description = "Whether users are allowed to create adhoc backups and backup schedules", required = true)
7777
private Boolean userDrivenBackups;
7878

79+
@Parameter(name = ApiConstants.RETENTION_PERIOD, type = CommandType.STRING, required = false,
80+
description = "Retention period of backup data")
81+
private String retentionPeriod;
82+
7983
/////////////////////////////////////////////////////
8084
/////////////////// Accessors ///////////////////////
8185
/////////////////////////////////////////////////////
@@ -100,6 +104,10 @@ public Boolean getUserDrivenBackups() {
100104
return userDrivenBackups == null ? false : userDrivenBackups;
101105
}
102106

107+
public String getRetentionPeriod() {
108+
return retentionPeriod;
109+
}
110+
103111
/////////////////////////////////////////////////////
104112
/////////////// API Implementation///////////////////
105113
/////////////////////////////////////////////////////

api/src/main/java/org/apache/cloudstack/api/command/admin/backup/UpdateBackupOfferingCmd.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ public class UpdateBackupOfferingCmd extends BaseCmd {
5757
@Parameter(name = ApiConstants.ALLOW_USER_DRIVEN_BACKUPS, type = CommandType.BOOLEAN, description = "Whether to allow user driven backups or not")
5858
private Boolean allowUserDrivenBackups;
5959

60+
@Parameter(name = ApiConstants.RETENTION_PERIOD, type = CommandType.STRING, required = false, description = "Retention period of backup data")
61+
private String retentionPeriod;
62+
6063
/////////////////////////////////////////////////////
6164
/////////////////// Accessors ///////////////////////
6265
/////////////////////////////////////////////////////
@@ -76,6 +79,10 @@ public Boolean getAllowUserDrivenBackups() {
7679
return allowUserDrivenBackups;
7780
}
7881

82+
public String getRetentionPeriod() {
83+
return retentionPeriod;
84+
}
85+
7986
/////////////////////////////////////////////////////
8087
/////////////// API Implementation///////////////////
8188
/////////////////////////////////////////////////////
@@ -91,7 +98,7 @@ public void execute() {
9198

9299
if (result == null) {
93100
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Failed to update backup offering %s.",
94-
ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "id", "name", "description", "allowUserDrivenBackups")));
101+
ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "id", "name", "description", "allowUserDrivenBackups", "retentionPeriod")));
95102
}
96103
BackupOfferingResponse response = _responseGenerator.createBackupOfferingResponse(result);
97104
response.setResponseName(getCommandName());
Lines changed: 297 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,297 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
package org.apache.cloudstack.api.command.user.snapshot;
18+
19+
import java.util.Collection;
20+
import java.util.HashMap;
21+
import java.util.List;
22+
import java.util.Map;
23+
24+
import org.apache.cloudstack.api.APICommand;
25+
import org.apache.cloudstack.api.ApiCommandResourceType;
26+
import org.apache.cloudstack.api.ApiConstants;
27+
import org.apache.cloudstack.api.ApiErrorCode;
28+
import org.apache.cloudstack.api.BaseAsyncCmd;
29+
import org.apache.cloudstack.api.BaseAsyncCreateCmd;
30+
import org.apache.cloudstack.api.Parameter;
31+
import org.apache.cloudstack.api.ServerApiException;
32+
import org.apache.cloudstack.api.response.DomainResponse;
33+
import org.apache.cloudstack.api.response.SnapshotPolicyResponse;
34+
import org.apache.cloudstack.api.response.SnapshotResponse;
35+
import org.apache.cloudstack.api.response.VolumeResponse;
36+
import org.apache.cloudstack.api.response.ZoneResponse;
37+
import org.apache.commons.collections.MapUtils;
38+
39+
import com.cloud.event.EventTypes;
40+
import com.cloud.exception.InvalidParameterValueException;
41+
import com.cloud.exception.PermissionDeniedException;
42+
import com.cloud.exception.ResourceAllocationException;
43+
import com.cloud.projects.Project;
44+
import com.cloud.storage.Snapshot;
45+
import com.cloud.storage.Volume;
46+
import com.cloud.user.Account;
47+
import com.cloud.utils.exception.CloudRuntimeException;
48+
49+
@APICommand(name = "createSnapshotBackup", description = "Creates an instant snapshot of a volume for backup.", responseObject = SnapshotResponse.class, entityType = {Snapshot.class},
50+
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
51+
public class CreateSnapshotBackupCmd extends BaseAsyncCreateCmd {
52+
53+
// ///////////////////////////////////////////////////
54+
// ////////////// API parameters /////////////////////
55+
// ///////////////////////////////////////////////////
56+
57+
@Parameter(name = ApiConstants.ACCOUNT,
58+
type = CommandType.STRING,
59+
description = "The account of the snapshot. The account parameter must be used with the domainId parameter.")
60+
private String accountName;
61+
62+
@Parameter(name = ApiConstants.DOMAIN_ID,
63+
type = CommandType.UUID,
64+
entityType = DomainResponse.class,
65+
description = "The domain ID of the snapshot. If used with the account parameter, specifies a domain for the account associated with the disk volume. If account is NOT provided then snapshot will be assigned to the caller account and domain.")
66+
private Long domainId;
67+
68+
@Parameter(name = ApiConstants.VOLUME_ID, type = CommandType.UUID, entityType = VolumeResponse.class, required = true, description = "The ID of the disk volume")
69+
private Long volumeId;
70+
71+
@Parameter(name = ApiConstants.POLICY_ID,
72+
type = CommandType.UUID,
73+
entityType = SnapshotPolicyResponse.class,
74+
description = "policy id of the snapshot, if this is null, then use MANUAL_POLICY.")
75+
private Long policyId;
76+
77+
@Parameter(name = ApiConstants.SNAPSHOT_QUIESCEVM, type = CommandType.BOOLEAN, required = false, description = "quiesce vm if true")
78+
private Boolean quiescevm;
79+
80+
@Parameter(name = ApiConstants.LOCATION_TYPE, type = CommandType.STRING, required = false, description = "Currently applicable only for managed storage. " +
81+
"Valid location types: 'primary', 'secondary'. Default = 'primary'.")
82+
private String locationType;
83+
84+
@Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "the name of the snapshot")
85+
private String snapshotName;
86+
87+
@Parameter(name = ApiConstants.ASYNC_BACKUP, type = CommandType.BOOLEAN, required = false, description = "asynchronous backup if true")
88+
private Boolean asyncBackup;
89+
90+
@Parameter(name = ApiConstants.TAGS, type = CommandType.MAP, description = "Map of tags (key/value pairs)")
91+
private Map tags;
92+
93+
@Parameter(name = ApiConstants.BACKUP, type = CommandType.BOOLEAN, required = true, description = "Skip secondary storage copy for backup solution use")
94+
private Boolean backup;
95+
96+
@Parameter(name = ApiConstants.ZONE_ID_LIST,
97+
type=CommandType.LIST,
98+
collectionType = CommandType.UUID,
99+
entityType = ZoneResponse.class,
100+
description = "A comma-separated list of IDs of the zones in which the snapshot will be made available. " +
101+
"The snapshot will always be made available in the zone in which the volume is present.",
102+
since = "4.19.0")
103+
protected List<Long> zoneIds;
104+
105+
private String syncObjectType = BaseAsyncCmd.snapshotHostSyncObject;
106+
107+
// ///////////////////////////////////////////////////
108+
// ///////////////// Accessors ///////////////////////
109+
// ///////////////////////////////////////////////////
110+
111+
public Boolean getQuiescevm() {
112+
if (quiescevm == null) {
113+
return false;
114+
} else {
115+
return quiescevm;
116+
}
117+
}
118+
119+
public String getAccountName() {
120+
return accountName;
121+
}
122+
123+
public Long getDomainId() {
124+
return domainId;
125+
}
126+
127+
public Long getVolumeId() {
128+
return volumeId;
129+
}
130+
131+
public String getSnapshotName() {
132+
return snapshotName;
133+
}
134+
135+
public Long getPolicyId() {
136+
if (policyId != null) {
137+
return policyId;
138+
} else {
139+
return Snapshot.MANUAL_POLICY_ID;
140+
}
141+
}
142+
143+
public Map<String, String> getTags() {
144+
Map<String, String> tagsMap = new HashMap<>();
145+
if (MapUtils.isNotEmpty(tags)) {
146+
for (Map<String, String> services : (Collection<Map<String, String>>)tags.values()) {
147+
String key = services.get("key");
148+
String value = services.get("value");
149+
tagsMap.put(key, value);
150+
}
151+
}
152+
return tagsMap;
153+
}
154+
155+
public Boolean getBackup() {
156+
return backup;
157+
}
158+
159+
private Long getHostId() {
160+
Volume volume = _entityMgr.findById(Volume.class, getVolumeId());
161+
if (volume == null) {
162+
throw new InvalidParameterValueException("Unable to find volume by id");
163+
}
164+
return _snapshotService.getHostIdForSnapshotOperation(volume);
165+
}
166+
167+
public List<Long> getZoneIds() {
168+
return zoneIds;
169+
}
170+
171+
// ///////////////////////////////////////////////////
172+
// ///////////// API Implementation///////////////////
173+
// ///////////////////////////////////////////////////
174+
175+
public static String getResultObjectName() {
176+
return ApiConstants.SNAPSHOT;
177+
}
178+
179+
@Override
180+
public long getEntityOwnerId() {
181+
182+
Volume volume = _entityMgr.findById(Volume.class, getVolumeId());
183+
if (volume == null) {
184+
throw new InvalidParameterValueException("Unable to find volume by id=" + volumeId);
185+
}
186+
187+
Account account = _accountService.getAccount(volume.getAccountId());
188+
//Can create templates for enabled projects/accounts only
189+
if (account.getType() == Account.Type.PROJECT) {
190+
Project project = _projectService.findByProjectAccountId(volume.getAccountId());
191+
if (project.getState() != Project.State.Active) {
192+
throw new PermissionDeniedException("Can't add resources to the project id=" + project.getId() + " in state=" + project.getState() +
193+
" as it's no longer active");
194+
}
195+
} else if (account.getState() == Account.State.DISABLED) {
196+
throw new PermissionDeniedException("The owner of template is disabled: " + account);
197+
}
198+
199+
return volume.getAccountId();
200+
}
201+
202+
@Override
203+
public String getEventType() {
204+
return EventTypes.EVENT_SNAPSHOT_CREATE;
205+
}
206+
207+
@Override
208+
public String getEventDescription() {
209+
return "creating snapshot for volume: " + getVolumeUuid();
210+
}
211+
212+
@Override
213+
public ApiCommandResourceType getApiResourceType() {
214+
return ApiCommandResourceType.Snapshot;
215+
}
216+
217+
@Override
218+
public void create() throws ResourceAllocationException {
219+
Snapshot snapshot = _volumeService.allocSnapshot(getVolumeId(), getPolicyId(), getSnapshotName(), getLocationType(), getZoneIds());
220+
if (snapshot != null) {
221+
setEntityId(snapshot.getId());
222+
setEntityUuid(snapshot.getUuid());
223+
} else {
224+
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create snapshot for volume" + getVolumeUuid());
225+
}
226+
}
227+
228+
@Override
229+
public void execute() {
230+
Snapshot snapshot;
231+
try {
232+
snapshot =
233+
_volumeService.takeSnapshot(getVolumeId(), getPolicyId(), getEntityId(), _accountService.getAccount(getEntityOwnerId()), getQuiescevm(), getLocationType(), getAsyncBackup(), getTags(), getZoneIds(), getBackup());
234+
235+
if (snapshot != null) {
236+
SnapshotResponse response = _responseGenerator.createSnapshotResponse(snapshot);
237+
response.setResponseName(getCommandName());
238+
setResponseObject(response);
239+
} else {
240+
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Snapshot from volume [%s] was not found in database.", getVolumeUuid()));
241+
}
242+
} catch (Exception e) {
243+
if (e.getCause() instanceof UnsupportedOperationException) {
244+
throw new ServerApiException(ApiErrorCode.UNSUPPORTED_ACTION_ERROR, String.format("Failed to create snapshot due to unsupported operation: %s", e.getCause().getMessage()));
245+
}
246+
247+
String errorMessage = "Failed to create snapshot due to an internal error creating snapshot for volume " + getVolumeUuid();
248+
logger.error(errorMessage, e);
249+
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, errorMessage);
250+
}
251+
}
252+
253+
private Snapshot.LocationType getLocationType() {
254+
255+
if (Snapshot.LocationType.values() == null || Snapshot.LocationType.values().length == 0 || locationType == null) {
256+
return null;
257+
}
258+
259+
try {
260+
String lType = locationType.trim().toUpperCase();
261+
return Snapshot.LocationType.valueOf(lType);
262+
} catch (IllegalArgumentException e) {
263+
String errMesg = "Invalid locationType " + locationType + "Specified for volume " + getVolumeId()
264+
+ " Valid values are: primary,secondary ";
265+
logger.warn(errMesg);
266+
throw new CloudRuntimeException(errMesg);
267+
}
268+
}
269+
270+
@Override
271+
public String getSyncObjType() {
272+
if (getSyncObjId() != null) {
273+
return syncObjectType;
274+
}
275+
return null;
276+
}
277+
278+
@Override
279+
public Long getSyncObjId() {
280+
if (getHostId() != null) {
281+
return getHostId();
282+
}
283+
return null;
284+
}
285+
286+
public Boolean getAsyncBackup() {
287+
if (asyncBackup == null) {
288+
return false;
289+
} else {
290+
return asyncBackup;
291+
}
292+
}
293+
294+
protected String getVolumeUuid() {
295+
return _uuidMgr.getUuid(Volume.class, getVolumeId());
296+
}
297+
}

0 commit comments

Comments
 (0)