From 9a692752c4c43342bdb495cf6a8c6f5cce2a4e38 Mon Sep 17 00:00:00 2001 From: nvazquez Date: Thu, 17 May 2018 13:09:21 -0300 Subject: [PATCH] API final additions and schema --- .../apache/cloudstack/api/ApiConstants.java | 3 + ...icyListCmd.java => BaseBackupListCmd.java} | 23 +- .../cloudstack/api/ResponseGenerator.java | 4 + .../user/backup/AssignBackupPolicyCmd.java | 2 +- .../user/backup/ListBackupPoliciesCmd.java | 36 ++- .../command/user/backup/ListBackupsCmd.java | 79 ++++++- .../command/user/backup/RestoreBackupCmd.java | 98 +++++++- .../backup/RestoreBackupVolumeCmd.java} | 71 ++++-- .../api/response/BackupResponse.java | 122 ++++++++++ .../cloudstack/backup/BackupManager.java | 19 +- .../cloudstack/backup/BackupPolicyTO.java | 6 +- .../backup/BackupPolicyVMMapVO.java | 77 +++++++ .../cloudstack/backup/BackupPolicyVO.java | 9 +- .../apache/cloudstack/backup/BackupVO.java | 214 ++++++++++++++++++ .../cloudstack/backup/dao/BackupDao.java | 33 +++ .../cloudstack/backup/dao/BackupDaoImpl.java | 110 +++++++++ .../backup/dao/BackupPolicyDaoImpl.java | 2 +- .../backup/dao/BackupPolicyVMMapDao.java | 31 +++ .../backup/dao/BackupPolicyVMMapDaoImpl.java | 69 ++++++ ...spring-engine-schema-core-daos-context.xml | 4 +- .../META-INF/db/schema-41110to41200.sql | 29 +++ .../cloudstack/backup/BackupVOTest.java | 61 +++++ .../cloudstack/framework/backup/Backup.java | 40 ++++ .../framework/backup/BackupPolicy.java | 2 +- .../framework/backup/BackupProvider.java | 5 + .../backup/DummyBackupProvider.java | 6 + .../backup/VeeamBackupProvider.java | 5 + .../main/java/com/cloud/api/ApiDBUtils.java | 11 + .../java/com/cloud/api/ApiResponseHelper.java | 7 + .../cloudstack/backup/BackupManagerImpl.java | 62 ++++- 30 files changed, 1190 insertions(+), 50 deletions(-) rename api/src/main/java/org/apache/cloudstack/api/{BaseBackupPolicyListCmd.java => BaseBackupListCmd.java} (69%) rename api/src/main/java/org/apache/cloudstack/api/command/{admin/backup/ListBackupProviderPoliciesCmd.java => user/backup/RestoreBackupVolumeCmd.java} (59%) create mode 100644 api/src/main/java/org/apache/cloudstack/api/response/BackupResponse.java create mode 100644 engine/schema/src/main/java/org/apache/cloudstack/backup/BackupPolicyVMMapVO.java create mode 100644 engine/schema/src/main/java/org/apache/cloudstack/backup/BackupVO.java create mode 100644 engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupDao.java create mode 100644 engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupDaoImpl.java create mode 100644 engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupPolicyVMMapDao.java create mode 100644 engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupPolicyVMMapDaoImpl.java create mode 100644 engine/schema/src/test/java/org/apache/cloudstack/backup/BackupVOTest.java create mode 100644 framework/backup/src/main/java/org/apache/cloudstack/framework/backup/Backup.java diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java index 504b2149837..a6ba5483039 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java @@ -32,6 +32,7 @@ public class ApiConstants { public static final String APPLIED = "applied"; public static final String LIST_LB_VMIPS = "lbvmips"; public static final String AVAILABLE = "available"; + public static final String BACKUP_ID = "backupid"; public static final String BITS = "bits"; public static final String BOOTABLE = "bootable"; public static final String BIND_DN = "binddn"; @@ -121,6 +122,7 @@ public class ApiConstants { public static final String EXTRA_DHCP_OPTION_NAME = "extradhcpoptionname"; public static final String EXTRA_DHCP_OPTION_CODE = "extradhcpoptioncode"; public static final String EXTRA_DHCP_OPTION_VALUE = "extradhcpvalue"; + public static final String EXTERNAL = "external"; public static final String FENCE = "fence"; public static final String FETCH_LATEST = "fetchlatest"; public static final String FIRSTNAME = "firstname"; @@ -335,6 +337,7 @@ public class ApiConstants { public static final String VNET = "vnet"; public static final String IS_VOLATILE = "isvolatile"; public static final String VOLUME_ID = "volumeid"; + public static final String VOLUME_IDS = "volumeids"; public static final String ZONE_ID = "zoneid"; public static final String ZONE_NAME = "zonename"; public static final String NETWORK_TYPE = "networktype"; diff --git a/api/src/main/java/org/apache/cloudstack/api/BaseBackupPolicyListCmd.java b/api/src/main/java/org/apache/cloudstack/api/BaseBackupListCmd.java similarity index 69% rename from api/src/main/java/org/apache/cloudstack/api/BaseBackupPolicyListCmd.java rename to api/src/main/java/org/apache/cloudstack/api/BaseBackupListCmd.java index 31aacc91ac6..2bf3d6009d0 100644 --- a/api/src/main/java/org/apache/cloudstack/api/BaseBackupPolicyListCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/BaseBackupListCmd.java @@ -18,16 +18,18 @@ package org.apache.cloudstack.api; import org.apache.cloudstack.api.response.BackupPolicyResponse; +import org.apache.cloudstack.api.response.BackupResponse; import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.framework.backup.Backup; import org.apache.cloudstack.framework.backup.BackupPolicy; import java.util.ArrayList; import java.util.List; -public abstract class BaseBackupPolicyListCmd extends BaseListCmd { +public abstract class BaseBackupListCmd extends BaseListCmd { - protected void setupResponse(final List policies) { + protected void setupResponseBackupPolicyList(final List policies) { final ListResponse response = new ListResponse<>(); final List responses = new ArrayList<>(); for (final BackupPolicy policy : policies) { @@ -35,7 +37,7 @@ public abstract class BaseBackupPolicyListCmd extends BaseListCmd { continue; } final BackupPolicyResponse backupPolicyResponse = new BackupPolicyResponse(); - if (!policy.isExternal()) { + if (!policy.isImported()) { backupPolicyResponse.setId(policy.getUuid()); } backupPolicyResponse.setName(policy.getName()); @@ -48,6 +50,21 @@ public abstract class BaseBackupPolicyListCmd extends BaseListCmd { setResponseObject(response); } + protected void setupResponseBackupList(final List backups) { + final ListResponse response = new ListResponse<>(); + final List responses = new ArrayList<>(); + for (Backup backup : backups) { + if (backup == null) { + continue; + } + BackupResponse backupResponse = _responseGenerator.createBackupResponse(backup); + responses.add(backupResponse); + } + response.setResponses(responses); + response.setResponseName(getCommandName()); + setResponseObject(response); + } + @Override public long getEntityOwnerId() { return CallContext.current().getCallingAccount().getId(); diff --git a/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java b/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java index 4fb248cd105..753f7f91f19 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java +++ b/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java @@ -34,6 +34,7 @@ import org.apache.cloudstack.api.response.AsyncJobResponse; import org.apache.cloudstack.api.response.AutoScalePolicyResponse; import org.apache.cloudstack.api.response.AutoScaleVmGroupResponse; import org.apache.cloudstack.api.response.AutoScaleVmProfileResponse; +import org.apache.cloudstack.api.response.BackupResponse; import org.apache.cloudstack.api.response.CapacityResponse; import org.apache.cloudstack.api.response.ClusterResponse; import org.apache.cloudstack.api.response.ConditionResponse; @@ -117,6 +118,7 @@ import org.apache.cloudstack.api.response.VpcResponse; import org.apache.cloudstack.api.response.VpnUsersResponse; import org.apache.cloudstack.api.response.ZoneResponse; import org.apache.cloudstack.config.Configuration; +import org.apache.cloudstack.framework.backup.Backup; import org.apache.cloudstack.network.lb.ApplicationLoadBalancerRule; import org.apache.cloudstack.region.PortableIp; import org.apache.cloudstack.region.PortableIpRange; @@ -462,4 +464,6 @@ public interface ResponseGenerator { ListResponse createUpgradeRouterTemplateResponse(List jobIds); SSHKeyPairResponse createSSHKeyPairResponse(SSHKeyPair sshkeyPair, boolean privatekey); + + BackupResponse createBackupResponse(Backup backup); } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/AssignBackupPolicyCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/AssignBackupPolicyCmd.java index b84300dbd95..92f12b29f1c 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/AssignBackupPolicyCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/AssignBackupPolicyCmd.java @@ -111,7 +111,7 @@ public class AssignBackupPolicyCmd extends BaseCmd { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to assign VM to backup policy"); } } catch (Exception e) { - throw new ServerApiException(ApiErrorCode.PARAM_ERROR, e.getMessage()); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage()); } } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/ListBackupPoliciesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/ListBackupPoliciesCmd.java index 8b8ef6e7088..2c6bd314b47 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/ListBackupPoliciesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/ListBackupPoliciesCmd.java @@ -21,10 +21,14 @@ import java.util.List; import javax.inject.Inject; import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiErrorCode; -import org.apache.cloudstack.api.BaseBackupPolicyListCmd; +import org.apache.cloudstack.api.BaseBackupListCmd; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.response.BackupPolicyResponse; +import org.apache.cloudstack.api.response.ZoneResponse; import org.apache.cloudstack.backup.BackupManager; import org.apache.cloudstack.framework.backup.BackupPolicy; @@ -36,13 +40,37 @@ import com.cloud.utils.exception.CloudRuntimeException; @APICommand(name = ListBackupPoliciesCmd.APINAME, description = "Lists backup policies", responseObject = BackupPolicyResponse.class, since = "4.12.0") -public class ListBackupPoliciesCmd extends BaseBackupPolicyListCmd { +public class ListBackupPoliciesCmd extends BaseBackupListCmd { public static final String APINAME = "listBackupPolicies"; @Inject BackupManager backupManager; + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ZONE_ID, type = BaseCmd.CommandType.UUID, entityType = ZoneResponse.class, + description = "The zone ID") + private Long zoneId; + + @Parameter(name = ApiConstants.EXTERNAL, type = CommandType.BOOLEAN, + description = "True if list external backup policies") + private Boolean external; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getZoneId() { + return zoneId; + } + + public Boolean isExternal() { + return external; + } + @Override public String getCommandName() { return APINAME.toLowerCase() + RESPONSE_SUFFIX; @@ -55,8 +83,8 @@ public class ListBackupPoliciesCmd extends BaseBackupPolicyListCmd { @Override public void execute() throws ResourceUnavailableException, ServerApiException, ConcurrentOperationException { try { - List backupPolicies = backupManager.listBackupPolicies(); - setupResponse(backupPolicies); + List backupPolicies = backupManager.listBackupPolicies(zoneId, external); + setupResponseBackupPolicyList(backupPolicies); } catch (InvalidParameterValueException e) { throw new ServerApiException(ApiErrorCode.PARAM_ERROR, e.getMessage()); } catch (CloudRuntimeException e) { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/ListBackupsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/ListBackupsCmd.java index 9dd89dd5ccf..dbefe1ea138 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/ListBackupsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/ListBackupsCmd.java @@ -17,5 +17,82 @@ package org.apache.cloudstack.api.command.user.backup; -public class ListBackupsCmd { +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.NetworkRuleConflictException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseBackupListCmd; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.BackupResponse; +import org.apache.cloudstack.api.response.UserVmResponse; +import org.apache.cloudstack.backup.BackupManager; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.framework.backup.Backup; + +import javax.inject.Inject; +import java.util.List; + +@APICommand(name = ListBackupsCmd.APINAME, + description = "Lists backups", + responseObject = BackupResponse.class, since = "4.12.0") +public class ListBackupsCmd extends BaseBackupListCmd { + public static final String APINAME = "listBackups"; + + @Inject + BackupManager backupManager; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, + type = CommandType.UUID, + entityType = UserVmResponse.class, + required = true, + description = "id of the VM") + private Long virtualMachineId; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getVirtualMachineId() { + return virtualMachineId; + } + + @Override + public String getCommandName() { + return APINAME.toLowerCase() + BaseCmd.RESPONSE_SUFFIX; + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccount().getId(); + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + private void setupResponse(List backups) { + + } + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { + try{ + List backups = backupManager.listBackups(virtualMachineId); + setupResponseBackupList(backups); + } catch (Exception e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage()); + } + } + + } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/RestoreBackupCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/RestoreBackupCmd.java index 1ae8a36224f..e63733c7c10 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/RestoreBackupCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/RestoreBackupCmd.java @@ -17,5 +17,101 @@ package org.apache.cloudstack.api.command.user.backup; -public class RestoreBackupCmd { +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.NetworkRuleConflictException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.utils.exception.CloudRuntimeException; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.api.response.UserVmResponse; +import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.cloudstack.backup.BackupManager; +import org.apache.cloudstack.context.CallContext; + +import javax.inject.Inject; + +@APICommand(name = RestoreBackupCmd.APINAME, + description = "Restore backup", + responseObject = SuccessResponse.class, since = "4.12.0") +public class RestoreBackupCmd extends BaseCmd { + public static final String APINAME = "restoreBackup"; + + @Inject + BackupManager backupManager; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, + type = CommandType.UUID, + entityType = UserVmResponse.class, + required = true, + description = "id of the VM") + private Long virtualMachineId; + + @Parameter(name = ApiConstants.BACKUP_ID, + type = CommandType.STRING, + required = true, + description = "id of the backup") + private String backupId; + + @Parameter(name = ApiConstants.ZONE_ID, type = BaseCmd.CommandType.UUID, entityType = ZoneResponse.class, + description = "The zone ID") + private Long zoneId; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getVirtualMachineId() { + return virtualMachineId; + } + + public String getBackupId() { + return backupId; + } + + public Long getZoneId() { + return zoneId; + } + + @Override + public String getCommandName() { + return APINAME.toLowerCase() + BaseCmd.RESPONSE_SUFFIX; + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccount().getId(); + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { + try { + boolean result = backupManager.restoreBackup(virtualMachineId, backupId, zoneId); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + response.setResponseName(getCommandName()); + setResponseObject(response); + } else { + throw new CloudRuntimeException("Error while restoring VM from backup"); + } + } catch (Exception e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage()); + } + } + + } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/ListBackupProviderPoliciesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/RestoreBackupVolumeCmd.java similarity index 59% rename from api/src/main/java/org/apache/cloudstack/api/command/admin/backup/ListBackupProviderPoliciesCmd.java rename to api/src/main/java/org/apache/cloudstack/api/command/user/backup/RestoreBackupVolumeCmd.java index bc0c4b77894..1977cf3722e 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/ListBackupProviderPoliciesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/RestoreBackupVolumeCmd.java @@ -15,40 +15,35 @@ // specific language governing permissions and limitations // under the License. -package org.apache.cloudstack.api.command.admin.backup; +package org.apache.cloudstack.api.command.user.backup; import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InsufficientCapacityException; -import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.NetworkRuleConflictException; import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.ResourceUnavailableException; import com.cloud.utils.exception.CloudRuntimeException; -import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiErrorCode; -import org.apache.cloudstack.api.BaseBackupPolicyListCmd; import org.apache.cloudstack.api.BaseCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; -import org.apache.cloudstack.api.command.user.backup.AssignBackupPolicyCmd; -import org.apache.cloudstack.api.response.BackupPolicyResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.api.response.UserVmResponse; +import org.apache.cloudstack.api.response.VolumeResponse; import org.apache.cloudstack.api.response.ZoneResponse; import org.apache.cloudstack.backup.BackupManager; import org.apache.cloudstack.context.CallContext; -import org.apache.cloudstack.framework.backup.BackupPolicy; import javax.inject.Inject; -import java.util.List; -@APICommand(name = ListBackupProviderPoliciesCmd.APINAME, - description = "Lists backup policies existing on the Backup and Recovery provider side", - responseObject = BackupPolicyResponse.class, since = "4.12.0", - authorized = {RoleType.Admin}) -public class ListBackupProviderPoliciesCmd extends BaseBackupPolicyListCmd { +@APICommand(name = RestoreBackupVolumeCmd.APINAME, + description = "Restore and attach a backed up volume to VM", + responseObject = SuccessResponse.class, since = "4.12.0") +public class RestoreBackupVolumeCmd extends BaseCmd { - public static final String APINAME = "listBackupProviderPolicies"; + public static final String APINAME = "restoreBackupVolume"; @Inject BackupManager backupManager; @@ -57,6 +52,26 @@ public class ListBackupProviderPoliciesCmd extends BaseBackupPolicyListCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// + @Parameter(name = ApiConstants.VOLUME_ID, + type = CommandType.UUID, + entityType = VolumeResponse.class, + required = true, + description = "id of the volume") + private Long volumeId; + + @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, + type = CommandType.UUID, + entityType = UserVmResponse.class, + required = true, + description = "id of the VM") + private Long virtualMachineId; + + @Parameter(name = ApiConstants.BACKUP_ID, + type = CommandType.STRING, + required = true, + description = "id of the backup") + private String backupId; + @Parameter(name = ApiConstants.ZONE_ID, type = BaseCmd.CommandType.UUID, entityType = ZoneResponse.class, description = "The zone ID") private Long zoneId; @@ -65,13 +80,25 @@ public class ListBackupProviderPoliciesCmd extends BaseBackupPolicyListCmd { /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// + public Long getVolumeId() { + return volumeId; + } + + public Long getVirtualMachineId() { + return virtualMachineId; + } + + public String getBackupId() { + return backupId; + } + public Long getZoneId() { return zoneId; } @Override public String getCommandName() { - return AssignBackupPolicyCmd.APINAME + RESPONSE_SUFFIX; + return APINAME.toLowerCase() + BaseCmd.RESPONSE_SUFFIX; } @Override @@ -86,11 +113,15 @@ public class ListBackupProviderPoliciesCmd extends BaseBackupPolicyListCmd { @Override public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { try { - List policies = backupManager.listBackupProviderPolicies(zoneId); - setupResponse(policies); - } catch (InvalidParameterValueException e) { - throw new ServerApiException(ApiErrorCode.PARAM_ERROR, e.getMessage()); - } catch (CloudRuntimeException e) { + boolean result = backupManager.restoreBackupVolume(volumeId, virtualMachineId, backupId, zoneId); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + response.setResponseName(getCommandName()); + setResponseObject(response); + } else { + throw new CloudRuntimeException("Error restoring volume and attaching to VM"); + } + } catch (Exception e) { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage()); } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/BackupResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/BackupResponse.java new file mode 100644 index 00000000000..9c6bac2ee33 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/BackupResponse.java @@ -0,0 +1,122 @@ +package org.apache.cloudstack.api.response; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.EntityReference; +import org.apache.cloudstack.framework.backup.Backup; + +import java.util.List; + +@EntityReference(value = Backup.class) +public class BackupResponse extends BaseResponse { + + @SerializedName(ApiConstants.ID) + @Param(description = "internal id of the backup") + private String id; + + @SerializedName(ApiConstants.ACCOUNT_ID) + @Param(description = "account id") + private String accountId; + + @SerializedName(ApiConstants.USER_ID) + @Param(description = "user id") + private String userId; + + @SerializedName(ApiConstants.NAME) + @Param(description = "backup name") + private String name; + + @SerializedName(ApiConstants.DESCRIPTION) + @Param(description = "backup description") + private String description; + + @SerializedName(ApiConstants.PARENT_ID) + @Param(description = "backup parent id") + private String parentId; + + @SerializedName(ApiConstants.VIRTUAL_MACHINE_ID) + @Param(description = "backup vm id") + private String vmId; + + @SerializedName(ApiConstants.VOLUME_IDS) + @Param(description = "backup volume ids") + private List volumeIds; + + @SerializedName(ApiConstants.STATUS) + @Param(description = "backup volume ids") + private Backup.Status status; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getAccountId() { + return accountId; + } + + public void setAccountId(String accountId) { + this.accountId = accountId; + } + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getParentId() { + return parentId; + } + + public void setParentId(String parentId) { + this.parentId = parentId; + } + + public String getVmId() { + return vmId; + } + + public void setVmId(String vmId) { + this.vmId = vmId; + } + + public Backup.Status getStatus() { + return status; + } + + public void setStatus(Backup.Status status) { + this.status = status; + } + + public List getVolumeIds() { + return volumeIds; + } + + public void setVolumeIds(List volumeIds) { + this.volumeIds = volumeIds; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/backup/BackupManager.java b/api/src/main/java/org/apache/cloudstack/backup/BackupManager.java index 95b11ca12cd..6c792c63296 100644 --- a/api/src/main/java/org/apache/cloudstack/backup/BackupManager.java +++ b/api/src/main/java/org/apache/cloudstack/backup/BackupManager.java @@ -20,6 +20,7 @@ package org.apache.cloudstack.backup; import java.util.List; import org.apache.cloudstack.api.response.BackupPolicyResponse; +import org.apache.cloudstack.framework.backup.Backup; import org.apache.cloudstack.framework.backup.BackupPolicy; import org.apache.cloudstack.framework.backup.BackupService; import org.apache.cloudstack.framework.config.ConfigKey; @@ -59,14 +60,24 @@ public interface BackupManager extends BackupService, Configurable, PluggableSer boolean assignVMToBackupPolicy(String policyUuid, Long virtualMachineId, Long zoneId); /** - * List existing backup policies + * List existing backups for a VM */ - List listBackupPolicies(); + List listBackups(Long vmId); /** - * List backup policies existing on the backup provider + * List backup policies + * @param zoneId zone id + * @param external if true, only external backup policies are listed */ - List listBackupProviderPolicies(Long zoneId); + List listBackupPolicies(Long zoneId, Boolean external); + + /** + * Restore a full backed up VM + */ + boolean restoreBackup(Long vmId, String backupId, Long zoneId); + + //TODO + boolean restoreBackupVolume(Long volumeId, Long vmId, String backupId, Long zoneId); /** * Deletes a backup policy diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupPolicyTO.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupPolicyTO.java index 05d8fbe0b92..1f0470d7d93 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupPolicyTO.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupPolicyTO.java @@ -29,7 +29,7 @@ public class BackupPolicyTO implements BackupPolicy { private long id; private String name; private String policyUuid; - private boolean external = true; + private boolean imported = false; public BackupPolicyTO() { this.uuid = UUID.randomUUID().toString(); @@ -61,7 +61,7 @@ public class BackupPolicyTO implements BackupPolicy { } @Override - public boolean isExternal() { - return external; + public boolean isImported() { + return imported; } } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupPolicyVMMapVO.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupPolicyVMMapVO.java new file mode 100644 index 00000000000..e06d153981d --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupPolicyVMMapVO.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.cloudstack.backup; + +import org.apache.cloudstack.api.InternalIdentity; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +@Entity +@Table(name = "backup_policy_vm_map") +public class BackupPolicyVMMapVO implements InternalIdentity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name = "policy_id") + private long policyId; + + @Column(name = "vm_id") + private long vmId; + + public BackupPolicyVMMapVO() { + } + + public BackupPolicyVMMapVO(long policyId, long vmId) { + this.policyId = policyId; + this.vmId = vmId; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public long getPolicyId() { + return policyId; + } + + public void setPolicyId(long policyId) { + this.policyId = policyId; + } + + public long getVmId() { + return vmId; + } + + public void setVmId(long vmId) { + this.vmId = vmId; + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupPolicyVO.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupPolicyVO.java index 18a76dd1dbb..2ed59b13703 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupPolicyVO.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupPolicyVO.java @@ -44,6 +44,7 @@ public class BackupPolicyVO implements BackupPolicy { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") private long id; @Column(name = "uuid") @@ -55,6 +56,9 @@ public class BackupPolicyVO implements BackupPolicy { @Column(name = "policy_uuid") private String policyUuid; + @Column(name = "imported") + private boolean imported = true; + public String getUuid() { return uuid; } @@ -87,7 +91,8 @@ public class BackupPolicyVO implements BackupPolicy { this.policyUuid = policyUuid; } - public boolean isExternal() { - return false; + @Override + public boolean isImported() { + return imported; } } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupVO.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupVO.java new file mode 100644 index 00000000000..a32f409b9e3 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupVO.java @@ -0,0 +1,214 @@ +//Licensed to the Apache Software Foundation (ASF) under one +//or more contributor license agreements. See the NOTICE file +//distributed with this work for additional information +//regarding copyright ownership. The ASF licenses this file +//to you under the Apache License, Version 2.0 (the +//"License"); you may not use this file except in compliance +//the License. You may obtain a copy of the License at +// +//http://www.apache.org/licenses/LICENSE-2.0 +// +//Unless required by applicable law or agreed to in writing, +//software distributed under the License is distributed on an +//"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +//KIND, either express or implied. See the License for the +//specific language governing permissions and limitations +//under the License. + +package org.apache.cloudstack.backup; + +import org.apache.cloudstack.framework.backup.Backup; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang.StringUtils; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.StringJoiner; +import java.util.UUID; + +@Entity +@Table(name = "backup") +public class BackupVO implements Backup { + + public BackupVO() { + this.uuid = UUID.randomUUID().toString(); + volumeIds = new ArrayList<>(); + } + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name = "uuid") + private String uuid; + + @Column(name = "account_id") + private long accountId; + + @Column(name = "user_id") + private long userId; + + @Column(name = "name") + private String name; + + @Column(name = "description") + private String description; + + @Column(name = "parent_id") + private Long parentId; + + @Column(name = "vm_id") + private long vmId; + + @Column(name = "volumes") + private String volumes; + + @Column(name = "status") + private Status status; + + @Column(name = "start") + private Date start; + + private List volumeIds; + + @Override + public Long getId() { + return id; + } + + @Override + public String getUuid() { + return uuid; + } + + @Override + public Long getAccountId() { + return accountId; + } + + @Override + public Long getUserId() { + return userId; + } + + @Override + public String getName() { + return name; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public Long getParentId() { + return parentId; + } + + @Override + public Long getVMId() { + return vmId; + } + + @Override + public Status getStatus() { + return status; + } + + @Override + public Date getStartTime() { + return start; + } + + public void setId(long id) { + this.id = id; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + public void setAccountId(long accountId) { + this.accountId = accountId; + } + + public void setUserId(long userId) { + this.userId = userId; + } + + public void setName(String name) { + this.name = name; + } + + public void setDescription(String description) { + this.description = description; + } + + public void setParentId(Long parentId) { + this.parentId = parentId; + } + + public void setVmId(long vmId) { + this.vmId = vmId; + } + + public void setStatus(Status status) { + this.status = status; + } + + public void setStart(Date start) { + this.start = start; + } + + protected void convertVolumeStringToList() { + volumeIds = new ArrayList<>(); + if (StringUtils.isNotBlank(volumes)) { + String[] strIds = StringUtils.substringBetween(volumes,"[", "]").split(","); + for (String strId: strIds) { + volumeIds.add(Long.valueOf(strId)); + } + } + } + + @Override + public List getVolumeIds() { + convertVolumeStringToList(); + return volumeIds; + } + + public void setVolumeIds(List volumes) { + if (CollectionUtils.isEmpty(volumes)) { + this.volumeIds = new ArrayList<>(); + } else { + this.volumeIds = new ArrayList<>(volumes); + } + convertVolumeIdsToString(); + } + + private void convertVolumeIdsToString() { + StringJoiner stringJoiner = new StringJoiner(",", "[", "]"); + if (CollectionUtils.isNotEmpty(this.volumeIds)) { + for (Long volId : this.volumeIds) { + stringJoiner.add(String.valueOf(volId)); + } + } + this.volumes = stringJoiner.toString(); + } + + protected String getVolumes() { + return volumes; + } + + protected void setVolumes(String volumes) { + this.volumes = volumes; + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupDao.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupDao.java new file mode 100644 index 00000000000..c031ff08a58 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupDao.java @@ -0,0 +1,33 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.backup.dao; + +import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.api.response.BackupResponse; +import org.apache.cloudstack.backup.BackupVO; +import org.apache.cloudstack.framework.backup.Backup; + +import java.util.List; + +public interface BackupDao extends GenericDao { + + List listByVmId(Long vmId); + List listByUserId(Long userId); + + BackupResponse newBackupResponse(Backup backup); +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupDaoImpl.java new file mode 100644 index 00000000000..8dd101ad027 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupDaoImpl.java @@ -0,0 +1,110 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.backup.dao; + +import com.cloud.storage.VolumeVO; +import com.cloud.storage.dao.VolumeDao; +import com.cloud.user.AccountVO; +import com.cloud.user.UserVO; +import com.cloud.user.dao.AccountDao; +import com.cloud.user.dao.UserDao; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import com.cloud.vm.VMInstanceVO; +import com.cloud.vm.dao.VMInstanceDao; +import org.apache.cloudstack.api.response.BackupResponse; +import org.apache.cloudstack.backup.BackupVO; +import org.apache.cloudstack.framework.backup.Backup; +import org.apache.commons.collections.CollectionUtils; + +import javax.annotation.PostConstruct; +import javax.inject.Inject; +import java.util.ArrayList; +import java.util.List; + +public class BackupDaoImpl extends GenericDaoBase implements BackupDao { + + @Inject + AccountDao accountDao; + + @Inject + UserDao userDao; + + @Inject + VMInstanceDao vmInstanceDao; + + @Inject + VolumeDao volumeDao; + + private SearchBuilder backupSearch; + + public BackupDaoImpl() { + } + + @PostConstruct + protected void init() { + backupSearch = createSearchBuilder(); + backupSearch.and("vm_id", backupSearch.entity().getVMId(), SearchCriteria.Op.EQ); + backupSearch.and("user_id", backupSearch.entity().getUserId(), SearchCriteria.Op.EQ); + backupSearch.done(); + } + @Override + public List listByVmId(Long vmId) { + SearchCriteria sc = backupSearch.create(); + sc.setParameters("vm_id", vmId); + return new ArrayList<>(listBy(sc)); + } + + @Override + public List listByUserId(Long userId) { + SearchCriteria sc = backupSearch.create(); + sc.setParameters("user_id", userId); + return new ArrayList<>(listBy(sc)); + } + + @Override + public BackupResponse newBackupResponse(Backup backup) { + AccountVO account = accountDao.findById(backup.getAccountId()); + UserVO user = userDao.findById(backup.getUserId()); + BackupVO parent = findById(backup.getParentId()); + VMInstanceVO vm = vmInstanceDao.findById(backup.getVMId()); + + BackupResponse backupResponse = new BackupResponse(); + backupResponse.setId(backup.getUuid()); + backupResponse.setAccountId(account.getUuid()); + backupResponse.setUserId(user.getUuid()); + backupResponse.setName(backup.getName()); + backupResponse.setDescription(backup.getDescription()); + if (parent != null) { + backupResponse.setParentId(parent.getUuid()); + } + backupResponse.setVmId(vm.getUuid()); + if (CollectionUtils.isNotEmpty(backup.getVolumeIds())) { + List volIds = new ArrayList<>(); + for (Long volId : backup.getVolumeIds()) { + VolumeVO volume = volumeDao.findById(volId); + volIds.add(volume.getUuid()); + } + backupResponse.setVolumeIds(volIds); + } + backupResponse.setStatus(backup.getStatus()); + backupResponse.setObjectName("backup"); + return backupResponse; + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupPolicyDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupPolicyDaoImpl.java index 54f20f82df9..dea8b00ee3c 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupPolicyDaoImpl.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupPolicyDaoImpl.java @@ -28,7 +28,7 @@ import com.cloud.utils.db.SearchBuilder; @Component public class BackupPolicyDaoImpl extends GenericDaoBase implements BackupPolicyDao { - protected SearchBuilder backupPoliciesSearch; + private SearchBuilder backupPoliciesSearch; public BackupPolicyDaoImpl() { } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupPolicyVMMapDao.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupPolicyVMMapDao.java new file mode 100644 index 00000000000..bcb8cc0e11a --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupPolicyVMMapDao.java @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.cloudstack.backup.dao; + +import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.backup.BackupPolicyVMMapVO; + +import java.util.List; + +public interface BackupPolicyVMMapDao extends GenericDao { + + BackupPolicyVMMapVO findByVMId(long vmId); + List listByPolicyId(long policyId); +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupPolicyVMMapDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupPolicyVMMapDaoImpl.java new file mode 100644 index 00000000000..a0f5e3c208d --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupPolicyVMMapDaoImpl.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.cloudstack.backup.dao; + +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.exception.CloudRuntimeException; +import org.apache.cloudstack.backup.BackupPolicyVMMapVO; +import org.apache.commons.collections.CollectionUtils; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import java.util.List; + +@Component +public class BackupPolicyVMMapDaoImpl extends GenericDaoBase implements BackupPolicyVMMapDao { + + private SearchBuilder mapSearch; + + public BackupPolicyVMMapDaoImpl() { + } + + @PostConstruct + protected void init() { + mapSearch = createSearchBuilder(); + mapSearch.and("vm_id", mapSearch.entity().getVmId(), SearchCriteria.Op.EQ); + mapSearch.and("policy_id", mapSearch.entity().getPolicyId(), SearchCriteria.Op.EQ); + mapSearch.done(); + } + + @Override + public BackupPolicyVMMapVO findByVMId(long vmId) { + SearchCriteria sc = mapSearch.create(); + sc.setParameters("vm_id", vmId); + List maps = listBy(sc); + if (CollectionUtils.isNotEmpty(maps)) { + if (maps.size() > 1) { + throw new CloudRuntimeException("Error: Vm " + vmId + " is assigned to multiple policies"); + } + return maps.get(0); + } + return null; + } + + @Override + public List listByPolicyId(long policyId) { + SearchCriteria sc = mapSearch.create(); + sc.setParameters("policy_id", policyId); + return listBy(sc); + } +} diff --git a/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml b/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml index 0809aa7d7ef..011075ae7c1 100644 --- a/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml +++ b/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml @@ -356,5 +356,7 @@ - + + + diff --git a/engine/schema/src/main/resources/META-INF/db/schema-41110to41200.sql b/engine/schema/src/main/resources/META-INF/db/schema-41110to41200.sql index f9b85124933..c599caacad5 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-41110to41200.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-41110to41200.sql @@ -41,6 +41,35 @@ CREATE TABLE IF NOT EXISTS `cloud`.`backup_policy` ( `uuid` varchar(40) NOT NULL, `name` varchar(255) NOT NULL COMMENT 'backup policy name', `policy_uuid` varchar(40) NOT NULL COMMENT 'backup policy ID on provider side', + `imported` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT 'true if policy has been imported from the backup provider', PRIMARY KEY (`id`), UNIQUE KEY `uuid` (`uuid`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE IF NOT EXISTS `cloud`.`backup_policy_vm_map` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `policy_id` bigint(20) unsigned NOT NULL, + `vm_id` bigint(20) unsigned NOT NULL, + PRIMARY KEY (`id`), + CONSTRAINT `fk_backup_policy_vm_map__policy_id` FOREIGN KEY (`policy_id`) REFERENCES `backup_policy` (`id`) ON DELETE CASCADE, + CONSTRAINT `fk_backup_policy_vm_map__vm_id` FOREIGN KEY (`vm_id`) REFERENCES `vm_instance` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE IF NOT EXISTS `cloud`.`backup` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `uuid` varchar(40) NOT NULL, + `account_id` bigint(20) unsigned NOT NULL, + `user_id` bigint(20) unsigned NOT NULL, + `name` varchar(255) NOT NULL COMMENT 'backup name', + `description` varchar(255) COMMENT 'backup description', + `parent_id` bigint(20) unsigned COMMENT 'backup parent id', + `vm_id` bigint(20) unsigned NOT NULL, + `volumes` varchar(100), + `status` varchar(20) NOT NULL, + `start` timestamp, + PRIMARY KEY (`id`), + CONSTRAINT `fk_backup__account_id` FOREIGN KEY (`account_id`) REFERENCES `account` (`id`) ON DELETE CASCADE, + CONSTRAINT `fk_backup__user_id` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE CASCADE, + CONSTRAINT `fk_backup__parent_id` FOREIGN KEY (`parent_id`) REFERENCES `backup` (`id`) ON DELETE CASCADE, + CONSTRAINT `fk_backup__vm_id` FOREIGN KEY (`vm_id`) REFERENCES `vm_instance` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8; \ No newline at end of file diff --git a/engine/schema/src/test/java/org/apache/cloudstack/backup/BackupVOTest.java b/engine/schema/src/test/java/org/apache/cloudstack/backup/BackupVOTest.java new file mode 100644 index 00000000000..06295ff93bf --- /dev/null +++ b/engine/schema/src/test/java/org/apache/cloudstack/backup/BackupVOTest.java @@ -0,0 +1,61 @@ +//Licensed to the Apache Software Foundation (ASF) under one +//or more contributor license agreements. See the NOTICE file +//distributed with this work for additional information +//regarding copyright ownership. The ASF licenses this file +//to you under the Apache License, Version 2.0 (the +//"License"); you may not use this file except in compliance +//the License. You may obtain a copy of the License at +// +//http://www.apache.org/licenses/LICENSE-2.0 +// +//Unless required by applicable law or agreed to in writing, +//software distributed under the License is distributed on an +//"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +//KIND, either express or implied. See the License for the +//specific language governing permissions and limitations +//under the License. + +package org.apache.cloudstack.backup; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +@RunWith(JUnit4.class) +public class BackupVOTest { + + private BackupVO vo = new BackupVO(); + + private final List ids = Arrays.asList(1L, 2L, 3L, 4L); + + @Test + public void testVolumeIdsNotEmptyList() { + vo.setVolumeIds(ids); + Assert.assertEquals("[1,2,3,4]", vo.getVolumes()); + Assert.assertEquals(ids, vo.getVolumeIds()); + } + + @Test + public void testVolumeIdsEmptyList() { + vo.setVolumeIds(new ArrayList<>()); + Assert.assertEquals("[]", vo.getVolumes()); + Assert.assertEquals(new ArrayList<>(), vo.getVolumeIds()); + } + + @Test + public void testVolumeIdsUnsetVolumeIds() { + Assert.assertEquals(null, vo.getVolumes()); + Assert.assertEquals(new ArrayList<>(), vo.getVolumeIds()); + } + + @Test + public void testDecodeVolumesString() { + vo.setVolumes("[1,2,3,4]"); + Assert.assertEquals(ids, vo.getVolumeIds()); + } +} diff --git a/framework/backup/src/main/java/org/apache/cloudstack/framework/backup/Backup.java b/framework/backup/src/main/java/org/apache/cloudstack/framework/backup/Backup.java new file mode 100644 index 00000000000..463593f8035 --- /dev/null +++ b/framework/backup/src/main/java/org/apache/cloudstack/framework/backup/Backup.java @@ -0,0 +1,40 @@ +//Licensed to the Apache Software Foundation (ASF) under one +//or more contributor license agreements. See the NOTICE file +//distributed with this work for additional information +//regarding copyright ownership. The ASF licenses this file +//to you under the Apache License, Version 2.0 (the +//"License"); you may not use this file except in compliance +//the License. You may obtain a copy of the License at +// +//http://www.apache.org/licenses/LICENSE-2.0 +// +//Unless required by applicable law or agreed to in writing, +//software distributed under the License is distributed on an +//"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +//KIND, either express or implied. See the License for the +//specific language governing permissions and limitations +//under the License. + +package org.apache.cloudstack.framework.backup; + +import java.util.Date; +import java.util.List; + +public interface Backup { + + enum Status { + BackingUp, BackedUp, Failed, Queued, Restoring + } + + Long getId(); + String getUuid(); + Long getAccountId(); + Long getUserId(); + String getName(); + String getDescription(); + Long getParentId(); + Long getVMId(); + List getVolumeIds(); + Status getStatus(); + Date getStartTime(); +} diff --git a/framework/backup/src/main/java/org/apache/cloudstack/framework/backup/BackupPolicy.java b/framework/backup/src/main/java/org/apache/cloudstack/framework/backup/BackupPolicy.java index d07a36d66cc..1fa693273c4 100644 --- a/framework/backup/src/main/java/org/apache/cloudstack/framework/backup/BackupPolicy.java +++ b/framework/backup/src/main/java/org/apache/cloudstack/framework/backup/BackupPolicy.java @@ -22,6 +22,6 @@ public interface BackupPolicy { String getUuid(); String getPolicyUuid(); String getName(); - boolean isExternal(); + boolean isImported(); } diff --git a/framework/backup/src/main/java/org/apache/cloudstack/framework/backup/BackupProvider.java b/framework/backup/src/main/java/org/apache/cloudstack/framework/backup/BackupProvider.java index f3d9b136ae0..6e08a5366a3 100644 --- a/framework/backup/src/main/java/org/apache/cloudstack/framework/backup/BackupProvider.java +++ b/framework/backup/src/main/java/org/apache/cloudstack/framework/backup/BackupProvider.java @@ -48,4 +48,9 @@ public interface BackupProvider { * True if policy with id uuid exists on the backup provider */ boolean isBackupPolicy(String uuid); + + /** + * Restore VM from backup + */ + boolean restoreVMFromBackup(String vmUuid, String backupUuid); } diff --git a/plugins/backup/dummy/src/main/java/org/apache/cloudstack/backup/DummyBackupProvider.java b/plugins/backup/dummy/src/main/java/org/apache/cloudstack/backup/DummyBackupProvider.java index 42d5cfddac0..c8e8b4eb605 100644 --- a/plugins/backup/dummy/src/main/java/org/apache/cloudstack/backup/DummyBackupProvider.java +++ b/plugins/backup/dummy/src/main/java/org/apache/cloudstack/backup/DummyBackupProvider.java @@ -58,4 +58,10 @@ public class DummyBackupProvider extends AdapterBase implements BackupProvider { s_logger.debug("Checking if backup policy exists on the Dummy Backup Provider"); return true; } + + @Override + public boolean restoreVMFromBackup(String vmUuid, String backupUuid) { + s_logger.debug("Restoring vm " + vmUuid + "from backup " + backupUuid + " on the Dummy Backup Provider"); + return true; + } } diff --git a/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/VeeamBackupProvider.java b/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/VeeamBackupProvider.java index 774d8bfa0f0..5e43a314e4a 100644 --- a/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/VeeamBackupProvider.java +++ b/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/VeeamBackupProvider.java @@ -85,6 +85,11 @@ public class VeeamBackupProvider extends AdapterBase implements BackupProvider, return false; } + @Override + public boolean restoreVMFromBackup(String vmUuid, String backupUuid) { + return false; + } + @Override public String getConfigComponentName() { return BackupService.class.getSimpleName(); diff --git a/server/src/main/java/com/cloud/api/ApiDBUtils.java b/server/src/main/java/com/cloud/api/ApiDBUtils.java index 22ed2437d4d..375d832c4f7 100644 --- a/server/src/main/java/com/cloud/api/ApiDBUtils.java +++ b/server/src/main/java/com/cloud/api/ApiDBUtils.java @@ -39,6 +39,7 @@ import org.apache.cloudstack.api.ApiConstants.VMDetails; import org.apache.cloudstack.api.ResponseObject.ResponseView; import org.apache.cloudstack.api.response.AccountResponse; import org.apache.cloudstack.api.response.AsyncJobResponse; +import org.apache.cloudstack.api.response.BackupResponse; import org.apache.cloudstack.api.response.DiskOfferingResponse; import org.apache.cloudstack.api.response.DomainResponse; import org.apache.cloudstack.api.response.DomainRouterResponse; @@ -61,9 +62,11 @@ import org.apache.cloudstack.api.response.UserResponse; import org.apache.cloudstack.api.response.UserVmResponse; import org.apache.cloudstack.api.response.VolumeResponse; import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.cloudstack.backup.dao.BackupDao; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService; +import org.apache.cloudstack.framework.backup.Backup; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.framework.jobs.AsyncJob; import org.apache.cloudstack.framework.jobs.AsyncJobManager; @@ -434,6 +437,7 @@ public class ApiDBUtils { static ResourceMetaDataService s_resourceDetailsService; static HostGpuGroupsDao s_hostGpuGroupsDao; static VGPUTypesDao s_vgpuTypesDao; + static BackupDao s_backupDao; @Inject private ManagementServer ms; @@ -666,6 +670,8 @@ public class ApiDBUtils { private HostGpuGroupsDao hostGpuGroupsDao; @Inject private VGPUTypesDao vgpuTypesDao; + @Inject + private BackupDao backupDao; @PostConstruct void init() { @@ -785,6 +791,7 @@ public class ApiDBUtils { s_resourceDetailsService = resourceDetailsService; s_hostGpuGroupsDao = hostGpuGroupsDao; s_vgpuTypesDao = vgpuTypesDao; + s_backupDao = backupDao; } // /////////////////////////////////////////////////////////// @@ -2000,4 +2007,8 @@ public class ApiDBUtils { public static List listResourceTagViewByResourceUUID(String resourceUUID, ResourceObjectType resourceType) { return s_tagJoinDao.listBy(resourceUUID, resourceType); } + + public static BackupResponse newBackupResponse(Backup backup) { + return s_backupDao.newBackupResponse(backup); + } } diff --git a/server/src/main/java/com/cloud/api/ApiResponseHelper.java b/server/src/main/java/com/cloud/api/ApiResponseHelper.java index 4d7de2a1a3f..eeb1e4c5a56 100644 --- a/server/src/main/java/com/cloud/api/ApiResponseHelper.java +++ b/server/src/main/java/com/cloud/api/ApiResponseHelper.java @@ -199,6 +199,7 @@ import org.apache.cloudstack.api.response.AsyncJobResponse; import org.apache.cloudstack.api.response.AutoScalePolicyResponse; import org.apache.cloudstack.api.response.AutoScaleVmGroupResponse; import org.apache.cloudstack.api.response.AutoScaleVmProfileResponse; +import org.apache.cloudstack.api.response.BackupResponse; import org.apache.cloudstack.api.response.CapabilityResponse; import org.apache.cloudstack.api.response.CapacityResponse; import org.apache.cloudstack.api.response.ClusterResponse; @@ -296,6 +297,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreCapabilities; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotDataFactory; import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; +import org.apache.cloudstack.framework.backup.Backup; import org.apache.cloudstack.framework.jobs.AsyncJob; import org.apache.cloudstack.framework.jobs.AsyncJobManager; import org.apache.cloudstack.network.lb.ApplicationLoadBalancerRule; @@ -3964,4 +3966,9 @@ public class ApiResponseHelper implements ResponseGenerator { response.setDomainName(domain.getName()); return response; } + + @Override + public BackupResponse createBackupResponse(Backup backup) { + return ApiDBUtils.newBackupResponse(backup); + } } diff --git a/server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java b/server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java index 828b41753a8..ae12ed4f32c 100644 --- a/server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java @@ -27,17 +27,23 @@ import javax.naming.ConfigurationException; import com.cloud.vm.VMInstanceVO; import com.cloud.vm.dao.VMInstanceDao; import org.apache.cloudstack.api.command.admin.backup.DeleteBackupPolicyCmd; -import org.apache.cloudstack.api.command.admin.backup.ListBackupProviderPoliciesCmd; import org.apache.cloudstack.api.command.user.backup.AssignBackupPolicyCmd; import org.apache.cloudstack.api.command.admin.backup.CreateBackupPolicyCmd; import org.apache.cloudstack.api.command.user.backup.ListBackupPoliciesCmd; import org.apache.cloudstack.api.command.admin.backup.ListBackupProvidersCmd; +import org.apache.cloudstack.api.command.user.backup.ListBackupsCmd; +import org.apache.cloudstack.api.command.user.backup.RestoreBackupCmd; +import org.apache.cloudstack.api.command.user.backup.RestoreBackupVolumeCmd; import org.apache.cloudstack.api.response.BackupPolicyResponse; +import org.apache.cloudstack.backup.dao.BackupDao; import org.apache.cloudstack.backup.dao.BackupPolicyDao; +import org.apache.cloudstack.backup.dao.BackupPolicyVMMapDao; +import org.apache.cloudstack.framework.backup.Backup; import org.apache.cloudstack.framework.backup.BackupPolicy; import org.apache.cloudstack.framework.backup.BackupProvider; import org.apache.cloudstack.framework.backup.BackupService; import org.apache.cloudstack.framework.config.ConfigKey; +import org.apache.commons.lang.BooleanUtils; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; @@ -58,6 +64,12 @@ public class BackupManagerImpl extends ManagerBase implements BackupManager { @Inject DataCenterDao dataCenterDao; + @Inject + BackupPolicyVMMapDao backupPolicyVMMapDao; + + @Inject + BackupDao backupDao; + private static Map backupProvidersMap = new HashMap<>(); private List backupProviders; @@ -83,23 +95,55 @@ public class BackupManagerImpl extends ManagerBase implements BackupManager { if (vmInstanceVO == null) { throw new CloudRuntimeException("VM " + virtualMachineId + " does not exist"); } + BackupPolicyVO policy = backupPolicyDao.findByUuid(policyUuid); + if (policy == null) { + throw new CloudRuntimeException("Policy " + policyUuid + " does not exist"); + } String vmUuid = vmInstanceVO.getUuid(); BackupProvider backupProvider = getBackupProvider(zoneId); - if (backupProvider == null) { - throw new CloudRuntimeException("Could not find a backup provider on zone " + zoneId); + boolean result = backupProvider.assignVMToBackupPolicy(vmUuid, policyUuid); + if (result) { + BackupPolicyVMMapVO map = backupPolicyVMMapDao.findByVMId(virtualMachineId); + if (map != null) { + backupPolicyVMMapDao.expunge(map.getId()); + } + map = new BackupPolicyVMMapVO(policy.getId(), virtualMachineId); + backupPolicyVMMapDao.persist(map); + LOG.debug("Successfully assigned VM " + virtualMachineId + " to backup policy " + policy.getName()); + } else { + LOG.debug("Could not assign VM " + virtualMachineId + " to backup policy " + policyUuid); } - return backupProvider.assignVMToBackupPolicy(vmUuid, policyUuid); + return result; } @Override - public List listBackupPolicies() { + public List listBackups(Long vmId) { + return backupDao.listByVmId(vmId); + } + + @Override + public List listBackupPolicies(Long zoneId, Boolean external) { + if (BooleanUtils.isTrue(external)) { + BackupProvider backupProvider = getBackupProvider(zoneId); + return backupProvider.listBackupPolicies(); + } return new ArrayList<>(backupPolicyDao.listAll()); } @Override - public List listBackupProviderPolicies(Long zoneId) { + public boolean restoreBackup(Long vmId, String backupId, Long zoneId) { BackupProvider backupProvider = getBackupProvider(zoneId); - return backupProvider.listBackupPolicies(); + VMInstanceVO vm = vmInstanceDao.findById(vmId); + if (vm == null) { + throw new CloudRuntimeException("VM " + vmId + " does not exist"); + } + return backupProvider.restoreVMFromBackup(vm.getUuid(), backupId); + } + + @Override + public boolean restoreBackupVolume(Long volumeId, Long vmId, String backupId, Long zoneId) { + //TODO + return false; } @Override @@ -147,8 +191,10 @@ public class BackupManagerImpl extends ManagerBase implements BackupManager { cmdList.add(ListBackupPoliciesCmd.class); cmdList.add(CreateBackupPolicyCmd.class); cmdList.add(AssignBackupPolicyCmd.class); - cmdList.add(ListBackupProviderPoliciesCmd.class); cmdList.add(DeleteBackupPolicyCmd.class); + cmdList.add(ListBackupsCmd.class); + cmdList.add(RestoreBackupCmd.class); + cmdList.add(RestoreBackupVolumeCmd.class); return cmdList; }