Refactor and marvin tests stabilization
diff --git a/api/src/main/java/com/cloud/event/EventTypes.java b/api/src/main/java/com/cloud/event/EventTypes.java
index 907b93e..a2baf7c 100644
--- a/api/src/main/java/com/cloud/event/EventTypes.java
+++ b/api/src/main/java/com/cloud/event/EventTypes.java
@@ -584,6 +584,15 @@
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
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/ImportBackupPolicyCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/ImportBackupPolicyCmd.java
index 1dbe033..c94b9e2 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/ImportBackupPolicyCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/ImportBackupPolicyCmd.java
@@ -18,10 +18,12 @@
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 @@
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 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 ;
+ }
}
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/AddVMToBackupPolicyCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/AddVMToBackupPolicyCmd.java
index c6d1219..3074188 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/AddVMToBackupPolicyCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/AddVMToBackupPolicyCmd.java
@@ -18,11 +18,12 @@
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 @@
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 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;
+ }
}
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/CreateVMBackupCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/CreateVMBackupCmd.java
index 6855c99..94a821a 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/CreateVMBackupCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/CreateVMBackupCmd.java
@@ -19,15 +19,18 @@
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 @@
@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 @@
@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 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;
+ }
}
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/DeleteVMBackupCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/DeleteVMBackupCmd.java
index 1faa628..fde8a9f 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/DeleteVMBackupCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/DeleteVMBackupCmd.java
@@ -26,8 +26,8 @@
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 @@
//////////////// 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 @@
@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());
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/ListVMBackupsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/ListVMBackupsCmd.java
index d444480..a9b183f 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/ListVMBackupsCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/ListVMBackupsCmd.java
@@ -31,7 +31,6 @@
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 @@
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 @@
return vmId;
}
- public Long getZoneId() {
- return zoneId;
- }
-
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////
@@ -85,7 +76,7 @@
@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());
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/RemoveVMFromBackupPolicyCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/RemoveVMFromBackupPolicyCmd.java
index c9c5518..e28e670 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/RemoveVMFromBackupPolicyCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/RemoveVMFromBackupPolicyCmd.java
@@ -19,11 +19,12 @@
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 @@
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 @@
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;
+ }
}
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/RestoreVMBackupCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/RestoreVMFromBackupCmd.java
similarity index 78%
rename from api/src/main/java/org/apache/cloudstack/api/command/user/backup/RestoreVMBackupCmd.java
rename to api/src/main/java/org/apache/cloudstack/api/command/user/backup/RestoreVMFromBackupCmd.java
index 7ffc881..019b535 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/RestoreVMBackupCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/RestoreVMFromBackupCmd.java
@@ -19,17 +19,17 @@
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.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 @@
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 @@
@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 @@
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;
+ }
}
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/RestoreBackupVolumeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/RestoreVolumeFromBackupAndAttachToVMCmd.java
similarity index 88%
rename from api/src/main/java/org/apache/cloudstack/api/command/user/backup/RestoreBackupVolumeCmd.java
rename to api/src/main/java/org/apache/cloudstack/api/command/user/backup/RestoreVolumeFromBackupAndAttachToVMCmd.java
index f9cbe26..1567e7f 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/RestoreBackupVolumeCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/RestoreVolumeFromBackupAndAttachToVMCmd.java
@@ -19,10 +19,12 @@
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.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.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 @@
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 @@
return backupId;
}
- public Long getZoneId() {
- return zoneId;
- }
-
@Override
public String getCommandName() {
return APINAME.toLowerCase() + BaseCmd.RESPONSE_SUFFIX;
@@ -120,7 +113,7 @@
@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 @@
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;
+ }
}
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
index 77e916a..2cc58af 100644
--- a/api/src/main/java/org/apache/cloudstack/api/response/BackupResponse.java
+++ b/api/src/main/java/org/apache/cloudstack/api/response/BackupResponse.java
@@ -24,6 +24,7 @@
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 @@
@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 void setVolumeIds(List<String> volumeIds) {
this.volumeIds = volumeIds;
}
+
+ public Date getStartDate() {
+ return startDate;
+ }
+
+ public void setStartDate(Date startDate) {
+ this.startDate = startDate;
+ }
}
diff --git a/api/src/main/java/org/apache/cloudstack/backup/Backup.java b/api/src/main/java/org/apache/cloudstack/backup/Backup.java
index 9ffbf4a..79d4626 100644
--- a/api/src/main/java/org/apache/cloudstack/backup/Backup.java
+++ b/api/src/main/java/org/apache/cloudstack/backup/Backup.java
@@ -39,4 +39,5 @@
List<Long> getVolumeIds();
Status getStatus();
Date getStartTime();
+ Date getRemoved();
}
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 0a83ee0..c25ef18 100644
--- a/api/src/main/java/org/apache/cloudstack/backup/BackupManager.java
+++ b/api/src/main/java/org/apache/cloudstack/backup/BackupManager.java
@@ -67,7 +67,7 @@
/**
* 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 @@
* @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
diff --git a/api/src/main/java/org/apache/cloudstack/backup/BackupProvider.java b/api/src/main/java/org/apache/cloudstack/backup/BackupProvider.java
index 0e3980b..4e857ae 100644
--- a/api/src/main/java/org/apache/cloudstack/backup/BackupProvider.java
+++ b/api/src/main/java/org/apache/cloudstack/backup/BackupProvider.java
@@ -63,7 +63,7 @@
* @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 @@
* List VM Backups
*/
List<Backup> listVMBackups(Long zoneId, VirtualMachine vm);
+
+ /**
+ * Remove a VM backup
+ */
+ boolean removeVMBackup(VirtualMachine vm, String backupId);
}
diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupTO.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupTO.java
index 61f0b90..6b3c24c 100644
--- a/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupTO.java
+++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupTO.java
@@ -165,6 +165,11 @@
return startTime;
}
+ @Override
+ public Date getRemoved() {
+ return null;
+ }
+
public void setStartTime(Date startTime) {
this.startTime = startTime;
}
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
index f7b3f78..9a6f04c 100644
--- a/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupVO.java
+++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupVO.java
@@ -71,7 +71,7 @@
private Long parentId;
@Column(name = "vm_id")
- private long vmId;
+ private Long vmId;
@Column(name = "volumes")
private String volumes;
@@ -83,6 +83,10 @@
@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 @@
this.parentId = parentId;
}
- public void setVmId(long vmId) {
+ public void setVmId(Long vmId) {
this.vmId = vmId;
}
@@ -224,6 +228,14 @@
}
}
+ public Date getRemoved() {
+ return removed;
+ }
+
+ public void setRemoved(Date removed) {
+ this.removed = removed;
+ }
+
protected String getVolumes() {
return 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
index fb6f01b..9bb8ea1 100644
--- 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
@@ -30,4 +30,5 @@
List<Backup> syncVMBackups(Long zoneId, Long vmId, List<Backup> externalBackups);
BackupResponse newBackupResponse(Backup backup);
+ BackupVO getBackupVO(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
index 0661ab8..f10d2f9 100644
--- 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
@@ -82,7 +82,7 @@
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 @@
backupResponse.setVolumeIds(volIds);
}
backupResponse.setStatus(backup.getStatus());
+ backupResponse.setStartDate(backup.getStartTime());
backupResponse.setObjectName("backup");
return backupResponse;
}
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 c347f7a..4e69653 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
@@ -64,14 +64,15 @@
`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,
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 8916451..0aacf91 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
@@ -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.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 @@
}
@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 @@
@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;
}
}
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 9bed15b..84e442a 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
@@ -128,8 +128,10 @@
}
@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
@@ -151,6 +153,12 @@
}
@Override
+ public boolean removeVMBackup(VirtualMachine vm, String backupId) {
+ //TODO: Implement
+ return false;
+ }
+
+ @Override
public String getConfigComponentName() {
return BackupService.class.getSimpleName();
}
diff --git a/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/VeeamBackup.java b/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/VeeamBackup.java
index a72e8d2..285542a 100644
--- a/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/VeeamBackup.java
+++ b/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/VeeamBackup.java
@@ -83,6 +83,11 @@
}
@Override
+ public Date getRemoved() {
+ return null;
+ }
+
+ @Override
public String getUuid() {
return uid;
}
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 623bac5..b57ab16 100644
--- a/server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java
+++ b/server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java
@@ -25,6 +25,8 @@
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.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 @@
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 @@
}
@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 @@
}
@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 @@
}
@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 @@
}
@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 @@
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;
}
diff --git a/test/integration/smoke/test_backup_recovery.py b/test/integration/smoke/test_backup_recovery.py
index b976434..f9a00b9 100644
--- a/test/integration/smoke/test_backup_recovery.py
+++ b/test/integration/smoke/test_backup_recovery.py
@@ -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 @@
# 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 @@
# 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 @@
# 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]
+ 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")
- 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")
-
+ # 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)
\ No newline at end of file
diff --git a/tools/marvin/marvin/lib/base.py b/tools/marvin/marvin/lib/base.py
index 6087753..56eaab2 100755
--- a/tools/marvin/marvin/lib/base.py
+++ b/tools/marvin/marvin/lib/base.py
@@ -5426,20 +5426,76 @@
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))
\ No newline at end of file
+ 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))
\ No newline at end of file