This commit is contained in:
PeterBackroll 2026-07-04 14:36:09 +00:00 committed by GitHub
commit 7cf2e379c7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
47 changed files with 3365 additions and 0 deletions

View File

@ -607,6 +607,11 @@
<artifactId>cloud-plugin-integrations-prometheus-exporter</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cloudstack</groupId>
<artifactId>cloud-plugin-backup-backroll</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cloudstack</groupId>
<artifactId>cloud-plugin-backup-dummy</artifactId>
@ -1143,6 +1148,11 @@
<artifactId>cloud-plugin-backup-veeam</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cloudstack</groupId>
<artifactId>cloud-plugin-backup-backroll</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</profile>
<profile>

View File

@ -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.
-->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="DefaultConfigResources" class="com.yourcompany.YourConfigClass">
<!-- properties of the bean -->
</bean>
</beans>

View File

@ -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.
-->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd"
>
<context:annotation-config />
<!-- Import additional configuration -->
<import resource="classpath:default-config-resources.xml"/>
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="order" value="100" />
<property name="ignoreResourceNotFound" value="true" />
<property name="ignoreUnresolvablePlaceholders" value="true" />
<property name="locations" ref="DefaultConfigResources" />
</bean>
</beans>

View File

@ -0,0 +1,53 @@
<!--
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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-plugin-backup-backroll</artifactId>
<name>Apache CloudStack Plugin - BackRoll Backup and Recovery Plugin by DIMSI</name>
<parent>
<artifactId>cloudstack-plugins</artifactId>
<groupId>org.apache.cloudstack</groupId>
<version>4.22.0.0-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<dependencies>
<dependency>
<groupId>org.apache.cloudstack</groupId>
<artifactId>cloud-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cloudstack</groupId>
<artifactId>cloud-utils</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
</dependency>
<dependency>
<groupId>com.github.tomakehurst</groupId>
<artifactId>wiremock-standalone</artifactId>
<version>${cs.wiremock.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -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<String> 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<String> BackrollAppNameConfigKey = new ConfigKey<>("Advanced", String.class,
"backup.plugin.backroll.config.appname",
"backroll-api",
"App Name for backroll plugin.", true, ConfigKey.Scope.Zone);
public ConfigKey<String> 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<BackupOffering> 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<BackupOffering> results = new ArrayList<BackupOffering>();
// 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<BackupOffering>();
}
@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<VirtualMachine, Backup.Metric> getBackupMetrics(Long zoneId, List<VirtualMachine> vms) {
final Map<VirtualMachine, Backup.Metric> metrics = new HashMap<>();
if (CollectionUtils.isEmpty(vms)) {
logger.warn("Unable to get VM Backup Metrics because the list of VMs is empty.");
return metrics;
}
List<String> 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<Backup> 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<RestorePoint> 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<Boolean, Backup> 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<Boolean,Backup>(result, backup);
}
} catch (ParseException | BackrollApiException | IOException e) {
logger.debug(e.getMessage());
throw new CloudRuntimeException("Failed to take backup");
}
return new Pair<Boolean,Backup>(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<Boolean, String> restoreBackedUpVolume(Backup backup, String volumeUuid, String hostIp, String dataStoreUuid, Pair<String, VirtualMachine.State> 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<RestorePoint> listRestorePoints(VirtualMachine vm) {
try {
final BackrollClient client = getClient(vm.getDataCenterId());
return client.listRestorePoints(vm.getUuid());
} catch (BackrollApiException | IOException e) {
logger.error(e);
throw new CloudRuntimeException("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;
}
}

View File

@ -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<BackupOffering> getBackupOfferings(String idTask) throws BackrollApiException, IOException {
logger.info("Trying to list backroll backup policies");
final List<BackupOffering> policies = new ArrayList<>();
BackupPoliciesResponse backupPoliciesResponse = httpProvider.waitGet(idTask, BackupPoliciesResponse.class);
logger.info(
"BackrollClient:getBackupOfferings:Apres Parse: " + backupPoliciesResponse.backupPolicies.get(0).name);
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<BackrollVmBackup> getAllBackupsfromVirtualMachine(String vmId)
throws BackrollApiException, IOException {
List<BackrollVmBackup> backups = new ArrayList<BackrollVmBackup>();
List<BackupInfos> backupInfos = getBackupInfosFromVm(vmId);
if (backupInfos != null && backupInfos.size() > 0) {
for (BackupInfos infos : backupInfos) {
var dateStart = new DateTime(infos.start);
backups.add(new BackrollVmBackup(infos.id, infos.name, dateStart.toDate()));
}
}
return backups;
}
public List<BackupInfos> getBackupInfosFromVm(String vmId)
throws BackrollApiException, IOException {
logger.info("Trying to retrieve all backups for vm {}", vmId);
BackrollTaskRequestResponse requestResponse = httpProvider
.get(String.format("/virtualmachines/%s/backups", vmId), BackrollTaskRequestResponse.class);
String urlToRequest = requestResponse.location.replace("/api/v1", "");
logger.debug(urlToRequest);
VirtualMachineBackupsResponse virtualMachineBackupsResponse = httpProvider.waitGet(urlToRequest,
VirtualMachineBackupsResponse.class);
if (virtualMachineBackupsResponse.state.equals(TaskState.SUCCESS)) {
return virtualMachineBackupsResponse.info.archives;
}
return null;
}
public List<Backup.RestorePoint> listRestorePoints(String vmId) throws BackrollApiException, IOException {
List<Backup.RestorePoint> backups = new ArrayList<Backup.RestorePoint>();
List<BackupInfos> backupInfos = getBackupInfosFromVm(vmId);
if (backupInfos != null && backupInfos.size() > 0) {
for (BackupInfos infos : backupInfos) {
var dateStart = new DateTime(infos.start);
backups.add(new Backup.RestorePoint(infos.name, dateStart.toDate(), "INCREMENTAL"));
}
}
return backups;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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";
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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<BackrollArchiveResponse> archives;
}

View File

@ -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;
}

View File

@ -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{
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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<BackupInfos> 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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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<BackrollBackupPolicyResponse> backupPolicies;
}

View File

@ -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;
}

View File

@ -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 {
}

View File

@ -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> T post(final String path, final JSONObject json, Class<T> 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> T get(String path, Class<T> 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> T delete(String path, Class<T> 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> T waitGet(String url, Class<T> 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");
}
}

View File

@ -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

View File

@ -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.
-->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"
>
<bean id="backrollBackupRecoveryDriver" class="org.apache.cloudstack.backup.BackrollBackupProvider">
<property name="name" value="backroll" />
</bean>
</beans>

View File

@ -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<String> BackrollUrlConfigKey;
@Mock
private ConfigKey<String> BackrollAppNameConfigKey;
@Mock
private ConfigKey<String> 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<BackupOffering> 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<Boolean, Backup> result = backupProvider.takeBackup(vmInstanceVO);
assertTrue(result.first());
}
@Test
public void restoreBackedUpVolume_Test() {
try {
backupProvider.restoreBackedUpVolume(new BackupVO(), "dummyString", "dummyString", "dummyString",
new Pair<String, VirtualMachine.State>("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<RestorePoint> 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<RestorePoint> 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);
}
}

View File

@ -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<BackrollVmBackup> 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<BackupOffering> 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<Backup.RestorePoint> 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());
}
}

View File

@ -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<HttpClientBuilder> 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<HttpClientBuilder> 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();
}
}

View File

@ -60,6 +60,7 @@
<module>api/rate-limit</module>
<module>api/solidfire-intg-test</module>
<module>backup/backroll</module>
<module>backup/dummy</module>
<module>backup/networker</module>
<module>backup/nas</module>
@ -231,6 +232,7 @@
</activation>
<modules>
<module>api/vmware-sioc</module>
<module>backup/backroll</module>
<module>backup/veeam</module>
<module>hypervisors/vmware</module>
<module>network-elements/cisco-vnmc</module>

BIN
root@169.254.109.99 Normal file

Binary file not shown.