mirror of https://github.com/apache/cloudstack.git
Bug fix + new features for backroll
This commit is contained in:
parent
3ec3007635
commit
1f518227ce
|
|
@ -104,3 +104,4 @@ scripts/.pydevproject
|
|||
venv
|
||||
node_modules
|
||||
.vscode
|
||||
*.bz2
|
||||
|
|
|
|||
|
|
@ -547,6 +547,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>
|
||||
|
|
@ -1001,6 +1006,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>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,76 @@
|
|||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
|
||||
# management server clustering parameters, change cluster.node.IP to the machine IP address
|
||||
# in which the management server is running
|
||||
cluster.node.IP=127.0.0.1
|
||||
cluster.servlet.port=9090
|
||||
region.id=1
|
||||
|
||||
# CloudStack database settings
|
||||
db.cloud.username=cloud
|
||||
db.cloud.password=cloud
|
||||
db.root.password=12345
|
||||
db.cloud.host=localhost
|
||||
db.cloud.driver=jdbc:mysql
|
||||
db.cloud.port=3306
|
||||
db.cloud.name=cloud
|
||||
|
||||
# CloudStack database tuning parameters
|
||||
db.cloud.maxActive=250
|
||||
db.cloud.maxIdle=30
|
||||
db.cloud.maxWait=10000
|
||||
db.cloud.autoReconnect=true
|
||||
db.cloud.validationQuery=SELECT 1
|
||||
db.cloud.testOnBorrow=true
|
||||
db.cloud.testWhileIdle=true
|
||||
db.cloud.timeBetweenEvictionRunsMillis=40000
|
||||
db.cloud.minEvictableIdleTimeMillis=240000
|
||||
db.cloud.poolPreparedStatements=false
|
||||
db.cloud.url.params=prepStmtCacheSize=517&cachePrepStmts=true&prepStmtCacheSqlLimit=4096&sessionVariables=sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION'&serverTimezone=UTC
|
||||
|
||||
# usage database settings
|
||||
db.usage.username=cloud
|
||||
db.usage.password=cloud
|
||||
db.usage.host=localhost
|
||||
# It's not guaranteed that using a different DB provider than the one from the regular cloud DB will work
|
||||
db.usage.driver=jdbc:mysql
|
||||
db.usage.port=3306
|
||||
db.usage.name=cloud_usage
|
||||
|
||||
# usage database tuning parameters
|
||||
db.usage.maxActive=100
|
||||
db.usage.maxIdle=30
|
||||
db.usage.maxWait=10000
|
||||
db.usage.autoReconnect=true
|
||||
db.usage.url.params=serverTimezone=UTC
|
||||
|
||||
# Simulator database settings
|
||||
db.simulator.username=cloud
|
||||
db.simulator.password=cloud
|
||||
db.simulator.host=localhost
|
||||
# It's not guaranteed that using a different DB provider than the one from the regular cloud DB will work
|
||||
db.simulator.driver=jdbc:mysql
|
||||
db.simulator.port=3306
|
||||
db.simulator.name=simulator
|
||||
db.simulator.maxActive=250
|
||||
db.simulator.maxIdle=30
|
||||
db.simulator.maxWait=10000
|
||||
db.simulator.autoReconnect=true
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
<!--
|
||||
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.19.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>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
|
@ -0,0 +1,390 @@
|
|||
// 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.net.URISyntaxException;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.text.MessageFormat;
|
||||
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.backroll.BackrollClient;
|
||||
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.dao.BackupDao;
|
||||
import org.apache.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;
|
||||
|
||||
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;
|
||||
|
||||
public class BackrollBackupProvider extends AdapterBase implements BackupProvider, Configurable {
|
||||
|
||||
private static final Logger s_logger = Logger.getLogger(BackrollBackupProvider.class);
|
||||
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 by DIMSI", 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 by DIMSI", true, ConfigKey.Scope.Zone);
|
||||
|
||||
public ConfigKey<String> BackrollPasswordConfigKey = new ConfigKey<>("Advanced", String.class,
|
||||
"backup.plugin.backroll.config.password",
|
||||
"VviX8dALauSyYJMqVYJqf3UyZOpO3joS",
|
||||
"Password for backroll plugin by DIMSI", true, ConfigKey.Scope.Zone);
|
||||
|
||||
private BackrollClient backrollClient;
|
||||
|
||||
@Inject
|
||||
private BackupDao backupDao;
|
||||
@Inject
|
||||
private VMInstanceDao vmInstanceDao;
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "backroll";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Backroll Backup Plugin";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BackupOffering> listBackupOfferings(Long zoneId) {
|
||||
s_logger.debug("Listing backup policies on backroll B&R Plugin");
|
||||
BackrollClient client = getClient(zoneId);
|
||||
String urlToRequest = client.getBackupOfferingUrl();
|
||||
if(!StringUtils.isEmpty(urlToRequest)){
|
||||
return client.getBackupOfferings(urlToRequest);
|
||||
}
|
||||
return new ArrayList<BackupOffering>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValidProviderOffering(Long zoneId, String uuid) {
|
||||
s_logger.info("Checking if backup offering exists on the Backroll Backup Provider");
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean assignVMToBackupOffering(VirtualMachine vm, BackupOffering backupOffering) {
|
||||
s_logger.info("Creating VM backup for VM " + vm.getInstanceName() + " from backup offering " + backupOffering.getName());
|
||||
((VMInstanceVO) vm).setBackupExternalId(backupOffering.getUuid());
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean restoreVMFromBackup(VirtualMachine vm, Backup backup) {
|
||||
s_logger.debug("Restoring vm " + vm.getUuid() + "from backup " + backup.getUuid() + " on the backroll Backup Provider");
|
||||
boolean isSuccess = getClient(vm.getDataCenterId()).restoreVMFromBackup(vm.getUuid(), getBackupName(backup));
|
||||
return isSuccess;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<Boolean, String> restoreBackedUpVolume(Backup backup, String volumeUuid, String hostIp, String dataStoreUuid) {
|
||||
s_logger.debug("Restoring volume " + volumeUuid + "from backup " + backup.getUuid() + " on the backroll Backup Provider");
|
||||
throw new CloudRuntimeException("Backroll plugin does not support this feature");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<VirtualMachine, Backup.Metric> getBackupMetrics(Long zoneId, List<VirtualMachine> vms) {
|
||||
final Map<VirtualMachine, Backup.Metric> metrics = new HashMap<>();
|
||||
if (CollectionUtils.isEmpty(vms)) {
|
||||
s_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());
|
||||
s_logger.debug(String.format("Get Backup Metrics for VMs: [%s].", String.join(", ", vmUuids)));
|
||||
|
||||
for (final VirtualMachine vm : vms) {
|
||||
if (vm == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Metric metric = getClient(zoneId).getVirtualMachineMetrics(vm.getUuid());
|
||||
s_logger.debug(String.format("Metrics for VM [uuid: %s, name: %s] is [backup size: %s, data size: %s].", vm.getUuid(),
|
||||
vm.getInstanceName(), metric.getBackupSize(), metric.getDataSize()));
|
||||
metrics.put(vm, metric);
|
||||
}
|
||||
return metrics;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeVMFromBackupOffering(VirtualMachine vm) {
|
||||
s_logger.info("Removing VM ID " + vm.getUuid() + " from Backrool backup offering ");
|
||||
|
||||
boolean isAnyProblemWhileRemovingBackups = false;
|
||||
|
||||
List<Backup> backupsInCs = backupDao.listByVmId(null, vm.getId());
|
||||
|
||||
for (Backup backup : backupsInCs) {
|
||||
s_logger.debug("Trying to remove backup with id" + backup.getId());
|
||||
|
||||
if (getClient(backup.getZoneId()).deleteBackup(vm.getUuid(), getBackupName(backup))) {
|
||||
var message = MessageFormat.format("Backup {0} deleted in Backroll for virtual machine {1}", backup.getId(), vm.getName());
|
||||
s_logger.info(message);
|
||||
if(!backupDao.remove(backup.getId())){
|
||||
isAnyProblemWhileRemovingBackups = true;
|
||||
}
|
||||
message = MessageFormat.format("Backup {0} deleted in CS for virtual machine {1}", backup.getId(), vm.getName());
|
||||
s_logger.info(message);
|
||||
} else {
|
||||
isAnyProblemWhileRemovingBackups = false;
|
||||
}
|
||||
}
|
||||
|
||||
if(isAnyProblemWhileRemovingBackups) {
|
||||
var message = MessageFormat.format("Problems occured while removing some backups for virtual machine {0}", vm.getName());
|
||||
s_logger.info(message);
|
||||
}
|
||||
return isAnyProblemWhileRemovingBackups;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean willDeleteBackupsOnOfferingRemoval() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean takeBackup(VirtualMachine vm) {
|
||||
s_logger.info("Starting backup for VM ID " + vm.getUuid() + " on backroll provider");
|
||||
final BackrollClient client = getClient(vm.getDataCenterId());
|
||||
|
||||
String idBackupTask = client.startBackupJob(vm.getUuid());
|
||||
if(!StringUtils.isEmpty(idBackupTask)) {
|
||||
BackupVO backup = new BackupVO();
|
||||
backup.setVmId(vm.getId());
|
||||
backup.setExternalId(idBackupTask);
|
||||
backup.setType("INCREMENTIAL");
|
||||
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());
|
||||
return backupDao.persist(backup) != null;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void syncBackups(VirtualMachine vm, Backup.Metric metric) {
|
||||
s_logger.info("Starting sync backup for VM ID " + vm.getUuid() + " on backroll provider");
|
||||
|
||||
final BackrollClient client = getClient(vm.getDataCenterId());
|
||||
List<Backup> backupsInDb = backupDao.listByVmId(null, vm.getId());
|
||||
|
||||
for (Backup backup : backupsInDb) {
|
||||
if(backup.getStatus().equals(Backup.Status.BackingUp)) {
|
||||
BackrollTaskStatus response = client.checkBackupTaskStatus(backup.getExternalId());
|
||||
if(response != null) {
|
||||
s_logger.debug("backroll backup id: " + backup.getExternalId());
|
||||
s_logger.debug("backroll backup status: " + response.getState());
|
||||
|
||||
BackupVO backupToUpdate = new BackupVO();
|
||||
backupToUpdate.setVmId(backup.getVmId());
|
||||
backupToUpdate.setExternalId(backup.getExternalId());
|
||||
backupToUpdate.setType(backup.getType());
|
||||
backupToUpdate.setDate(backup.getDate());
|
||||
backupToUpdate.setSize(backup.getSize());
|
||||
backupToUpdate.setProtectedSize(backup.getProtectedSize());
|
||||
backupToUpdate.setBackupOfferingId(vm.getBackupOfferingId());
|
||||
backupToUpdate.setAccountId(backup.getAccountId());
|
||||
backupToUpdate.setDomainId(backup.getDomainId());
|
||||
backupToUpdate.setZoneId(backup.getZoneId());
|
||||
|
||||
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(backup.getExternalId() + "," + response.getInfo());
|
||||
|
||||
BackrollBackupMetrics backupMetrics = client.getBackupMetrics(vm.getUuid() , response.getInfo());
|
||||
if(backupMetrics != null) {
|
||||
backupToUpdate.setSize(backupMetrics.getDeduplicated()); // real size
|
||||
backupToUpdate.setProtectedSize(backupMetrics.getSize()); // total size
|
||||
}
|
||||
} else {
|
||||
backupToUpdate.setStatus(Backup.Status.BackingUp);
|
||||
}
|
||||
|
||||
if(backupDao.persist(backupToUpdate) != null) {
|
||||
s_logger.info("Backroll mise à jour enregistrée");
|
||||
backupDao.remove(backup.getId());
|
||||
}
|
||||
}
|
||||
} else if(backup.getStatus().equals(Backup.Status.BackedUp) && backup.getSize().equals(0L)) {
|
||||
String backupId = backup.getExternalId().contains(",") ? backup.getExternalId().split(",")[1] : backup.getExternalId();
|
||||
BackrollBackupMetrics backupMetrics = client.getBackupMetrics(vm.getUuid() , backupId);
|
||||
if(backupMetrics != null) {
|
||||
BackupVO backupToUpdate = ((BackupVO) backup);
|
||||
backupToUpdate.setSize(backupMetrics.getDeduplicated()); // real size
|
||||
backupToUpdate.setProtectedSize(backupMetrics.getSize()); // total size
|
||||
backupDao.persist(backupToUpdate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Backups synchronisation between Backroll ad CS Db
|
||||
List<BackrollVmBackup> backupsFromBackroll = client.getAllBackupsfromVirtualMachine(vm.getUuid());
|
||||
backupsInDb = backupDao.listByVmId(null, vm.getId());
|
||||
|
||||
// insert new backroll backup in CS
|
||||
for (BackrollVmBackup backupInBackroll : backupsFromBackroll) {
|
||||
Backup backupToFind = backupsInDb.stream()
|
||||
.filter(backupInDb -> backupInDb.getExternalId().contains(backupInBackroll.getName()))
|
||||
.findAny()
|
||||
.orElse(null);
|
||||
|
||||
if(backupToFind == null) {
|
||||
BackupVO backupToInsert = new BackupVO();
|
||||
backupToInsert.setVmId(vm.getId());
|
||||
backupToInsert.setExternalId(backupInBackroll.getId() + "," + backupInBackroll.getName());
|
||||
backupToInsert.setType("INCREMENTIAL");
|
||||
backupToInsert.setDate(backupInBackroll.getDate());
|
||||
backupToInsert.setSize(0L);
|
||||
backupToInsert.setProtectedSize(0L);
|
||||
backupToInsert.setStatus(Backup.Status.BackedUp);
|
||||
backupToInsert.setBackupOfferingId(vm.getBackupOfferingId());
|
||||
backupToInsert.setAccountId(vm.getAccountId());
|
||||
backupToInsert.setDomainId(vm.getDomainId());
|
||||
backupToInsert.setZoneId(vm.getDataCenterId());
|
||||
backupDao.persist(backupToInsert);
|
||||
}
|
||||
if(backupToFind != null && backupToFind.getStatus() == Backup.Status.Removed) {
|
||||
BackupVO backupToUpdate = ((BackupVO) backupToFind);
|
||||
backupToUpdate.setStatus(Backup.Status.BackedUp);
|
||||
if(backupDao.persist(backupToUpdate) != null) {
|
||||
s_logger.info("Backroll mise à jour enregistrée");
|
||||
backupDao.remove(backupToFind.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// delete deleted backroll backup in CS
|
||||
backupsInDb = backupDao.listByVmId(null, vm.getId());
|
||||
for (Backup backup : backupsInDb) {
|
||||
String backupName = backup.getExternalId().contains(",") ? backup.getExternalId().split(",")[1] : backup.getExternalId();
|
||||
BackrollVmBackup backupToFind = backupsFromBackroll.stream()
|
||||
.filter(backupInBackroll -> backupInBackroll.getName().contains(backupName))
|
||||
.findAny()
|
||||
.orElse(null);
|
||||
|
||||
if(backupToFind == null) {
|
||||
BackupVO backupToUpdate = ((BackupVO) backup);
|
||||
backupToUpdate.setStatus(Backup.Status.Removed);
|
||||
if(backupDao.persist(backupToUpdate) != null) {
|
||||
s_logger.debug("Backroll suppression enregistrée (sync)");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@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) {
|
||||
s_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 {
|
||||
s_logger.debug("backroll - try delete backup");
|
||||
VMInstanceVO vm = vmInstanceDao.findByIdIncludingRemoved(backup.getVmId());
|
||||
|
||||
if(backup.getStatus().equals(Backup.Status.Removed) || backup.getStatus().equals(Backup.Status.Failed)){
|
||||
return deleteBackupInDb(backup);
|
||||
} else {
|
||||
if (getClient(backup.getZoneId()).deleteBackup(vm.getUuid(), getBackupName(backup))) {
|
||||
s_logger.debug("backroll delete backup ok on backroll side");
|
||||
return deleteBackupInDb(backup);
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean deleteBackupInDb(Backup backup) {
|
||||
BackupVO backupToUpdate = ((BackupVO) backup);
|
||||
backupToUpdate.setStatus(Backup.Status.Removed);
|
||||
if(backupDao.persist(backupToUpdate) != null) {
|
||||
s_logger.debug("Backroll backup delete in database");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected BackrollClient getClient(final Long zoneId) {
|
||||
s_logger.debug("Backroll Provider GetClient");
|
||||
try {
|
||||
if(backrollClient == null){
|
||||
s_logger.debug("backroll client null - instanciation of new one ");
|
||||
backrollClient = new BackrollClient(BackrollUrlConfigKey.valueIn(zoneId), BackrollAppNameConfigKey.valueIn(zoneId), BackrollPasswordConfigKey.valueIn(zoneId), true, 300, 600);
|
||||
}
|
||||
return backrollClient;
|
||||
} catch (URISyntaxException e) {
|
||||
throw new CloudRuntimeException("Failed to parse Backroll API URL: " + e.getMessage());
|
||||
} catch (NoSuchAlgorithmException | KeyManagementException e) {
|
||||
s_logger.info("Failed to build Backroll API client due to: ", e);
|
||||
}
|
||||
throw new CloudRuntimeException("Failed to build Backroll API client");
|
||||
}
|
||||
|
||||
private String getBackupName(Backup backup) {
|
||||
return backup.getExternalId().substring(backup.getExternalId().indexOf(",") + 1);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,612 @@
|
|||
package org.apache.cloudstack.backup.backroll;
|
||||
|
||||
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.ArrayList;
|
||||
import java.util.List;
|
||||
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.model.BackrollBackup;
|
||||
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.api.LoginApiResponse;
|
||||
import org.apache.cloudstack.backup.backroll.model.response.archive.BackrollArchiveResponse;
|
||||
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.utils.security.SSLUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.HttpHeaders;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.config.RequestConfig;
|
||||
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.HttpClientBuilder;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.joda.time.DateTime;
|
||||
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;
|
||||
import org.apache.cloudstack.backup.BackupOffering;
|
||||
import org.apache.cloudstack.backup.Backup.Metric;
|
||||
|
||||
public class BackrollClient {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(BackrollClient.class);
|
||||
|
||||
private int restoreTimeout;
|
||||
|
||||
private final URI apiURI;
|
||||
|
||||
private final HttpClient httpClient;
|
||||
|
||||
private String backrollToken = null;
|
||||
private String appname = null;
|
||||
private String password = null;
|
||||
|
||||
public BackrollClient(final String url, final String appname, final String password,
|
||||
final boolean validateCertificate, final int timeout,
|
||||
final int restoreTimeout)
|
||||
throws URISyntaxException, NoSuchAlgorithmException, KeyManagementException {
|
||||
this.apiURI = new URI(url);
|
||||
this.restoreTimeout = restoreTimeout;
|
||||
this.appname = appname;
|
||||
this.password = password;
|
||||
|
||||
final RequestConfig config = RequestConfig.custom()
|
||||
.setConnectTimeout(timeout * 1000)
|
||||
.setConnectionRequestTimeout(timeout * 1000)
|
||||
.setSocketTimeout(timeout * 1000)
|
||||
.build();
|
||||
|
||||
if (!validateCertificate) {
|
||||
final SSLContext sslcontext = SSLUtils.getSSLContext();
|
||||
sslcontext.init(null, new X509TrustManager[] { new TrustAllManager() }, new SecureRandom());
|
||||
final SSLConnectionSocketFactory factory = new SSLConnectionSocketFactory(sslcontext,
|
||||
NoopHostnameVerifier.INSTANCE);
|
||||
this.httpClient = HttpClientBuilder.create()
|
||||
.setDefaultRequestConfig(config)
|
||||
.setSSLSocketFactory(factory)
|
||||
.build();
|
||||
} else {
|
||||
this.httpClient = HttpClientBuilder.create()
|
||||
.setDefaultRequestConfig(config)
|
||||
.build();
|
||||
}
|
||||
|
||||
if (StringUtils.isEmpty(backrollToken) || !isAuthentificated()) {
|
||||
login(appname, password);
|
||||
}
|
||||
}
|
||||
|
||||
public String startBackupJob(final String jobId) {
|
||||
LOG.info(String.format("Trying to start backup for Backroll job: %s", jobId));
|
||||
|
||||
try {
|
||||
loginIfAuthentificationFailed();
|
||||
|
||||
final HttpResponse response = post(String.format("/tasks/singlebackup/%s", jobId), null);
|
||||
if(isResponseOk(response)) {
|
||||
HttpEntity body = response.getEntity();
|
||||
String bodyStr = EntityUtils.toString(body);
|
||||
BackrollTaskRequestResponse backupTaskRequestResponse = new ObjectMapper().readValue(bodyStr, BackrollTaskRequestResponse.class);
|
||||
String idBackupTask = backupTaskRequestResponse.location.replace("/api/v1/status/", "");
|
||||
EntityUtils.consumeQuietly(response.getEntity());
|
||||
return idBackupTask;
|
||||
}
|
||||
} catch (final IOException e) {
|
||||
LOG.error(String.format("Failed to start Backroll backup job due to: %s", e.getMessage()));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getBackupOfferingUrl(){
|
||||
LOG.info("Trying to list backroll backup policies");
|
||||
|
||||
loginIfAuthentificationFailed();
|
||||
|
||||
try {
|
||||
|
||||
final HttpResponse backupPoliciesRequestResponse = get("/backup_policies");
|
||||
|
||||
if(isResponseOk(backupPoliciesRequestResponse)) {
|
||||
HttpEntity body = backupPoliciesRequestResponse.getEntity();
|
||||
String bodyStr = EntityUtils.toString(body);
|
||||
BackrollTaskRequestResponse backupTaskRequestResponse = new ObjectMapper().readValue(bodyStr, BackrollTaskRequestResponse.class);
|
||||
EntityUtils.consumeQuietly(backupPoliciesRequestResponse.getEntity());
|
||||
return backupTaskRequestResponse.location.replace("/api/v1", "");
|
||||
}
|
||||
} catch (final IOException e) {
|
||||
LOG.error(String.format("Failed to list Backroll jobs due to: %s", e.getMessage()));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<BackupOffering> getBackupOfferings(String idTask) {
|
||||
LOG.info("Trying to list backroll backup policies");
|
||||
|
||||
loginIfAuthentificationFailed();
|
||||
|
||||
try {
|
||||
String bodyStr = waitForGetRequestResponse(idTask);
|
||||
|
||||
BackupPoliciesResponse backupPoliciesResponse = new ObjectMapper().readValue(bodyStr, BackupPoliciesResponse.class);
|
||||
|
||||
final List<BackupOffering> policies = new ArrayList<>();
|
||||
for (final BackrollBackupPolicyResponse policy : backupPoliciesResponse.backupPolicies) {
|
||||
policies.add(new BackrollOffering(policy.name, policy.id));
|
||||
}
|
||||
return policies;
|
||||
} catch (final IOException e) {
|
||||
LOG.error(String.format("Failed to list Backroll jobs due to: %s", e.getMessage()));
|
||||
} catch (InterruptedException e) {
|
||||
LOG.error(String.format("Failed to list Backroll jobs due to: %s", e.getMessage()));
|
||||
}
|
||||
return new ArrayList<BackupOffering>();
|
||||
}
|
||||
|
||||
public boolean restoreVMFromBackup(final String vmId, final String backupName) {
|
||||
LOG.info(String.format("Start restore backup with backroll with backup %s for vm %s", backupName, vmId));
|
||||
|
||||
loginIfAuthentificationFailed();
|
||||
|
||||
try {
|
||||
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) {
|
||||
LOG.error(String.format("Backroll Error: %s", e.getMessage()));
|
||||
}
|
||||
|
||||
final HttpResponse response = post(String.format("/tasks/restore/%s", vmId), jsonBody);
|
||||
LOG.debug(response.toString());
|
||||
|
||||
if(isResponseOk(response)) {
|
||||
HttpEntity body = response.getEntity();
|
||||
String bodyStr = EntityUtils.toString(body);
|
||||
LOG.debug(bodyStr);
|
||||
|
||||
BackrollTaskRequestResponse backupTaskRequestResponse = new ObjectMapper().readValue(bodyStr, BackrollTaskRequestResponse.class);
|
||||
String urlToRequest = backupTaskRequestResponse.location.replace("/api/v1", "");
|
||||
EntityUtils.consumeQuietly(response.getEntity());
|
||||
|
||||
String responseStatus = waitForGetRequestResponse(urlToRequest);
|
||||
LOG.debug("RESTORE " + responseStatus);
|
||||
if(responseStatus.contains("SUCCESS")) {
|
||||
LOG.debug("RESTORE SUCCESS");
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} catch (final IOException | InterruptedException e) {
|
||||
LOG.error(String.format("Ouch! Failed to restore VM with Backroll due to: %s", e.getMessage()));
|
||||
throw new CloudRuntimeException(String.format("Ouch! Failed to restore VM with Backroll due to: %s", e.getMessage()));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public BackrollTaskStatus checkBackupTaskStatus(String taskId) {
|
||||
LOG.info(String.format("Trying to get backup status for Backroll task: %s", taskId));
|
||||
|
||||
loginIfAuthentificationFailed();
|
||||
|
||||
try {
|
||||
final HttpResponse backupStatusResponse = get("/status/" + taskId);
|
||||
|
||||
if(isResponseOk(backupStatusResponse)) {
|
||||
BackrollTaskStatus status = new BackrollTaskStatus();
|
||||
|
||||
HttpEntity body = backupStatusResponse.getEntity();
|
||||
String bodyStr = EntityUtils.toString(body);
|
||||
|
||||
if(bodyStr.contains("FAILURE") || bodyStr.contains("PENDING")) {
|
||||
BackrollBackupStatusResponse backupStatusRequestResponse = new ObjectMapper().readValue(bodyStr, BackrollBackupStatusResponse.class);
|
||||
EntityUtils.consumeQuietly(backupStatusResponse.getEntity());
|
||||
status.setState(backupStatusRequestResponse.state);
|
||||
} else {
|
||||
BackrollBackupStatusSuccessResponse backupStatusSuccessRequestResponse = new ObjectMapper().readValue(bodyStr, BackrollBackupStatusSuccessResponse.class);
|
||||
EntityUtils.consumeQuietly(backupStatusResponse.getEntity());
|
||||
status.setState(backupStatusSuccessRequestResponse.state);
|
||||
status.setInfo(backupStatusSuccessRequestResponse.info);
|
||||
}
|
||||
|
||||
return status;
|
||||
} else {
|
||||
EntityUtils.consumeQuietly(backupStatusResponse.getEntity());
|
||||
// throw new CloudRuntimeException("Failed to retrieve backups status for this VM via Backroll");
|
||||
LOG.error(String.format("Failed to retrieve backups status for this VM via Backroll"));
|
||||
}
|
||||
} catch (final IOException e) {
|
||||
LOG.error(String.format("Failed to check backups status due to: %s", e.getMessage()));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean deleteBackup(final String vmId, final String backupName) {
|
||||
LOG.info(String.format("Trying to delete backup %s for vm %s using Backroll", vmId, backupName));
|
||||
|
||||
loginIfAuthentificationFailed();
|
||||
|
||||
try {
|
||||
final HttpResponse response = delete(String.format("/virtualmachines/%s/backups/%s", vmId, backupName));
|
||||
LOG.debug(response.toString());
|
||||
if(isResponseOk(response)) {
|
||||
HttpEntity body = response.getEntity();
|
||||
String bodyStr = EntityUtils.toString(body);
|
||||
LOG.debug(bodyStr);
|
||||
BackrollTaskRequestResponse backupTaskRequestResponse = new ObjectMapper().readValue(bodyStr, BackrollTaskRequestResponse.class);
|
||||
String urlToRequest = backupTaskRequestResponse.location.replace("/api/v1", "");
|
||||
EntityUtils.consumeQuietly(response.getEntity());
|
||||
|
||||
String responseStatus = waitForGetRequestResponse(urlToRequest);
|
||||
LOG.debug(responseStatus);
|
||||
BackrollBackupsFromVMResponse backrollBackupsFromVMResponse = new ObjectMapper().readValue(responseStatus, BackrollBackupsFromVMResponse.class);
|
||||
if(backrollBackupsFromVMResponse.state.equals("SUCCESS")) {
|
||||
EntityUtils.consumeQuietly(response.getEntity());
|
||||
return true;
|
||||
} else {
|
||||
EntityUtils.consumeQuietly(response.getEntity());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
EntityUtils.consumeQuietly(response.getEntity());
|
||||
return false;
|
||||
} catch (final IOException | InterruptedException e) {
|
||||
LOG.error(String.format("Failed to delete backup using Backroll due to: %s", e.getMessage()));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public Metric getVirtualMachineMetrics(final String vmId){
|
||||
LOG.info(String.format("Trying to retrieve virtual machine metric from Backroll for vm %s", vmId));
|
||||
|
||||
loginIfAuthentificationFailed();
|
||||
|
||||
Metric metric = new Metric(0L,0L);
|
||||
|
||||
try {
|
||||
|
||||
final HttpResponse metricsRequestResponse = get(String.format("/virtualmachines/%s/repository", vmId));
|
||||
|
||||
if(isResponseOk(metricsRequestResponse)) {
|
||||
HttpEntity body = metricsRequestResponse.getEntity();
|
||||
String bodyStr = EntityUtils.toString(body);
|
||||
LOG.debug(bodyStr);
|
||||
BackrollTaskRequestResponse backupTaskRequestResponse = new ObjectMapper().readValue(bodyStr, BackrollTaskRequestResponse.class);
|
||||
String urlToRequest = backupTaskRequestResponse.location.replace("/api/v1", "");
|
||||
LOG.debug(urlToRequest);
|
||||
EntityUtils.consumeQuietly(metricsRequestResponse.getEntity());
|
||||
bodyStr = waitForGetRequestResponse(urlToRequest);
|
||||
LOG.debug(bodyStr);
|
||||
|
||||
BackrollVmMetricsResponse vmMetricsResponse = new ObjectMapper().readValue(bodyStr, BackrollVmMetricsResponse.class);
|
||||
|
||||
if(vmMetricsResponse != null) {
|
||||
|
||||
if(vmMetricsResponse.state.equals("SUCCESS")) {
|
||||
LOG.debug("SUCCESS ok");
|
||||
if(vmMetricsResponse.infos != null) {
|
||||
if(vmMetricsResponse.infos.cache != null) {
|
||||
if(vmMetricsResponse.infos.cache.stats != null) {
|
||||
CacheStats stats = vmMetricsResponse.infos.cache.stats;
|
||||
long size = Long.parseLong(stats.totalSize);
|
||||
return new Metric(size,size);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EntityUtils.consumeQuietly(metricsRequestResponse.getEntity());
|
||||
}
|
||||
} catch (final IOException | InterruptedException e) {
|
||||
LOG.error(String.format("Failed to retrieve virtual machine metrics with Backroll due to: %s", e.getMessage()));
|
||||
}
|
||||
|
||||
return metric;
|
||||
}
|
||||
|
||||
public BackrollBackupMetrics getBackupMetrics(String vmId, String backupId) {
|
||||
LOG.info(String.format("Trying to get backup metrics for VM: %s, and backup: %s", vmId, backupId));
|
||||
|
||||
loginIfAuthentificationFailed();
|
||||
|
||||
try {
|
||||
final HttpResponse backupMetricsResponse = get(String.format("/virtualmachines/%s/backups/%s", vmId, backupId));
|
||||
|
||||
if(isResponseOk(backupMetricsResponse)) {
|
||||
HttpEntity body = backupMetricsResponse.getEntity();
|
||||
String bodyStr = EntityUtils.toString(body);
|
||||
|
||||
BackrollTaskRequestResponse backupTaskRequestResponse = new ObjectMapper().readValue(bodyStr, BackrollTaskRequestResponse.class);
|
||||
String urlToRequest = backupTaskRequestResponse.location.replace("/api/v1", "");
|
||||
LOG.debug(urlToRequest);
|
||||
EntityUtils.consumeQuietly(backupMetricsResponse.getEntity());
|
||||
bodyStr = waitForGetRequestResponse(urlToRequest);
|
||||
LOG.debug(bodyStr);
|
||||
|
||||
BackrollBackupMetricsResponse metrics = new ObjectMapper().readValue(bodyStr, BackrollBackupMetricsResponse.class);
|
||||
EntityUtils.consumeQuietly(backupMetricsResponse.getEntity());
|
||||
if(metrics.info != null) {
|
||||
return new BackrollBackupMetrics(Long.parseLong(metrics.info.originalSize), Long.parseLong(metrics.info.deduplicatedSize));
|
||||
}
|
||||
} else {
|
||||
EntityUtils.consumeQuietly(backupMetricsResponse.getEntity());
|
||||
throw new CloudRuntimeException("Failed to retrieve backups status for this VM via Backroll");
|
||||
}
|
||||
} catch (final IOException | InterruptedException e) {
|
||||
LOG.error(String.format("Failed to check backups status due to: %s", e.getMessage()));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<BackrollVmBackup> getAllBackupsfromVirtualMachine(String vmId) {
|
||||
LOG.info(String.format("Trying to retrieve all backups for vm %s", vmId));
|
||||
|
||||
List<BackrollVmBackup> backups = new ArrayList<BackrollVmBackup>();
|
||||
|
||||
try {
|
||||
HttpResponse backupsResponse = get(String.format("/virtualmachines/%s/backups", vmId));
|
||||
|
||||
if(isResponseOk(backupsResponse)) {
|
||||
HttpEntity body = backupsResponse.getEntity();
|
||||
String bodyStr = EntityUtils.toString(body);
|
||||
|
||||
BackrollTaskRequestResponse backupTaskRequestResponse = new ObjectMapper().readValue(bodyStr, BackrollTaskRequestResponse.class);
|
||||
String urlToRequest = backupTaskRequestResponse.location.replace("/api/v1", "");
|
||||
LOG.debug(urlToRequest);
|
||||
EntityUtils.consumeQuietly(backupsResponse.getEntity());
|
||||
|
||||
bodyStr = waitForGetRequestResponse(urlToRequest);
|
||||
|
||||
VirtualMachineBackupsResponse response = new ObjectMapper().readValue(bodyStr, VirtualMachineBackupsResponse.class);
|
||||
EntityUtils.consumeQuietly(backupsResponse.getEntity());
|
||||
|
||||
if(response.state.equals("SUCCESS")) {
|
||||
if(response.info.archives.size() > 0) {
|
||||
for (BackupInfos infos : response.info.archives) {
|
||||
var dateStart = new DateTime(infos.start);
|
||||
backups.add(new BackrollVmBackup(infos.id, infos.name, dateStart.toDate()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (IOException | InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return backups;
|
||||
}
|
||||
|
||||
private HttpResponse post(final String path, final JSONObject json) throws IOException {
|
||||
String xml = null;
|
||||
StringEntity params = null;
|
||||
if (json != null) {
|
||||
LOG.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 HttpResponse response = httpClient.execute(request);
|
||||
|
||||
LOG.info(String.format("Response received in POST request with body [%s] is: [%s] for URL [%s].", xml, response.toString(), url));
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
protected HttpResponse get(final String path) throws IOException {
|
||||
String url = apiURI.toString() + path;
|
||||
LOG.debug(String.format("Backroll URL %s", url));
|
||||
final HttpGet request = new HttpGet(url);
|
||||
request.setHeader(HttpHeaders.AUTHORIZATION, "Bearer " + backrollToken);
|
||||
request.setHeader("Content-type", "application/json");
|
||||
final HttpResponse response = httpClient.execute(request);
|
||||
LOG.debug(String.format("Response received in GET request is: [%s] for URL: [%s].", response.toString(), url));
|
||||
return response;
|
||||
}
|
||||
|
||||
protected HttpResponse delete(final String path) throws IOException {
|
||||
String url = apiURI.toString() + path;
|
||||
final HttpDelete request = new HttpDelete(url);
|
||||
request.setHeader(HttpHeaders.AUTHORIZATION, "Bearer " + backrollToken);
|
||||
request.setHeader("Content-type", "application/json");
|
||||
final HttpResponse response = httpClient.execute(request);
|
||||
LOG.debug(String.format("Response received in GET request is: [%s] for URL: [%s].", response.toString(), url));
|
||||
return response;
|
||||
}
|
||||
|
||||
private boolean isResponseOk(final HttpResponse response) {
|
||||
if((response.getStatusLine().getStatusCode() == HttpStatus.SC_OK ||
|
||||
response.getStatusLine().getStatusCode() == HttpStatus.SC_ACCEPTED)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isResponseAuthorized(final HttpResponse response) {
|
||||
return (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK || response.getStatusLine().getStatusCode() == HttpStatus.SC_ACCEPTED) ? true : false;
|
||||
}
|
||||
|
||||
private String waitForGetRequestResponse(String urlToRequest) throws IOException, InterruptedException {
|
||||
HttpEntity body;
|
||||
String bodyStr = "";
|
||||
int cpt = 0;
|
||||
//int threshold = 30; // 5 minutes
|
||||
int threshold = 12; // 2 minutes
|
||||
|
||||
do {
|
||||
if(cpt == threshold){
|
||||
break;
|
||||
} else {
|
||||
final HttpResponse response = get(urlToRequest);
|
||||
LOG.debug("Backroll!!!" + response);
|
||||
if (isResponseOk(response)) {
|
||||
|
||||
body = response.getEntity();
|
||||
bodyStr = EntityUtils.toString(body);
|
||||
LOG.debug(bodyStr);
|
||||
EntityUtils.consumeQuietly(response.getEntity());
|
||||
} else {
|
||||
throw new CloudRuntimeException("An error occured with Backroll");
|
||||
}
|
||||
cpt++;
|
||||
TimeUnit.SECONDS.sleep(10);
|
||||
}
|
||||
|
||||
} while (bodyStr.contains("PENDING"));
|
||||
|
||||
if(cpt == threshold){
|
||||
bodyStr = "ERROR";
|
||||
}
|
||||
|
||||
return bodyStr;
|
||||
}
|
||||
|
||||
private boolean isAuthentificated() {
|
||||
boolean result = false;
|
||||
try {
|
||||
final HttpResponse response = post("/auth", null);
|
||||
result = isResponseAuthorized(response);
|
||||
EntityUtils.consumeQuietly(response.getEntity());
|
||||
} catch (IOException e) {
|
||||
LOG.error(String.format("Failed to authentificate to Backroll due to: %s", e.getMessage()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void loginIfAuthentificationFailed() {
|
||||
if(!isAuthentificated()) {
|
||||
login(appname, password);
|
||||
}
|
||||
}
|
||||
|
||||
private void login(final String appname, final String appsecret) {
|
||||
LOG.info("Backroll client - start login");
|
||||
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", appsecret);
|
||||
params = new StringEntity(jsonBody.toString());
|
||||
request.setEntity(params);
|
||||
|
||||
final HttpResponse response = httpClient.execute(request);
|
||||
|
||||
if (isResponseOk(response)) {
|
||||
HttpEntity body = response.getEntity();
|
||||
String bodyStr = EntityUtils.toString(body);
|
||||
|
||||
LoginApiResponse loginResponse = new ObjectMapper().readValue(bodyStr, LoginApiResponse.class);
|
||||
backrollToken = loginResponse.accessToken;
|
||||
LOG.debug(String.format("Backroll client - Token : %s", backrollToken));
|
||||
|
||||
EntityUtils.consumeQuietly(response.getEntity());
|
||||
|
||||
if (StringUtils.isEmpty(loginResponse.accessToken)) {
|
||||
throw new CloudRuntimeException("Backroll token is not available to perform API requests");
|
||||
}
|
||||
} else {
|
||||
EntityUtils.consumeQuietly(response.getEntity());
|
||||
if (response.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) {
|
||||
throw new CloudRuntimeException("Failed to authenticate Backroll API service due to:" + e.getMessage());
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
LOG.info("Backroll client - end login");
|
||||
}
|
||||
|
||||
private List<BackrollBackup> getBackrollBackups(final String vmId) {
|
||||
try {
|
||||
LOG.info("start to list Backroll backups for vm " + vmId);
|
||||
final HttpResponse backupsAskingResponse = get("/virtualmachines/" + vmId + "/backups");
|
||||
|
||||
if(isResponseOk(backupsAskingResponse)) {
|
||||
HttpEntity body = backupsAskingResponse.getEntity();
|
||||
String bodyStr = EntityUtils.toString(body);
|
||||
BackrollTaskRequestResponse backupTaskRequestResponse = new ObjectMapper().readValue(bodyStr, BackrollTaskRequestResponse.class);
|
||||
String urlToRequest = backupTaskRequestResponse.location.replace("/api/v1", "");
|
||||
LOG.debug(urlToRequest);
|
||||
EntityUtils.consumeQuietly(backupsAskingResponse.getEntity());
|
||||
bodyStr = waitForGetRequestResponse(urlToRequest);
|
||||
LOG.debug(bodyStr);
|
||||
BackrollBackupsFromVMResponse backrollBackupsFromVMResponse = new ObjectMapper().readValue(bodyStr, BackrollBackupsFromVMResponse.class);
|
||||
|
||||
final List<BackrollBackup> backups = new ArrayList<>();
|
||||
for (final BackrollArchiveResponse archive : backrollBackupsFromVMResponse.archives.archives) {
|
||||
backups.add(new BackrollBackup(archive.name));
|
||||
LOG.debug(archive.name);
|
||||
}
|
||||
return backups;
|
||||
} else {
|
||||
throw new CloudRuntimeException(
|
||||
"Failed to retrieve backups for this VM via Backroll");
|
||||
}
|
||||
} catch (final IOException e) {
|
||||
LOG.error("Failed to list backup form vm with Backroll due to:", e);
|
||||
} catch (InterruptedException e) {
|
||||
LOG.error("Backroll Error:", e);
|
||||
}
|
||||
return new ArrayList<BackrollBackup>();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
// 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.api.command;
|
||||
|
||||
/* import org.apache.log4j.Logger;
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.apache.cloudstack.api.BaseCmd;
|
||||
import org.apache.cloudstack.backup.backroll.api.response.ApiHelloResponse; */
|
||||
|
||||
/* @APICommand(name = "getBackrollHello", responseObject = ApiHelloResponse.class, description = "Get Hello",
|
||||
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) */
|
||||
public class GetBackrollHello {//extends BaseCmd {
|
||||
/* private static final Logger LOGGER = Logger.getLogger(GetBackrollHello.class.getName());
|
||||
private static final String NAME = "gethelloresponse";
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////// API Implementation///////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public String getCommandName() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getEntityOwnerId() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
LOGGER.info("'getBackrollHello.execute' method invoked");
|
||||
|
||||
ApiHelloResponse response = new ApiHelloResponse("Hello");
|
||||
|
||||
response.setResponseName(getCommandName());
|
||||
response.setObjectName("msg");
|
||||
|
||||
setResponseObject(response);
|
||||
} */
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
// 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.api.response;
|
||||
|
||||
import org.apache.cloudstack.api.BaseResponse;
|
||||
|
||||
import com.cloud.serializer.Param;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
public class ApiHelloResponse extends BaseResponse {
|
||||
@SerializedName("TEXT")
|
||||
@Param(description = "Text")
|
||||
private String msg;
|
||||
|
||||
public ApiHelloResponse(String msg) {
|
||||
this.msg = msg;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package org.apache.cloudstack.backup.backroll.model.response;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public class BackrollTaskRequestResponse {
|
||||
@JsonProperty("Location")
|
||||
public String location;
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
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;
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
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;
|
||||
|
||||
@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;
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
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;
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package org.apache.cloudstack.backup.backroll.model.response.archive;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public class BackrollBackupsFromVMResponse {
|
||||
@JsonProperty("state")
|
||||
public String state;
|
||||
|
||||
@JsonProperty("info")
|
||||
public BackrollArchivesResponse archives;
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
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;
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
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;
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package org.apache.cloudstack.backup.backroll.model.response.metrics.backup;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public class BackrollBackupMetricsResponse {
|
||||
@JsonProperty("state")
|
||||
public String state;
|
||||
|
||||
@JsonProperty("info")
|
||||
public BackupMetricsInfo info;
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
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;
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package org.apache.cloudstack.backup.backroll.model.response.metrics.virtualMachine;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public class BackrollVmMetricsResponse {
|
||||
@JsonProperty("state")
|
||||
public String state;
|
||||
|
||||
@JsonProperty("info")
|
||||
public MetricsInfos infos;
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
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;
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
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;
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package org.apache.cloudstack.backup.backroll.model.response.metrics.virtualMachine;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public class InfosEncryption {
|
||||
@JsonProperty("mode")
|
||||
public String mode;
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
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;
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
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;
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
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;
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
package org.apache.cloudstack.backup.backroll.model.response.metrics.virtualMachineBackups;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public class BackupInfos {
|
||||
@JsonProperty("archive")
|
||||
public String archive;
|
||||
|
||||
@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;
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package org.apache.cloudstack.backup.backroll.model.response.metrics.virtualMachineBackups;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public class InfosEncryption {
|
||||
@JsonProperty("mode")
|
||||
public String mode;
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
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;
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package org.apache.cloudstack.backup.backroll.model.response.metrics.virtualMachineBackups;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public class VirtualMachineBackupsResponse {
|
||||
@JsonProperty("state")
|
||||
public String state;
|
||||
|
||||
@JsonProperty("info")
|
||||
public Archives info;
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
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;
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package org.apache.cloudstack.backup.backroll.model.response.policy;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public class BackupPoliciesResponse {
|
||||
@JsonProperty("state")
|
||||
public String state;
|
||||
|
||||
@JsonProperty("info")
|
||||
@JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
|
||||
public List<BackrollBackupPolicyResponse> backupPolicies;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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>
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
// 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;
|
||||
|
||||
|
||||
//@RunWith(MockitoJUnitRunner.class)
|
||||
public class BackrollBackupProviderTest {
|
||||
/*@Spy
|
||||
@InjectMocks
|
||||
BackrollBackupProvider backupProvider = new BackrollBackupProvider();
|
||||
|
||||
@Mock
|
||||
BackrollClient client;
|
||||
|
||||
@Mock
|
||||
VMInstanceDao vmInstanceDao;
|
||||
|
||||
@Mock
|
||||
BackupDao backupDao;
|
||||
|
||||
@Test
|
||||
public void providerGetDescription() {
|
||||
assertEquals("Backroll Backup Plugin", backupProvider.getDescription());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void listBackupOfferingsToBackrollTestOk() {
|
||||
|
||||
List<BackupOffering> results = backupProvider.listBackupOfferings(0L);
|
||||
assertEquals("Backroll Backup Plugin", "Backroll Backup Plugin");
|
||||
|
||||
}*/
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
||||
public class BackrollClientTest {
|
||||
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
||||
|
|
@ -194,6 +195,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>
|
||||
|
|
|
|||
|
|
@ -2486,9 +2486,9 @@
|
|||
}
|
||||
},
|
||||
"@types/connect-history-api-fallback": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz",
|
||||
"integrity": "sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==",
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.1.tgz",
|
||||
"integrity": "sha512-iaQslNbARe8fctL5Lk+DsmgWOM83lM+7FzP0eQUJs1jd3kBE8NWqBTIT2S8SqQOJjxvt2eyIjpOuYeRXq2AdMw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/express-serve-static-core": "*",
|
||||
|
|
@ -2589,9 +2589,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"@types/http-proxy": {
|
||||
"version": "1.17.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.9.tgz",
|
||||
"integrity": "sha512-QsbSjA/fSk7xB+UXlCT3wHBy5ai9wOcNDWwZAtud+jXhwOM3l+EYZh8Lng4+/6n8uar0J7xILzqftJdJ/Wdfkw==",
|
||||
"version": "1.17.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.12.tgz",
|
||||
"integrity": "sha512-kQtujO08dVtQ2wXAuSFfk9ASy3sug4+ogFR8Kd8UgP8PEuc1/G/8yjYRmp//PcDNJEUKOza/MrQu15bouEUCiw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
|
|
@ -2803,9 +2803,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"@types/tapable": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/tapable/-/tapable-1.0.8.tgz",
|
||||
"integrity": "sha512-ipixuVrh2OdNmauvtT51o3d8z12p6LtFW9in7U79der/kwejjdNchQC5UMn5u/KxNoM7VHHOs/l8KS8uHxhODQ==",
|
||||
"version": "1.0.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/tapable/-/tapable-1.0.9.tgz",
|
||||
"integrity": "sha512-fOHIwZua0sRltqWzODGUM6b4ffZrf/vzGUmNXdR+4DzuJP42PMbM5dLKcdzlYvv8bMJ3GALOzkk1q7cDm2zPyA==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/through": {
|
||||
|
|
@ -2818,9 +2818,9 @@
|
|||
}
|
||||
},
|
||||
"@types/uglify-js": {
|
||||
"version": "3.16.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.16.0.tgz",
|
||||
"integrity": "sha512-0yeUr92L3r0GLRnBOvtYK1v2SjqMIqQDHMl7GLb+l2L8+6LSFWEEWEIgVsPdMn5ImLM8qzWT8xFPtQYpp8co0g==",
|
||||
"version": "3.17.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.17.2.tgz",
|
||||
"integrity": "sha512-9SjrHO54LINgC/6Ehr81NjAxAYvwEZqjUHLjJYvC4Nmr9jbLQCIZbWSvl4vXQkkmR1UAuaKDycau3O1kWGFyXQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"source-map": "^0.6.1"
|
||||
|
|
@ -2832,9 +2832,9 @@
|
|||
"integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw=="
|
||||
},
|
||||
"@types/webpack": {
|
||||
"version": "4.41.32",
|
||||
"resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.32.tgz",
|
||||
"integrity": "sha512-cb+0ioil/7oz5//7tZUSwbrSAN/NWHrQylz5cW8G0dWTcF/g+/dSdMlKVZspBYuMAN1+WnwHrkxiRrLcwd0Heg==",
|
||||
"version": "4.41.33",
|
||||
"resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.33.tgz",
|
||||
"integrity": "sha512-PPajH64Ft2vWevkerISMtnZ8rTs4YmRbs+23c402J0INmxDKCrhZNvwZYtzx96gY2wAtXdrK1BS2fiC8MlLr3g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "*",
|
||||
|
|
@ -3489,18 +3489,18 @@
|
|||
"dev": true
|
||||
},
|
||||
"json5": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
|
||||
"integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
|
||||
"integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"minimist": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"loader-utils": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
|
||||
"integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==",
|
||||
"version": "1.4.2",
|
||||
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz",
|
||||
"integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"big.js": "^5.2.2",
|
||||
|
|
@ -3529,9 +3529,9 @@
|
|||
}
|
||||
},
|
||||
"vue-loader": {
|
||||
"version": "15.9.8",
|
||||
"resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-15.9.8.tgz",
|
||||
"integrity": "sha512-GwSkxPrihfLR69/dSV3+5CdMQ0D+jXg8Ma1S4nQXKJAznYFX14vHdc/NetQc34Dw+rBbIJyP7JOuVb9Fhprvog==",
|
||||
"version": "15.10.2",
|
||||
"resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-15.10.2.tgz",
|
||||
"integrity": "sha512-ndeSe/8KQc/nlA7TJ+OBhv2qalmj1s+uBs7yHDRFaAXscFTApBzY9F1jES3bautmgWjDlDct0fw8rPuySDLwxw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@vue/component-compiler-utils": "^3.1.0",
|
||||
|
|
@ -4211,9 +4211,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"address": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/address/-/address-1.2.0.tgz",
|
||||
"integrity": "sha512-tNEZYz5G/zYunxFm7sfhAxkXEuLj3K6BKwv6ZURlsF6yiUQ65z0Q2wZW9L5cPUl9ocofGvXOdFYbFHp0+6MOig==",
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/address/-/address-1.2.2.tgz",
|
||||
"integrity": "sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==",
|
||||
"dev": true
|
||||
},
|
||||
"agent-base": {
|
||||
|
|
@ -7866,18 +7866,18 @@
|
|||
"dev": true
|
||||
},
|
||||
"json5": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
|
||||
"integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
|
||||
"integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"minimist": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"loader-utils": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
|
||||
"integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==",
|
||||
"version": "1.4.2",
|
||||
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz",
|
||||
"integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"big.js": "^5.2.2",
|
||||
|
|
@ -8179,18 +8179,18 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"json5": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
|
||||
"integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
|
||||
"integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"minimist": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"loader-utils": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
|
||||
"integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==",
|
||||
"version": "1.4.2",
|
||||
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz",
|
||||
"integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"big.js": "^5.2.2",
|
||||
|
|
@ -8199,9 +8199,9 @@
|
|||
}
|
||||
},
|
||||
"semver": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
|
|
@ -10813,18 +10813,18 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"json5": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
|
||||
"integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
|
||||
"integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"minimist": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"loader-utils": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
|
||||
"integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==",
|
||||
"version": "1.4.2",
|
||||
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz",
|
||||
"integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"big.js": "^5.2.2",
|
||||
|
|
@ -15140,12 +15140,30 @@
|
|||
}
|
||||
},
|
||||
"launch-editor-middleware": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/launch-editor-middleware/-/launch-editor-middleware-2.4.0.tgz",
|
||||
"integrity": "sha512-/M7AX/6xktZY60KE7j71XLrj9U6H5TBoP+mJzhYB3fcdAq8rcazit/K0qWiu1jvytUPXP4lJRd1VJFwvdMQ/uw==",
|
||||
"version": "2.6.0",
|
||||
"resolved": "https://registry.npmjs.org/launch-editor-middleware/-/launch-editor-middleware-2.6.0.tgz",
|
||||
"integrity": "sha512-K2yxgljj5TdCeRN1lBtO3/J26+AIDDDw+04y6VAiZbWcTdBwsYN6RrZBnW5DN/QiSIdKNjKdATLUUluWWFYTIA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"launch-editor": "^2.4.0"
|
||||
"launch-editor": "^2.6.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"launch-editor": {
|
||||
"version": "2.6.0",
|
||||
"resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.6.0.tgz",
|
||||
"integrity": "sha512-JpDCcQnyAAzZZaZ7vEiSqL690w7dAEyLao+KC96zBplnYbJS7TYNjvM3M7y3dGz+v7aIsJk3hllWuc0kWAjyRQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"picocolors": "^1.0.0",
|
||||
"shell-quote": "^1.7.3"
|
||||
}
|
||||
},
|
||||
"picocolors": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
|
||||
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"left-pad": {
|
||||
|
|
@ -15848,18 +15866,18 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"json5": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
|
||||
"integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
|
||||
"integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"minimist": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"loader-utils": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
|
||||
"integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==",
|
||||
"version": "1.4.2",
|
||||
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz",
|
||||
"integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"big.js": "^5.2.2",
|
||||
|
|
@ -17637,18 +17655,18 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"json5": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
|
||||
"integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
|
||||
"integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"minimist": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"loader-utils": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
|
||||
"integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==",
|
||||
"version": "1.4.2",
|
||||
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz",
|
||||
"integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"big.js": "^5.2.2",
|
||||
|
|
@ -18063,9 +18081,9 @@
|
|||
}
|
||||
},
|
||||
"postcss-selector-parser": {
|
||||
"version": "6.0.10",
|
||||
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz",
|
||||
"integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==",
|
||||
"version": "6.0.13",
|
||||
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz",
|
||||
"integrity": "sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"cssesc": "^3.0.0",
|
||||
|
|
@ -18120,9 +18138,9 @@
|
|||
"integrity": "sha512-ravE6m9Atw9Z/jjttRUZ+clIXogdghyZAuWJ3qEzjT+jI/dL1ifAqhZeC5VHzQp1MSt1+jxKkFNemj/iO7tVUA=="
|
||||
},
|
||||
"prettier": {
|
||||
"version": "2.7.1",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz",
|
||||
"integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==",
|
||||
"version": "2.8.8",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz",
|
||||
"integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
|
|
@ -20413,9 +20431,9 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"readable-stream": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
|
||||
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
|
||||
"version": "3.6.2",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
|
||||
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"inherits": "^2.0.3",
|
||||
|
|
@ -22084,18 +22102,18 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"json5": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
|
||||
"integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
|
||||
"integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"minimist": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"loader-utils": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
|
||||
"integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==",
|
||||
"version": "1.4.2",
|
||||
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz",
|
||||
"integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"big.js": "^5.2.2",
|
||||
|
|
@ -22638,18 +22656,18 @@
|
|||
"dev": true
|
||||
},
|
||||
"json5": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
|
||||
"integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
|
||||
"integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"minimist": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"loader-utils": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
|
||||
"integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==",
|
||||
"version": "1.4.2",
|
||||
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz",
|
||||
"integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"big.js": "^5.2.2",
|
||||
|
|
@ -23415,9 +23433,9 @@
|
|||
}
|
||||
},
|
||||
"semver": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||
"dev": true
|
||||
},
|
||||
"string-width": {
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@
|
|||
"@vue/cli-plugin-router": "~4.5.0",
|
||||
"@vue/cli-plugin-unit-jest": "~4.5.0",
|
||||
"@vue/cli-plugin-vuex": "~4.5.0",
|
||||
"@vue/cli-service": "~4.5.0",
|
||||
"@vue/cli-service": "^4.5.19",
|
||||
"@vue/compiler-sfc": "^3.2.31",
|
||||
"@vue/eslint-config-standard": "^5.1.2",
|
||||
"@vue/test-utils": "^2.0.0-0",
|
||||
|
|
|
|||
Loading…
Reference in New Issue