mirror of https://github.com/apache/cloudstack.git
prototype backup e2e
Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com>
This commit is contained in:
parent
f3bc03955a
commit
f13769d33d
|
|
@ -21,16 +21,15 @@ package org.apache.cloudstack.backup;
|
|||
|
||||
import com.cloud.agent.api.Answer;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class BackupAnswer extends Answer {
|
||||
Long size;
|
||||
Long virtualSize;
|
||||
String path;
|
||||
Map<String, String> volumes;
|
||||
|
||||
public BackupAnswer(TakeBackupCommand command, Long size, Long virtualSize, String path) {
|
||||
super(command);
|
||||
this.size = size;
|
||||
this.virtualSize = virtualSize;
|
||||
this.path = path;
|
||||
public BackupAnswer(final TakeBackupCommand command, final boolean success, final String details) {
|
||||
super(command, success, details);
|
||||
}
|
||||
|
||||
public Long getSize() {
|
||||
|
|
@ -49,11 +48,11 @@ public class BackupAnswer extends Answer {
|
|||
this.virtualSize = virtualSize;
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
public Map<String, String> getVolumes() {
|
||||
return volumes;
|
||||
}
|
||||
|
||||
public void setPath(String path) {
|
||||
this.path = path;
|
||||
public void setVolumes(Map<String, String> volumes) {
|
||||
this.volumes = volumes;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,13 +26,15 @@ import java.util.Map;
|
|||
|
||||
public class TakeBackupCommand extends Command {
|
||||
private String vmName;
|
||||
private String backupPath;
|
||||
private String backupStoragePath;
|
||||
@LogLevel(LogLevel.Log4jLevel.Off)
|
||||
private Map<String, String> details;
|
||||
|
||||
public TakeBackupCommand(String vmName, String backupStoragePath, Map<String, String> details) {
|
||||
public TakeBackupCommand(String vmName, String backupPath, String backupStoragePath, Map<String, String> details) {
|
||||
super();
|
||||
this.vmName = vmName;
|
||||
this.backupPath = backupPath;
|
||||
this.backupStoragePath = backupStoragePath;
|
||||
this.details = details;
|
||||
}
|
||||
|
|
@ -45,6 +47,14 @@ public class TakeBackupCommand extends Command {
|
|||
this.vmName = vmName;
|
||||
}
|
||||
|
||||
public String getBackupPath() {
|
||||
return backupPath;
|
||||
}
|
||||
|
||||
public void setBackupPath(String backupPath) {
|
||||
this.backupPath = backupPath;
|
||||
}
|
||||
|
||||
public String getBackupStoragePath() {
|
||||
return backupStoragePath;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ import org.apache.commons.collections.CollectionUtils;
|
|||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import javax.inject.Inject;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
|
|
@ -139,19 +140,21 @@ public class NASBackupProvider extends AdapterBase implements BackupProvider, Co
|
|||
|
||||
@Override
|
||||
public boolean takeBackup(VirtualMachine vm) {
|
||||
Host host = getRunningVMHypervisorHost(vm);
|
||||
final Host host = getRunningVMHypervisorHost(vm);
|
||||
if (host == null || !Status.Up.equals(host.getStatus()) || !Hypervisor.HypervisorType.KVM.equals(host.getHypervisorType())) {
|
||||
throw new CloudRuntimeException("Unable to contact backend control plane to initiate backup");
|
||||
}
|
||||
|
||||
BackupOfferingVO backupOffering = new BackupOfferingDaoImpl().findById(vm.getBackupOfferingId());
|
||||
final BackupOfferingVO backupOffering = new BackupOfferingDaoImpl().findById(vm.getBackupOfferingId());
|
||||
|
||||
final String backupStoragePath = getBackupStoragePath(vm.getDataCenterId());
|
||||
final String nasType = getNasType(vm.getDataCenterId());
|
||||
final Map<String, String> backupDetails = Map.of(
|
||||
"type", nasType
|
||||
);
|
||||
final String backupPath = new SimpleDateFormat("yyyy.MM.dd.HH.mm.ss").format(new java.util.Date());
|
||||
|
||||
TakeBackupCommand command = new TakeBackupCommand(vm.getInstanceName(), backupStoragePath, backupDetails);
|
||||
TakeBackupCommand command = new TakeBackupCommand(vm.getInstanceName(), backupPath, backupStoragePath, backupDetails);
|
||||
|
||||
BackupAnswer answer = null;
|
||||
try {
|
||||
|
|
@ -165,7 +168,7 @@ public class NASBackupProvider extends AdapterBase implements BackupProvider, Co
|
|||
if (answer != null) {
|
||||
BackupVO backup = new BackupVO();
|
||||
backup.setVmId(vm.getId());
|
||||
backup.setExternalId(String.format("%s|%s|%s", nasType, backupStoragePath, answer.getPath()));
|
||||
backup.setExternalId(String.format("%s|%s|%s", nasType, backupStoragePath, backupPath));
|
||||
backup.setType("FULL");
|
||||
backup.setDate(new Date());
|
||||
backup.setSize(answer.getSize());
|
||||
|
|
|
|||
|
|
@ -326,6 +326,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
|||
private String createTmplPath;
|
||||
private String heartBeatPath;
|
||||
private String vmActivityCheckPath;
|
||||
private String nasBackupPath;
|
||||
private String securityGroupPath;
|
||||
private String ovsPvlanDhcpHostPath;
|
||||
private String ovsPvlanVmPath;
|
||||
|
|
@ -714,6 +715,10 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
|||
return vmActivityCheckPath;
|
||||
}
|
||||
|
||||
public String getNasBackupPath() {
|
||||
return nasBackupPath;
|
||||
}
|
||||
|
||||
public String getOvsPvlanDhcpHostPath() {
|
||||
return ovsPvlanDhcpHostPath;
|
||||
}
|
||||
|
|
@ -984,6 +989,11 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
|||
throw new ConfigurationException("Unable to find kvmvmactivity.sh");
|
||||
}
|
||||
|
||||
nasBackupPath = Script.findScript(kvmScriptsDir, "nasbackup.sh");
|
||||
if (nasBackupPath == null) {
|
||||
throw new ConfigurationException("Unable to find nasbackup.sh");
|
||||
}
|
||||
|
||||
createTmplPath = Script.findScript(storageScriptsDir, "createtmplt.sh");
|
||||
if (createTmplPath == null) {
|
||||
throw new ConfigurationException("Unable to find the createtmplt.sh");
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
//
|
||||
// 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 com.cloud.hypervisor.kvm.resource.wrapper;
|
||||
|
||||
import com.cloud.agent.api.Answer;
|
||||
import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource;
|
||||
import com.cloud.resource.CommandWrapper;
|
||||
import com.cloud.resource.ResourceWrapper;
|
||||
import com.cloud.utils.script.Script;
|
||||
import org.apache.cloudstack.backup.BackupAnswer;
|
||||
import org.apache.cloudstack.backup.TakeBackupCommand;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
@ResourceWrapper(handles = TakeBackupCommand.class)
|
||||
public class LibvirtTakeBackupCommandWrapper extends CommandWrapper<TakeBackupCommand, Answer, LibvirtComputingResource> {
|
||||
@Override
|
||||
public Answer execute(TakeBackupCommand command, LibvirtComputingResource libvirtComputingResource) {
|
||||
final String vmName = command.getVmName();
|
||||
final String backupStoragePath = command.getBackupStoragePath();
|
||||
final String backupFolder = command.getBackupPath();
|
||||
Script cmd = new Script(libvirtComputingResource.getNasBackupPath(), libvirtComputingResource.getCmdsTimeout(), logger);
|
||||
cmd.add("-b", vmName);
|
||||
cmd.add("-s", backupStoragePath);
|
||||
cmd.add("-p", String.format("%s%s%s", vmName, File.separator, backupFolder));
|
||||
String result = cmd.execute();
|
||||
if (result == null) {
|
||||
logger.debug("Failed to take VM backup: " + result);
|
||||
return new BackupAnswer(command, false, result);
|
||||
}
|
||||
BackupAnswer answer = new BackupAnswer(command, true, null);
|
||||
answer.setSize(Long.valueOf(result));
|
||||
return answer;
|
||||
}
|
||||
}
|
||||
|
|
@ -16,30 +16,89 @@
|
|||
## specific language governing permissions and limitations
|
||||
## under the License.
|
||||
|
||||
# CloudStack B&R NAS (KVM) Backup and Recovery Tool
|
||||
# CloudStack B&R NAS Backup and Recovery Tool for KVM
|
||||
|
||||
# TODO: do libvirt version & other dependency checks
|
||||
# TODO: do libvirt/logging etc checks
|
||||
|
||||
# TODO: logging & args/opts handling
|
||||
backup_vm() {
|
||||
vm=$1
|
||||
path=$2
|
||||
storage=$3
|
||||
|
||||
backup_domain() {
|
||||
domain=$1 # TODO: get as opt?
|
||||
# TODO: santiy checks on domain
|
||||
# get domblklist
|
||||
# gather external snapshot diskspec/target
|
||||
# ensure the target nas is mounted / path is known or available
|
||||
# virsh snapshot-create-as -> snapshot domain with --no-metadata--atomic --quiesce (?) --disk-only
|
||||
# merge/commit target -> domblklist; blockcommit
|
||||
# backup this domain & its disks, xml completely
|
||||
# cleanup snapshot(s)
|
||||
mount_point=$(mktemp -d -t csbackup.XXXXX)
|
||||
dest="$mount_point/$path"
|
||||
|
||||
mount -t nfs $storage $mount_point
|
||||
mkdir -p $dest
|
||||
|
||||
echo "<domainbackup mode='push'><disks>" > $dest/backup.xml
|
||||
for disk in $(virsh -c qemu:///system domblklist $vm --details | awk '/disk/{print$3}'); do
|
||||
echo "<disk name='$disk' backup='yes' type='file' backupmode='full'><driver type='qcow2'/><target file='$dest/$disk' /></disk>" >> $dest/backup.xml
|
||||
done
|
||||
echo "</disks></domainbackup>" > $dest/backup.xml
|
||||
|
||||
virsh -c qemu:///system backup-begin --domain $vm --backupxml $dest/backup.xml
|
||||
virsh -c qemu:///system dumpxml $vm > $dest/domain-$vm.xml
|
||||
sync
|
||||
|
||||
# Print directory size
|
||||
du -sb $dest | cut -f1
|
||||
umount $mount_point
|
||||
rmdir $mount_point
|
||||
}
|
||||
|
||||
restore_all_volumes() {
|
||||
# check and mount nas target & copy files
|
||||
# check and restore all volumes
|
||||
OP=""
|
||||
VM=""
|
||||
PATH=""
|
||||
NAS=""
|
||||
TYPE=""
|
||||
|
||||
function usage {
|
||||
echo ""
|
||||
echo "Usage: $0 -b <domain> -s <NAS storage mount path> -p <backup dest path>"
|
||||
echo ""
|
||||
exit 1
|
||||
}
|
||||
|
||||
restore_volume() {
|
||||
# check and mount nas target & copy files
|
||||
# check and restore specific volume (qcow2)
|
||||
}
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
-b|--backup)
|
||||
OP="backup"
|
||||
VM="$2"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
-s|--storage)
|
||||
NAS="$2"
|
||||
TYPE="nfs"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
-p|--path)
|
||||
PATH="$2"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
-r|--recover)
|
||||
OP="recover"
|
||||
shift
|
||||
;;
|
||||
-rv|--recover)
|
||||
OP="recover-volume"
|
||||
shift
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
echo "Invalid option: $1"
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ "$OP" = "backup" ]; then
|
||||
backup_vm $VM $PATH $NAS
|
||||
fi
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue