Update BackrollProvider from BackupProvider

This commit is contained in:
Pierre Charton 2025-03-12 10:09:09 +01:00
parent cfad83413d
commit fa8faf5198
3 changed files with 127 additions and 42 deletions

View File

@ -29,7 +29,9 @@ import java.util.stream.Collectors;
import javax.inject.Inject;
import org.apache.cloudstack.backup.Backup.Metric;
import org.apache.cloudstack.backup.Backup.RestorePoint;
import org.apache.cloudstack.backup.backroll.BackrollClient;
import org.apache.cloudstack.backup.backroll.model.BackrollBackupMetrics;
import org.apache.cloudstack.backup.backroll.model.BackrollTaskStatus;
@ -153,6 +155,9 @@ public class BackrollBackupProvider extends AdapterBase implements BackupProvide
@Override
public Map<VirtualMachine, Backup.Metric> getBackupMetrics(Long zoneId, List<VirtualMachine> vms) {
Long vmBackupSize=0L;
Long vmBackupProtectedSize=0L;
final Map<VirtualMachine, Backup.Metric> metrics = new HashMap<>();
if (CollectionUtils.isEmpty(vms)) {
logger.warn("Unable to get VM Backup Metrics because the list of VMs is empty.");
@ -170,7 +175,13 @@ public class BackrollBackupProvider extends AdapterBase implements BackupProvide
Metric metric;
try {
metric = client.getVirtualMachineMetrics(vm.getUuid());
final List<BackrollVmBackup> backups = client.getAllBackupsfromVirtualMachine(vm.getUuid());
for (BackrollVmBackup backup : backups ) {
BackrollBackupMetrics backupMetric = client.getBackupMetrics(vm.getUuid(), backup.getId());
vmBackupProtectedSize += backupMetric.getDeduplicated();
vmBackupSize += backupMetric.getSize();
}
metric = new Metric(vmBackupSize, vmBackupProtectedSize);
} catch (BackrollApiException | IOException e) {
throw new CloudRuntimeException("Failed to retrieve backup metrics");
}
@ -192,7 +203,7 @@ public class BackrollBackupProvider extends AdapterBase implements BackupProvide
}
@Override
public boolean takeBackup(VirtualMachine vm) {
public Pair<Boolean, Backup> takeBackup(VirtualMachine vm) {
logger.info("Starting backup for VM ID {} on backroll provider", vm.getUuid());
final BackrollClient client = getClient(vm.getDataCenterId());
@ -215,18 +226,16 @@ public class BackrollBackupProvider extends AdapterBase implements BackupProvide
backup.setZoneId(vm.getDataCenterId());
Boolean result = backupDao.persist(backup) != null;
client.triggerTaskStatus(urlToRequest);
syncBackups(vm, null);
return result;
return new Pair<Boolean,Backup>(result, backup);
}
} catch (ParseException | BackrollApiException | IOException e) {
logger.debug(e.getMessage());
throw new CloudRuntimeException("Failed to take backup");
}
return false;
return new Pair<Boolean,Backup>(false, null);
}
@Override
public void syncBackups(VirtualMachine vm, Backup.Metric metric) {
logger.info("Starting sync backup for VM ID " + vm.getUuid() + " on backroll provider");
@ -401,6 +410,7 @@ public class BackrollBackupProvider extends AdapterBase implements BackupProvide
backupToUpdate.setStatus(Backup.Status.Removed);
if (backupDao.persist(backupToUpdate) != null) {
logger.debug("Backroll backup {} deleted in database.", backup.getUuid());
VMInstanceVO vm = vmInstanceDao.findByIdIncludingRemoved(backup.getVmId());
return true;
}
return false;
@ -433,4 +443,47 @@ public class BackrollBackupProvider extends AdapterBase implements BackupProvide
logger.debug("Restoring volume {} from backup {} on the Backroll Backup Provider", volumeUuid, backup.getUuid());
throw new CloudRuntimeException("Backroll plugin does not support this feature");
}
@Override
public List<RestorePoint> listRestorePoints(VirtualMachine vm) {
try {
final BackrollClient client = getClient(vm.getDataCenterId());
return client.listRestorePoints(vm.getUuid());
} catch (BackrollApiException | IOException e) {
logger.error(e);
throw new CloudRuntimeException("Failed to delete backup");
}
}
@Override
public Backup createNewBackupEntryForRestorePoint(RestorePoint restorePoint, VirtualMachine vm, Metric metric) {
final BackrollClient client = getClient(vm.getDataCenterId());
BackupVO backupToInsert = new BackupVO();
backupToInsert.setVmId(vm.getId());
backupToInsert.setExternalId(restorePoint.getId());
backupToInsert.setType("INCREMENTAL");
backupToInsert.setDate(restorePoint.getCreated());
backupToInsert.setStatus(Backup.Status.BackedUp);
backupToInsert.setBackupOfferingId(vm.getBackupOfferingId());
backupToInsert.setAccountId(vm.getAccountId());
backupToInsert.setDomainId(vm.getDomainId());
backupToInsert.setZoneId(vm.getDataCenterId());
if(metric == null || metric.getBackupSize().equals(0L)){
try {
BackrollBackupMetrics backupMetrics = client.getBackupMetrics(vm.getUuid() , restorePoint.getId());
if (backupMetrics != null) {
backupToInsert.setSize(backupMetrics.getDeduplicated()); // real size
backupToInsert.setProtectedSize(backupMetrics.getSize()); // total size
}
} catch (BackrollApiException | IOException e) {
logger.error(e);
throw new CloudRuntimeException("Failed to get backup metrics");
}
}else{
backupToInsert.setSize(metric.getBackupSize());
backupToInsert.setProtectedSize(metric.getDataSize());
}
backupDao.persist(backupToInsert);
return backupToInsert;
}
}

View File

@ -22,6 +22,8 @@ import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import org.apache.cloudstack.backup.Backup;
import org.apache.cloudstack.backup.BackupOffering;
import org.apache.cloudstack.backup.Backup.Metric;
import org.apache.cloudstack.backup.backroll.model.BackrollBackupMetrics;
@ -42,7 +44,6 @@ import org.apache.cloudstack.backup.backroll.model.response.policy.BackrollBacku
import org.apache.cloudstack.backup.backroll.model.response.policy.BackupPoliciesResponse;
import org.apache.cloudstack.backup.backroll.utils.BackrollApiException;
import org.apache.cloudstack.backup.backroll.utils.BackrollHttpClientProvider;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
@ -54,8 +55,6 @@ import org.json.JSONException;
import org.json.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
public class BackrollClient {
protected Logger logger = LogManager.getLogger(BackrollClient.class);
@ -71,16 +70,18 @@ public class BackrollClient {
public String startBackupJob(final String jobId) throws IOException, BackrollApiException {
logger.info("startBackupJob : Trying to start backup for Backroll job: {}", jobId);
String backupJob = "";
BackrollTaskRequestResponse requestResponse = httpProvider.post(String.format("/tasks/singlebackup/%s", jobId), null, BackrollTaskRequestResponse.class);
BackrollTaskRequestResponse requestResponse = httpProvider.post(String.format("/tasks/singlebackup/%s", jobId),
null, BackrollTaskRequestResponse.class);
logger.info("startBackupJob : BackupJob status link: {}", requestResponse.location);
backupJob = requestResponse.location.replace("/api/v1", "");
return StringUtils.isEmpty(backupJob) ? null : backupJob;
}
public String getBackupOfferingUrl() throws IOException, BackrollApiException {
public String getBackupOfferingUrl() throws IOException, BackrollApiException {
logger.info("Trying to get backroll backup policies url");
String url = "";
BackrollTaskRequestResponse requestResponse = httpProvider.get("/backup_policies", BackrollTaskRequestResponse.class);
BackrollTaskRequestResponse requestResponse = httpProvider.get("/backup_policies",
BackrollTaskRequestResponse.class);
logger.info("BackrollClient:getBackupOfferingUrl:Apres Parse: " + requestResponse.location);
url = requestResponse.location.replace("/api/v1", "");
return StringUtils.isEmpty(url) ? null : url;
@ -90,7 +91,8 @@ public class BackrollClient {
logger.info("Trying to list backroll backup policies");
final List<BackupOffering> policies = new ArrayList<>();
BackupPoliciesResponse backupPoliciesResponse = httpProvider.waitGet(idTask, BackupPoliciesResponse.class);
logger.info("BackrollClient:getBackupOfferings:Apres Parse: " + backupPoliciesResponse.backupPolicies.get(0).name);
logger.info(
"BackrollClient:getBackupOfferings:Apres Parse: " + backupPoliciesResponse.backupPolicies.get(0).name);
for (final BackrollBackupPolicyResponse policy : backupPoliciesResponse.backupPolicies) {
policies.add(new BackrollOffering(policy.name, policy.id));
}
@ -98,7 +100,8 @@ public class BackrollClient {
return policies;
}
public boolean restoreVMFromBackup(final String vmId, final String backupName) throws IOException, BackrollApiException {
public boolean restoreVMFromBackup(final String vmId, final String backupName)
throws IOException, BackrollApiException {
logger.info("Start restore backup with backroll with backup {} for vm {}", backupName, vmId);
boolean isRestoreOk = false;
@ -114,11 +117,12 @@ public class BackrollClient {
logger.error("Backroll Error: {}", e.getMessage());
}
BackrollTaskRequestResponse requestResponse = httpProvider.post(String.format("/tasks/restore/%s", vmId), jsonBody, BackrollTaskRequestResponse.class);
BackrollTaskRequestResponse requestResponse = httpProvider.post(String.format("/tasks/restore/%s", vmId),
jsonBody, BackrollTaskRequestResponse.class);
String urlToRequest = requestResponse.location.replace("/api/v1", "");
String result = httpProvider.waitGetWithoutParseResponse(urlToRequest);
if(result.contains("SUCCESS")) {
if (result.contains("SUCCESS")) {
logger.debug("RESTORE SUCCESS content : " + result);
logger.debug("RESTORE SUCCESS");
isRestoreOk = true;
@ -135,10 +139,12 @@ public class BackrollClient {
String backupResponse = httpProvider.getWithoutParseResponse("/status/" + taskId);
if (backupResponse.contains(TaskState.FAILURE) || backupResponse.contains(TaskState.PENDING)) {
BackrollBackupStatusResponse backupStatusRequestResponse = new ObjectMapper().readValue(backupResponse, BackrollBackupStatusResponse.class);
BackrollBackupStatusResponse backupStatusRequestResponse = new ObjectMapper().readValue(backupResponse,
BackrollBackupStatusResponse.class);
status.setState(backupStatusRequestResponse.state);
} else {
BackrollBackupStatusSuccessResponse backupStatusSuccessRequestResponse = new ObjectMapper().readValue(backupResponse, BackrollBackupStatusSuccessResponse.class);
BackrollBackupStatusSuccessResponse backupStatusSuccessRequestResponse = new ObjectMapper()
.readValue(backupResponse, BackrollBackupStatusSuccessResponse.class);
status.setState(backupStatusSuccessRequestResponse.state);
status.setInfo(backupStatusSuccessRequestResponse.info);
}
@ -146,14 +152,16 @@ public class BackrollClient {
return StringUtils.isEmpty(status.getState()) ? null : status;
}
public boolean deleteBackup(final String vmId, final String backupName) throws IOException, BackrollApiException{
public boolean deleteBackup(final String vmId, final String backupName) throws IOException, BackrollApiException {
logger.info("Trying to delete backup {} for vm {} using Backroll", vmId, backupName);
boolean isBackupDeleted = false;
BackrollTaskRequestResponse requestResponse = httpProvider.delete(String.format("/virtualmachines/%s/backups/%s", vmId, backupName), BackrollTaskRequestResponse.class);
BackrollTaskRequestResponse requestResponse = httpProvider.delete(
String.format("/virtualmachines/%s/backups/%s", vmId, backupName), BackrollTaskRequestResponse.class);
String urlToRequest = requestResponse.location.replace("/api/v1", "");
BackrollBackupsFromVMResponse backrollBackupsFromVMResponse = httpProvider.waitGet(urlToRequest, BackrollBackupsFromVMResponse.class);
BackrollBackupsFromVMResponse backrollBackupsFromVMResponse = httpProvider.waitGet(urlToRequest,
BackrollBackupsFromVMResponse.class);
logger.debug(backrollBackupsFromVMResponse.state);
isBackupDeleted = backrollBackupsFromVMResponse.state.equals(TaskState.SUCCESS);
@ -161,8 +169,8 @@ public class BackrollClient {
}
public void triggerTaskStatus(String urlToRequest)
throws IOException, BackrollApiException{
httpProvider.waitGetWithoutParseResponse(urlToRequest);
throws IOException, BackrollApiException {
httpProvider.waitGetWithoutParseResponse(urlToRequest);
}
public Metric getVirtualMachineMetrics(final String vmId) throws IOException, BackrollApiException {
@ -170,11 +178,13 @@ public class BackrollClient {
Metric metric = new Metric(0L, 0L);
BackrollTaskRequestResponse requestResponse = httpProvider.get(String.format("/virtualmachines/%s/repository", vmId), BackrollTaskRequestResponse.class);
BackrollTaskRequestResponse requestResponse = httpProvider
.get(String.format("/virtualmachines/%s/repository", vmId), BackrollTaskRequestResponse.class);
String urlToRequest = requestResponse.location.replace("/api/v1", "");
BackrollVmMetricsResponse vmMetricsResponse = httpProvider.waitGet(urlToRequest, BackrollVmMetricsResponse.class);
BackrollVmMetricsResponse vmMetricsResponse = httpProvider.waitGet(urlToRequest,
BackrollVmMetricsResponse.class);
if (vmMetricsResponse != null && vmMetricsResponse.state.equals(TaskState.SUCCESS)) {
logger.debug("SUCCESS ok");
@ -192,18 +202,21 @@ public class BackrollClient {
return metric;
}
public BackrollBackupMetrics getBackupMetrics(String vmId, String backupId) throws IOException, BackrollApiException {
public BackrollBackupMetrics getBackupMetrics(String vmId, String backupId)
throws IOException, BackrollApiException {
logger.info("Trying to get backup metrics for VM: {}, and backup: {}", vmId, backupId);
BackrollBackupMetrics metrics = null;
BackrollTaskRequestResponse requestResponse = httpProvider.get(String.format("/virtualmachines/%s/backups/%s", vmId, backupId), BackrollTaskRequestResponse.class);
BackrollTaskRequestResponse requestResponse = httpProvider.get(
String.format("/virtualmachines/%s/backups/%s", vmId, backupId), BackrollTaskRequestResponse.class);
String urlToRequest = requestResponse.location.replace("/api/v1", "");
logger.debug(urlToRequest);
BackrollBackupMetricsResponse metricsResponse = httpProvider.waitGet(urlToRequest, BackrollBackupMetricsResponse.class);
BackrollBackupMetricsResponse metricsResponse = httpProvider.waitGet(urlToRequest,
BackrollBackupMetricsResponse.class);
if (metricsResponse.info != null) {
metrics = new BackrollBackupMetrics(Long.parseLong(metricsResponse.info.originalSize),
Long.parseLong(metricsResponse.info.deduplicatedSize));
@ -211,26 +224,45 @@ public class BackrollClient {
return metrics;
}
public List<BackrollVmBackup> getAllBackupsfromVirtualMachine(String vmId) throws BackrollApiException, IOException {
logger.info("Trying to retrieve all backups for vm {}", vmId);
public List<BackrollVmBackup> getAllBackupsfromVirtualMachine(String vmId)
throws BackrollApiException, IOException {
List<BackrollVmBackup> backups = new ArrayList<BackrollVmBackup>();
List<BackupInfos> backupInfos = getBackupInfosFromVm(vmId);
if (backupInfos != null && backupInfos.size() > 0) {
for (BackupInfos infos : backupInfos) {
var dateStart = new DateTime(infos.start);
backups.add(new BackrollVmBackup(infos.id, infos.name, dateStart.toDate()));
}
}
return backups;
}
BackrollTaskRequestResponse requestResponse = httpProvider.get(String.format("/virtualmachines/%s/backups", vmId), BackrollTaskRequestResponse.class);
private List<BackupInfos> getBackupInfosFromVm(String vmId)
throws BackrollApiException, IOException {
logger.info("Trying to retrieve all backups for vm {}", vmId);
BackrollTaskRequestResponse requestResponse = httpProvider
.get(String.format("/virtualmachines/%s/backups", vmId), BackrollTaskRequestResponse.class);
String urlToRequest = requestResponse.location.replace("/api/v1", "");
logger.debug(urlToRequest);
VirtualMachineBackupsResponse virtualMachineBackupsResponse = httpProvider.waitGet(urlToRequest, VirtualMachineBackupsResponse.class);
VirtualMachineBackupsResponse virtualMachineBackupsResponse = httpProvider.waitGet(urlToRequest,
VirtualMachineBackupsResponse.class);
if (virtualMachineBackupsResponse.state.equals(TaskState.SUCCESS)) {
if (virtualMachineBackupsResponse.info.archives.size() > 0) {
for (BackupInfos infos : virtualMachineBackupsResponse.info.archives) {
var dateStart = new DateTime(infos.start);
backups.add(new BackrollVmBackup(infos.id, infos.name, dateStart.toDate()));
}
return virtualMachineBackupsResponse.info.archives;
}
return null;
}
public List<Backup.RestorePoint> listRestorePoints(String vmId) throws BackrollApiException, IOException {
List<Backup.RestorePoint> backups = new ArrayList<Backup.RestorePoint>();
List<BackupInfos> backupInfos = getBackupInfosFromVm(vmId);
if (backupInfos != null && backupInfos.size() > 0) {
for (BackupInfos infos : backupInfos) {
var dateStart = new DateTime(infos.start);
backups.add(new Backup.RestorePoint(infos.id, dateStart.toDate(), "INCREMENTAL"));
}
}
return backups;
}
}

View File

@ -110,8 +110,8 @@ public class BackrollBackupProviderTest {
Mockito.doReturn(new BackupVO()).when(backupDao).persist(Mockito.any(BackupVO.class));
Mockito.doNothing().when(clientMock).triggerTaskStatus(Mockito.anyString());
syncBackups_Test();
Boolean result = backupProvider.takeBackup(vmInstanceVO);
assertTrue(result);
Pair<Boolean, Backup> result = backupProvider.takeBackup(vmInstanceVO);
assertTrue(result.first());
}
@Test