Skip to content

Commit 23c9e83

Browse files
authored
Create Instance from backup on another Zone (DRaaS use case) (#11560)
* draas initial changes * Added option to enable disaster recovery on a backup respository. Added UpdateBackupRepositoryCmd api. * Added timeout for mount operation in backup restore configurable via global setting * Addressed review comments * fix for simulator test failures * Added UT for coverage * Fix create instance from backup ui for other providers * Added events to add/update backup repository * Fix race in fetchZones * One more fix in fetchZones in DeployVMFromBackup.vue * Fix zone selection in createNetwork via Create Instance from backup form. * Allow template/iso selection in create instance from backup ui * rename draasenabled to crosszoneinstancecreation * Added Cross-zone instance creation in test_backup_recovery_nas.py * Added UT in BackupManagerTest and UserVmManagerImplTest * Integration test added for Cross-zone instance creation in test_backup_recovery_nas.py
1 parent b0c7719 commit 23c9e83

File tree

39 files changed

+1959
-204
lines changed

39 files changed

+1959
-204
lines changed

api/src/main/java/com/cloud/event/EventTypes.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.apache.cloudstack.api.response.HostResponse;
2828
import org.apache.cloudstack.api.response.PodResponse;
2929
import org.apache.cloudstack.api.response.ZoneResponse;
30+
import org.apache.cloudstack.backup.BackupRepositoryService;
3031
import org.apache.cloudstack.config.Configuration;
3132
import org.apache.cloudstack.datacenter.DataCenterIpv4GuestSubnet;
3233
import org.apache.cloudstack.extension.Extension;
@@ -852,6 +853,10 @@ public class EventTypes {
852853
// Custom Action
853854
public static final String EVENT_CUSTOM_ACTION = "CUSTOM.ACTION";
854855

856+
// Backup Repository
857+
public static final String EVENT_BACKUP_REPOSITORY_ADD = "BACKUP.REPOSITORY.ADD";
858+
public static final String EVENT_BACKUP_REPOSITORY_UPDATE = "BACKUP.REPOSITORY.UPDATE";
859+
855860
static {
856861

857862
// TODO: need a way to force author adding event types to declare the entity details as well, with out braking
@@ -1385,6 +1390,10 @@ public class EventTypes {
13851390
entityEventDetails.put(EVENT_EXTENSION_CUSTOM_ACTION_ADD, ExtensionCustomAction.class);
13861391
entityEventDetails.put(EVENT_EXTENSION_CUSTOM_ACTION_UPDATE, ExtensionCustomAction.class);
13871392
entityEventDetails.put(EVENT_EXTENSION_CUSTOM_ACTION_DELETE, ExtensionCustomAction.class);
1393+
1394+
// Backup Repository
1395+
entityEventDetails.put(EVENT_BACKUP_REPOSITORY_ADD, BackupRepositoryService.class);
1396+
entityEventDetails.put(EVENT_BACKUP_REPOSITORY_UPDATE, BackupRepositoryService.class);
13881397
}
13891398

13901399
public static boolean isNetworkEvent(String eventType) {

api/src/main/java/com/cloud/vm/VirtualMachineProfile.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ public static class Param {
7878
public static final Param BootIntoSetup = new Param("enterHardwareSetup");
7979
public static final Param PreserveNics = new Param("PreserveNics");
8080
public static final Param ConsiderLastHost = new Param("ConsiderLastHost");
81+
public static final Param ReturnAfterVolumePrepare = new Param("ReturnAfterVolumePrepare");
8182

8283
private String name;
8384

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ public class ApiConstants {
139139
public static final String CPU_SPEED = "cpuspeed";
140140
public static final String CPU_LOAD_AVERAGE = "cpuloadaverage";
141141
public static final String CREATED = "created";
142+
public static final String CROSS_ZONE_INSTANCE_CREATION = "crosszoneinstancecreation";
142143
public static final String CTX_ACCOUNT_ID = "ctxaccountid";
143144
public static final String CTX_DETAILS = "ctxDetails";
144145
public static final String CTX_USER_ID = "ctxuserid";

api/src/main/java/org/apache/cloudstack/api/command/user/backup/repository/AddBackupRepositoryCmd.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,14 @@ public class AddBackupRepositoryCmd extends BaseCmd {
6363
type = CommandType.UUID,
6464
entityType = ZoneResponse.class,
6565
required = true,
66-
description = "ID of the zone where the backup repository is to be added")
66+
description = "ID of the zone where the backup repository is to be added for taking backups")
6767
private Long zoneId;
6868

6969
@Parameter(name = ApiConstants.CAPACITY_BYTES, type = CommandType.LONG, description = "capacity of this backup repository")
7070
private Long capacityBytes;
7171

72+
@Parameter(name = ApiConstants.CROSS_ZONE_INSTANCE_CREATION, type = CommandType.BOOLEAN, description = "backups on this repository can be used to create Instances on all Zones", since = "4.22.0")
73+
private Boolean crossZoneInstanceCreation;
7274

7375
/////////////////////////////////////////////////////
7476
/////////////////// Accessors ///////////////////////
@@ -109,6 +111,10 @@ public Long getCapacityBytes() {
109111
return capacityBytes;
110112
}
111113

114+
public Boolean crossZoneInstanceCreationEnabled() {
115+
return crossZoneInstanceCreation;
116+
}
117+
112118
/////////////////////////////////////////////////////
113119
/////////////////// Accessors ///////////////////////
114120
/////////////////////////////////////////////////////
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
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+
18+
package org.apache.cloudstack.api.command.user.backup.repository;
19+
20+
import org.apache.cloudstack.acl.RoleType;
21+
import org.apache.cloudstack.api.APICommand;
22+
import org.apache.cloudstack.api.ApiConstants;
23+
import org.apache.cloudstack.api.ApiErrorCode;
24+
import org.apache.cloudstack.api.BaseCmd;
25+
import org.apache.cloudstack.api.Parameter;
26+
import org.apache.cloudstack.api.ServerApiException;
27+
import org.apache.cloudstack.api.response.BackupRepositoryResponse;
28+
import org.apache.cloudstack.backup.BackupRepository;
29+
import org.apache.cloudstack.backup.BackupRepositoryService;
30+
import org.apache.cloudstack.context.CallContext;
31+
32+
import javax.inject.Inject;
33+
34+
@APICommand(name = "updateBackupRepository",
35+
description = "Update a backup repository",
36+
responseObject = BackupRepositoryResponse.class, since = "4.22.0",
37+
authorized = {RoleType.Admin})
38+
public class UpdateBackupRepositoryCmd extends BaseCmd {
39+
40+
@Inject
41+
private BackupRepositoryService backupRepositoryService;
42+
43+
/////////////////////////////////////////////////////
44+
//////////////// API parameters /////////////////////
45+
/////////////////////////////////////////////////////
46+
47+
@Parameter(name = ApiConstants.ID, type = CommandType.UUID, required = true, entityType = BackupRepositoryResponse.class, description = "ID of the backup repository")
48+
private Long id;
49+
50+
@Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "name of the backup repository")
51+
private String name;
52+
53+
@Parameter(name = ApiConstants.ADDRESS, type = CommandType.STRING, description = "address of the backup repository")
54+
private String address;
55+
56+
@Parameter(name = ApiConstants.MOUNT_OPTIONS, type = CommandType.STRING, description = "shared storage mount options")
57+
private String mountOptions;
58+
59+
@Parameter(name = ApiConstants.CROSS_ZONE_INSTANCE_CREATION, type = CommandType.BOOLEAN, description = "backups in this repository can be used to create Instances on all Zones")
60+
private Boolean crossZoneInstanceCreation;
61+
62+
/////////////////////////////////////////////////////
63+
/////////////////// Accessors ///////////////////////
64+
/////////////////////////////////////////////////////
65+
66+
public BackupRepositoryService getBackupRepositoryService() {
67+
return backupRepositoryService;
68+
}
69+
70+
public Long getId() {
71+
return id;
72+
}
73+
74+
public String getName() {
75+
return name;
76+
}
77+
78+
public String getAddress() {
79+
return address;
80+
}
81+
82+
public String getMountOptions() {
83+
return mountOptions == null ? "" : mountOptions;
84+
}
85+
86+
public Boolean crossZoneInstanceCreationEnabled() {
87+
return crossZoneInstanceCreation;
88+
}
89+
90+
/////////////////////////////////////////////////////
91+
/////////////////////////////////////////////////////
92+
/////////////////// Accessors ///////////////////////
93+
/////////////////////////////////////////////////////
94+
95+
@Override
96+
public void execute() {
97+
try {
98+
BackupRepository result = backupRepositoryService.updateBackupRepository(this);
99+
if (result != null) {
100+
BackupRepositoryResponse response = _responseGenerator.createBackupRepositoryResponse(result);
101+
response.setResponseName(getCommandName());
102+
this.setResponseObject(response);
103+
} else {
104+
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update the backup repository");
105+
}
106+
} catch (Exception ex4) {
107+
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex4.getMessage());
108+
}
109+
110+
}
111+
112+
@Override
113+
public long getEntityOwnerId() {
114+
return CallContext.current().getCallingAccount().getId();
115+
}
116+
}

api/src/main/java/org/apache/cloudstack/api/response/BackupRepositoryResponse.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@ public class BackupRepositoryResponse extends BaseResponse {
6161
@Param(description = "capacity of the backup repository")
6262
private Long capacityBytes;
6363

64+
@SerializedName(ApiConstants.CROSS_ZONE_INSTANCE_CREATION)
65+
@Param(description = "the backups in this repository can be used to create Instances on all Zones")
66+
private Boolean crossZoneInstanceCreation;
67+
6468
@SerializedName("created")
6569
@Param(description = "the date and time the backup repository was added")
6670
private Date created;
@@ -132,6 +136,14 @@ public void setCapacityBytes(Long capacityBytes) {
132136
this.capacityBytes = capacityBytes;
133137
}
134138

139+
public Boolean getCrossZoneInstanceCreation() {
140+
return crossZoneInstanceCreation;
141+
}
142+
143+
public void setCrossZoneInstanceCreation(Boolean crossZoneInstanceCreation) {
144+
this.crossZoneInstanceCreation = crossZoneInstanceCreation;
145+
}
146+
135147
public Date getCreated() {
136148
return created;
137149
}

api/src/main/java/org/apache/cloudstack/backup/BackupManager.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,8 @@ public interface BackupManager extends BackupService, Configurable, PluggableSer
205205

206206
Boolean canCreateInstanceFromBackup(Long backupId);
207207

208+
Boolean canCreateInstanceFromBackupAcrossZones(Long backupId);
209+
208210
/**
209211
* Restore a backup to a new Instance
210212
*/

api/src/main/java/org/apache/cloudstack/backup/BackupProvider.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323

2424
public interface BackupProvider {
2525

26+
Boolean crossZoneInstanceCreationEnabled(BackupOffering backupOffering);
27+
2628
/**
2729
* Returns the unique name of the provider
2830
* @return returns provider name
@@ -85,7 +87,7 @@ public interface BackupProvider {
8587
*/
8688
boolean deleteBackup(Backup backup, boolean forced);
8789

88-
boolean restoreBackupToVM(VirtualMachine vm, Backup backup, String hostIp, String dataStoreUuid);
90+
Pair<Boolean, String> restoreBackupToVM(VirtualMachine vm, Backup backup, String hostIp, String dataStoreUuid);
8991

9092
/**
9193
* Restore VM from backup

api/src/main/java/org/apache/cloudstack/backup/BackupRepository.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,12 @@ public interface BackupRepository extends InternalIdentity, Identity {
2828
String getType();
2929
String getAddress();
3030
String getMountOptions();
31+
void setMountOptions(String mountOptions);
3132
void setUsedBytes(Long usedBytes);
3233
Long getCapacityBytes();
3334
Long getUsedBytes();
3435
void setCapacityBytes(Long capacityBytes);
36+
Boolean crossZoneInstanceCreationEnabled();
37+
void setCrossZoneInstanceCreation(Boolean crossZoneInstanceCreation);
3538
Date getCreated();
3639
}

api/src/main/java/org/apache/cloudstack/backup/BackupRepositoryService.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,13 @@
2323
import org.apache.cloudstack.api.command.user.backup.repository.AddBackupRepositoryCmd;
2424
import org.apache.cloudstack.api.command.user.backup.repository.DeleteBackupRepositoryCmd;
2525
import org.apache.cloudstack.api.command.user.backup.repository.ListBackupRepositoriesCmd;
26+
import org.apache.cloudstack.api.command.user.backup.repository.UpdateBackupRepositoryCmd;
2627

2728
import java.util.List;
2829

2930
public interface BackupRepositoryService {
3031
BackupRepository addBackupRepository(AddBackupRepositoryCmd cmd);
32+
BackupRepository updateBackupRepository(UpdateBackupRepositoryCmd cmd);
3133
boolean deleteBackupRepository(DeleteBackupRepositoryCmd cmd);
3234
Pair<List<BackupRepository>, Integer> listBackupRepositories(ListBackupRepositoriesCmd cmd);
3335

0 commit comments

Comments
 (0)