Refactor and marvin tests stabilization

This commit is contained in:
nvazquez 2018-06-09 20:34:31 -03:00
parent 279bc68706
commit 1caa1173c7
24 changed files with 387 additions and 154 deletions

View File

@ -584,6 +584,15 @@ public class EventTypes {
public static final String EVENT_TEMPLATE_DIRECT_DOWNLOAD_FAILURE = "TEMPLATE.DIRECT.DOWNLOAD.FAILURE";
public static final String EVENT_ISO_DIRECT_DOWNLOAD_FAILURE = "ISO.DIRECT.DOWNLOAD.FAILURE";
// Backup and Recovery events
public static final String EVENT_ADD_VM_TO_BACKUP_POLICY = "ADD.VM.TO.BACKUP.POLICY";
public static final String EVENT_REMOVE_VM_FROM_BACKUP_POLICY = "REMOVE.VM.FROM.BACKUP.POLICY";
public static final String EVENT_IMPORT_BACKUP_POLICY = "IMPORT.BACKUP.POLICY";
public static final String EVENT_CREATE_VM_BACKUP = "CREATE.VM.BACKUP";
public static final String EVENT_DELETE_VM_BACKUP = "DELETE.VM.BACKUP";
public static final String EVENT_RESTORE_VM_FROM_BACKUP = "RESTORE.VM.FROM.BACKUP";
public static final String EVENT_RESTORE_VOLUME_FROM_BACKUP_AND_ATTACH_TO_VM = "RESTORE.VOLUME.FROM.BACKUP.AND.ATTACH.TO.VM";
static {
// TODO: need a way to force author adding event types to declare the entity details as well, with out braking

View File

@ -18,10 +18,12 @@ package org.apache.cloudstack.api.command.admin.backup;
import javax.inject.Inject;
import com.cloud.event.EventTypes;
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.BaseAsyncCmd;
import org.apache.cloudstack.api.BaseCmd;
import org.apache.cloudstack.api.Parameter;
import org.apache.cloudstack.api.ServerApiException;
@ -43,7 +45,7 @@ import com.cloud.utils.exception.CloudRuntimeException;
description = "Imports a backup policy from the backup provider",
responseObject = BackupPolicyResponse.class, since = "4.12.0",
authorized = {RoleType.Admin})
public class ImportBackupPolicyCmd extends BaseCmd {
public class ImportBackupPolicyCmd extends BaseAsyncCmd {
public static final String APINAME = "importBackupPolicy";
@Inject
@ -122,4 +124,14 @@ public class ImportBackupPolicyCmd extends BaseCmd {
public long getEntityOwnerId() {
return CallContext.current().getCallingAccount().getId();
}
@Override
public String getEventType() {
return EventTypes.EVENT_IMPORT_BACKUP_POLICY;
}
@Override
public String getEventDescription() {
return "Importing backup policy: " + policyName + " (externalId=" + policyExternalId + ") on zone " + zoneId ;
}
}

View File

@ -18,11 +18,12 @@ package org.apache.cloudstack.api.command.user.backup;
import javax.inject.Inject;
import com.cloud.event.EventTypes;
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.BaseCmd;
import org.apache.cloudstack.api.BaseAsyncCmd;
import org.apache.cloudstack.api.Parameter;
import org.apache.cloudstack.api.ServerApiException;
import org.apache.cloudstack.api.response.BackupPolicyResponse;
@ -41,7 +42,7 @@ import com.cloud.exception.ResourceUnavailableException;
description = "Assigns a VM to an existing backup policy",
responseObject = SuccessResponse.class, since = "4.12.0",
authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
public class AddVMToBackupPolicyCmd extends BaseCmd {
public class AddVMToBackupPolicyCmd extends BaseAsyncCmd {
public static final String APINAME = "addVMToBackupPolicy";
@Inject
@ -106,4 +107,14 @@ public class AddVMToBackupPolicyCmd extends BaseCmd {
public long getEntityOwnerId() {
return CallContext.current().getCallingAccount().getId();
}
@Override
public String getEventType() {
return EventTypes.EVENT_ADD_VM_TO_BACKUP_POLICY;
}
@Override
public String getEventDescription() {
return "Adding VM " + vmId + " to backup policy " + policyId;
}
}

View File

@ -19,15 +19,18 @@ package org.apache.cloudstack.api.command.user.backup;
import javax.inject.Inject;
import com.cloud.event.EventTypes;
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.BaseAsyncCmd;
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.BackupResponse;
import org.apache.cloudstack.api.response.UserVmResponse;
import org.apache.cloudstack.backup.Backup;
import org.apache.cloudstack.backup.BackupManager;
import org.apache.cloudstack.context.CallContext;
@ -40,9 +43,9 @@ import com.cloud.utils.exception.CloudRuntimeException;
@APICommand(name = CreateVMBackupCmd.APINAME,
description = "Create VM backup",
responseObject = SuccessResponse.class, since = "4.12.0",
responseObject = BackupResponse.class, since = "4.12.0",
authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
public class CreateVMBackupCmd extends BaseCmd {
public class CreateVMBackupCmd extends BaseAsyncCmd {
public static final String APINAME = "createVMBackup";
@Inject
@ -76,10 +79,9 @@ public class CreateVMBackupCmd extends BaseCmd {
@Override
public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException {
try {
boolean result = backupManager.createBackup(vmId);
// FIXME: the response type
if (result) {
SuccessResponse response = new SuccessResponse(getCommandName());
Backup backup = backupManager.createBackup(vmId);
if (backup != null) {
BackupResponse response = _responseGenerator.createBackupResponse(backup);
response.setResponseName(getCommandName());
setResponseObject(response);
} else {
@ -99,4 +101,14 @@ public class CreateVMBackupCmd extends BaseCmd {
public long getEntityOwnerId() {
return CallContext.current().getCallingAccount().getId();
}
@Override
public String getEventType() {
return EventTypes.EVENT_CREATE_VM_BACKUP;
}
@Override
public String getEventDescription() {
return "Creating backup for VM " + vmId;
}
}

View File

@ -26,8 +26,8 @@ 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.BackupResponse;
import org.apache.cloudstack.api.response.SuccessResponse;
import org.apache.cloudstack.api.response.UserVmResponse;
import org.apache.cloudstack.backup.BackupManager;
import org.apache.cloudstack.context.CallContext;
@ -52,19 +52,19 @@ public class DeleteVMBackupCmd extends BaseCmd {
//////////////// API parameters /////////////////////
/////////////////////////////////////////////////////
@Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID,
@Parameter(name = ApiConstants.ID,
type = CommandType.UUID,
entityType = UserVmResponse.class,
entityType = BackupResponse.class,
required = true,
description = "id of the VM")
private Long vmId;
description = "id of backup")
private Long backupId;
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
public Long getVmId() {
return vmId;
public Long getId() {
return backupId;
}
/////////////////////////////////////////////////////
@ -74,7 +74,7 @@ public class DeleteVMBackupCmd extends BaseCmd {
@Override
public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException {
try {
boolean result = backupManager.deleteBackup(vmId);
boolean result = backupManager.deleteBackup(backupId);
// FIXME: the response type
if (result) {
SuccessResponse response = new SuccessResponse(getCommandName());

View File

@ -31,7 +31,6 @@ 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.api.response.ZoneResponse;
import org.apache.cloudstack.backup.Backup;
import org.apache.cloudstack.backup.BackupManager;
import org.apache.cloudstack.context.CallContext;
@ -62,10 +61,6 @@ public class ListVMBackupsCmd extends BaseBackupListCmd {
description = "id of the VM")
private Long vmId;
@Parameter(name = ApiConstants.ZONE_ID, type = BaseCmd.CommandType.UUID, entityType = ZoneResponse.class,
description = "The zone ID")
private Long zoneId;
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
@ -74,10 +69,6 @@ public class ListVMBackupsCmd extends BaseBackupListCmd {
return vmId;
}
public Long getZoneId() {
return zoneId;
}
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////
@ -85,7 +76,7 @@ public class ListVMBackupsCmd extends BaseBackupListCmd {
@Override
public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException {
try{
List<Backup> backups = backupManager.listVMBackups(getZoneId(), getVmId());
List<Backup> backups = backupManager.listVMBackups(getVmId());
setupResponseBackupList(backups);
} catch (Exception e) {
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage());

View File

@ -19,11 +19,12 @@ package org.apache.cloudstack.api.command.user.backup;
import javax.inject.Inject;
import com.cloud.event.EventTypes;
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.BaseCmd;
import org.apache.cloudstack.api.BaseAsyncCmd;
import org.apache.cloudstack.api.Parameter;
import org.apache.cloudstack.api.ServerApiException;
import org.apache.cloudstack.api.response.BackupPolicyResponse;
@ -42,7 +43,7 @@ import com.cloud.exception.ResourceUnavailableException;
description = "Removes a VM from an existing backup policy",
responseObject = SuccessResponse.class, since = "4.12.0",
authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
public class RemoveVMFromBackupPolicyCmd extends BaseCmd {
public class RemoveVMFromBackupPolicyCmd extends BaseAsyncCmd {
public static final String APINAME = "removeVMFromBackupPolicy";
@Inject
@ -108,4 +109,13 @@ public class RemoveVMFromBackupPolicyCmd extends BaseCmd {
return CallContext.current().getCallingAccount().getId();
}
@Override
public String getEventType() {
return EventTypes.EVENT_REMOVE_VM_FROM_BACKUP_POLICY;
}
@Override
public String getEventDescription() {
return "Removing VM " + vmId + " from backup policy " + policyId;
}
}

View File

@ -19,17 +19,17 @@ package org.apache.cloudstack.api.command.user.backup;
import javax.inject.Inject;
import com.cloud.event.EventTypes;
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.BaseAsyncCmd;
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.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;
@ -40,12 +40,12 @@ import com.cloud.exception.ResourceAllocationException;
import com.cloud.exception.ResourceUnavailableException;
import com.cloud.utils.exception.CloudRuntimeException;
@APICommand(name = RestoreVMBackupCmd.APINAME,
description = "Restore VM backup",
@APICommand(name = RestoreVMFromBackupCmd.APINAME,
description = "Restore VM from backup",
responseObject = SuccessResponse.class, since = "4.12.0",
authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
public class RestoreVMBackupCmd extends BaseCmd {
public static final String APINAME = "restoreVMBackup";
public class RestoreVMFromBackupCmd extends BaseAsyncCmd {
public static final String APINAME = "restoreVMFromBackup";
@Inject
private BackupManager backupManager;
@ -61,34 +61,14 @@ public class RestoreVMBackupCmd extends BaseCmd {
description = "id of the backup")
private Long backupId;
//FIXME: is this necessary when backup id is known? unless we want to restore to a different VM?
@Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID,
type = CommandType.UUID,
entityType = UserVmResponse.class,
required = true,
description = "id of the VM")
private Long vmId;
@Parameter(name = ApiConstants.ZONE_ID, type = BaseCmd.CommandType.UUID, entityType = ZoneResponse.class,
description = "The zone ID")
private Long zoneId;
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
public Long getVmId() {
return vmId;
}
public Long getBackupId() {
return backupId;
}
public Long getZoneId() {
return zoneId;
}
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////
@ -96,7 +76,7 @@ public class RestoreVMBackupCmd extends BaseCmd {
@Override
public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException {
try {
boolean result = backupManager.restoreBackup(zoneId, vmId, backupId);
boolean result = backupManager.restoreVMFromBackup(backupId);
if (result) {
SuccessResponse response = new SuccessResponse(getCommandName());
response.setResponseName(getCommandName());
@ -119,4 +99,13 @@ public class RestoreVMBackupCmd extends BaseCmd {
return CallContext.current().getCallingAccount().getId();
}
@Override
public String getEventType() {
return EventTypes.EVENT_RESTORE_VM_FROM_BACKUP;
}
@Override
public String getEventDescription() {
return "Restoring VM from backup " + backupId;
}
}

View File

@ -19,10 +19,12 @@ package org.apache.cloudstack.api.command.user.backup;
import javax.inject.Inject;
import com.cloud.event.EventTypes;
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.BaseAsyncCmd;
import org.apache.cloudstack.api.BaseCmd;
import org.apache.cloudstack.api.Parameter;
import org.apache.cloudstack.api.ServerApiException;
@ -30,7 +32,6 @@ import org.apache.cloudstack.api.response.BackupResponse;
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;
@ -41,12 +42,12 @@ import com.cloud.exception.ResourceAllocationException;
import com.cloud.exception.ResourceUnavailableException;
import com.cloud.utils.exception.CloudRuntimeException;
@APICommand(name = RestoreBackupVolumeCmd.APINAME,
@APICommand(name = RestoreVolumeFromBackupAndAttachToVMCmd.APINAME,
description = "Restore and attach a backed up volume to VM",
responseObject = SuccessResponse.class, since = "4.12.0",
authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
public class RestoreBackupVolumeCmd extends BaseCmd {
public static final String APINAME = "restoreBackupVolumeAndAttachToVM";
public class RestoreVolumeFromBackupAndAttachToVMCmd extends BaseAsyncCmd {
public static final String APINAME = "restoreVolumeFromBackupAndAttachToVM";
@Inject
private BackupManager backupManager;
@ -79,10 +80,6 @@ public class RestoreBackupVolumeCmd extends BaseCmd {
description = "id of the VM where to attach the restored volume")
private Long vmId;
@Parameter(name = ApiConstants.ZONE_ID, type = BaseCmd.CommandType.UUID, entityType = ZoneResponse.class,
description = "The zone ID")
private Long zoneId;
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
@ -99,10 +96,6 @@ public class RestoreBackupVolumeCmd extends BaseCmd {
return backupId;
}
public Long getZoneId() {
return zoneId;
}
@Override
public String getCommandName() {
return APINAME.toLowerCase() + BaseCmd.RESPONSE_SUFFIX;
@ -120,7 +113,7 @@ public class RestoreBackupVolumeCmd extends BaseCmd {
@Override
public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException {
try {
boolean result = backupManager.restoreBackupVolumeAndAttachToVM(zoneId, volumeId, vmId, backupId);
boolean result = backupManager.restoreBackupVolumeAndAttachToVM(volumeId, vmId, backupId);
if (result) {
SuccessResponse response = new SuccessResponse(getCommandName());
response.setResponseName(getCommandName());
@ -132,4 +125,14 @@ public class RestoreBackupVolumeCmd extends BaseCmd {
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage());
}
}
@Override
public String getEventType() {
return EventTypes.EVENT_RESTORE_VOLUME_FROM_BACKUP_AND_ATTACH_TO_VM;
}
@Override
public String getEventDescription() {
return "Restoring volume "+ volumeId + " from backup " + backupId + " and attaching it to VM " + vmId;
}
}

View File

@ -24,6 +24,7 @@ import org.apache.cloudstack.api.BaseResponse;
import org.apache.cloudstack.api.EntityReference;
import org.apache.cloudstack.backup.Backup;
import java.util.Date;
import java.util.List;
@EntityReference(value = Backup.class)
@ -69,6 +70,10 @@ public class BackupResponse extends BaseResponse {
@Param(description = "backup volume ids")
private Backup.Status status;
@SerializedName(ApiConstants.START_DATE)
@Param(description = "backup start date")
private Date startDate;
public String getId() {
return id;
}
@ -148,4 +153,12 @@ public class BackupResponse extends BaseResponse {
public void setVolumeIds(List<String> volumeIds) {
this.volumeIds = volumeIds;
}
public Date getStartDate() {
return startDate;
}
public void setStartDate(Date startDate) {
this.startDate = startDate;
}
}

View File

@ -39,4 +39,5 @@ public interface Backup extends InternalIdentity, Identity {
List<Long> getVolumeIds();
Status getStatus();
Date getStartTime();
Date getRemoved();
}

View File

@ -67,7 +67,7 @@ public interface BackupManager extends BackupService, Configurable, PluggableSer
/**
* List existing backups for a VM
*/
List<Backup> listVMBackups(Long zoneId, Long vmId);
List<Backup> listVMBackups(Long vmId);
/**
* List backup policies
@ -82,24 +82,23 @@ public interface BackupManager extends BackupService, Configurable, PluggableSer
* @param vmId Virtual Machine ID
* @return returns operation success
*/
boolean createBackup(Long vmId);
Backup createBackup(Long vmId);
/**
* Deletes backup of a VM
* @param vmId Virtual Machine ID
* Deletes a backup
* @return returns operation success
*/
boolean deleteBackup(Long vmId);
boolean deleteBackup(Long backupId);
/**
* Restore a full backed up VM
* Restore a full VM from backup
*/
boolean restoreBackup(Long zoneId, Long vmId, Long backupId);
boolean restoreVMFromBackup(Long backupId);
/**
* Restore a backed up volume and attach it to a VM
*/
boolean restoreBackupVolumeAndAttachToVM(Long zoneId, Long volumeId, Long vmId, Long backupId);
boolean restoreBackupVolumeAndAttachToVM(Long volumeId, Long vmId, Long backupId);
/**
* Deletes a backup policy

View File

@ -63,7 +63,7 @@ public interface BackupProvider {
* @param vm
* @return true if backup successfully starts
*/
boolean startBackup(BackupPolicy policy, VirtualMachine vm);
Backup createVMBackup(BackupPolicy policy, VirtualMachine vm);
/**
* Restore VM from backup
@ -79,4 +79,9 @@ public interface BackupProvider {
* List VM Backups
*/
List<Backup> listVMBackups(Long zoneId, VirtualMachine vm);
/**
* Remove a VM backup
*/
boolean removeVMBackup(VirtualMachine vm, String backupId);
}

View File

@ -165,6 +165,11 @@ public class BackupTO implements Backup {
return startTime;
}
@Override
public Date getRemoved() {
return null;
}
public void setStartTime(Date startTime) {
this.startTime = startTime;
}

View File

@ -71,7 +71,7 @@ public class BackupVO implements Backup {
private Long parentId;
@Column(name = "vm_id")
private long vmId;
private Long vmId;
@Column(name = "volumes")
private String volumes;
@ -83,6 +83,10 @@ public class BackupVO implements Backup {
@Temporal(value = TemporalType.TIMESTAMP)
private Date startTime;
@Column(name = "removed")
@Temporal(value = TemporalType.TIMESTAMP)
private Date removed;
@Transient
private List<Long> volumeIds;
@ -173,7 +177,7 @@ public class BackupVO implements Backup {
this.parentId = parentId;
}
public void setVmId(long vmId) {
public void setVmId(Long vmId) {
this.vmId = vmId;
}
@ -224,6 +228,14 @@ public class BackupVO implements Backup {
}
}
public Date getRemoved() {
return removed;
}
public void setRemoved(Date removed) {
this.removed = removed;
}
protected String getVolumes() {
return volumes;
}

View File

@ -30,4 +30,5 @@ public interface BackupDao extends GenericDao<BackupVO, Long> {
List<Backup> syncVMBackups(Long zoneId, Long vmId, List<Backup> externalBackups);
BackupResponse newBackupResponse(Backup backup);
BackupVO getBackupVO(Backup backup);
}

View File

@ -82,7 +82,7 @@ public class BackupDaoImpl extends GenericDaoBase<BackupVO, Long> implements Bac
return findOneBy(sc);
}
private BackupVO getBackupVO(Backup backup) {
public BackupVO getBackupVO(Backup backup) {
BackupVO backupVO = new BackupVO();
backupVO.setZoneId(backup.getZoneId());
backupVO.setAccountId(backup.getAccountId());
@ -146,6 +146,7 @@ public class BackupDaoImpl extends GenericDaoBase<BackupVO, Long> implements Bac
backupResponse.setVolumeIds(volIds);
}
backupResponse.setStatus(backup.getStatus());
backupResponse.setStartDate(backup.getStartTime());
backupResponse.setObjectName("backup");
return backupResponse;
}

View File

@ -64,14 +64,15 @@ CREATE TABLE IF NOT EXISTS `cloud`.`backup` (
`uuid` varchar(40) NOT NULL,
`account_id` bigint(20) unsigned NOT NULL,
`zone_id` bigint(20) unsigned NOT NULL,
`external_id` varchar(40) NOT NULL COMMENT 'backup ID on provider side',
`external_id` varchar(80) NOT NULL COMMENT 'backup ID on provider side',
`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,
`start` datetime DEFAULT NULL,
`removed` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
CONSTRAINT `fk_backup__account_id` FOREIGN KEY (`account_id`) REFERENCES `account` (`id`) ON DELETE CASCADE,
CONSTRAINT `fk_backup__zone_id` FOREIGN KEY (`zone_id`) REFERENCES `data_center` (`id`) ON DELETE CASCADE,

View File

@ -17,9 +17,11 @@
package org.apache.cloudstack.backup;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import org.apache.cloudstack.backup.dao.BackupDao;
import org.apache.log4j.Logger;
import com.cloud.agent.api.to.VolumeTO;
@ -28,10 +30,15 @@ import com.cloud.storage.Volume;
import com.cloud.utils.component.AdapterBase;
import com.cloud.vm.VirtualMachine;
import javax.inject.Inject;
public class DummyBackupProvider extends AdapterBase implements BackupProvider {
private static final Logger s_logger = Logger.getLogger(DummyBackupProvider.class);
@Inject
private BackupDao backupDao;
@Override
public String getName() {
return "dummy";
@ -69,8 +76,24 @@ public class DummyBackupProvider extends AdapterBase implements BackupProvider {
}
@Override
public boolean startBackup(BackupPolicy policy, VirtualMachine vm) {
return true;
public Backup createVMBackup(BackupPolicy policy, VirtualMachine vm) {
s_logger.debug("Creating VM backup for VM " + vm.getInstanceName() + " from backup policy " + policy.getName());
List<Backup> backups = backupDao.listByVmId(vm.getDataCenterId(), vm.getId());
String backupNumber = String.valueOf(backups.size() + 1);
Backup lastBackup = null;
if (backups.size() > 0) {
backups.sort(Comparator.comparing(Backup::getStartTime));
lastBackup = backups.get(backups.size() - 1);
}
BackupTO newBackup = new BackupTO(vm.getDataCenterId(), vm.getAccountId(),
"xxxx-xxxx-" + vm.getUuid() + "-" + backupNumber, "Backup-" + vm.getUuid() + backupNumber,
"VM-" + vm.getInstanceName() + "-backup-" + backupNumber,
lastBackup != null ? lastBackup.getExternalId() : null, vm.getId(), null,
Backup.Status.BackedUp, new Date());
backups.add(newBackup);
return newBackup;
}
@Override
@ -89,15 +112,19 @@ public class DummyBackupProvider extends AdapterBase implements BackupProvider {
@Override
public List<Backup> listVMBackups(Long zoneId, VirtualMachine vm) {
s_logger.debug("Listing VM " + vm.getInstanceName() + "backups on the Dummy Backup Provider");
return backupDao.listByVmId(vm.getDataCenterId(), vm.getId());
}
BackupTO backup1 = new BackupTO(zoneId, vm.getAccountId(),
"xxxx-xxxx", "Backup-1", "VM-" + vm.getInstanceName() + "-backup-1",
null, vm.getId(), null, Backup.Status.BackedUp, new Date());
@Override
public boolean removeVMBackup(VirtualMachine vm, String backupId) {
s_logger.debug("Removing VM backup " + backupId + " for VM " + vm.getInstanceName() + " on the Dummy Backup Provider");
BackupTO backup2 = new BackupTO(zoneId, vm.getAccountId(), "yyyy-yyyy",
"Backup-2", "VM-" + vm.getInstanceName() + "-backup-2",
backup1.getExternalId(), vm.getId(), null, Backup.Status.BackedUp, new Date());
return Arrays.asList(backup1, backup2);
List<Backup> backups = backupDao.listByVmId(vm.getDataCenterId(), vm.getId());
for (Backup backup : backups) {
if (backup.getExternalId().equals(backupId)) {
return true;
}
}
return false;
}
}

View File

@ -128,8 +128,10 @@ public class VeeamBackupProvider extends AdapterBase implements BackupProvider,
}
@Override
public boolean startBackup(BackupPolicy policy, VirtualMachine vm) {
return getClient(vm.getDataCenterId()).startBackupJob(policy.getExternalId());
public Backup createVMBackup(BackupPolicy policy, VirtualMachine vm) {
//TODO: Return backup
getClient(vm.getDataCenterId()).startBackupJob(policy.getExternalId());
return null;
}
@Override
@ -150,6 +152,12 @@ public class VeeamBackupProvider extends AdapterBase implements BackupProvider,
return null;
}
@Override
public boolean removeVMBackup(VirtualMachine vm, String backupId) {
//TODO: Implement
return false;
}
@Override
public String getConfigComponentName() {
return BackupService.class.getSimpleName();

View File

@ -82,6 +82,11 @@ public class VeeamBackup implements Backup {
return null;
}
@Override
public Date getRemoved() {
return null;
}
@Override
public String getUuid() {
return uid;

View File

@ -25,6 +25,8 @@ import java.util.Map;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
import com.cloud.event.ActionEvent;
import com.cloud.event.EventTypes;
import org.apache.cloudstack.api.command.admin.backup.DeleteBackupPolicyCmd;
import org.apache.cloudstack.api.command.admin.backup.ImportBackupPolicyCmd;
import org.apache.cloudstack.api.command.admin.backup.ListBackupProvidersCmd;
@ -35,14 +37,13 @@ import org.apache.cloudstack.api.command.user.backup.ListBackupPoliciesCmd;
import org.apache.cloudstack.api.command.user.backup.ListBackupPolicyVMMappingsCmd;
import org.apache.cloudstack.api.command.user.backup.ListVMBackupsCmd;
import org.apache.cloudstack.api.command.user.backup.RemoveVMFromBackupPolicyCmd;
import org.apache.cloudstack.api.command.user.backup.RestoreBackupVolumeCmd;
import org.apache.cloudstack.api.command.user.backup.RestoreVMBackupCmd;
import org.apache.cloudstack.api.command.user.backup.RestoreVolumeFromBackupAndAttachToVMCmd;
import org.apache.cloudstack.api.command.user.backup.RestoreVMFromBackupCmd;
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.context.CallContext;
import org.apache.cloudstack.framework.config.ConfigKey;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.BooleanUtils;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Component;
@ -89,6 +90,7 @@ public class BackupManagerImpl extends ManagerBase implements BackupManager {
private List<BackupProvider> backupProviders;
@Override
@ActionEvent(eventType = EventTypes.EVENT_IMPORT_BACKUP_POLICY, eventDescription = "importing backup policy", async = true)
public BackupPolicy importBackupPolicy(Long zoneId, String policyExternalId, String policyName, String policyDescription) {
final BackupProvider provider = getBackupProvider(zoneId);
if (!provider.isBackupPolicy(zoneId, policyExternalId)) {
@ -105,6 +107,7 @@ public class BackupManagerImpl extends ManagerBase implements BackupManager {
}
@Override
@ActionEvent(eventType = EventTypes.EVENT_ADD_VM_TO_BACKUP_POLICY, eventDescription = "adding VM to backup policy", async = true)
public boolean addVMToBackupPolicy(Long policyId, Long virtualMachineId) {
VMInstanceVO vm = vmInstanceDao.findById(virtualMachineId);
if (vm == null) {
@ -132,6 +135,7 @@ public class BackupManagerImpl extends ManagerBase implements BackupManager {
}
@Override
@ActionEvent(eventType = EventTypes.EVENT_REMOVE_VM_FROM_BACKUP_POLICY, eventDescription = "removing VM from backup policy", async = true)
public boolean removeVMFromBackupPolicy(Long policyId, Long vmId) {
BackupPolicyVO policy = backupPolicyDao.findById(policyId);
if (policy == null) {
@ -171,19 +175,15 @@ public class BackupManagerImpl extends ManagerBase implements BackupManager {
}
@Override
public List<Backup> listVMBackups(Long zoneId, Long vmId) {
BackupProvider backupProvider = getBackupProvider(zoneId);
//TODO: Add background job to sync VM backups from the provider
public List<Backup> listVMBackups(Long vmId) {
VMInstanceVO vm = vmInstanceDao.findById(vmId);
if (vm == null) {
throw new CloudRuntimeException("VM " + vmId + " does not exist");
}
List<Backup> existingBackups = backupDao.listByVmId(zoneId, vmId);
if (CollectionUtils.isNotEmpty(existingBackups)) {
return existingBackups;
} else {
List<Backup> externalBackups = backupProvider.listVMBackups(zoneId, vm);
return backupDao.syncVMBackups(zoneId, vmId, externalBackups);
}
Long zoneId = vm.getDataCenterId();
BackupProvider backupProvider = getBackupProvider(zoneId);
return backupDao.listByVmId(zoneId, vmId);
}
/**
@ -229,48 +229,76 @@ public class BackupManagerImpl extends ManagerBase implements BackupManager {
}
@Override
public boolean createBackup(Long vmId) {
@ActionEvent(eventType = EventTypes.EVENT_CREATE_VM_BACKUP, eventDescription = "creating VM backup", async = true)
public Backup createBackup(Long vmId) {
VMInstanceVO vm = vmInstanceDao.findById(vmId);
if (vm == null) {
throw new CloudRuntimeException("VM does not exist");
}
BackupPolicyVMMap vmMap = backupPolicyVMMapDao.findByVMId(vmId);
if (vmMap == null) {
throw new CloudRuntimeException("VM " + vmId + " is not assigned to any backup policy");
}
BackupPolicyVO policy = backupPolicyDao.findById(vmMap.getPolicyId());
if (policy == null) {
throw new CloudRuntimeException("Policy does not exist");
}
BackupProvider backupProvider = getBackupProvider(vm.getDataCenterId());
// FIXME: on successfully started, add an entry in DB?
return backupProvider.startBackup(policy, vm);
Backup vmBackup = backupProvider.createVMBackup(policy, vm);
if (vmBackup == null) {
return null;
}
BackupVO backupVO = backupDao.getBackupVO(vmBackup);
return backupDao.persist(backupVO);
}
@Override
public boolean deleteBackup(Long vmId) {
// TODO: implement me
return false;
}
@Override
public boolean restoreBackup(Long zoneId, Long vmId, Long backupId) {
BackupProvider backupProvider = getBackupProvider(zoneId);
@ActionEvent(eventType = EventTypes.EVENT_DELETE_VM_BACKUP, eventDescription = "deleting VM backup", async = true)
public boolean deleteBackup(Long backupId) {
BackupVO backup = backupDao.findById(backupId);
if (backup == null) {
throw new CloudRuntimeException("Backup " + backupId + " does not exist");
}
Long zoneId = backup.getZoneId();
Long vmId = backup.getVmId();
VMInstanceVO vm = vmInstanceDao.findById(vmId);
if (vm == null) {
throw new CloudRuntimeException("VM " + vmId + " does not exist");
}
BackupProvider backupProvider = getBackupProvider(backup.getZoneId());
boolean result = backupProvider.removeVMBackup(vm, backup.getExternalId());
if (result) {
backupDao.remove(backupId);
}
return result;
}
@Override
@ActionEvent(eventType = EventTypes.EVENT_RESTORE_VM_FROM_BACKUP, eventDescription = "restoring VM from backup", async = true)
public boolean restoreVMFromBackup(Long backupId) {
BackupVO backup = backupDao.findById(backupId);
if (backup == null) {
throw new CloudRuntimeException("Backup " + backupId + " does not exist");
}
Long vmId = backup.getVmId();
BackupProvider backupProvider = getBackupProvider(backup.getZoneId());
VMInstanceVO vm = vmInstanceDao.findById(vmId);
if (vm == null) {
throw new CloudRuntimeException("VM " + vmId + " does not exist");
}
return backupProvider.restoreVMFromBackup(vm.getUuid(), backup.getUuid());
}
@Override
public boolean restoreBackupVolumeAndAttachToVM(Long zoneId, Long volumeId, Long vmId, Long backupId) {
BackupProvider backupProvider = getBackupProvider(zoneId);
@ActionEvent(eventType = EventTypes.EVENT_RESTORE_VM_FROM_BACKUP, eventDescription = "restoring VM from backup", async = true)
public boolean restoreBackupVolumeAndAttachToVM(Long volumeId, Long vmId, Long backupId) {
BackupVO backup = backupDao.findById(backupId);
if (backup == null) {
throw new CloudRuntimeException("Backup " + backupId + " does not exist");
}
BackupProvider backupProvider = getBackupProvider(backup.getZoneId());
VMInstanceVO vm = vmInstanceDao.findById(vmId);
if (vm == null) {
throw new CloudRuntimeException("VM " + vmId + " does not exist");
@ -346,8 +374,8 @@ public class BackupManagerImpl extends ManagerBase implements BackupManager {
cmdList.add(ListVMBackupsCmd.class);
cmdList.add(CreateVMBackupCmd.class);
cmdList.add(DeleteVMBackupCmd.class);
cmdList.add(RestoreVMBackupCmd.class);
cmdList.add(RestoreBackupVolumeCmd.class);
cmdList.add(RestoreVMFromBackupCmd.class);
cmdList.add(RestoreVolumeFromBackupAndAttachToVMCmd.class);
return cmdList;
}

View File

@ -18,7 +18,7 @@
from marvin.cloudstackTestCase import cloudstackTestCase
from marvin.lib.utils import (cleanup_resources)
from marvin.lib.base import (Account, ServiceOffering, VirtualMachine, BackupPolicy, Configurations)
from marvin.lib.base import (Account, ServiceOffering, VirtualMachine, BackupPolicy, Configurations, VMBackup)
from marvin.lib.common import (get_domain, get_zone, get_template)
from nose.plugins.attrib import attr
from marvin.codes import FAILED
@ -50,8 +50,8 @@ class TestDummyBackupAndRecovery(cloudstackTestCase):
# Check backup configuration values, set them to enable the dummy provider
backup_enabled_cfg = Configurations.list(cls.api_client, name='backup.framework.enabled')
backup_provider_cfg = Configurations.list(cls.api_client, name='backup.framework.provider.plugin')
backup_enabled_cfg = Configurations.list(cls.api_client, name='backup.framework.enabled', zoneid=cls.zone.id)
backup_provider_cfg = Configurations.list(cls.api_client, name='backup.framework.provider.plugin', zoneid=cls.zone.id)
cls.backup_enabled = backup_enabled_cfg[0].value
cls.backup_provider = backup_provider_cfg[0].value
@ -110,20 +110,24 @@ class TestDummyBackupAndRecovery(cloudstackTestCase):
# 3. Delete backup policy
# 4. List internal backup policies, policy id should not be listed
# Import backup policy
ext_policy = self.external_policies[1]
self.debug("Importing backup policy %s - %s" % (ext_policy.externalid, ext_policy.name))
policy = BackupPolicy.importExisting(self.apiclient, self.zone.id, ext_policy.externalid,
ext_policy.name, ext_policy.description)
# Verify policy is listed
imported_policies = BackupPolicy.listInternal(self.apiclient, self.zone.id)
self.assertIsInstance(imported_policies, list, "List Backup Policies should return a valid response")
self.assertNotEqual(len(imported_policies), 0, "Check if the list API returns a non-empty response")
matching_policies = [x for x in imported_policies if x.id == policy.id]
self.assertNotEqual(len(matching_policies), 0, "Check if there is a matching policy")
# Delete backup policy
self.debug("Deleting backup policy %s" % policy.id)
policy.delete(self.apiclient)
# Verify policy is not listed
imported_policies = BackupPolicy.listInternal(self.apiclient, self.zone.id)
self.assertIsInstance(imported_policies, list, "List Backup Policies should return a valid response")
matching_policies = [x for x in imported_policies if x.id == policy.id]
@ -141,28 +145,58 @@ class TestDummyBackupAndRecovery(cloudstackTestCase):
# 3. Remove VM from backup policy
# 4. Verify there is no mapping between the VM and the backup policy
# Add VM to backup policy
self.debug("Adding VM %s to backup policy %s" % (self.vm.id, self.policy.id))
self.policy.addVM(self.apiclient, self.vm.id, self.zone.id)
self.policy.addVM(self.apiclient, self.vm.id)
# Verify a mapping between backup policy and VM is created on DB
qresultset = self.dbclient.execute("select id from vm_instance where uuid='%s';" % self.vm.id)
vm_id = qresultset[0][0]
qresultset = self.dbclient.execute("select id from backup_policy where uuid='%s';" % self.policy.id)
policy_id = qresultset[0][0]
qresultset = self.dbclient.execute("select id from backup_policy_vm_map where policy_id='%d' and vm_id = '%d';"
% (policy_id, vm_id))
map = qresultset[0]
self.assertNotEqual(len(map), 0, "A mapping between VM and backup policy should exist on DB")
self.assertNotEqual(map[0], None, "A mapping between VM and backup policy should exist on DB")
mappings = BackupPolicy.listVMMappings(self.apiclient, self.policy.id, self.vm.id, self.zone.id)
self.assertNotEqual(len(mappings), 0, "A mapping between VM and backup policy should exist")
self.assertNotEqual(mappings[0], None, "A mapping between VM and backup policy should exist")
# Remove VM from backup policy
self.debug("Removing VM %s from backup policy %s" % (self.vm.id, self.policy.id))
self.policy.removeVM(self.apiclient, self.vm.id, self.zone.id)
self.policy.removeVM(self.apiclient, self.vm.id)
# Verify mapping is removed from DB
qresultset = self.dbclient.execute("select id from backup_policy_vm_map where policy_id='%d' and vm_id = '%d';"
% (policy_id, vm_id))
# Verify mapping is removed
zone_mappings = BackupPolicy.listVMMappings(self.apiclient, zoneid=self.zone.id)
matching_mappings = [x for x in zone_mappings if x.policyid == self.policy.id and x.virtualmachineid == self.vm.id]
self.assertEqual(len(matching_mappings), 0, "The mapping between VM and backup policy should be removed")
self.assertEqual(len(qresultset), 0, "The mapping between VM and backup policy should be removed from DB")
@attr(tags=["advanced", "backup"], required_hardware="false")
def test_vm_backup_lifecycle(self):
"""
Test VM backup lifecycle
"""
# Validate the following:
# 1. List VM backups, verify no backups are created
# 2. Add VM to policy
# 3. Create VM backup
# 4. List VM backups, verify backup is created
# 5. Delete VM backup
# 6. List VM backups, verify backup is deleted
# 7. Remove VM from policy
# Verify there are no backups for the VM
backups = VMBackup.list(self.apiclient, self.vm.id)
self.assertEqual(backups, None, "There should not exist any backup for the VM")
# Create a VM backup
self.policy.addVM(self.apiclient, self.vm.id)
VMBackup.create(self.apiclient, self.vm.id)
# Verify backup is created for the VM
backups = VMBackup.list(self.apiclient, self.vm.id)
self.assertEqual(len(backups), 1, "There should exist only one backup for the VM")
backup = backups[0]
# Delete backup
VMBackup.delete(self.apiclient, backup.id)
# Verify backup is deleted
backups = VMBackup.list(self.apiclient, self.vm.id)
self.assertEqual(backups, None, "There should not exist any backup for the VM")
# Remove VM from policy
self.policy.removeVM(self.apiclient, self.vm.id)

View File

@ -5426,20 +5426,76 @@ class BackupPolicy:
cmd.id = self.id
return (apiclient.deleteBackupPolicy(cmd))
def addVM(self, apiclient, vmid, zoneid):
def addVM(self, apiclient, vmid):
"""Add a VM to a backup policy"""
cmd = addVirtualMachineToBackupPolicy.addVirtualMachineToBackupPolicyCmd()
cmd.backuppolicyid = self.id
cmd = addVMToBackupPolicy.addVMToBackupPolicyCmd()
cmd.policyid = self.id
cmd.virtualmachineid = vmid
cmd.zoneid = zoneid
return (apiclient.addVirtualMachineToBackupPolicy(cmd))
return (apiclient.addVMToBackupPolicy(cmd))
def removeVM(self, apiclient, vmid, zoneid):
def removeVM(self, apiclient, vmid):
"""Remove a VM from a backup policy"""
cmd = removeVirtualMachineFromBackupPolicy.removeVirtualMachineFromBackupPolicyCmd()
cmd.backuppolicyid = self.id
cmd = removeVMFromBackupPolicy.removeVMFromBackupPolicyCmd()
cmd.policyid = self.id
cmd.virtualmachineid = vmid
cmd.zoneid = zoneid
return (apiclient.removeVirtualMachineFromBackupPolicy(cmd))
return (apiclient.removeVMFromBackupPolicy(cmd))
@classmethod
def listVMMappings(self, apiclient, policyid=None, vmid=None, zoneid=None):
"""List VM - Backup policies mappings"""
cmd = listBackupPolicyVMMappings.listBackupPolicyVMMappingsCmd()
if vmid:
cmd.virtualmachineid = vmid
if zoneid:
cmd.zoneid = zoneid
if policyid:
cmd.policyid = policyid
return (apiclient.listBackupPolicyVMMappings(cmd))
class VMBackup:
def __init__(self, items):
self.__dict__.update(items)
@classmethod
def create(self, apiclient, vmid):
"""Create VM backup"""
cmd = createVMBackup.createVMBackupCmd()
cmd.virtualmachineid = vmid
return (apiclient.createVMBackup(cmd))
@classmethod
def delete(self, apiclient, id):
"""Delete VM backup"""
cmd = deleteVMBackup.deleteVMBackupCmd()
cmd.id = id
return (apiclient.deleteVMBackup(cmd))
@classmethod
def list(self, apiclient, vmid):
"""List VM backups"""
cmd = listVMBackups.listVMBackupsCmd()
cmd.virtualmachineid = vmid
return (apiclient.listVMBackups(cmd))
def restoreVM(self, apiclient):
"""Restore VM from backup"""
cmd = restoreVMFromBackup.restoreVMFromBackupCmd()
cmd.id = self.id
return (apiclient.restoreVMFromBackup(cmd))
def restoreVolumeAndAttachToVM(self, apiclient, volumeid, vmid):
"""Restore volume from backup and attach it to VM"""
cmd = restoreVolumeFromBackupAndAttachToVM.restoreVolumeFromBackupAndAttachToVMCmd()
cmd.id = self.id
cmd.volumeid = volumeid
cmd.virtualmachineid = vmid
return (apiclient.restoreVolumeFromBackupAndAttachToVM(cmd))