Skip to content

Commit 318924d

Browse files
authored
CloudStack Backup & Recovery Framework (#3553)
1 parent 4e3f7c2 commit 318924d

File tree

136 files changed

+11072
-110
lines changed

Some content is hidden

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

136 files changed

+11072
-110
lines changed

.travis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ env:
4646
smoke/test_affinity_groups
4747
smoke/test_affinity_groups_projects
4848
smoke/test_async_job
49+
smoke/test_backup_recovery_dummy
4950
smoke/test_create_list_domain_account_project
5051
smoke/test_create_network
5152
smoke/test_deploy_vgpu_enabled_vm

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -478,6 +478,17 @@ public class EventTypes {
478478
public static final String EVENT_VM_SNAPSHOT_OFF_PRIMARY = "VMSNAPSHOT.OFF_PRIMARY";
479479
public static final String EVENT_VM_SNAPSHOT_REVERT = "VMSNAPSHOT.REVERTTO";
480480

481+
// Backup and Recovery events
482+
public static final String EVENT_VM_BACKUP_IMPORT_OFFERING = "BACKUP.IMPORT.OFFERING";
483+
public static final String EVENT_VM_BACKUP_OFFERING_ASSIGN = "BACKUP.OFFERING.ASSIGN";
484+
public static final String EVENT_VM_BACKUP_OFFERING_REMOVE = "BACKUP.OFFERING.REMOVE";
485+
public static final String EVENT_VM_BACKUP_CREATE = "BACKUP.CREATE";
486+
public static final String EVENT_VM_BACKUP_RESTORE = "BACKUP.RESTORE";
487+
public static final String EVENT_VM_BACKUP_DELETE = "BACKUP.DELETE";
488+
public static final String EVENT_VM_BACKUP_RESTORE_VOLUME_TO_VM = "BACKUP.RESTORE.VOLUME.TO.VM";
489+
public static final String EVENT_VM_BACKUP_SCHEDULE_CONFIGURE = "BACKUP.SCHEDULE.CONFIGURE";
490+
public static final String EVENT_VM_BACKUP_SCHEDULE_DELETE = "BACKUP.SCHEDULE.DELETE";
491+
481492
// external network device events
482493
public static final String EVENT_EXTERNAL_NVP_CONTROLLER_ADD = "PHYSICAL.NVPCONTROLLER.ADD";
483494
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 importVirtualMachineFromBackup(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/com/cloud/vm/VirtualMachine.java

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,21 @@
1616
// under the License.
1717
package com.cloud.vm;
1818

19+
import java.util.Arrays;
20+
import java.util.Date;
21+
import java.util.List;
22+
import java.util.Map;
23+
24+
import org.apache.cloudstack.acl.ControlledEntity;
25+
import org.apache.cloudstack.api.Displayable;
26+
import org.apache.cloudstack.backup.Backup;
27+
import org.apache.cloudstack.kernel.Partition;
28+
1929
import com.cloud.hypervisor.Hypervisor.HypervisorType;
2030
import com.cloud.utils.fsm.StateMachine2;
2131
import com.cloud.utils.fsm.StateMachine2.Transition;
2232
import com.cloud.utils.fsm.StateMachine2.Transition.Impact;
2333
import com.cloud.utils.fsm.StateObject;
24-
import org.apache.cloudstack.acl.ControlledEntity;
25-
import org.apache.cloudstack.api.Displayable;
26-
import org.apache.cloudstack.kernel.Partition;
27-
28-
import java.util.Arrays;
29-
import java.util.Date;
30-
import java.util.Map;
3134

3235
/**
3336
* VirtualMachine describes the properties held by a virtual machine
@@ -319,6 +322,12 @@ public boolean isUsedBySystem() {
319322

320323
Long getDiskOfferingId();
321324

325+
Long getBackupOfferingId();
326+
327+
String getBackupExternalId();
328+
329+
List<Backup.VolumeInfo> getBackupVolumeList();
330+
322331
Type getType();
323332

324333
HypervisorType getHypervisorType();

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ public enum ApiCommandJobType {
2323
Volume,
2424
ConsoleProxy,
2525
Snapshot,
26+
Backup,
2627
Template,
2728
Iso,
2829
SystemVm,

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ public class ApiConstants {
3333
public static final String APPLIED = "applied";
3434
public static final String LIST_LB_VMIPS = "lbvmips";
3535
public static final String AVAILABLE = "available";
36+
public static final String BACKUP_ID = "backupid";
37+
public static final String BACKUP_OFFERING_NAME = "backupofferingname";
38+
public static final String BACKUP_OFFERING_ID = "backupofferingid";
3639
public static final String BITS = "bits";
3740
public static final String BOOTABLE = "bootable";
3841
public static final String BIND_DN = "binddn";
@@ -144,6 +147,7 @@ public class ApiConstants {
144147
public static final String EXTRA_DHCP_OPTION_NAME = "extradhcpoptionname";
145148
public static final String EXTRA_DHCP_OPTION_CODE = "extradhcpoptioncode";
146149
public static final String EXTRA_DHCP_OPTION_VALUE = "extradhcpvalue";
150+
public static final String EXTERNAL = "external";
147151
public static final String FENCE = "fence";
148152
public static final String FETCH_LATEST = "fetchlatest";
149153
public static final String FIRSTNAME = "firstname";
@@ -366,6 +370,7 @@ public class ApiConstants {
366370
public static final String VALUE = "value";
367371
public static final String VIRTUAL_MACHINE_ID = "virtualmachineid";
368372
public static final String VIRTUAL_MACHINE_IDS = "virtualmachineids";
373+
public static final String VIRTUAL_MACHINE_NAME = "virtualmachinename";
369374
public static final String VIRTUAL_MACHINE_ID_IP = "vmidipmap";
370375
public static final String VIRTUAL_MACHINE_COUNT = "virtualmachinecount";
371376
public static final String USAGE_ID = "usageid";
@@ -385,6 +390,7 @@ public class ApiConstants {
385390
public static final String VNET = "vnet";
386391
public static final String IS_VOLATILE = "isvolatile";
387392
public static final String VOLUME_ID = "volumeid";
393+
public static final String VOLUMES = "volumes";
388394
public static final String ZONE = "zone";
389395
public static final String ZONE_ID = "zoneid";
390396
public static final String ZONE_NAME = "zonename";
@@ -531,6 +537,7 @@ public class ApiConstants {
531537
public static final String REQUIRED = "required";
532538
public static final String RESTART_REQUIRED = "restartrequired";
533539
public static final String ALLOW_USER_CREATE_PROJECTS = "allowusercreateprojects";
540+
public static final String ALLOW_USER_DRIVEN_BACKUPS = "allowuserdrivenbackups";
534541
public static final String CONSERVE_MODE = "conservemode";
535542
public static final String TRAFFIC_TYPE_IMPLEMENTOR = "traffictypeimplementor";
536543
public static final String KEYWORD = "keyword";
@@ -780,7 +787,7 @@ public enum HostDetails {
780787
}
781788

782789
public enum VMDetails {
783-
all, group, nics, stats, secgrp, tmpl, servoff, diskoff, iso, volume, min, affgrp;
790+
all, group, nics, stats, secgrp, tmpl, servoff, diskoff, backoff, iso, volume, min, affgrp;
784791
}
785792

786793
public enum DomainDetails {
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
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.backup.BackupOffering;
26+
import org.apache.cloudstack.context.CallContext;
27+
28+
public abstract class BaseBackupListCmd extends BaseListCmd {
29+
30+
protected void setupResponseBackupOfferingsList(final List<BackupOffering> offerings, final Integer count) {
31+
final ListResponse<BackupOfferingResponse> response = new ListResponse<>();
32+
final List<BackupOfferingResponse> responses = new ArrayList<>();
33+
for (final BackupOffering offering : offerings) {
34+
if (offering == null) {
35+
continue;
36+
}
37+
BackupOfferingResponse backupOfferingResponse = _responseGenerator.createBackupOfferingResponse(offering);
38+
responses.add(backupOfferingResponse);
39+
}
40+
response.setResponses(responses, count);
41+
response.setResponseName(getCommandName());
42+
setResponseObject(response);
43+
}
44+
45+
@Override
46+
public long getEntityOwnerId() {
47+
return CallContext.current().getCallingAccount().getId();
48+
}
49+
}

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

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,6 @@
2222
import java.util.Map;
2323
import java.util.Set;
2424

25-
import org.apache.cloudstack.api.response.RouterHealthCheckResultResponse;
26-
import org.apache.cloudstack.management.ManagementServerHost;
2725
import org.apache.cloudstack.affinity.AffinityGroup;
2826
import org.apache.cloudstack.affinity.AffinityGroupResponse;
2927
import org.apache.cloudstack.api.ApiConstants.HostDetails;
@@ -36,6 +34,8 @@
3634
import org.apache.cloudstack.api.response.AutoScalePolicyResponse;
3735
import org.apache.cloudstack.api.response.AutoScaleVmGroupResponse;
3836
import org.apache.cloudstack.api.response.AutoScaleVmProfileResponse;
37+
import org.apache.cloudstack.api.response.BackupOfferingResponse;
38+
import org.apache.cloudstack.api.response.BackupScheduleResponse;
3939
import org.apache.cloudstack.api.response.CapacityResponse;
4040
import org.apache.cloudstack.api.response.ClusterResponse;
4141
import org.apache.cloudstack.api.response.ConditionResponse;
@@ -88,6 +88,7 @@
8888
import org.apache.cloudstack.api.response.ResourceCountResponse;
8989
import org.apache.cloudstack.api.response.ResourceLimitResponse;
9090
import org.apache.cloudstack.api.response.ResourceTagResponse;
91+
import org.apache.cloudstack.api.response.RouterHealthCheckResultResponse;
9192
import org.apache.cloudstack.api.response.SSHKeyPairResponse;
9293
import org.apache.cloudstack.api.response.SecurityGroupResponse;
9394
import org.apache.cloudstack.api.response.ServiceOfferingResponse;
@@ -111,6 +112,7 @@
111112
import org.apache.cloudstack.api.response.UsageRecordResponse;
112113
import org.apache.cloudstack.api.response.UserResponse;
113114
import org.apache.cloudstack.api.response.UserVmResponse;
115+
import org.apache.cloudstack.api.response.BackupResponse;
114116
import org.apache.cloudstack.api.response.VMSnapshotResponse;
115117
import org.apache.cloudstack.api.response.VirtualRouterProviderResponse;
116118
import org.apache.cloudstack.api.response.VlanIpRangeResponse;
@@ -119,7 +121,11 @@
119121
import org.apache.cloudstack.api.response.VpcResponse;
120122
import org.apache.cloudstack.api.response.VpnUsersResponse;
121123
import org.apache.cloudstack.api.response.ZoneResponse;
124+
import org.apache.cloudstack.backup.BackupOffering;
125+
import org.apache.cloudstack.backup.Backup;
126+
import org.apache.cloudstack.backup.BackupSchedule;
122127
import org.apache.cloudstack.config.Configuration;
128+
import org.apache.cloudstack.management.ManagementServerHost;
123129
import org.apache.cloudstack.network.lb.ApplicationLoadBalancerRule;
124130
import org.apache.cloudstack.region.PortableIp;
125131
import org.apache.cloudstack.region.PortableIpRange;
@@ -467,6 +473,12 @@ List<TemplateResponse> createTemplateResponses(ResponseView view, VirtualMachine
467473

468474
SSHKeyPairResponse createSSHKeyPairResponse(SSHKeyPair sshkeyPair, boolean privatekey);
469475

476+
BackupResponse createBackupResponse(Backup backup);
477+
478+
BackupScheduleResponse createBackupScheduleResponse(BackupSchedule backup);
479+
480+
BackupOfferingResponse createBackupOfferingResponse(BackupOffering policy);
481+
470482
ManagementServerResponse createManagementResponse(ManagementServerHost mgmt);
471483

472484
List<RouterHealthCheckResultResponse> createHealthCheckResponse(VirtualMachine router, List<RouterHealthCheckResult> healthCheckResults);
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)