diff --git a/client/pom.xml b/client/pom.xml
index d8fa433d5be3..6ef335e94e4a 100644
--- a/client/pom.xml
+++ b/client/pom.xml
@@ -597,6 +597,11 @@
cloud-plugin-integrations-prometheus-exporter
${project.version}
+
+ org.apache.cloudstack
+ cloud-plugin-backup-backroll
+ ${project.version}
+
org.apache.cloudstack
cloud-plugin-backup-dummy
@@ -1122,6 +1127,11 @@
cloud-plugin-backup-veeam
${project.version}
+
+ org.apache.cloudstack
+ cloud-plugin-backup-backroll
+ ${project.version}
+
diff --git a/core/src/main/resources/META-INF/cloudstack/bootstrap/default-config-resources.xml b/core/src/main/resources/META-INF/cloudstack/bootstrap/default-config-resources.xml
new file mode 100644
index 000000000000..d2895050b9e3
--- /dev/null
+++ b/core/src/main/resources/META-INF/cloudstack/bootstrap/default-config-resources.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
diff --git a/core/src/main/resources/META-INF/cloudstack/bootstrap/spring-bootstrap-context-inheritable.xml.ori b/core/src/main/resources/META-INF/cloudstack/bootstrap/spring-bootstrap-context-inheritable.xml.ori
new file mode 100644
index 000000000000..fd1b68f24637
--- /dev/null
+++ b/core/src/main/resources/META-INF/cloudstack/bootstrap/spring-bootstrap-context-inheritable.xml.ori
@@ -0,0 +1,44 @@
+!--
+
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+
+-->
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/backup/backroll/pom.xml b/plugins/backup/backroll/pom.xml
new file mode 100644
index 000000000000..0c00cb580d6b
--- /dev/null
+++ b/plugins/backup/backroll/pom.xml
@@ -0,0 +1,53 @@
+
+
+ 4.0.0
+ cloud-plugin-backup-backroll
+ Apache CloudStack Plugin - BackRoll Backup and Recovery Plugin by DIMSI
+
+ cloudstack-plugins
+ org.apache.cloudstack
+ 4.21.0.0-SNAPSHOT
+ ../../pom.xml
+
+
+
+ org.apache.cloudstack
+ cloud-api
+ ${project.version}
+
+
+ org.apache.cloudstack
+ cloud-utils
+ ${project.version}
+
+
+ org.json
+ json
+
+
+ com.github.tomakehurst
+ wiremock-standalone
+ ${cs.wiremock.version}
+ test
+
+
+
diff --git a/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/BackrollBackupProvider.java b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/BackrollBackupProvider.java
new file mode 100644
index 000000000000..018686a2083c
--- /dev/null
+++ b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/BackrollBackupProvider.java
@@ -0,0 +1,443 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.backup;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+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;
+import org.apache.cloudstack.backup.backroll.utils.BackrollApiException;
+import org.apache.cloudstack.backup.backroll.utils.BackrollHttpClientProvider;
+import org.apache.cloudstack.backup.dao.BackupDao;
+import org.apache.cloudstack.framework.config.ConfigKey;
+import org.apache.cloudstack.framework.config.Configurable;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.http.ParseException;
+import org.apache.logging.log4j.Logger;
+import org.joda.time.DateTime;
+
+import com.cloud.utils.Pair;
+import com.cloud.utils.component.AdapterBase;
+import com.cloud.utils.exception.CloudRuntimeException;
+import com.cloud.vm.VMInstanceVO;
+import com.cloud.vm.VirtualMachine;
+import com.cloud.vm.dao.VMInstanceDao;
+
+public class BackrollBackupProvider extends AdapterBase implements BackupProvider, Configurable {
+
+ public static final String BACKUP_IDENTIFIER = "-CSBKP-";
+
+ public ConfigKey BackrollUrlConfigKey = new ConfigKey<>("Advanced", String.class,
+ "backup.plugin.backroll.config.url",
+ "http://api.backup.demo.ccc:5050/api/v1",
+ "Url for backroll plugin.", true, ConfigKey.Scope.Zone);
+
+ public ConfigKey BackrollAppNameConfigKey = new ConfigKey<>("Advanced", String.class,
+ "backup.plugin.backroll.config.appname",
+ "backroll-api",
+ "App Name for backroll plugin.", true, ConfigKey.Scope.Zone);
+
+ public ConfigKey BackrollPasswordConfigKey = new ConfigKey<>("Advanced", String.class,
+ "backup.plugin.backroll.config.password",
+ "VviX8dALauSyYJMqVYJqf3UyZOpO3joS",
+ "Password for backroll plugin.", true, ConfigKey.Scope.Zone);
+
+ private BackrollClient backrollClient;
+
+ @Inject
+ private BackupDao backupDao;
+ @Inject
+ private VMInstanceDao vmInstanceDao;
+
+ public BackrollBackupProvider(BackupDao backupDao, VMInstanceDao vmInstanceDao, BackrollClient client, Logger logger){
+ this.backupDao = backupDao;
+ this.vmInstanceDao = vmInstanceDao;
+ this.backrollClient = client;
+ this.logger = logger;
+ }
+
+ public BackrollBackupProvider(){}
+
+ @Override
+ public String getName() {
+ return "backroll";
+ }
+
+ @Override
+ public String getDescription() {
+ return "Backroll Backup Plugin";
+ }
+
+ @Override
+ public List listBackupOfferings(Long zoneId) {
+ logger.debug("Listing backup policies on backroll B&R Plugin");
+ BackrollClient client = getClient(zoneId);
+ try{
+ String urlToRequest = client.getBackupOfferingUrl();
+ logger.info("BackrollProvider: urlToRequest: " + urlToRequest);
+ if (!StringUtils.isEmpty(urlToRequest)){
+ List results = new ArrayList();
+ // return client.getBackupOfferings(urlToRequest);
+ results = client.getBackupOfferings(urlToRequest);
+ if(results.size()>0) {
+ logger.info("BackrollProvider: results > 0");
+ } else {
+ logger.info("BackrollProvider: results <= 0");
+ }
+ return results;
+ }
+ } catch (ParseException | BackrollApiException | IOException e) {
+ logger.info("BackrollProvider: catch erreur: {}", e);
+ throw new CloudRuntimeException("Failed to load backup offerings");
+ }
+ return new ArrayList();
+ }
+
+ @Override
+ public boolean isValidProviderOffering(Long zoneId, String uuid) {
+ logger.info("Checking if backup offering exists on the Backroll Backup Provider");
+ return true;
+ }
+
+ @Override
+ public boolean assignVMToBackupOffering(VirtualMachine vm, BackupOffering backupOffering) {
+ logger.info("Creating VM backup for VM {} from backup offering {}", vm.getInstanceName(), backupOffering.getName());
+ if(vm instanceof VMInstanceVO) {
+ ((VMInstanceVO) vm).setBackupExternalId(backupOffering.getUuid());
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean restoreVMFromBackup(VirtualMachine vm, Backup backup) {
+ logger.debug("Restoring vm {} from backup {} on the backroll Backup Provider", vm.getUuid(), backup.getUuid());
+ boolean isSuccess;
+ try {
+ isSuccess = getClient(vm.getDataCenterId()).restoreVMFromBackup(vm.getUuid(), getBackupName(backup));
+ } catch (ParseException | BackrollApiException | IOException e) {
+ throw new CloudRuntimeException("Failed to restore VM from Backup");
+ }
+ return isSuccess;
+ }
+
+ @Override
+ public Map getBackupMetrics(Long zoneId, List vms) {
+ final Map metrics = new HashMap<>();
+ if (CollectionUtils.isEmpty(vms)) {
+ logger.warn("Unable to get VM Backup Metrics because the list of VMs is empty.");
+ return metrics;
+ }
+
+ List vmUuids = vms.stream().filter(Objects::nonNull).map(VirtualMachine::getUuid).collect(Collectors.toList());
+ logger.debug("Get Backup Metrics for VMs: {}.", String.join(", ", vmUuids));
+
+ BackrollClient client = getClient(zoneId);
+ for (final VirtualMachine vm : vms) {
+ if (vm == null) {
+ continue;
+ }
+ try {
+ // get backups from database
+ List backupsInDb = backupDao.listByVmId(zoneId, vm.getId());
+
+ // check backing up task
+ for (Backup backup : backupsInDb) {
+ if (backup.getStatus().equals(Backup.Status.BackingUp)) {
+ BackrollTaskStatus response;
+ try {
+ response = client.checkBackupTaskStatus(backup.getExternalId());
+ } catch (ParseException | BackrollApiException | IOException e) {
+ logger.error(e);
+ throw new CloudRuntimeException("Failed to sync backups");
+ }
+
+ if (response != null) {
+ logger.debug("backroll backup id: {}", backup.getExternalId());
+ logger.debug("backroll backup status: {}", response.getState());
+
+ BackupVO backupToUpdate = ((BackupVO) backup);
+
+ if (response.getState().equals("PENDING")) {
+ backupToUpdate.setStatus(Backup.Status.BackingUp);
+ } else if (response.getState().equals("FAILURE")) {
+ backupToUpdate.setStatus(Backup.Status.Failed);
+ } else if (response.getState().equals("SUCCESS")) {
+ backupToUpdate.setStatus(Backup.Status.BackedUp);
+ backupToUpdate.setExternalId(response.getInfo());
+
+ BackrollBackupMetrics backupMetrics = null;
+ try {
+ backupMetrics = client.getBackupMetrics(vm.getUuid() , response.getInfo());
+ if (backupMetrics != null) {
+ backupToUpdate.setProtectedSize(backupMetrics.getDeduplicated());
+ backupToUpdate.setSize(backupMetrics.getSize());
+ }
+ } catch (BackrollApiException | IOException e) {
+ logger.error(e);
+ throw new CloudRuntimeException("Failed to get backup metrics");
+ }
+ } else {
+ backupToUpdate.setStatus(Backup.Status.BackingUp);
+ }
+
+ if (backupDao.persist(backupToUpdate) != null) {
+ logger.info("Backroll backup updated");
+ }
+ }
+ } else {
+ if(backup.getExternalId().contains(",")) {
+ String backupId = backup.getExternalId().split(",")[1];
+ BackupVO backupToUpdate = ((BackupVO) backup);
+ backupToUpdate.setExternalId(backupId);
+ try {
+ BackrollBackupMetrics backupMetrics = client.getBackupMetrics(vm.getUuid() , backupId);
+ if (backupMetrics != null) {
+ backupToUpdate.setProtectedSize(backupMetrics.getDeduplicated());
+ backupToUpdate.setSize(backupMetrics.getSize());
+ }
+ } catch (BackrollApiException | IOException e) {
+ logger.error(e);
+ throw new CloudRuntimeException("Failed to get backup metrics");
+ }
+ if (backupDao.persist(backupToUpdate) != null) {
+ logger.info("Backroll backup updated");
+ }
+ }
+ }
+ }
+
+ // refresh backup in database list
+ backupsInDb = backupDao.listByVmId(zoneId, vm.getId());
+
+ Long usedSize = 0L;
+ Long dataSize = 0L;
+ List backups = client.listRestorePoints(vm.getUuid());
+ for (RestorePoint backup : backups) {
+
+ BackrollBackupMetrics backupMetrics = client.getBackupMetrics(vm.getUuid() , getBackupName(backup.getId()));
+ if (backupMetrics != null) {
+ usedSize += Long.valueOf(backupMetrics.getDeduplicated());
+ dataSize += Long.valueOf(backupMetrics.getSize());
+
+ // update backup metrics
+ Backup backupToFind = backupsInDb.stream()
+ .filter(backupInDb -> backupInDb.getExternalId().contains(backup.getId()))
+ .findAny()
+ .orElse(null);
+
+ if (backupToFind != null) {
+ BackupVO backupToUpdate = ((BackupVO) backupToFind);
+ backupToUpdate.setProtectedSize(usedSize);
+ backupToUpdate.setSize(dataSize);
+ backupDao.persist(backupToUpdate);
+ }
+
+ }
+ }
+ Metric metric = new Metric(dataSize, usedSize);
+ logger.debug("Metrics for VM [uuid: {}, name: {}] is [backup size: {}, data size: {}].", vm.getUuid(),
+ vm.getInstanceName(), metric.getBackupSize(), metric.getDataSize());
+ metrics.put(vm, metric);
+ } catch (BackrollApiException | IOException e) {
+ throw new CloudRuntimeException("Failed to retrieve backup metrics");
+ }
+ }
+ return metrics;
+ }
+
+ @Override
+ public boolean removeVMFromBackupOffering(VirtualMachine vm) {
+ return true;
+ }
+
+ @Override
+ public boolean willDeleteBackupsOnOfferingRemoval() {
+ return false;
+ }
+
+ @Override
+ public Pair takeBackup(VirtualMachine vm) {
+ logger.info("Starting backup for VM ID {} on backroll provider", vm.getUuid());
+ final BackrollClient client = getClient(vm.getDataCenterId());
+
+ try {
+ String urlToRequest = client.startBackupJob(vm.getUuid());
+ logger.info("BackrollProvider: urlToRequest: " + urlToRequest);
+ String backupJob = urlToRequest.replace("/status/", "");
+ if (!StringUtils.isEmpty(backupJob)) {
+ BackupVO backup = new BackupVO();
+ backup.setVmId(vm.getId());
+ backup.setExternalId(backupJob);
+ backup.setType("INCREMENTAL");
+ backup.setDate(new DateTime().toDate());
+ backup.setSize(0L);
+ backup.setProtectedSize(0L);
+ backup.setStatus(Backup.Status.BackingUp);
+ backup.setBackupOfferingId(vm.getBackupOfferingId());
+ backup.setAccountId(vm.getAccountId());
+ backup.setDomainId(vm.getDomainId());
+ backup.setZoneId(vm.getDataCenterId());
+ Boolean result = backupDao.persist(backup) != null;
+ return new Pair(result, backup);
+ }
+ } catch (ParseException | BackrollApiException | IOException e) {
+ logger.debug(e.getMessage());
+ throw new CloudRuntimeException("Failed to take backup");
+ }
+ return new Pair(false, null);
+ }
+
+ @Override
+ public String getConfigComponentName() {
+ return BackupService.class.getSimpleName();
+ }
+
+ @Override
+ public ConfigKey>[] getConfigKeys() {
+ return new ConfigKey[]{
+ BackrollUrlConfigKey,
+ BackrollAppNameConfigKey,
+ BackrollPasswordConfigKey
+ };
+ }
+
+ @Override
+ public boolean deleteBackup(Backup backup, boolean forced) {
+ logger.info("BACKROLL: delete backup id: {}", backup.getExternalId());
+ if (backup.getStatus().equals(Backup.Status.BackingUp)) {
+ throw new CloudRuntimeException("You can't delete a backup while it still BackingUp");
+ } else {
+ logger.debug("BACKROLL: try delete backup");
+
+ if (backup.getStatus().equals(Backup.Status.Removed) || backup.getStatus().equals(Backup.Status.Failed)){
+ return deleteBackupInDb(backup);
+ } else {
+ VMInstanceVO vm = vmInstanceDao.findByIdIncludingRemoved(backup.getVmId());
+ try {
+ if (getClient(backup.getZoneId()).deleteBackup(vm.getUuid(), getBackupName(backup))) {
+ logger.debug("BACKROLL: Backup deletion for backup {} complete on backroll side.", backup.getUuid());
+ return deleteBackupInDb(backup);
+ }
+ } catch (BackrollApiException | IOException e) {
+ logger.error(e);
+ throw new CloudRuntimeException("BACKROLL: Failed to delete backup");
+ }
+ }
+ }
+ return false;
+ }
+
+ private boolean deleteBackupInDb(Backup backup) {
+ BackupVO backupToUpdate = ((BackupVO) backup);
+ backupToUpdate.setStatus(Backup.Status.Removed);
+ if (backupDao.persist(backupToUpdate) != null) {
+ logger.debug("BACKROLL: Backroll backup {} deleted in database.", backup.getUuid());
+ VMInstanceVO vm = vmInstanceDao.findByIdIncludingRemoved(backup.getVmId());
+ return true;
+ }
+ return false;
+ }
+
+ protected BackrollClient getClient(final Long zoneId) {
+ logger.debug("Backroll Provider GetClient with zone id {}", zoneId);
+ try {
+ if (backrollClient == null) {
+ logger.debug("backroll client null - instantiation of new one ");
+ BackrollHttpClientProvider provider = BackrollHttpClientProvider.createProvider(new BackrollHttpClientProvider(), BackrollUrlConfigKey.valueIn(zoneId), BackrollAppNameConfigKey.valueIn(zoneId), BackrollPasswordConfigKey.valueIn(zoneId), true, 300, 600);
+ backrollClient = new BackrollClient(provider);
+ }
+ return backrollClient;
+ } catch (URISyntaxException e) {
+ logger.error(e);
+ throw new CloudRuntimeException("Failed to parse Backroll API URL: " + e.getMessage());
+ } catch (NoSuchAlgorithmException | KeyManagementException e) {
+ logger.error(e);
+ throw new CloudRuntimeException("Failed to build Backroll API client");
+ }
+ }
+
+ private String getBackupName(Backup backup) {
+ return getBackupName(backup.getExternalId());
+ }
+
+ private String getBackupName(String externalId) {
+ return externalId.substring(externalId.indexOf(",") + 1);
+ }
+
+ @Override
+ public Pair restoreBackedUpVolume(Backup backup, String volumeUuid, String hostIp, String dataStoreUuid, Pair vmNameAndState) {
+ 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 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("Error while listing restore points");
+ }
+ }
+
+ @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());
+
+ try {
+ BackrollBackupMetrics backupMetrics = client.getBackupMetrics(vm.getUuid() , getBackupName(restorePoint.getId()));
+ if (backupMetrics != null) {
+ backupToInsert.setProtectedSize(backupMetrics.getDeduplicated());
+ backupToInsert.setSize(backupMetrics.getSize());
+ }
+ } catch (IOException | BackrollApiException e) {
+ logger.error(e);
+ }
+
+ backupDao.persist(backupToInsert);
+ return backupToInsert;
+ }
+}
diff --git a/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/BackrollClient.java b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/BackrollClient.java
new file mode 100644
index 000000000000..bd5717fb049c
--- /dev/null
+++ b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/BackrollClient.java
@@ -0,0 +1,263 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.backup.backroll;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+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;
+import org.apache.cloudstack.backup.backroll.model.BackrollOffering;
+import org.apache.cloudstack.backup.backroll.model.BackrollTaskStatus;
+import org.apache.cloudstack.backup.backroll.model.BackrollVmBackup;
+import org.apache.cloudstack.backup.backroll.model.response.BackrollTaskRequestResponse;
+import org.apache.cloudstack.backup.backroll.model.response.TaskState;
+import org.apache.cloudstack.backup.backroll.model.response.archive.BackrollBackupsFromVMResponse;
+import org.apache.cloudstack.backup.backroll.model.response.backup.BackrollBackupStatusResponse;
+import org.apache.cloudstack.backup.backroll.model.response.backup.BackrollBackupStatusSuccessResponse;
+import org.apache.cloudstack.backup.backroll.model.response.metrics.backup.BackrollBackupMetricsResponse;
+import org.apache.cloudstack.backup.backroll.model.response.metrics.virtualMachine.BackrollVmMetricsResponse;
+import org.apache.cloudstack.backup.backroll.model.response.metrics.virtualMachine.CacheStats;
+import org.apache.cloudstack.backup.backroll.model.response.metrics.virtualMachineBackups.BackupInfos;
+import org.apache.cloudstack.backup.backroll.model.response.metrics.virtualMachineBackups.VirtualMachineBackupsResponse;
+import org.apache.cloudstack.backup.backroll.model.response.policy.BackrollBackupPolicyResponse;
+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;
+import org.apache.logging.log4j.Logger;
+
+import org.joda.time.DateTime;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class BackrollClient {
+
+ protected Logger logger = LogManager.getLogger(BackrollClient.class);
+
+ private BackrollHttpClientProvider httpProvider;
+
+ public BackrollClient(BackrollHttpClientProvider httpProvider)
+ throws URISyntaxException, NoSuchAlgorithmException, KeyManagementException {
+
+ this.httpProvider = httpProvider;
+ }
+
+ 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);
+ 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 {
+ logger.info("Trying to get backroll backup policies url");
+ String url = "";
+ 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;
+ }
+
+ public List getBackupOfferings(String idTask) throws BackrollApiException, IOException {
+ logger.info("Trying to list backroll backup policies");
+ final List policies = new ArrayList<>();
+ BackupPoliciesResponse backupPoliciesResponse = httpProvider.waitGet(idTask, BackupPoliciesResponse.class);
+ 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));
+ }
+
+ return policies;
+ }
+
+ 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;
+
+ JSONObject jsonBody = new JSONObject();
+ try {
+ jsonBody.put("virtual_machine_id", vmId);
+ jsonBody.put("backup_name", backupName);
+ jsonBody.put("storage", "");
+ jsonBody.put("mode", "single");
+
+ } catch (JSONException e) {
+ logger.error("Backroll Error: {}", e.getMessage());
+ }
+
+ 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")) {
+ logger.debug("RESTORE SUCCESS content : " + result);
+ logger.debug("RESTORE SUCCESS");
+ isRestoreOk = true;
+ }
+
+ return isRestoreOk;
+ }
+
+ public BackrollTaskStatus checkBackupTaskStatus(String taskId) throws IOException, BackrollApiException {
+ logger.info("Trying to get backup status for Backroll task: {}", taskId);
+
+ BackrollTaskStatus status = new BackrollTaskStatus();
+
+ String backupResponse = httpProvider.getWithoutParseResponse("/status/" + taskId);
+
+ if (backupResponse.contains(TaskState.FAILURE) || backupResponse.contains(TaskState.PENDING)) {
+ BackrollBackupStatusResponse backupStatusRequestResponse = new ObjectMapper().readValue(backupResponse,
+ BackrollBackupStatusResponse.class);
+ status.setState(backupStatusRequestResponse.state);
+ } else {
+ BackrollBackupStatusSuccessResponse backupStatusSuccessRequestResponse = new ObjectMapper()
+ .readValue(backupResponse, BackrollBackupStatusSuccessResponse.class);
+ status.setState(backupStatusSuccessRequestResponse.state);
+ status.setInfo(backupStatusSuccessRequestResponse.info);
+ }
+
+ return StringUtils.isEmpty(status.getState()) ? null : status;
+ }
+
+ public boolean deleteBackup(final String vmId, final String backupName) throws IOException, BackrollApiException {
+ logger.info("BACKROLL: 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);
+ String urlToRequest = requestResponse.location.replace("/api/v1", "");
+
+ BackrollBackupsFromVMResponse backrollBackupsFromVMResponse = httpProvider.waitGet(urlToRequest,
+ BackrollBackupsFromVMResponse.class);
+ logger.debug(backrollBackupsFromVMResponse.state);
+ isBackupDeleted = backrollBackupsFromVMResponse.state.equals(TaskState.SUCCESS);
+
+ return isBackupDeleted;
+ }
+
+ public Metric getVirtualMachineMetrics(final String vmId) throws IOException, BackrollApiException {
+ logger.info("Trying to retrieve virtual machine metric from Backroll for vm {}", vmId);
+
+ Metric metric = new Metric(0L, 0L);
+
+ 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);
+
+ if (vmMetricsResponse != null && vmMetricsResponse.state.equals(TaskState.SUCCESS)) {
+ logger.debug("SUCCESS ok");
+ CacheStats stats = null;
+ try {
+ stats = vmMetricsResponse.infos.cache.stats;
+ } catch (NullPointerException e) {
+ }
+ if (stats != null) {
+ long size = Long.parseLong(stats.totalSize);
+ metric = new Metric(size, size);
+ }
+ }
+
+ return metric;
+ }
+
+ 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);
+
+ String urlToRequest = requestResponse.location.replace("/api/v1", "");
+
+ logger.debug(urlToRequest);
+
+ BackrollBackupMetricsResponse metricsResponse = httpProvider.waitGet(urlToRequest,
+ BackrollBackupMetricsResponse.class);
+ if (metricsResponse.info != null) {
+ metrics = new BackrollBackupMetrics(Long.parseLong(metricsResponse.info.originalSize),
+ Long.parseLong(metricsResponse.info.deduplicatedSize));
+ }
+ return metrics;
+ }
+
+ public List getAllBackupsfromVirtualMachine(String vmId)
+ throws BackrollApiException, IOException {
+ List backups = new ArrayList();
+ List 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;
+ }
+
+ public List 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);
+ if (virtualMachineBackupsResponse.state.equals(TaskState.SUCCESS)) {
+ return virtualMachineBackupsResponse.info.archives;
+ }
+ return null;
+
+ }
+
+ public List listRestorePoints(String vmId) throws BackrollApiException, IOException {
+ List backups = new ArrayList();
+ List 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.name, dateStart.toDate(), "INCREMENTAL"));
+ }
+ }
+ return backups;
+ }
+}
diff --git a/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/BackrollBackup.java b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/BackrollBackup.java
new file mode 100644
index 000000000000..a0d5af0b5c6e
--- /dev/null
+++ b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/BackrollBackup.java
@@ -0,0 +1,29 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.backup.backroll.model;
+
+public class BackrollBackup {
+ private String archive;
+
+ public String getArchive() {
+ return archive;
+ }
+
+ public BackrollBackup(String archive) {
+ this.archive = archive;
+ }
+}
diff --git a/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/BackrollBackupMetrics.java b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/BackrollBackupMetrics.java
new file mode 100644
index 000000000000..44a9a0357b4d
--- /dev/null
+++ b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/BackrollBackupMetrics.java
@@ -0,0 +1,35 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.backup.backroll.model;
+
+public class BackrollBackupMetrics {
+ private long size;
+ private long deduplicated;
+
+ public long getSize() {
+ return size;
+ }
+
+ public long getDeduplicated() {
+ return deduplicated;
+ }
+
+ public BackrollBackupMetrics(long size, long deduplicated) {
+ this.size = size;
+ this.deduplicated = deduplicated;
+ }
+}
diff --git a/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/BackrollOffering.java b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/BackrollOffering.java
new file mode 100644
index 000000000000..9a58ff127c08
--- /dev/null
+++ b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/BackrollOffering.java
@@ -0,0 +1,77 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.backup.backroll.model;
+
+import java.util.Date;
+
+import org.apache.cloudstack.backup.BackupOffering;
+
+public class BackrollOffering implements BackupOffering {
+
+ private String name;
+ private String uid;
+
+ public BackrollOffering(String name, String uid) {
+ this.name = name;
+ this.uid = uid;
+ }
+
+ @Override
+ public String getExternalId() {
+ return uid;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public String getDescription() {
+ return "Backroll Backup Offering (Job)";
+ }
+
+ @Override
+ public long getZoneId() {
+ return -1;
+ }
+
+ @Override
+ public boolean isUserDrivenBackupAllowed() {
+ return false;
+ }
+
+ @Override
+ public String getProvider() {
+ return "backroll";
+ }
+
+ @Override
+ public Date getCreated() {
+ return null;
+ }
+
+ @Override
+ public String getUuid() {
+ return uid;
+ }
+
+ @Override
+ public long getId() {
+ return -1;
+ }
+}
diff --git a/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/BackrollTaskStatus.java b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/BackrollTaskStatus.java
new file mode 100644
index 000000000000..54a08c341a65
--- /dev/null
+++ b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/BackrollTaskStatus.java
@@ -0,0 +1,47 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.backup.backroll.model;
+
+public class BackrollTaskStatus {
+ private String state;
+ private String error;
+ private String info;
+
+ public String getState() {
+ return state;
+ }
+
+ public void setState(String state) {
+ this.state = state;
+ }
+
+ public String getError() {
+ return error;
+ }
+
+ public void setError(String error) {
+ this.error = error;
+ }
+
+ public String getInfo() {
+ return info;
+ }
+
+ public void setInfo(String info) {
+ this.info = info;
+ }
+}
diff --git a/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/BackrollVmBackup.java b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/BackrollVmBackup.java
new file mode 100644
index 000000000000..c338f74dafc2
--- /dev/null
+++ b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/BackrollVmBackup.java
@@ -0,0 +1,43 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.backup.backroll.model;
+
+import java.util.Date;
+
+public class BackrollVmBackup {
+ private String id;
+ private String name;
+ private Date date;
+
+ public String getId() {
+ return id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public Date getDate() {
+ return date;
+ }
+
+ public BackrollVmBackup(String id, String name, Date date) {
+ this.id = id;
+ this.name = name;
+ this.date = date;
+ }
+}
diff --git a/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/BackrollAsyncResponse.java b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/BackrollAsyncResponse.java
new file mode 100644
index 000000000000..84c94b5fbbcd
--- /dev/null
+++ b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/BackrollAsyncResponse.java
@@ -0,0 +1,24 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.backup.backroll.model.response;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public abstract class BackrollAsyncResponse {
+ @JsonProperty("state")
+ public String state;
+}
diff --git a/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/BackrollTaskRequestResponse.java b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/BackrollTaskRequestResponse.java
new file mode 100644
index 000000000000..decc191318df
--- /dev/null
+++ b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/BackrollTaskRequestResponse.java
@@ -0,0 +1,24 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.backup.backroll.model.response;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class BackrollTaskRequestResponse {
+ @JsonProperty("Location")
+ public String location;
+}
diff --git a/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/TaskState.java b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/TaskState.java
new file mode 100644
index 000000000000..a9abd43fd8b1
--- /dev/null
+++ b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/TaskState.java
@@ -0,0 +1,23 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.backup.backroll.model.response;
+
+public class TaskState {
+ public static final String PENDING = "PENDING";
+ public static final String FAILURE = "FAILURE";
+ public static final String SUCCESS = "SUCCESS";
+}
diff --git a/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/TaskStateResponse.java b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/TaskStateResponse.java
new file mode 100644
index 000000000000..10bd25d98039
--- /dev/null
+++ b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/TaskStateResponse.java
@@ -0,0 +1,24 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.backup.backroll.model.response;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class TaskStateResponse {
+ @JsonProperty("state")
+ public String state;
+}
diff --git a/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/api/LoginApiResponse.java b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/api/LoginApiResponse.java
new file mode 100644
index 000000000000..7fbb5ea18ee2
--- /dev/null
+++ b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/api/LoginApiResponse.java
@@ -0,0 +1,39 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.backup.backroll.model.response.api;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class LoginApiResponse {
+ @JsonProperty("access_token")
+ public String accessToken;
+
+ @JsonProperty("expires_in")
+ public int expiresIn;
+
+ @JsonProperty("refresh_expires_in")
+ public String refreshExpiresIn;
+
+ @JsonProperty("token_type")
+ public String tokenType;
+
+ @JsonProperty("not-before-policy")
+ public String notBeforePolicy;
+
+ @JsonProperty("scope")
+ public String scope;
+}
diff --git a/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/archive/BackrollArchiveResponse.java b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/archive/BackrollArchiveResponse.java
new file mode 100644
index 000000000000..92c2f1c927ba
--- /dev/null
+++ b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/archive/BackrollArchiveResponse.java
@@ -0,0 +1,42 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.backup.backroll.model.response.archive;
+
+import org.joda.time.DateTime;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class BackrollArchiveResponse {
+ @JsonProperty("archive")
+ public String archive;
+
+ // TODO Merged typo ?
+ @JsonProperty("barchive")
+ public String barchive;
+
+ @JsonProperty("id")
+ public String id;
+
+ @JsonProperty("name")
+ public String name;
+
+ @JsonProperty("start")
+ public DateTime start;
+
+ @JsonProperty("time")
+ public DateTime time;
+}
diff --git a/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/archive/BackrollArchivesResponse.java b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/archive/BackrollArchivesResponse.java
new file mode 100644
index 000000000000..5cb8634d5d80
--- /dev/null
+++ b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/archive/BackrollArchivesResponse.java
@@ -0,0 +1,28 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.backup.backroll.model.response.archive;
+
+import java.util.List;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class BackrollArchivesResponse {
+ @JsonProperty("archives")
+ @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
+ public List archives;
+}
diff --git a/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/archive/BackrollBackupsFromVMResponse.java b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/archive/BackrollBackupsFromVMResponse.java
new file mode 100644
index 000000000000..bde2f3645dbd
--- /dev/null
+++ b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/archive/BackrollBackupsFromVMResponse.java
@@ -0,0 +1,26 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.backup.backroll.model.response.archive;
+
+import org.apache.cloudstack.backup.backroll.model.response.BackrollAsyncResponse;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class BackrollBackupsFromVMResponse extends BackrollAsyncResponse {
+ @JsonProperty("info")
+ public BackrollArchivesResponse archives;
+}
diff --git a/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/backup/BackrollBackupResponse.java b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/backup/BackrollBackupResponse.java
new file mode 100644
index 000000000000..f4ce131eb8c4
--- /dev/null
+++ b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/backup/BackrollBackupResponse.java
@@ -0,0 +1,23 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.backup.backroll.model.response.backup;
+
+import org.apache.cloudstack.backup.backroll.model.response.BackrollAsyncResponse;
+
+public class BackrollBackupResponse extends BackrollAsyncResponse{
+
+}
diff --git a/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/backup/BackrollBackupStatusResponse.java b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/backup/BackrollBackupStatusResponse.java
new file mode 100644
index 000000000000..5edf97380128
--- /dev/null
+++ b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/backup/BackrollBackupStatusResponse.java
@@ -0,0 +1,33 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.backup.backroll.model.response.backup;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class BackrollBackupStatusResponse {
+ @JsonProperty("state")
+ public String state;
+
+ @JsonProperty("current")
+ public String current;
+
+ @JsonProperty("total")
+ public String total;
+
+ @JsonProperty("status")
+ public String status;
+}
diff --git a/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/backup/BackrollBackupStatusSuccessResponse.java b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/backup/BackrollBackupStatusSuccessResponse.java
new file mode 100644
index 000000000000..b60e5fad495e
--- /dev/null
+++ b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/backup/BackrollBackupStatusSuccessResponse.java
@@ -0,0 +1,27 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.backup.backroll.model.response.backup;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class BackrollBackupStatusSuccessResponse {
+ @JsonProperty("state")
+ public String state;
+
+ @JsonProperty("info")
+ public String info;
+}
diff --git a/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/metrics/backup/BackrollBackupMetricsResponse.java b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/metrics/backup/BackrollBackupMetricsResponse.java
new file mode 100644
index 000000000000..1582c0133f2f
--- /dev/null
+++ b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/metrics/backup/BackrollBackupMetricsResponse.java
@@ -0,0 +1,26 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.backup.backroll.model.response.metrics.backup;
+
+import org.apache.cloudstack.backup.backroll.model.response.BackrollAsyncResponse;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class BackrollBackupMetricsResponse extends BackrollAsyncResponse {
+ @JsonProperty("info")
+ public BackupMetricsInfo info;
+}
diff --git a/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/metrics/backup/BackupMetricsInfo.java b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/metrics/backup/BackupMetricsInfo.java
new file mode 100644
index 000000000000..e762debc7ca8
--- /dev/null
+++ b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/metrics/backup/BackupMetricsInfo.java
@@ -0,0 +1,33 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.backup.backroll.model.response.metrics.backup;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class BackupMetricsInfo {
+ @JsonProperty("compressed_size")
+ public String compressedSize;
+
+ @JsonProperty("deduplicated_size")
+ public String deduplicatedSize;
+
+ @JsonProperty("nfiles")
+ public String nFiles;
+
+ @JsonProperty("original_size")
+ public String originalSize;
+}
diff --git a/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/metrics/virtualMachine/BackrollVmMetricsResponse.java b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/metrics/virtualMachine/BackrollVmMetricsResponse.java
new file mode 100644
index 000000000000..74bbca646ae6
--- /dev/null
+++ b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/metrics/virtualMachine/BackrollVmMetricsResponse.java
@@ -0,0 +1,26 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.backup.backroll.model.response.metrics.virtualMachine;
+
+import org.apache.cloudstack.backup.backroll.model.response.BackrollAsyncResponse;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class BackrollVmMetricsResponse extends BackrollAsyncResponse{
+ @JsonProperty("info")
+ public MetricsInfos infos;
+}
diff --git a/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/metrics/virtualMachine/CacheStats.java b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/metrics/virtualMachine/CacheStats.java
new file mode 100644
index 000000000000..179b8bd78ff1
--- /dev/null
+++ b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/metrics/virtualMachine/CacheStats.java
@@ -0,0 +1,39 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.backup.backroll.model.response.metrics.virtualMachine;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class CacheStats {
+ @JsonProperty("total_chunks")
+ public String totalChunks;
+
+ @JsonProperty("total_csize")
+ public String totalCsize;
+
+ @JsonProperty("total_size")
+ public String totalSize;
+
+ @JsonProperty("total_unique_chunks")
+ public String totalUniqueChunks;
+
+ @JsonProperty("unique_csize")
+ public String uniqueCsize;
+
+ @JsonProperty("unique_size")
+ public String uniqueSize;
+}
diff --git a/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/metrics/virtualMachine/InfosCache.java b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/metrics/virtualMachine/InfosCache.java
new file mode 100644
index 000000000000..1d9f09d36494
--- /dev/null
+++ b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/metrics/virtualMachine/InfosCache.java
@@ -0,0 +1,27 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.backup.backroll.model.response.metrics.virtualMachine;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class InfosCache {
+ @JsonProperty("path")
+ public String path;
+
+ @JsonProperty("stats")
+ public CacheStats stats;
+}
diff --git a/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/metrics/virtualMachine/InfosEncryption.java b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/metrics/virtualMachine/InfosEncryption.java
new file mode 100644
index 000000000000..39d90ee04928
--- /dev/null
+++ b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/metrics/virtualMachine/InfosEncryption.java
@@ -0,0 +1,24 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.backup.backroll.model.response.metrics.virtualMachine;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class InfosEncryption {
+ @JsonProperty("mode")
+ public String mode;
+}
diff --git a/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/metrics/virtualMachine/InfosRepository.java b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/metrics/virtualMachine/InfosRepository.java
new file mode 100644
index 000000000000..80949c0ad781
--- /dev/null
+++ b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/metrics/virtualMachine/InfosRepository.java
@@ -0,0 +1,30 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.backup.backroll.model.response.metrics.virtualMachine;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class InfosRepository {
+ @JsonProperty("id")
+ public String id;
+
+ @JsonProperty("last_modified")
+ public String lastModified;
+
+ @JsonProperty("location")
+ public String location;
+}
diff --git a/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/metrics/virtualMachine/MetricsInfos.java b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/metrics/virtualMachine/MetricsInfos.java
new file mode 100644
index 000000000000..bf2109c1d2a0
--- /dev/null
+++ b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/metrics/virtualMachine/MetricsInfos.java
@@ -0,0 +1,33 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.backup.backroll.model.response.metrics.virtualMachine;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class MetricsInfos {
+ @JsonProperty("cache")
+ public InfosCache cache;
+
+ @JsonProperty("encryption")
+ public InfosEncryption encryption;
+
+ @JsonProperty("repository")
+ public InfosRepository repository;
+
+ @JsonProperty("security_dir")
+ public String securityDir;
+}
diff --git a/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/metrics/virtualMachineBackups/Archives.java b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/metrics/virtualMachineBackups/Archives.java
new file mode 100644
index 000000000000..f4e9067aef69
--- /dev/null
+++ b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/metrics/virtualMachineBackups/Archives.java
@@ -0,0 +1,35 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.backup.backroll.model.response.metrics.virtualMachineBackups;
+
+import java.util.List;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class Archives {
+ @JsonProperty("archives")
+ public List archives;
+
+ @JsonProperty(value = "encryption", required = false)
+ public InfosEncryption encryption;
+
+ @JsonProperty(value = "repository", required = false)
+ public InfosRepository repository;
+
+ @JsonProperty(value = "state", required = false)
+ public String state;
+}
diff --git a/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/metrics/virtualMachineBackups/BackupInfos.java b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/metrics/virtualMachineBackups/BackupInfos.java
new file mode 100644
index 000000000000..87d9a7269b18
--- /dev/null
+++ b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/metrics/virtualMachineBackups/BackupInfos.java
@@ -0,0 +1,40 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.backup.backroll.model.response.metrics.virtualMachineBackups;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class BackupInfos {
+ @JsonProperty("archive")
+ public String archive;
+
+ // TODO Merged typo ?
+ @JsonProperty("barchive")
+ public String barchive;
+
+ @JsonProperty("id")
+ public String id;
+
+ @JsonProperty("name")
+ public String name;
+
+ @JsonProperty("start")
+ public String start;
+
+ @JsonProperty("time")
+ public String time;
+}
diff --git a/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/metrics/virtualMachineBackups/InfosEncryption.java b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/metrics/virtualMachineBackups/InfosEncryption.java
new file mode 100644
index 000000000000..ba6418c9985f
--- /dev/null
+++ b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/metrics/virtualMachineBackups/InfosEncryption.java
@@ -0,0 +1,24 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.backup.backroll.model.response.metrics.virtualMachineBackups;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class InfosEncryption {
+ @JsonProperty("mode")
+ public String mode;
+}
diff --git a/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/metrics/virtualMachineBackups/InfosRepository.java b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/metrics/virtualMachineBackups/InfosRepository.java
new file mode 100644
index 000000000000..90302899bccc
--- /dev/null
+++ b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/metrics/virtualMachineBackups/InfosRepository.java
@@ -0,0 +1,30 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.backup.backroll.model.response.metrics.virtualMachineBackups;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class InfosRepository {
+ @JsonProperty("id")
+ public String id;
+
+ @JsonProperty("last_modified")
+ public String lastModified;
+
+ @JsonProperty("location")
+ public String location;
+}
diff --git a/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/metrics/virtualMachineBackups/VirtualMachineBackupsResponse.java b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/metrics/virtualMachineBackups/VirtualMachineBackupsResponse.java
new file mode 100644
index 000000000000..11ed8267be6f
--- /dev/null
+++ b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/metrics/virtualMachineBackups/VirtualMachineBackupsResponse.java
@@ -0,0 +1,26 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.backup.backroll.model.response.metrics.virtualMachineBackups;
+
+import org.apache.cloudstack.backup.backroll.model.response.BackrollAsyncResponse;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class VirtualMachineBackupsResponse extends BackrollAsyncResponse {
+ @JsonProperty("info")
+ public Archives info;
+}
diff --git a/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/policy/BackrollBackupPolicyResponse.java b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/policy/BackrollBackupPolicyResponse.java
new file mode 100644
index 000000000000..125c60ac94ac
--- /dev/null
+++ b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/policy/BackrollBackupPolicyResponse.java
@@ -0,0 +1,54 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.backup.backroll.model.response.policy;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class BackrollBackupPolicyResponse {
+ @JsonProperty("name")
+ public String name;
+
+ @JsonProperty("retention_day")
+ public int retentionDay;
+
+ @JsonProperty("schedule")
+ public String schedule;
+
+ @JsonProperty("retention_month")
+ public int retentionMonth;
+
+ @JsonProperty("storage")
+ public String storage;
+
+ @JsonProperty("enabled")
+ public Boolean enabled;
+
+ @JsonProperty("description")
+ public String description;
+
+ @JsonProperty("id")
+ public String id;
+
+ @JsonProperty("retention_week")
+ public int retentionWeek;
+
+ @JsonProperty("retention_year")
+ public int retentionYear;
+
+ @JsonProperty("externalhook")
+ public String externalHook;
+}
diff --git a/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/policy/BackupPoliciesResponse.java b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/policy/BackupPoliciesResponse.java
new file mode 100644
index 000000000000..f9512520386d
--- /dev/null
+++ b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/policy/BackupPoliciesResponse.java
@@ -0,0 +1,31 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.backup.backroll.model.response.policy;
+
+import java.util.List;
+
+import org.apache.cloudstack.backup.backroll.model.response.BackrollAsyncResponse;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class BackupPoliciesResponse extends BackrollAsyncResponse {
+
+ @JsonProperty("info")
+ @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
+ public List backupPolicies;
+}
diff --git a/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/restore/BackrollRestoreResponse.java b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/restore/BackrollRestoreResponse.java
new file mode 100644
index 000000000000..8f831f49e2f3
--- /dev/null
+++ b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/model/response/restore/BackrollRestoreResponse.java
@@ -0,0 +1,28 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.backup.backroll.model.response.restore;
+
+import org.apache.cloudstack.backup.backroll.model.response.BackrollAsyncResponse;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class BackrollRestoreResponse extends BackrollAsyncResponse {
+ @JsonProperty("info")
+ public String info;
+}
diff --git a/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/utils/BackrollApiException.java b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/utils/BackrollApiException.java
new file mode 100644
index 000000000000..a07f9e9863da
--- /dev/null
+++ b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/utils/BackrollApiException.java
@@ -0,0 +1,22 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.backup.backroll.utils;
+
+
+public class BackrollApiException extends Exception {
+
+}
diff --git a/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/utils/BackrollHttpClientProvider.java b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/utils/BackrollHttpClientProvider.java
new file mode 100644
index 000000000000..496f6c4d7095
--- /dev/null
+++ b/plugins/backup/backroll/src/main/java/org/apache/cloudstack/backup/backroll/utils/BackrollHttpClientProvider.java
@@ -0,0 +1,403 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.backup.backroll.utils;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.concurrent.TimeUnit;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.X509TrustManager;
+
+import org.apache.cloudstack.api.ApiErrorCode;
+import org.apache.cloudstack.api.ServerApiException;
+import org.apache.cloudstack.backup.backroll.BackrollClient;
+import org.apache.cloudstack.backup.backroll.model.response.TaskState;
+import org.apache.cloudstack.backup.backroll.model.response.api.LoginApiResponse;
+import org.apache.cloudstack.utils.security.SSLUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpHeaders;
+import org.apache.http.HttpStatus;
+import org.apache.http.ParseException;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpDelete;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.conn.ssl.NoopHostnameVerifier;
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.util.EntityUtils;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import com.cloud.utils.exception.CloudRuntimeException;
+import com.cloud.utils.nio.TrustAllManager;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class BackrollHttpClientProvider {
+ private URI apiURI;;
+ private String backrollToken = null;
+ private String appname = null;
+ private String password = null;
+ private RequestConfig config = null;
+ private boolean validateCertificate = false;
+
+ private Logger logger = LogManager.getLogger(BackrollClient.class);
+
+ public static BackrollHttpClientProvider createProvider(final BackrollHttpClientProvider backrollHttpClientProvider, final String url, final String appname, final String password,
+ final boolean validateCertificate, final int timeout,
+ final int restoreTimeout) throws URISyntaxException, NoSuchAlgorithmException, KeyManagementException {
+ //BackrollHttpClientProvider backrollHttpClientProvider = new BackrollHttpClientProvider();
+ backrollHttpClientProvider.apiURI = new URI(url);
+ backrollHttpClientProvider.appname = appname;
+ backrollHttpClientProvider.password = password;
+ backrollHttpClientProvider.validateCertificate = validateCertificate;
+
+ backrollHttpClientProvider.config = RequestConfig.custom()
+ .setConnectTimeout(timeout * 1000)
+ .setConnectionRequestTimeout(timeout * 1000)
+ .setSocketTimeout(timeout * 1000)
+ .build();
+
+ return backrollHttpClientProvider;
+ }
+
+ protected CloseableHttpClient createHttpClient() throws BackrollApiException {
+ if(!validateCertificate) {
+ SSLContext sslContext;
+ try {
+ sslContext = SSLUtils.getSSLContext();
+ sslContext.init(null, new X509TrustManager[] { new TrustAllManager() }, new SecureRandom());
+ final SSLConnectionSocketFactory factory = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);
+ return HttpClientBuilder.create()
+ .setDefaultRequestConfig(config)
+ .setSSLSocketFactory(factory)
+ .build();
+ } catch (NoSuchAlgorithmException | KeyManagementException e) {
+ logger.error(e);
+ e.printStackTrace();
+ throw new BackrollApiException();
+ }
+ } else {
+ return HttpClientBuilder.create()
+ .setDefaultRequestConfig(config)
+ .build();
+ }
+ }
+
+ public T post(final String path, final JSONObject json, Class classOfT) throws BackrollApiException, IOException {
+
+ loginIfAuthenticationFailed();
+
+ try (CloseableHttpClient httpClient = createHttpClient()) {
+
+ String xml = null;
+ StringEntity params = null;
+ if (json != null) {
+ logger.debug("JSON {}", json.toString());
+ params = new StringEntity(json.toString(), ContentType.APPLICATION_JSON);
+ }
+
+ String url = apiURI.toString() + path;
+ final HttpPost request = new HttpPost(url);
+
+ if (params != null) {
+ request.setEntity(params);
+ }
+
+ request.setHeader(HttpHeaders.AUTHORIZATION, "Bearer " + backrollToken);
+ request.setHeader("Content-type", "application/json");
+
+ final CloseableHttpResponse response = httpClient.execute(request);
+
+ logger.debug("Response received in POST request with body {} is: {} for URL {}.", xml, response.toString(), url);
+
+ String result = okBody(response);
+
+ T requestResponse = new ObjectMapper().readValue(result, classOfT);
+ //response.close();
+
+ return requestResponse;
+ } catch (ParseException | NotOkBodyException e) {
+ logger.error(e);
+ e.printStackTrace();
+ throw new BackrollApiException();
+ }
+ }
+
+ public T get(String path, Class classOfT) throws IOException, BackrollApiException{
+ logger.debug("Backroll Get Auth ");
+ loginIfAuthenticationFailed();
+ logger.debug("Backroll Get Auth ok");
+ try (CloseableHttpClient httpClient = createHttpClient()) {
+
+ String url = apiURI.toString() + path;
+ logger.debug("Backroll URL {}", url);
+
+ HttpGet request = new HttpGet(url);
+ request.setHeader(HttpHeaders.AUTHORIZATION, "Bearer " + backrollToken);
+ request.setHeader("Content-type", "application/json");
+ CloseableHttpResponse response = httpClient.execute(request);
+ logger.debug("Response received in GET request is: {} for URL: {}.", response.toString(), url);
+
+ String result = okBody(response);
+ T requestResponse = new ObjectMapper().readValue(result, classOfT);
+ //response.close();
+
+ return requestResponse;
+ } catch (NotOkBodyException e) {
+ logger.error(e);
+ e.printStackTrace();
+ throw new BackrollApiException();
+ }
+ }
+
+ public String getWithoutParseResponse(String path) throws IOException, BackrollApiException{
+ logger.debug("Backroll Get Auth ");
+ loginIfAuthenticationFailed();
+ logger.debug("Backroll Get Auth ok");
+ try (CloseableHttpClient httpClient = createHttpClient()) {
+
+ String url = apiURI.toString() + path;
+ logger.debug("Backroll URL {}", url);
+
+ HttpGet request = new HttpGet(url);
+ request.setHeader(HttpHeaders.AUTHORIZATION, "Bearer " + backrollToken);
+ request.setHeader("Content-type", "application/json");
+ CloseableHttpResponse response = httpClient.execute(request);
+ logger.debug("Response received in GET request is: {} for URL: {}.", response.toString(), url);
+
+ String result = okBody(response);
+ //response.close();
+
+ return result;
+ } catch (NotOkBodyException e) {
+ logger.error(e);
+ e.printStackTrace();
+ throw new BackrollApiException();
+ }
+ }
+
+ public T delete(String path, Class classOfT) throws IOException, BackrollApiException {
+
+ loginIfAuthenticationFailed();
+
+ try (CloseableHttpClient httpClient = createHttpClient()) {
+
+ String url = apiURI.toString() + path;
+ final HttpDelete request = new HttpDelete(url);
+ request.setHeader(HttpHeaders.AUTHORIZATION, "Bearer " + backrollToken);
+ request.setHeader("Content-type", "application/json");
+ final CloseableHttpResponse response = httpClient.execute(request);
+ logger.debug("Response received in GET request is: {} for URL: {}.", response.toString(), url);
+
+ String result = okBody(response);
+ T requestResponse = new ObjectMapper().readValue(result, classOfT);
+ //response.close();
+
+ return requestResponse;
+ } catch (NotOkBodyException e) {
+ logger.error(e);
+ e.printStackTrace();
+ throw new BackrollApiException();
+ }
+ }
+
+ public class NotOkBodyException extends Exception {
+ public NotOkBodyException() {
+ super();
+ }
+ }
+
+ public String okBody(final CloseableHttpResponse response) throws NotOkBodyException {
+ String result = "";
+ switch (response.getStatusLine().getStatusCode()) {
+ case HttpStatus.SC_OK:
+ case HttpStatus.SC_ACCEPTED:
+ HttpEntity bodyEntity = response.getEntity();
+ try {
+ logger.debug("bodyentity : {}", bodyEntity);
+ result = EntityUtils.toString(bodyEntity);
+ logger.debug("bodyentity : result {}", result);
+ EntityUtils.consumeQuietly(bodyEntity);
+ return result;
+ } catch (ParseException | IOException e) {
+ e.printStackTrace();
+ throw new NotOkBodyException();
+ } finally {
+ EntityUtils.consumeQuietly(bodyEntity);
+ }
+ default:
+ try {
+ closeConnection(response);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ throw new NotOkBodyException();
+ }
+ }
+
+ public T waitGet(String url, Class classOfT) throws IOException, BackrollApiException {
+ // int threshold = 30; // 5 minutes
+ int maxAttempts = 12; // 2 minutes
+
+ for (int attempt = 1; attempt <= maxAttempts; attempt++) {
+ String body = getWithoutParseResponse(url);
+ if(!StringUtils.isEmpty(body)){
+ if (!body.contains(TaskState.PENDING)) {
+ logger.debug("METRICS waitGetWithoutParseResponse : result {}", body);
+ T result = new ObjectMapper().readValue(body, classOfT);
+ return result;
+ }
+ }
+
+ try {
+ TimeUnit.SECONDS.sleep(10);
+ } catch (InterruptedException e) {
+ logger.error(e);
+ e.printStackTrace();
+ throw new BackrollApiException();
+ }
+ }
+
+ return null;
+ }
+
+ public String waitGetWithoutParseResponse(String url)
+ throws IOException, BackrollApiException {
+ // int threshold = 30; // 5 minutes
+ int maxAttempts = 12; // 2 minutes
+
+ for (int attempt = 1; attempt <= maxAttempts; attempt++) {
+
+ String body = getWithoutParseResponse(url);
+ if (!body.contains(TaskState.PENDING)) {
+ logger.debug("waitGetWithoutParseResponse : result {}", body);
+ return body;
+ }
+
+ try {
+ TimeUnit.SECONDS.sleep(10);
+ } catch (InterruptedException e) {
+ logger.error(e);
+ e.printStackTrace();
+ throw new BackrollApiException();
+ }
+ }
+ return null;
+ }
+
+ protected boolean isAuthenticated() throws BackrollApiException, IOException {
+ boolean result = false;
+
+ if(StringUtils.isEmpty(backrollToken)) {
+ logger.debug("Backroll Tocken empty : " + backrollToken);
+ return result;
+ }
+
+ try (CloseableHttpClient httpClient = createHttpClient()) {
+ final HttpPost request = new HttpPost(apiURI.toString() + "/auth");
+ CloseableHttpResponse httpResponse = httpClient.execute(request);
+ logger.debug("Backroll Auth response : " + EntityUtils.toString(httpResponse.getEntity()));
+ logger.debug("Backroll Auth response status : " + httpResponse.getStatusLine().getStatusCode());
+ String response = okBody(httpResponse);
+ logger.debug("Backroll Auth response 2 : " + response);
+ logger.debug("Backroll Auth ok");
+ result = true;
+ }catch (NotOkBodyException e) {
+ logger.error(e);
+ e.printStackTrace();
+ result = false;
+ }
+ return result;
+ }
+
+ private void closeConnection(CloseableHttpResponse closeableHttpResponse) throws IOException {
+ closeableHttpResponse.close();
+ }
+
+ public void loginIfAuthenticationFailed() throws BackrollApiException, IOException {
+ if (!isAuthenticated()) {
+ login();
+ }
+ }
+
+ protected void login() throws BackrollApiException, IOException {
+ logger.debug("Backroll client - start login");
+
+ CloseableHttpClient httpClient = createHttpClient();
+ CloseableHttpResponse httpResponse = null;
+
+ final HttpPost request = new HttpPost(apiURI.toString() + "/login");
+ request.addHeader("content-type", "application/json");
+
+ JSONObject jsonBody = new JSONObject();
+ StringEntity params;
+
+ try {
+ jsonBody.put("app_id", appname);
+ jsonBody.put("app_secret", password);
+ params = new StringEntity(jsonBody.toString());
+ request.setEntity(params);
+
+ httpResponse = httpClient.execute(request);
+ try {
+ String response = okBody(httpResponse);
+ ObjectMapper objectMapper = new ObjectMapper();
+ logger.info("BACKROLL: " + response);
+ LoginApiResponse loginResponse = objectMapper.readValue(response, LoginApiResponse.class);
+ logger.info("ok");
+ backrollToken = loginResponse.accessToken;
+ logger.debug("Backroll client - Token : {}", backrollToken);
+
+ if (StringUtils.isEmpty(loginResponse.accessToken)) {
+ throw new CloudRuntimeException("Backroll token is not available to perform API requests");
+ }
+ } catch (final NotOkBodyException e) {
+ logger.error(e);
+ if (httpResponse.getStatusLine().getStatusCode() != HttpStatus.SC_CREATED) {
+ throw new CloudRuntimeException("Failed to create and authenticate Backroll client, please check the settings.");
+ } else {
+ throw new ServerApiException(ApiErrorCode.UNAUTHORIZED, "Backroll API call unauthorized, please ask your administrator to fix integration issues.");
+ }
+ }
+ } catch (final IOException e) {
+ logger.error(e);
+ throw new CloudRuntimeException("Failed to authenticate Backroll API service due to:" + e.getMessage());
+ } catch (JSONException e) {
+ e.printStackTrace();
+ logger.error(e);
+ throw new CloudRuntimeException("Failed to authenticate Backroll API service due to:" + e.getMessage());
+ }
+ finally {
+ closeConnection(httpResponse);
+ }
+ logger.debug("Backroll client - end login");
+ }
+}
diff --git a/plugins/backup/backroll/src/main/resources/META-INF/cloudstack/backroll-backup/module.properties b/plugins/backup/backroll/src/main/resources/META-INF/cloudstack/backroll-backup/module.properties
new file mode 100644
index 000000000000..a181dadfb203
--- /dev/null
+++ b/plugins/backup/backroll/src/main/resources/META-INF/cloudstack/backroll-backup/module.properties
@@ -0,0 +1,18 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+name=backroll-backup
+parent=backup
diff --git a/plugins/backup/backroll/src/main/resources/META-INF/cloudstack/backroll-backup/spring-backup-backroll-context.xml b/plugins/backup/backroll/src/main/resources/META-INF/cloudstack/backroll-backup/spring-backup-backroll-context.xml
new file mode 100644
index 000000000000..231c694ab8b1
--- /dev/null
+++ b/plugins/backup/backroll/src/main/resources/META-INF/cloudstack/backroll-backup/spring-backup-backroll-context.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
diff --git a/plugins/backup/backroll/src/test/java/org/apache/cloudstack/backup/BackrollBackupProviderTest.java b/plugins/backup/backroll/src/test/java/org/apache/cloudstack/backup/BackrollBackupProviderTest.java
new file mode 100644
index 000000000000..a2b3f3db647f
--- /dev/null
+++ b/plugins/backup/backroll/src/test/java/org/apache/cloudstack/backup/BackrollBackupProviderTest.java
@@ -0,0 +1,394 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.backup;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.UUID;
+
+import org.apache.cloudstack.backup.backroll.BackrollClient;
+import org.apache.cloudstack.backup.backroll.utils.BackrollApiException;
+import org.apache.cloudstack.backup.backroll.model.BackrollBackupMetrics;
+import org.apache.cloudstack.backup.backroll.model.BackrollOffering;
+import org.apache.cloudstack.backup.Backup.RestorePoint;
+import org.apache.cloudstack.backup.dao.BackupDao;
+import org.apache.logging.log4j.Logger;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.Spy;
+
+import com.cloud.utils.Pair;
+import com.cloud.utils.exception.CloudRuntimeException;
+import com.cloud.vm.VMInstanceVO;
+import com.cloud.vm.dao.VMInstanceDao;
+import com.cloud.vm.VirtualMachine;
+import org.apache.cloudstack.framework.config.ConfigKey;
+
+public class BackrollBackupProviderTest {
+ @Mock
+ BackrollClient clientMock;
+
+ @Spy
+ @InjectMocks
+ BackrollBackupProvider backupProvider;
+
+ @Mock
+ BackrollBackupProvider backupProviderMock;
+
+ @Mock
+ VMInstanceDao vmInstanceDao;
+
+ @Mock
+ private ConfigKey BackrollUrlConfigKey;
+
+ @Mock
+ private ConfigKey BackrollAppNameConfigKey;
+
+ @Mock
+ private ConfigKey BackrollPasswordConfigKey;
+
+ @Mock
+ BackupDao backupDao;
+
+ @Before
+ public void setUp() throws Exception {
+ vmInstanceDao = mock(VMInstanceDao.class);
+ clientMock = mock(BackrollClient.class);
+ backupDao = mock(BackupDao.class);
+ backupProvider = new BackrollBackupProvider(backupDao, vmInstanceDao, clientMock, Mockito.mock(Logger.class));
+ backupProvider.BackrollAppNameConfigKey = BackrollAppNameConfigKey;
+ backupProvider.BackrollPasswordConfigKey = BackrollPasswordConfigKey;
+ backupProvider.BackrollUrlConfigKey = BackrollUrlConfigKey;
+ }
+
+ @Test
+ public void listBackupOfferings_Test() throws BackrollApiException, IOException {
+ Mockito.doReturn("dummyUrlToRequest").when(clientMock).getBackupOfferingUrl();
+ Mockito.doReturn(Arrays.asList(new BackrollOffering("dummyName", "dummyId"))).when(clientMock)
+ .getBackupOfferings(Mockito.anyString());
+ List results = backupProvider.listBackupOfferings(2L);
+ assertTrue(results.size() == 1);
+ }
+
+ @Test
+ public void takeBackup_Test() throws BackrollApiException, IOException {
+
+ VMInstanceVO vmInstanceVO = new VMInstanceVO();
+ vmInstanceVO.setInstanceName("test");
+ vmInstanceVO.setDataCenterId(2l);
+ vmInstanceVO.setUuid(UUID.randomUUID().toString());
+ vmInstanceVO.setBackupOfferingId(2L);
+
+ Mockito.doReturn("/status/f32092e4-3e8a-461b-8733-ed93e23fa782").when(clientMock)
+ .startBackupJob(Mockito.anyString());
+ Mockito.doReturn(new BackupVO()).when(backupDao).persist(Mockito.any(BackupVO.class));
+ Pair result = backupProvider.takeBackup(vmInstanceVO);
+ assertTrue(result.first());
+ }
+
+ @Test
+ public void restoreBackedUpVolume_Test() {
+ try {
+ backupProvider.restoreBackedUpVolume(new BackupVO(), "dummyString", "dummyString", "dummyString",
+ new Pair("dummyString", VirtualMachine.State.Shutdown));
+ } catch (Exception e) {
+ assertEquals(CloudRuntimeException.class, e.getClass());
+ String expected = String.format("Backroll plugin does not support this feature");
+ assertEquals(expected, e.getMessage());
+ }
+ }
+
+ @Test
+ public void getConfigKeys_Test() {
+ assertEquals(3, backupProvider.getConfigKeys().length);
+ }
+
+ @Test
+ public void getConfigComponentName_Test() {
+ assertEquals(BackupService.class.getSimpleName(), backupProvider.getConfigComponentName());
+ }
+
+ @Test
+ public void getBackupMetricsEmpty_Test() {
+ assertEquals(backupProvider.getBackupMetrics(2L, Arrays.asList()).size(), 0);
+ }
+
+ @Test
+ public void getBackupMetrics_Test() throws BackrollApiException, IOException{
+
+ VMInstanceVO vmInstanceVO = new VMInstanceVO();
+ vmInstanceVO.setInstanceName("test");
+ vmInstanceVO.setDataCenterId(1l);
+ vmInstanceVO.setBackupOfferingId(1l);
+
+ VMInstanceVO vmInstanceVO2 = new VMInstanceVO();
+ vmInstanceVO2.setInstanceName("test2");
+ vmInstanceVO2.setDataCenterId(2l);
+ vmInstanceVO2.setBackupOfferingId(2l);
+
+ VMInstanceVO vmInstanceVO3 = new VMInstanceVO();
+ vmInstanceVO3.setInstanceName("test3");
+ vmInstanceVO3.setDataCenterId(3l);
+ vmInstanceVO3.setBackupOfferingId(3l);
+
+ BackrollBackupMetrics metrics = new BackrollBackupMetrics(5L, 6L);
+
+ Mockito.doReturn(metrics).when(clientMock).getBackupMetrics(Mockito.anyString(), Mockito.anyString());
+ assertEquals(backupProvider.getBackupMetrics(2L, Arrays.asList(vmInstanceVO, vmInstanceVO2, vmInstanceVO3)).size(), 1);
+
+ // Mockito.verify(clientMock, times(3)).getBackupMetrics(Mockito.anyString(), Mockito.anyString());
+ }
+
+ @Test
+ public void restoreVMFromBackupTrue_Test() throws BackrollApiException, IOException {
+ VMInstanceVO vmInstanceVO = new VMInstanceVO();
+ vmInstanceVO.setDataCenterId(2l);
+
+ BackupVO backupVo = new BackupVO();
+ backupVo.setExternalId("abc,defgh");
+
+ Mockito.doReturn(true).when(clientMock).restoreVMFromBackup(Mockito.anyString(), Mockito.anyString());
+
+ Boolean result = backupProvider.restoreVMFromBackup(vmInstanceVO, backupVo);
+
+ assertTrue(result);
+ }
+
+ @Test
+ public void restoreVMFromBackupFalse_Test() throws BackrollApiException, IOException {
+ VMInstanceVO vmInstanceVO = new VMInstanceVO();
+ vmInstanceVO.setDataCenterId(2l);
+
+ BackupVO backupVo = new BackupVO();
+ backupVo.setExternalId("abc,defgh");
+
+ Mockito.doReturn(false).when(clientMock).restoreVMFromBackup(Mockito.anyString(), Mockito.anyString());
+
+ try {
+ backupProvider.restoreVMFromBackup(vmInstanceVO, backupVo);
+ } catch (Exception e) {
+ assertEquals(CloudRuntimeException.class, e.getClass());
+ String expected = String.format("Failed to restore VM from Backup");
+ assertEquals(expected, e.getMessage());
+ }
+ }
+
+ @Test
+ public void getDescription_Test() {
+ assertEquals("Backroll Backup Plugin", backupProvider.getDescription());
+ }
+
+ @Test
+ public void isValidProviderOffering_Test() {
+ assertTrue(backupProvider.isValidProviderOffering(2L, "dummyString"));
+ }
+
+ @Test
+ public void getName_Test() {
+ assertEquals("backroll", backupProvider.getName());
+ }
+
+ @Test
+ public void assignVMToBackupOffering_Test() {
+ VMInstanceVO vmInstanceVO = new VMInstanceVO();
+ BackrollOffering backrollOf = new BackrollOffering("dummyName", UUID.randomUUID().toString());
+ assertTrue(backupProvider.assignVMToBackupOffering(vmInstanceVO, backrollOf));
+ }
+
+ @Test
+ public void removeVMFromBackupOffering_Test() {
+ assertTrue(backupProvider.removeVMFromBackupOffering(new VMInstanceVO()));
+ }
+
+ @Test
+ public void willDeleteBackupsOnOfferingRemoval_Test() {
+ assertFalse(backupProvider.willDeleteBackupsOnOfferingRemoval());
+ }
+
+ @Test
+ public void getClient_Test() {
+ BackrollClient client = backupProvider.getClient(2L);
+ assertEquals(client, clientMock);
+ }
+
+ @Test
+ public void deleteBackupTestSuccess_Test() throws BackrollApiException, IOException {
+ VMInstanceVO vmInstanceVO = new VMInstanceVO();
+ vmInstanceVO.setInstanceName("test");
+ vmInstanceVO.setDataCenterId(2l);
+ vmInstanceVO.setUuid(UUID.randomUUID().toString());
+ BackupVO backup = new BackupVO();
+ backup.setVmId(1l);
+ backup.setExternalId("abc,defgh");
+ backup.setType("Full");
+ backup.setZoneId(2l);
+ backup.setStatus(Backup.Status.Removed);
+ Boolean deleteBackupResponse = true;
+ Mockito.doReturn(vmInstanceVO).when(vmInstanceDao).findByIdIncludingRemoved(Mockito.anyLong());
+ Mockito.doReturn(deleteBackupResponse).when(clientMock).deleteBackup(Mockito.anyString(), Mockito.anyString());
+ Mockito.doReturn(new BackupVO()).when(backupDao).persist(Mockito.any(BackupVO.class));
+ boolean result = backupProvider.deleteBackup(backup, true);
+ assertEquals(true, result);
+ }
+
+ @Test
+ public void deleteBackupBackinUp_Test() {
+ BackupVO backup = new BackupVO();
+ backup.setStatus(Backup.Status.BackingUp);
+ try {
+ backupProvider.deleteBackup(backup, false);
+ } catch (Exception e) {
+ assertEquals(CloudRuntimeException.class, e.getClass());
+ String expected = String.format("You can't delete a backup while it still BackingUp");
+ assertEquals(expected, e.getMessage());
+ }
+ }
+
+ @Test
+ public void listRestorePoints_Test() throws BackrollApiException, IOException {
+ List rps = Arrays.asList(new RestorePoint("rp1", new Date(), "incremental"),
+ new RestorePoint("rp2", new Date(), "incremental"),
+ new RestorePoint("rp3", new Date(), "incremental"),
+ new RestorePoint("rp4", new Date(), "incremental"));
+
+ VMInstanceVO vmInstanceVO3 = new VMInstanceVO();
+ vmInstanceVO3.setInstanceName("test3");
+ vmInstanceVO3.setDataCenterId(3l);
+ vmInstanceVO3.setBackupOfferingId(3l);
+
+ Mockito.doReturn(rps).when(clientMock).listRestorePoints(Mockito.anyString());
+
+ List rPoints = backupProvider.listRestorePoints(vmInstanceVO3);
+
+ assertEquals(rPoints.size(), rps.size());
+
+ }
+
+ @Test
+ public void createNewBackupEntryForRestorePoint_Test() throws BackrollApiException, IOException {
+ RestorePoint restorePoint = new RestorePoint("restore-123", new Date(), "INCREMENTAL");
+
+ VMInstanceVO vm = new VMInstanceVO();
+ vm.setUuid("vm-uuid-456");
+ vm.setDataCenterId(2L);
+ vm.setBackupOfferingId(3L);
+
+ Backup.Metric metric = null;
+
+ BackrollBackupMetrics backupMetrics = new BackrollBackupMetrics(100L, 200L);
+ Mockito.doReturn(backupMetrics).when(clientMock).getBackupMetrics(vm.getUuid(), restorePoint.getId());
+
+ BackupVO savedBackup = new BackupVO();
+ Mockito.doReturn(savedBackup).when(backupDao).persist(Mockito.any(BackupVO.class));
+
+ Backup result = backupProvider.createNewBackupEntryForRestorePoint(restorePoint, vm, metric);
+
+ assertNotNull(result);
+ assertEquals(vm.getId(), result.getVmId());
+ assertEquals(restorePoint.getId(), result.getExternalId());
+ assertEquals("INCREMENTAL", result.getType());
+ assertEquals(restorePoint.getCreated(), result.getDate());
+ assertEquals(Backup.Status.BackedUp, result.getStatus());
+ assertEquals(vm.getBackupOfferingId(), (Long)result.getBackupOfferingId());
+ assertEquals(vm.getAccountId(), result.getAccountId());
+ assertEquals(vm.getDomainId(), result.getDomainId());
+ assertEquals(vm.getDataCenterId(), result.getZoneId());
+ assertEquals((Long)backupMetrics.getSize(), result.getSize());
+ assertEquals((Long)backupMetrics.getDeduplicated(), result.getProtectedSize());
+
+ Mockito.verify(clientMock).getBackupMetrics(vm.getUuid(), restorePoint.getId());
+ Mockito.verify(backupDao).persist(Mockito.any(BackupVO.class));
+ }
+
+ @Test
+ public void createNewBackupEntryForRestorePoint_WithMetric_Test() throws BackrollApiException, IOException {
+ RestorePoint restorePoint = new RestorePoint("restore-789", new Date(), "INCREMENTAL");
+
+ VMInstanceVO vm = new VMInstanceVO();
+ vm.setUuid("vm-uuid-789");
+ vm.setDataCenterId(3L);
+ vm.setBackupOfferingId(4L);
+
+ Backup.Metric metric = new Backup.Metric(150L, 250L);
+
+ BackupVO savedBackup = new BackupVO();
+ Mockito.doReturn(savedBackup).when(backupDao).persist(Mockito.any(BackupVO.class));
+
+ Backup result = backupProvider.createNewBackupEntryForRestorePoint(restorePoint, vm, metric);
+
+ assertNotNull(result);
+ assertEquals(vm.getId(), result.getVmId());
+ assertEquals(restorePoint.getId(), result.getExternalId());
+ assertEquals("INCREMENTAL", result.getType());
+ assertEquals(restorePoint.getCreated(), result.getDate());
+ assertEquals(Backup.Status.BackedUp, result.getStatus());
+ assertEquals(vm.getBackupOfferingId(), (Long)result.getBackupOfferingId());
+ assertEquals(vm.getAccountId(), result.getAccountId());
+ assertEquals(vm.getDomainId(), result.getDomainId());
+ assertEquals(vm.getDataCenterId(), result.getZoneId());
+
+ Mockito.verify(backupDao).persist(Mockito.any(BackupVO.class));
+ }
+
+ @Test
+ public void createNewBackupEntryForRestorePoint_BackrollApiException_Test()
+ throws BackrollApiException, IOException {
+
+ RestorePoint restorePoint =new RestorePoint("restore-404", new Date(), "INCREMENTAL");
+
+ VMInstanceVO vm = new VMInstanceVO();
+ vm.setUuid("vm-uuid-404");
+ vm.setDataCenterId(4L);
+ vm.setBackupOfferingId(5L);
+
+ Backup.Metric metric = null;
+
+ Mockito.doThrow(new BackrollApiException()).when(clientMock).getBackupMetrics(vm.getUuid(),
+ restorePoint.getId());
+
+ backupProvider.createNewBackupEntryForRestorePoint(restorePoint, vm, metric);
+ }
+
+ @Test
+ public void createNewBackupEntryForRestorePoint_IOException_Test() throws BackrollApiException, IOException {
+
+ RestorePoint restorePoint = new RestorePoint("restore-500", new Date(), "INCREMENTAL");
+
+ VMInstanceVO vm = new VMInstanceVO();
+ vm.setUuid("vm-uuid-500");
+ vm.setDataCenterId(5L);
+ vm.setBackupOfferingId(6L);
+
+ Backup.Metric metric = null;
+
+ Mockito.doThrow(new IOException("IO Error")).when(clientMock).getBackupMetrics(vm.getUuid(),
+ restorePoint.getId());
+
+ backupProvider.createNewBackupEntryForRestorePoint(restorePoint, vm, metric);
+ }
+}
diff --git a/plugins/backup/backroll/src/test/java/org/apache/cloudstack/backup/backroll/BackrollClientTest.java b/plugins/backup/backroll/src/test/java/org/apache/cloudstack/backup/backroll/BackrollClientTest.java
new file mode 100644
index 000000000000..cada5de72d99
--- /dev/null
+++ b/plugins/backup/backroll/src/test/java/org/apache/cloudstack/backup/backroll/BackrollClientTest.java
@@ -0,0 +1,291 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.backup.backroll;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.cloudstack.backup.Backup;
+import org.apache.cloudstack.backup.Backup.Metric;
+import org.apache.cloudstack.backup.BackupOffering;
+import org.apache.cloudstack.backup.backroll.model.BackrollBackupMetrics;
+import org.apache.cloudstack.backup.backroll.model.BackrollTaskStatus;
+import org.apache.cloudstack.backup.backroll.model.BackrollVmBackup;
+import org.apache.cloudstack.backup.backroll.model.response.BackrollTaskRequestResponse;
+import org.apache.cloudstack.backup.backroll.model.response.TaskState;
+import org.apache.cloudstack.backup.backroll.model.response.archive.BackrollArchiveResponse;
+import org.apache.cloudstack.backup.backroll.model.response.archive.BackrollArchivesResponse;
+import org.apache.cloudstack.backup.backroll.model.response.archive.BackrollBackupsFromVMResponse;
+import org.apache.cloudstack.backup.backroll.model.response.metrics.backup.BackrollBackupMetricsResponse;
+import org.apache.cloudstack.backup.backroll.model.response.metrics.backup.BackupMetricsInfo;
+import org.apache.cloudstack.backup.backroll.model.response.metrics.virtualMachine.BackrollVmMetricsResponse;
+import org.apache.cloudstack.backup.backroll.model.response.metrics.virtualMachine.CacheStats;
+import org.apache.cloudstack.backup.backroll.model.response.metrics.virtualMachine.InfosCache;
+import org.apache.cloudstack.backup.backroll.model.response.metrics.virtualMachine.MetricsInfos;
+import org.apache.cloudstack.backup.backroll.model.response.metrics.virtualMachineBackups.VirtualMachineBackupsResponse;
+import org.apache.cloudstack.backup.backroll.model.response.policy.BackrollBackupPolicyResponse;
+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.logging.log4j.Logger;
+import org.joda.time.DateTime;
+import org.json.JSONObject;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.github.tomakehurst.wiremock.junit.WireMockRule;
+
+public class BackrollClientTest {
+ private BackrollClient client;
+
+ @Mock
+ private BackrollHttpClientProvider backrollHttpClientProviderMock;
+
+ @Rule
+ public WireMockRule wireMockRule = new WireMockRule(9399);
+
+ @Before
+ public void setUp() throws Exception {
+ backrollHttpClientProviderMock = mock(BackrollHttpClientProvider.class);
+ client = new BackrollClient(backrollHttpClientProviderMock);
+ client.logger = Mockito.mock(Logger.class);
+ }
+
+ @Test
+ public void getAllBackupsfromVirtualMachine_test() throws Exception {
+ String vmId = "TEST-vm_uuid";
+ String virtualMachineResponseString = "{ \"state\": \"SUCCESS\", \"info\": { \"archives\": [ { \"archive\": \"ROOT-00000\", \"barchive\": \"ROOT-00000\", \"id\": \"25d55ad283aa400af464c76d713c07ad7d163abdd3b8fbcdbdc46b827e5e0457\", \"name\": \"ROOT-00000\", \"start\": \"2024-11-08T18:24:48.000000\", \"time\": \"2024-11-08T18:24:48.000000\" } ], \"encryption\": { \"mode\": \"none\" }, \"repository\": { \"id\": \"36a11ebc0775a097c927735cc7015d19be7309be69fc15b896c5b1fd87fcbd79\", \"last_modified\": \"2024-11-29T09:53:09.000000\", \"location\": \"/mnt/backup/backup1\" } } }";
+ BackrollTaskRequestResponse backrollTaskReqResponseMock = new BackrollTaskRequestResponse();
+ backrollTaskReqResponseMock.location = "/api/v1/status/f32092e4-3e8a-461b-8733-ed93e23fa782";
+ VirtualMachineBackupsResponse virtualMachineBackupsResponseMock = new ObjectMapper()
+ .readValue(virtualMachineResponseString, VirtualMachineBackupsResponse.class);
+
+ // Mocking client responses
+ doReturn(backrollTaskReqResponseMock).when(backrollHttpClientProviderMock)
+ .get(Mockito.matches(".*/virtualmachines/.*"), Mockito.any());
+ doReturn(virtualMachineBackupsResponseMock).when(backrollHttpClientProviderMock).waitGet(Mockito.anyString(),
+ Mockito.any());
+ // Run the method under test
+ List backupsTestList = client.getAllBackupsfromVirtualMachine(vmId);
+
+ // Check results
+ assertEquals(1, backupsTestList.size()); // Should be 1 based on provided mock data
+ }
+
+ @Test
+ public void getBackupMetrics_Test() throws IOException, BackrollApiException {
+ BackrollTaskRequestResponse backrollTaskReqResponseMock = new BackrollTaskRequestResponse();
+ backrollTaskReqResponseMock.location = "/api/v1/status/f32092e4-3e8a-461b-8733-ed93e23fa782";
+ BackrollBackupMetricsResponse mockResponse = new BackrollBackupMetricsResponse();
+ mockResponse.info = new BackupMetricsInfo();
+ mockResponse.info.originalSize = "1000";
+ mockResponse.info.deduplicatedSize = "800";
+
+ doReturn(backrollTaskReqResponseMock).when(backrollHttpClientProviderMock)
+ .get(Mockito.matches(".*/virtualmachines/.*"), Mockito.any());
+ doReturn(mockResponse).when(backrollHttpClientProviderMock).waitGet(Mockito.anyString(), Mockito.any());
+
+ BackrollBackupMetrics metrics = client.getBackupMetrics("dummyVMId", "dummyBackupId");
+
+ assertEquals(1000L, metrics.getSize());
+ assertEquals(800L, metrics.getDeduplicated());
+ }
+
+ @Test
+ public void getVirtualMachineMetrics_Test() throws IOException, BackrollApiException {
+ BackrollTaskRequestResponse backrollTaskReqResponseMock = new BackrollTaskRequestResponse();
+ backrollTaskReqResponseMock.location = "/api/v1/status/f32092e4-3e8a-461b-8733-ed93e23fa782";
+
+ BackrollVmMetricsResponse mockResponse = new BackrollVmMetricsResponse();
+ mockResponse.state = TaskState.SUCCESS;
+ mockResponse.infos = new MetricsInfos();
+ mockResponse.infos.cache = new InfosCache();
+ mockResponse.infos.cache.stats = new CacheStats();
+ mockResponse.infos.cache.stats.totalSize = "10000";
+
+ doReturn(backrollTaskReqResponseMock).when(backrollHttpClientProviderMock)
+ .get(Mockito.matches(".*/virtualmachines/.*"), Mockito.any());
+ doReturn(mockResponse).when(backrollHttpClientProviderMock).waitGet(Mockito.anyString(), Mockito.any());
+
+ Metric metrics = client.getVirtualMachineMetrics("dummyVMId");
+
+ assertEquals(10000L, (long) metrics.getBackupSize());
+ assertEquals(10000L, (long) metrics.getDataSize());
+ }
+
+ @Test
+ public void deleteBackup_Test() throws IOException, BackrollApiException {
+ BackrollTaskRequestResponse backrollTaskReqResponseMock = new BackrollTaskRequestResponse();
+ backrollTaskReqResponseMock.location = "/api/v1/status/f32092e4-3e8a-461b-8733-ed93e23fa782";
+
+ BackrollBackupsFromVMResponse mockResponse = new BackrollBackupsFromVMResponse();
+ mockResponse.state = TaskState.SUCCESS;
+ mockResponse.archives = new BackrollArchivesResponse();
+ mockResponse.archives.archives = Arrays.asList(new BackrollArchiveResponse());
+
+ doReturn(backrollTaskReqResponseMock).when(backrollHttpClientProviderMock)
+ .delete(Mockito.matches(".*/virtualmachines/.*"), Mockito.any());
+ doReturn(mockResponse).when(backrollHttpClientProviderMock).waitGet(Mockito.anyString(), Mockito.any());
+
+ Boolean isBackupDeleted = client.deleteBackup("dummyVMId", "dummyBackUpName");
+
+ assertTrue(isBackupDeleted);
+ }
+
+ @Test
+ public void checkBackupTaskStatusSuccess_Test() throws IOException, BackrollApiException {
+ String backupResponse = "{\"state\":\"SUCCESS\",\"info\":\"test\"}";
+ doReturn(backupResponse).when(backrollHttpClientProviderMock)
+ .getWithoutParseResponse(Mockito.matches(".*status/.*"));
+
+ BackrollTaskStatus status = client.checkBackupTaskStatus("dummytaskid");
+
+ assertEquals(TaskState.SUCCESS, status.getState());
+ assertEquals("test", status.getInfo());
+ }
+
+ @Test
+ public void checkBackupTaskStatus_Test() throws IOException, BackrollApiException {
+ String backupResponse = "{\"state\":\"PENDING\",\"current\":0,\"total\":1,\"status\":\"Pending...\"}";
+ doReturn(backupResponse).when(backrollHttpClientProviderMock)
+ .getWithoutParseResponse(Mockito.matches(".*/status/.*"));
+
+ BackrollTaskStatus status = client.checkBackupTaskStatus("dummytaskid");
+
+ assertEquals(TaskState.PENDING, status.getState());
+ }
+
+ @Test
+ public void restoreVMFromBackup_Test() throws IOException, BackrollApiException {
+ BackrollTaskRequestResponse backrollTaskReqResponseMock = new BackrollTaskRequestResponse();
+ backrollTaskReqResponseMock.location = "/api/v1/status/f32092e4-3e8a-461b-8733-ed93e23fa782";
+ String resultMock = "SUCCESS WOW YOUHOU";
+ doReturn(backrollTaskReqResponseMock).when(backrollHttpClientProviderMock)
+ .post(Mockito.matches(".*/tasks/restore/.*"), Mockito.any(JSONObject.class), Mockito.any());
+ doReturn(resultMock).when(backrollHttpClientProviderMock).waitGetWithoutParseResponse(Mockito.anyString());
+
+ Boolean isRestoreOk = client.restoreVMFromBackup("dummyVMId", "dummyBackUpName");
+
+ assertTrue(isRestoreOk);
+ }
+
+ @Test
+ public void startBackupJob_Test() throws IOException, BackrollApiException {
+
+ BackrollTaskRequestResponse backrollTaskReqResponseMock = new BackrollTaskRequestResponse();
+ backrollTaskReqResponseMock.location = "/api/v1/status/f32092e4-3e8a-461b-8733-ed93e23fa782";
+ doReturn(backrollTaskReqResponseMock).when(backrollHttpClientProviderMock)
+ .post(Mockito.matches(".*/tasks/singlebackup/.*"), Mockito.nullable(JSONObject.class), Mockito.any());
+
+ String response = client.startBackupJob("dummyJobId");
+
+ assertEquals("/status/f32092e4-3e8a-461b-8733-ed93e23fa782", response);
+ }
+
+ @Test
+ public void getBackupOfferingUrl_Test() throws IOException, BackrollApiException {
+ BackrollTaskRequestResponse backrollTaskReqResponseMock = new BackrollTaskRequestResponse();
+ backrollTaskReqResponseMock.location = "/api/v1/status/f32092e4-3e8a-461b-8733-ed93e23fa782";
+ doReturn(backrollTaskReqResponseMock).when(backrollHttpClientProviderMock)
+ .get(Mockito.matches(".*/backup_policies.*"), Mockito.any());
+
+ String response = client.getBackupOfferingUrl();
+
+ assertEquals("/status/f32092e4-3e8a-461b-8733-ed93e23fa782", response);
+ }
+
+ @Test
+ public void getBackupOfferings_Test() throws BackrollApiException, IOException {
+
+ BackrollBackupPolicyResponse policy1 = new BackrollBackupPolicyResponse();
+ policy1.name = "User-Policy-1";
+ policy1.retentionDay = 6;
+ policy1.schedule = "0 0 * * 1";
+ policy1.retentionMonth = 0;
+ policy1.storage = "f32092e4-3e8a-461b-8733-ed93e23fa782";
+ policy1.enabled = false;
+ policy1.description = "User's policy 1 description";
+ policy1.id = "f32092e4-3e8a-461b-8733-ed93e23fa782";
+ policy1.retentionWeek = 0;
+ policy1.retentionYear = 0;
+ policy1.externalHook = null;
+
+ BackrollBackupPolicyResponse policy2 = new BackrollBackupPolicyResponse();
+ policy1.name = "User-Policy-2";
+ policy1.retentionDay = 6;
+ policy1.schedule = "0 0 * * 1";
+ policy1.retentionMonth = 0;
+ policy1.storage = "f32092e4-3e8a-461b-8733-ed93e23fa782";
+ policy1.enabled = false;
+ policy1.description = "User's policy 2 description";
+ policy1.id = "f32092e4-3e8a-461b-8733-ed93e23fa782";
+ policy1.retentionWeek = 0;
+ policy1.retentionYear = 0;
+ policy1.externalHook = null;
+ BackupPoliciesResponse backupPoliciesResponseMock = new BackupPoliciesResponse();
+ backupPoliciesResponseMock.backupPolicies = Arrays.asList(policy1, policy2);
+
+ doReturn(backupPoliciesResponseMock).when(backrollHttpClientProviderMock)
+ .waitGet(Mockito.matches("/status/f32092e4-3e8a-461b-8733-ed93e23fa782"), Mockito.any());
+
+ List response = client.getBackupOfferings("/status/f32092e4-3e8a-461b-8733-ed93e23fa782");
+
+ assertEquals(response.size(), 2);
+
+ }
+
+ @Test
+ public void listRestorePoints_Test() throws BackrollApiException, IOException {
+ String vmId = "vm-123";
+
+ String virtualMachineResponseString = "{ \"state\": \"SUCCESS\", \"info\": { \"archives\": [ { \"archive\": \"ROOT-00000\", \"barchive\": \"ROOT-00000\", \"id\": \"25d55ad283aa400af464c76d713c07ad7d163abdd3b8fbcdbdc46b827e5e0457\", \"name\": \"ROOT-00000\", \"start\": \"2024-11-08T18:24:48.000000\", \"time\": \"2024-11-08T18:24:48.000000\" } ], \"encryption\": { \"mode\": \"none\" }, \"repository\": { \"id\": \"36a11ebc0775a097c927735cc7015d19be7309be69fc15b896c5b1fd87fcbd79\", \"last_modified\": \"2024-11-29T09:53:09.000000\", \"location\": \"/mnt/backup/backup1\" } } }";
+ BackrollTaskRequestResponse backrollTaskReqResponseMock = new BackrollTaskRequestResponse();
+ backrollTaskReqResponseMock.location = "/api/v1/status/f32092e4-3e8a-461b-8733-ed93e23fa782";
+ VirtualMachineBackupsResponse virtualMachineBackupsResponseMock = new ObjectMapper()
+ .readValue(virtualMachineResponseString, VirtualMachineBackupsResponse.class);
+
+ doReturn(backrollTaskReqResponseMock).when(backrollHttpClientProviderMock)
+ .get(Mockito.matches(".*/virtualmachines/.*"), Mockito.any());
+ doReturn(virtualMachineBackupsResponseMock).when(backrollHttpClientProviderMock).waitGet(Mockito.anyString(),
+ Mockito.any());
+
+ List restorePoints = client.listRestorePoints(vmId);
+
+ assertNotNull(restorePoints);
+ assertEquals(1, restorePoints.size());
+
+ Backup.RestorePoint rp1 = restorePoints.get(0);
+ assertEquals("ROOT-00000", rp1.getId());
+ assertEquals(new DateTime("2024-11-08T18:24:48.000000").toDate(), rp1.getCreated());
+ assertEquals("INCREMENTAL", rp1.getType());
+
+ Mockito.verify(backrollHttpClientProviderMock).get(Mockito.matches(".*/virtualmachines/.*"), Mockito.any());
+ Mockito.verify(backrollHttpClientProviderMock).waitGet(anyString(), Mockito.any());
+ }
+}
diff --git a/plugins/backup/backroll/src/test/java/org/apache/cloudstack/backup/backroll/utils/BackrollHttpClientProviderTest.java b/plugins/backup/backroll/src/test/java/org/apache/cloudstack/backup/backroll/utils/BackrollHttpClientProviderTest.java
new file mode 100644
index 000000000000..877c7107f27e
--- /dev/null
+++ b/plugins/backup/backroll/src/test/java/org/apache/cloudstack/backup/backroll/utils/BackrollHttpClientProviderTest.java
@@ -0,0 +1,315 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.backup.backroll.utils;
+
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
+
+import javax.net.ssl.SSLContext;
+
+import org.apache.cloudstack.backup.backroll.model.response.api.LoginApiResponse;
+import org.apache.cloudstack.backup.backroll.model.response.metrics.virtualMachineBackups.VirtualMachineBackupsResponse;
+import org.apache.cloudstack.backup.backroll.utils.BackrollHttpClientProvider.NotOkBodyException;
+import org.apache.http.HttpStatus;
+import org.apache.http.StatusLine;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpDelete;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.json.JSONObject;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockedStatic;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
+import org.apache.cloudstack.utils.security.SSLUtils;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+
+public class BackrollHttpClientProviderTest {
+
+ @Spy
+ @InjectMocks
+ BackrollHttpClientProvider backupHttpClientProvider;
+
+ @Mock
+ private CloseableHttpClient httpClient;
+
+ @Mock
+ private CloseableHttpResponse response;
+
+ @Mock
+ private RequestConfig config;
+
+ @Spy
+ private SSLUtils sslUtils;
+
+ @Spy
+ private HttpClientBuilder httpClientBuilder;
+
+ @Mock
+ private SSLContext sslContext;
+
+ @Mock
+ private SSLConnectionSocketFactory sslConnectionSocketFactory;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.openMocks(this);
+ backupHttpClientProvider = BackrollHttpClientProvider.createProvider(backupHttpClientProvider,
+ "http://api.backup.demo.ccc:5050/api/v1", "backroll-api", "VviX8dALauSyYJMqVYJqf3UyZOpO3joS", true,
+ 300, 600);
+ }
+
+ private void defaultTestHttpClient(String path)
+ throws BackrollApiException, ClientProtocolException, IOException, NotOkBodyException {
+
+ LoginApiResponse responseLogin = new LoginApiResponse();
+ responseLogin.accessToken = "dummyToken";
+ responseLogin.expiresIn = 3600;
+ responseLogin.notBeforePolicy = "dummyNotBeforePolicy";
+ responseLogin.refreshExpiresIn = "7200";
+ responseLogin.scope = "dummyScope";
+
+ String virtualMachineResponseString = "{ \"state\": \"SUCCESS\", \"info\": { \"archives\": [ { \"archive\": \"ROOT-00000\", \"barchive\": \"ROOT-00000\", \"id\": \"25d55ad283aa400af464c76d713c07ad7d163abdd3b8fbcdbdc46b827e5e0457\", \"name\": \"ROOT-00000\", \"start\": \"2024-11-08T18:24:48.000000\", \"time\": \"2024-11-08T18:24:48.000000\" } ], \"encryption\": { \"mode\": \"none\" }, \"repository\": { \"id\": \"36a11ebc0775a097c927735cc7015d19be7309be69fc15b896c5b1fd87fcbd79\", \"last_modified\": \"2024-11-29T09:53:09.000000\", \"location\": \"/mnt/backup/backup1\" } } }";
+
+ CloseableHttpResponse response2 = mock(CloseableHttpResponse.class);
+ StatusLine statusLine = mock(StatusLine.class);
+
+ doReturn(httpClient).when(backupHttpClientProvider).createHttpClient();
+ doReturn(response).when(httpClient)
+ .execute(argThat(argument -> argument != null && argument.getURI().toString().contains("login")));
+
+ doReturn(response2).when(httpClient)
+ .execute(argThat(argument -> argument != null && argument.getURI().toString().contains(path)));
+
+ doReturn(new ObjectMapper().writeValueAsString(responseLogin)).when(backupHttpClientProvider).okBody(response);
+ doReturn(virtualMachineResponseString).when(backupHttpClientProvider).okBody(response2);
+
+ doReturn(statusLine).when(response).getStatusLine();
+ doReturn(HttpStatus.SC_OK).when(statusLine).getStatusCode();
+
+ doNothing().when(response).close();
+
+ doReturn(new StringEntity("{\"mockKey\": \"mockValue\"}", ContentType.APPLICATION_JSON)).when(response)
+ .getEntity();
+ }
+
+ @Test
+ public void testCreateHttpClient_WithValidateCertificateTrue()
+ throws KeyManagementException, NoSuchAlgorithmException, URISyntaxException, BackrollApiException {
+ backupHttpClientProvider = BackrollHttpClientProvider.createProvider(backupHttpClientProvider,
+ "http://api.backup.demo.ccc:5050/api/v1", "backroll-api", "VviX8dALauSyYJMqVYJqf3UyZOpO3joS", true,
+ 300, 600);
+
+ // Mock HttpClientBuilder
+ HttpClientBuilder mockBuilder = mock(HttpClientBuilder.class);
+ try (MockedStatic utilities = Mockito.mockStatic(HttpClientBuilder.class)) {
+ utilities.when(HttpClientBuilder::create).thenReturn(mockBuilder);
+ when(mockBuilder.setDefaultRequestConfig(config)).thenReturn(mockBuilder);
+ when(mockBuilder.build()).thenReturn(httpClient);
+ }
+
+ // Test the method
+ CloseableHttpClient client = backupHttpClientProvider.createHttpClient();
+
+ // Verify and assert
+ assertNotNull(client);
+ }
+
+ @Test
+ public void testCreateHttpClient_WithValidateCertificateFalse()
+ throws KeyManagementException, NoSuchAlgorithmException, URISyntaxException, BackrollApiException {
+ backupHttpClientProvider = BackrollHttpClientProvider.createProvider(backupHttpClientProvider,
+ "http://api.backup.demo.ccc:5050/api/v1", "backroll-api", "VviX8dALauSyYJMqVYJqf3UyZOpO3joS", false,
+ 300, 600);
+
+ // Mock HttpClientBuilder
+ HttpClientBuilder mockBuilder = mock(HttpClientBuilder.class);
+ try (MockedStatic utilities = Mockito.mockStatic(HttpClientBuilder.class)) {
+ utilities.when(HttpClientBuilder::create).thenReturn(mockBuilder);
+ when(mockBuilder.setDefaultRequestConfig(config)).thenReturn(mockBuilder);
+ when(mockBuilder.setSSLSocketFactory(any(SSLConnectionSocketFactory.class))).thenReturn(mockBuilder);
+ when(mockBuilder.build()).thenReturn(httpClient);
+ }
+
+ // Test the method
+ CloseableHttpClient client = backupHttpClientProvider.createHttpClient();
+
+ // Verify and assert
+ assertNotNull(client);
+ }
+
+ @Test
+ public void NotOkBodyException_Test(){
+ BackrollHttpClientProvider.NotOkBodyException exception = backupHttpClientProvider.new NotOkBodyException();
+ assertNotNull(exception);
+ }
+
+ @Test
+ public void get_Test_success() throws Exception {
+ // Arrange
+ String path = "/test";
+ defaultTestHttpClient(path);
+
+ // Act
+ VirtualMachineBackupsResponse result = backupHttpClientProvider.get(path,
+ VirtualMachineBackupsResponse.class);
+
+ // Assert
+ assertNotNull(result);
+ verify(backupHttpClientProvider, times(2)).okBody(Mockito.any(CloseableHttpResponse.class));
+ verify(httpClient, times(1)).execute(Mockito.any(HttpPost.class));
+ verify(httpClient, times(1)).execute(Mockito.any(HttpGet.class));
+ verify(response, times(1)).close();
+ }
+
+ @Test
+ public void delete_Test_success() throws Exception {
+ // Arrange
+ String path = "/test";
+ defaultTestHttpClient(path);
+
+ // Act
+ VirtualMachineBackupsResponse result = backupHttpClientProvider.delete(path,
+ VirtualMachineBackupsResponse.class);
+
+ // Assert
+ assertNotNull(result);
+ verify(backupHttpClientProvider, times(2)).okBody(Mockito.any(CloseableHttpResponse.class));
+ verify(httpClient, times(1)).execute(Mockito.any(HttpPost.class));
+ verify(httpClient, times(1)).execute(Mockito.any(HttpDelete.class));
+ verify(response, times(1)).close();
+ }
+
+ @Test
+ public void okBody_Test_success() throws BackrollApiException, IOException, NotOkBodyException {
+ StatusLine statusLine = mock(StatusLine.class);
+ doReturn(statusLine).when(response).getStatusLine();
+ doReturn(HttpStatus.SC_OK).when(statusLine).getStatusCode();
+ doReturn(new StringEntity("{\"mockKey\": \"mockValue\"}", ContentType.APPLICATION_JSON)).when(response)
+ .getEntity();
+ doNothing().when(response).close();
+
+ String result = backupHttpClientProvider.okBody(response);
+ assertNotNull(result);
+ }
+
+ @Test(expected = BackrollHttpClientProvider.NotOkBodyException.class)
+ public void okBody_Test_Error() throws BackrollApiException, IOException, NotOkBodyException {
+ StatusLine statusLine = mock(StatusLine.class);
+ doReturn(statusLine).when(response).getStatusLine();
+ doReturn(HttpStatus.SC_INTERNAL_SERVER_ERROR).when(statusLine).getStatusCode();
+
+ backupHttpClientProvider.okBody(response);
+ }
+
+ @Test
+ public void waitGet_Test() throws Exception {
+ String path = "/test";
+ defaultTestHttpClient(path);
+
+ // Act
+ VirtualMachineBackupsResponse result = backupHttpClientProvider.waitGet(path,
+ VirtualMachineBackupsResponse.class);
+
+ // Assert
+ assertNotNull(result);
+ verify(backupHttpClientProvider, times(2)).okBody(Mockito.any(CloseableHttpResponse.class));
+ verify(httpClient, times(1)).execute(Mockito.any(HttpGet.class));
+ verify(response, times(1)).close();
+ }
+
+ @Test
+ public void waitGetWithoutParseResponse_Test() throws Exception {
+ String path = "/test";
+ defaultTestHttpClient(path);
+
+ // Act
+ String result = backupHttpClientProvider.waitGetWithoutParseResponse(path);
+
+ // Assert
+ assertNotNull(result);
+ verify(backupHttpClientProvider, times(2)).okBody(Mockito.any(CloseableHttpResponse.class));
+ verify(httpClient, times(1)).execute(Mockito.any(HttpGet.class));
+ verify(response, times(1)).close();
+ }
+
+ @Test
+ public void testPost_success() throws Exception {
+ // Arrange
+ String path = "/test";
+ JSONObject json = new JSONObject();
+ defaultTestHttpClient(path);
+
+ // Act
+ VirtualMachineBackupsResponse result = backupHttpClientProvider.post(path, json,
+ VirtualMachineBackupsResponse.class);
+
+ // Assert
+ assertNotNull(result);
+ verify(backupHttpClientProvider, times(2)).okBody(Mockito.any(CloseableHttpResponse.class));
+ verify(httpClient, times(2)).execute(Mockito.any(HttpPost.class));
+ verify(response, times(1)).close();
+ }
+
+ @Test
+ public void testAuthenticationFailure() throws Exception {
+ doReturn(false).when(backupHttpClientProvider).isAuthenticated();
+ doNothing().when(backupHttpClientProvider).login();
+ backupHttpClientProvider.loginIfAuthenticationFailed();
+ verify(backupHttpClientProvider).login();
+ }
+
+ @Test(expected = BackrollApiException.class)
+ public void testLoginFailure() throws Exception {
+ doReturn(false).when(backupHttpClientProvider).isAuthenticated();
+ doThrow(BackrollApiException.class).when(backupHttpClientProvider).login();
+ backupHttpClientProvider.loginIfAuthenticationFailed();
+ }
+
+ @Test
+ public void testLoginSuccess() throws Exception {
+ doReturn(true).when(backupHttpClientProvider).isAuthenticated();
+ backupHttpClientProvider.loginIfAuthenticationFailed();
+ verify(backupHttpClientProvider, times(0)).login();
+ }
+}
diff --git a/plugins/pom.xml b/plugins/pom.xml
index e7d13871285e..df48ec6a03a7 100755
--- a/plugins/pom.xml
+++ b/plugins/pom.xml
@@ -60,6 +60,7 @@
api/rate-limit
api/solidfire-intg-test
+ backup/backroll
backup/dummy
backup/networker
backup/nas
@@ -229,6 +230,7 @@
api/vmware-sioc
+ backup/backroll
backup/veeam
hypervisors/vmware
network-elements/cisco-vnmc
diff --git a/root@169.254.109.99 b/root@169.254.109.99
new file mode 100644
index 000000000000..5d1bf6e36850
Binary files /dev/null and b/root@169.254.109.99 differ