Skip to content

Commit 0c1969a

Browse files
committed
feature: VM Backup and Recovery
This introduces a new framework that provides CloudStack users the ability to backup their guest VMs for recovery purposes, in case they suffer a hardware or software issue with their instances or the underlying infrastructure. This framework allows CloudStack to be integrated with Backup and Recovery providers, as pluggable solutions. Each provider must implement the backup and recovery methods provided by the framework in their own way. Two B&R plugins ship with this change: - Veeam B&R: for VMware - Dummy: for testing purposes FS: https://cwiki.apache.org/confluence/display/CLOUDSTACK/Backup+and+Recovery+Framework Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com>
1 parent c8681f5 commit 0c1969a

File tree

112 files changed

+8654
-92
lines changed

Some content is hidden

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

112 files changed

+8654
-92
lines changed

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

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,13 @@
1919
import java.util.HashMap;
2020
import java.util.Map;
2121

22+
import org.apache.cloudstack.acl.Role;
23+
import org.apache.cloudstack.acl.RolePermission;
24+
import org.apache.cloudstack.annotation.Annotation;
25+
import org.apache.cloudstack.config.Configuration;
26+
import org.apache.cloudstack.ha.HAConfig;
27+
import org.apache.cloudstack.usage.Usage;
28+
2229
import com.cloud.dc.DataCenter;
2330
import com.cloud.dc.Pod;
2431
import com.cloud.dc.StorageNetworkIpRange;
@@ -69,12 +76,6 @@
6976
import com.cloud.vm.Nic;
7077
import com.cloud.vm.NicSecondaryIp;
7178
import com.cloud.vm.VirtualMachine;
72-
import org.apache.cloudstack.acl.Role;
73-
import org.apache.cloudstack.acl.RolePermission;
74-
import org.apache.cloudstack.annotation.Annotation;
75-
import org.apache.cloudstack.config.Configuration;
76-
import org.apache.cloudstack.ha.HAConfig;
77-
import org.apache.cloudstack.usage.Usage;
7879

7980
public class EventTypes {
8081

@@ -472,6 +473,14 @@ public class EventTypes {
472473
public static final String EVENT_VM_SNAPSHOT_OFF_PRIMARY = "VMSNAPSHOT.OFF_PRIMARY";
473474
public static final String EVENT_VM_SNAPSHOT_REVERT = "VMSNAPSHOT.REVERTTO";
474475

476+
// Backup and Recovery events
477+
public static final String EVENT_VM_BACKUP_IMPORT_OFFERING = "BACKUP.IMPORT.OFFERING";
478+
public static final String EVENT_VM_BACKUP_CREATE = "BACKUP.CREATE";
479+
public static final String EVENT_VM_BACKUP_RESTORE = "BACKUP.RESTORE";
480+
public static final String EVENT_VM_BACKUP_DELETE = "BACKUP.DELETE";
481+
public static final String EVENT_VM_BACKUP_CUSTOM_SCHEDULE = "BACKUP.CUSTOM.SCHEDULE";
482+
public static final String EVENT_VM_BACKUP_RESTORE_VOLUME_TO_VM = "BACKUP.RESTORE.VOLUME.TO.VM";
483+
475484
// external network device events
476485
public static final String EVENT_EXTERNAL_NVP_CONTROLLER_ADD = "PHYSICAL.NVPCONTROLLER.ADD";
477486
public static final String EVENT_EXTERNAL_NVP_CONTROLLER_DELETE = "PHYSICAL.NVPCONTROLLER.DELETE";

api/src/main/java/com/cloud/hypervisor/HypervisorGuru.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,14 @@
1919
import java.util.List;
2020
import java.util.Map;
2121

22-
import com.cloud.storage.StoragePool;
22+
import org.apache.cloudstack.backup.Backup;
2323
import org.apache.cloudstack.framework.config.ConfigKey;
2424

2525
import com.cloud.agent.api.Command;
2626
import com.cloud.agent.api.to.NicTO;
2727
import com.cloud.agent.api.to.VirtualMachineTO;
2828
import com.cloud.hypervisor.Hypervisor.HypervisorType;
29+
import com.cloud.storage.StoragePool;
2930
import com.cloud.utils.Pair;
3031
import com.cloud.utils.component.Adapter;
3132
import com.cloud.vm.NicProfile;
@@ -86,6 +87,11 @@ public interface HypervisorGuru extends Adapter {
8687

8788
Map<String, String> getClusterSettings(long vmId);
8889

90+
VirtualMachine importVirtualMachine(long zoneId, long domainId, long accountId, long userId,
91+
String vmInternalName, Backup backup) throws Exception;
92+
93+
boolean attachRestoredVolumeToVirtualMachine(long zoneId, String location, Backup.VolumeInfo volumeInfo,
94+
VirtualMachine vm, long poolId, Backup backup) throws Exception;
8995
/**
9096
* Will generate commands to migrate a vm to a pool. For now this will only work for stopped VMs on Vmware.
9197
*

api/src/main/java/com/cloud/server/ResourceTag.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ public enum ResourceObjectType {
2929
ISO(true, false),
3030
Volume(true, true),
3131
Snapshot(true, false),
32+
Backup(true, false),
3233
Network(true, true),
3334
Nic(false, true),
3435
LoadBalancer(true, true),

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ public class ApiConstants {
3232
public static final String APPLIED = "applied";
3333
public static final String LIST_LB_VMIPS = "lbvmips";
3434
public static final String AVAILABLE = "available";
35+
public static final String BACKUP_ID = "backupid";
3536
public static final String BITS = "bits";
3637
public static final String BOOTABLE = "bootable";
3738
public static final String BIND_DN = "binddn";
@@ -133,6 +134,7 @@ public class ApiConstants {
133134
public static final String EXTRA_DHCP_OPTION_NAME = "extradhcpoptionname";
134135
public static final String EXTRA_DHCP_OPTION_CODE = "extradhcpoptioncode";
135136
public static final String EXTRA_DHCP_OPTION_VALUE = "extradhcpvalue";
137+
public static final String EXTERNAL = "external";
136138
public static final String FENCE = "fence";
137139
public static final String FETCH_LATEST = "fetchlatest";
138140
public static final String FIRSTNAME = "firstname";
@@ -209,6 +211,7 @@ public class ApiConstants {
209211
public static final String LBID = "lbruleid";
210212
public static final String MAX = "max";
211213
public static final String MAC_ADDRESS = "macaddress";
214+
public static final String MAX_BACKUPS = "maxbackups";
212215
public static final String MAX_SNAPS = "maxsnaps";
213216
public static final String MAX_CPU_NUMBER = "maxcpunumber";
214217
public static final String MAX_MEMORY = "maxmemory";
@@ -277,6 +280,8 @@ public class ApiConstants {
277280
public static final String RESOURCE_TYPE = "resourcetype";
278281
public static final String RESOURCE_TYPE_NAME = "resourcetypename";
279282
public static final String RESPONSE = "response";
283+
public static final String RESTORE_POINTS = "restorepoints";
284+
public static final String RESTORE_POINT_ID = "restorepointid";
280285
public static final String REVERTABLE = "revertable";
281286
public static final String REGISTERED = "registered";
282287
public static final String QUALIFIERS = "qualifiers";
@@ -344,6 +349,7 @@ public class ApiConstants {
344349
public static final String VALUE = "value";
345350
public static final String VIRTUAL_MACHINE_ID = "virtualmachineid";
346351
public static final String VIRTUAL_MACHINE_IDS = "virtualmachineids";
352+
public static final String VIRTUAL_MACHINE_NAME = "virtualmachinename";
347353
public static final String VIRTUAL_MACHINE_ID_IP = "vmidipmap";
348354
public static final String VIRTUAL_MACHINE_COUNT = "virtualmachinecount";
349355
public static final String USAGE_ID = "usageid";
@@ -362,6 +368,7 @@ public class ApiConstants {
362368
public static final String VNET = "vnet";
363369
public static final String IS_VOLATILE = "isvolatile";
364370
public static final String VOLUME_ID = "volumeid";
371+
public static final String VOLUMES = "volumes";
365372
public static final String ZONE = "zone";
366373
public static final String ZONE_ID = "zoneid";
367374
public static final String ZONE_NAME = "zonename";
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
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;
19+
20+
import java.util.ArrayList;
21+
import java.util.List;
22+
23+
import org.apache.cloudstack.api.response.BackupOfferingResponse;
24+
import org.apache.cloudstack.api.response.ListResponse;
25+
import org.apache.cloudstack.api.response.BackupResponse;
26+
import org.apache.cloudstack.api.response.BackupRestorePointResponse;
27+
import org.apache.cloudstack.backup.BackupOffering;
28+
import org.apache.cloudstack.backup.Backup;
29+
import org.apache.cloudstack.context.CallContext;
30+
31+
public abstract class BaseBackupListCmd extends BaseListCmd {
32+
33+
protected void setupResponseBackupOfferingsList(final List<BackupOffering> offerings) {
34+
final ListResponse<BackupOfferingResponse> response = new ListResponse<>();
35+
final List<BackupOfferingResponse> responses = new ArrayList<>();
36+
for (final BackupOffering offering : offerings) {
37+
if (offering == null) {
38+
continue;
39+
}
40+
BackupOfferingResponse backupOfferingResponse = _responseGenerator.createBackupOfferingResponse(offering);
41+
responses.add(backupOfferingResponse);
42+
}
43+
response.setResponses(responses);
44+
response.setResponseName(getCommandName());
45+
setResponseObject(response);
46+
}
47+
48+
protected void setupResponseBackupList(final List<Backup> backups, final List<Backup.RestorePoint> restorePoints) {
49+
final ListResponse<BackupResponse> response = new ListResponse<>();
50+
final List<BackupResponse> responses = new ArrayList<>();
51+
for (Backup backup : backups) {
52+
if (backup == null) {
53+
continue;
54+
}
55+
BackupResponse backupResponse = _responseGenerator.createBackupResponse(backup);
56+
if (restorePoints != null && !restorePoints.isEmpty()) {
57+
final List<BackupRestorePointResponse> restorePointResponses = new ArrayList<>();
58+
for (Backup.RestorePoint rp : restorePoints) {
59+
if (rp == null) {
60+
continue;
61+
}
62+
BackupRestorePointResponse rpResponse = new BackupRestorePointResponse();
63+
rpResponse.setId(rp.getId());
64+
rpResponse.setCreated(rp.getCreated());
65+
rpResponse.setType(rp.getType());
66+
restorePointResponses.add(rpResponse);
67+
}
68+
backupResponse.setRestorePoints(restorePointResponses);
69+
}
70+
responses.add(backupResponse);
71+
}
72+
response.setResponses(responses);
73+
response.setResponseName(getCommandName());
74+
setResponseObject(response);
75+
}
76+
77+
@Override
78+
public long getEntityOwnerId() {
79+
return CallContext.current().getCallingAccount().getId();
80+
}
81+
}

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
import java.util.Map;
2323
import java.util.Set;
2424

25-
import org.apache.cloudstack.management.ManagementServerHost;
2625
import org.apache.cloudstack.affinity.AffinityGroup;
2726
import org.apache.cloudstack.affinity.AffinityGroupResponse;
2827
import org.apache.cloudstack.api.ApiConstants.HostDetails;
@@ -35,6 +34,7 @@
3534
import org.apache.cloudstack.api.response.AutoScalePolicyResponse;
3635
import org.apache.cloudstack.api.response.AutoScaleVmGroupResponse;
3736
import org.apache.cloudstack.api.response.AutoScaleVmProfileResponse;
37+
import org.apache.cloudstack.api.response.BackupOfferingResponse;
3838
import org.apache.cloudstack.api.response.CapacityResponse;
3939
import org.apache.cloudstack.api.response.ClusterResponse;
4040
import org.apache.cloudstack.api.response.ConditionResponse;
@@ -110,6 +110,7 @@
110110
import org.apache.cloudstack.api.response.UsageRecordResponse;
111111
import org.apache.cloudstack.api.response.UserResponse;
112112
import org.apache.cloudstack.api.response.UserVmResponse;
113+
import org.apache.cloudstack.api.response.BackupResponse;
113114
import org.apache.cloudstack.api.response.VMSnapshotResponse;
114115
import org.apache.cloudstack.api.response.VirtualRouterProviderResponse;
115116
import org.apache.cloudstack.api.response.VlanIpRangeResponse;
@@ -118,7 +119,10 @@
118119
import org.apache.cloudstack.api.response.VpcResponse;
119120
import org.apache.cloudstack.api.response.VpnUsersResponse;
120121
import org.apache.cloudstack.api.response.ZoneResponse;
122+
import org.apache.cloudstack.backup.BackupOffering;
123+
import org.apache.cloudstack.backup.Backup;
121124
import org.apache.cloudstack.config.Configuration;
125+
import org.apache.cloudstack.management.ManagementServerHost;
122126
import org.apache.cloudstack.network.lb.ApplicationLoadBalancerRule;
123127
import org.apache.cloudstack.region.PortableIp;
124128
import org.apache.cloudstack.region.PortableIpRange;
@@ -465,5 +469,9 @@ List<TemplateResponse> createTemplateResponses(ResponseView view, VirtualMachine
465469

466470
SSHKeyPairResponse createSSHKeyPairResponse(SSHKeyPair sshkeyPair, boolean privatekey);
467471

472+
BackupResponse createBackupResponse(Backup backup);
473+
474+
BackupOfferingResponse createBackupOfferingResponse(BackupOffering policy);
475+
468476
ManagementServerResponse createManagementResponse(ManagementServerHost mgmt);
469477
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
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.admin.backup;
19+
20+
import javax.inject.Inject;
21+
22+
import org.apache.cloudstack.acl.RoleType;
23+
import org.apache.cloudstack.api.APICommand;
24+
import org.apache.cloudstack.api.ApiConstants;
25+
import org.apache.cloudstack.api.ApiErrorCode;
26+
import org.apache.cloudstack.api.BaseCmd;
27+
import org.apache.cloudstack.api.Parameter;
28+
import org.apache.cloudstack.api.ServerApiException;
29+
import org.apache.cloudstack.api.response.BackupOfferingResponse;
30+
import org.apache.cloudstack.api.response.SuccessResponse;
31+
import org.apache.cloudstack.backup.BackupManager;
32+
import org.apache.cloudstack.context.CallContext;
33+
34+
import com.cloud.exception.ConcurrentOperationException;
35+
import com.cloud.exception.InsufficientCapacityException;
36+
import com.cloud.exception.NetworkRuleConflictException;
37+
import com.cloud.exception.ResourceAllocationException;
38+
import com.cloud.exception.ResourceUnavailableException;
39+
40+
@APICommand(name = DeleteBackupOfferingCmd.APINAME,
41+
description = "Deletes a backup offering",
42+
responseObject = SuccessResponse.class, since = "4.14.0",
43+
authorized = {RoleType.Admin})
44+
public class DeleteBackupOfferingCmd extends BaseCmd {
45+
public static final String APINAME = "deleteBackupOffering";
46+
47+
@Inject
48+
private BackupManager backupManager;
49+
50+
/////////////////////////////////////////////////////
51+
//////////////// API parameters /////////////////////
52+
////////////////////////////////////////////////////
53+
54+
@Parameter(name = ApiConstants.ID,
55+
type = CommandType.UUID,
56+
entityType = BackupOfferingResponse.class,
57+
required = true,
58+
description = "ID of the backup offering")
59+
private Long id;
60+
61+
/////////////////////////////////////////////////////
62+
/////////////////// Accessors ///////////////////////
63+
/////////////////////////////////////////////////////
64+
65+
public Long getId() {
66+
return id;
67+
}
68+
69+
/////////////////////////////////////////////////////
70+
/////////////// API Implementation///////////////////
71+
/////////////////////////////////////////////////////
72+
73+
@Override
74+
public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException {
75+
if (backupManager.deleteBackupOffering(getId())) {
76+
SuccessResponse response = new SuccessResponse(getCommandName());
77+
setResponseObject(response);
78+
} else {
79+
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Unable to remove backup offering: " + getId());
80+
}
81+
}
82+
83+
@Override
84+
public String getCommandName() {
85+
return APINAME.toLowerCase() + BaseCmd.RESPONSE_SUFFIX;
86+
}
87+
88+
@Override
89+
public long getEntityOwnerId() {
90+
return CallContext.current().getCallingAccount().getId();
91+
}
92+
}

0 commit comments

Comments
 (0)