mirror of https://github.com/apache/cloudstack.git
Support multiple disks and checkpoints
This commit is contained in:
parent
b926c7474d
commit
da62e9a3ed
|
|
@ -25,16 +25,18 @@ public class CreateImageTransferCommand extends Command {
|
|||
private String exportName;
|
||||
private int nbdPort;
|
||||
private String direction;
|
||||
private String checkpointId;
|
||||
|
||||
public CreateImageTransferCommand() {
|
||||
}
|
||||
|
||||
public CreateImageTransferCommand(String transferId, String hostIpAddress, String exportName, int nbdPort, String direction) {
|
||||
public CreateImageTransferCommand(String transferId, String hostIpAddress, String exportName, int nbdPort, String direction, String checkpointId) {
|
||||
this.transferId = transferId;
|
||||
this.hostIpAddress = hostIpAddress;
|
||||
this.exportName = exportName;
|
||||
this.nbdPort = nbdPort;
|
||||
this.direction = direction;
|
||||
this.checkpointId = checkpointId;
|
||||
}
|
||||
|
||||
public String getExportName() {
|
||||
|
|
@ -61,4 +63,8 @@ public class CreateImageTransferCommand extends Command {
|
|||
public String getDirection() {
|
||||
return direction;
|
||||
}
|
||||
|
||||
public String getCheckpointId() {
|
||||
return checkpointId;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,19 +26,19 @@ public class StartBackupCommand extends Command {
|
|||
private String toCheckpointId;
|
||||
private String fromCheckpointId;
|
||||
private int nbdPort;
|
||||
private Map<String, String> diskVolumePaths; // volumeId -> path mapping
|
||||
private Map<String, String> diskPathUuidMap;
|
||||
private String hostIpAddress;
|
||||
|
||||
public StartBackupCommand() {
|
||||
}
|
||||
|
||||
public StartBackupCommand(String vmName, String toCheckpointId, String fromCheckpointId,
|
||||
int nbdPort, Map<String, String> diskVolumePaths, String hostIpAddress) {
|
||||
int nbdPort, Map<String, String> diskPathUuidMap, String hostIpAddress) {
|
||||
this.vmName = vmName;
|
||||
this.toCheckpointId = toCheckpointId;
|
||||
this.fromCheckpointId = fromCheckpointId;
|
||||
this.nbdPort = nbdPort;
|
||||
this.diskVolumePaths = diskVolumePaths;
|
||||
this.diskPathUuidMap = diskPathUuidMap;
|
||||
this.hostIpAddress = hostIpAddress;
|
||||
}
|
||||
|
||||
|
|
@ -58,8 +58,8 @@ public class StartBackupCommand extends Command {
|
|||
return nbdPort;
|
||||
}
|
||||
|
||||
public Map<String, String> getDiskVolumePaths() {
|
||||
return diskVolumePaths;
|
||||
public Map<String, String> getDiskPathUuidMap() {
|
||||
return diskPathUuidMap;
|
||||
}
|
||||
|
||||
public boolean isIncremental() {
|
||||
|
|
|
|||
|
|
@ -5227,6 +5227,24 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
|||
logger.debug("Removed all checkpoints of volume [{}] on VM [{}].", volumeUuid, vmName);
|
||||
}
|
||||
|
||||
public Map<String, String> getDiskPathLabelMap(String vmName) {
|
||||
try {
|
||||
Connect conn = LibvirtConnection.getConnectionByVmName(vmName);
|
||||
List<DiskDef> disks = getDisks(conn, vmName);
|
||||
Map<String, String> diskPathLabelMap = new HashMap<>();
|
||||
for (DiskDef disk : disks) {
|
||||
if (disk.getDeviceType() != DeviceType.DISK) {
|
||||
continue;
|
||||
}
|
||||
diskPathLabelMap.put(disk.getDiskPath(), disk.getDiskLabel());
|
||||
}
|
||||
return diskPathLabelMap;
|
||||
} catch (LibvirtException e) {
|
||||
logger.error("Failed to get disk path label map for VM [{}] due to: [{}].", vmName, e.getMessage(), e);
|
||||
throw new CloudRuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean recreateCheckpointsOnVm(List<VolumeObjectTO> volumes, String vmName, Connect conn) {
|
||||
logger.debug("Trying to recreate checkpoints on VM [{}] with volumes [{}].", vmName, volumes);
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource;
|
|||
import com.cloud.hypervisor.kvm.resource.LibvirtConnection;
|
||||
import com.cloud.resource.CommandWrapper;
|
||||
import com.cloud.resource.ResourceWrapper;
|
||||
import com.cloud.utils.StringUtils;
|
||||
import com.cloud.utils.script.Script;
|
||||
|
||||
@ResourceWrapper(handles = StartBackupCommand.class)
|
||||
|
|
@ -61,7 +62,7 @@ public class LibvirtStartBackupCommandWrapper extends CommandWrapper<StartBackup
|
|||
}
|
||||
|
||||
// Create backup XML
|
||||
String backupXml = createBackupXml(cmd, fromCheckpointId, nbdPort);
|
||||
String backupXml = createBackupXml(cmd, fromCheckpointId, nbdPort, resource);
|
||||
String checkpointXml = createCheckpointXml(toCheckpointId);
|
||||
|
||||
// Write XMLs to temp files
|
||||
|
|
@ -101,28 +102,36 @@ public class LibvirtStartBackupCommandWrapper extends CommandWrapper<StartBackup
|
|||
}
|
||||
}
|
||||
|
||||
private String createBackupXml(StartBackupCommand cmd, String fromCheckpointId, int nbdPort) {
|
||||
private String createBackupXml(StartBackupCommand cmd, String fromCheckpointId, int nbdPort, LibvirtComputingResource resource) {
|
||||
StringBuilder xml = new StringBuilder();
|
||||
xml.append("<domainbackup mode=\"pull\">\n");
|
||||
|
||||
if (fromCheckpointId != null && !fromCheckpointId.isEmpty()) {
|
||||
if (StringUtils.isNotBlank(fromCheckpointId)) {
|
||||
xml.append(" <incremental>").append(fromCheckpointId).append("</incremental>\n");
|
||||
}
|
||||
|
||||
xml.append(String.format(" <server transport=\"tcp\" name=\"%s\" port=\"%s\"/>\n", cmd.getHostIpAddress(), nbdPort));
|
||||
xml.append(String.format(" <server transport=\"tcp\" name=\"%s\" port=\"%d\"/>\n", cmd.getHostIpAddress(), nbdPort));
|
||||
xml.append(" <disks>\n");
|
||||
|
||||
// Add disk entries - simplified for POC
|
||||
Map<String, String> diskPaths = cmd.getDiskVolumePaths();
|
||||
int diskIndex = 0;
|
||||
for (Map.Entry<String, String> entry : diskPaths.entrySet()) {
|
||||
String deviceName = "vd" + (char)('a' + diskIndex);
|
||||
String scratchFile = "/var/tmp/scratch-" + entry.getKey() + ".qcow2";
|
||||
xml.append(" <disk name=\"").append(deviceName).append("\" type=\"file\" exportname=\"")
|
||||
.append(entry.getKey()).append("\">\n");
|
||||
Map<String, String> diskPathUuidMap = cmd.getDiskPathUuidMap();
|
||||
Map<String, String> diskPathLabelMap = resource.getDiskPathLabelMap(cmd.getVmName());
|
||||
|
||||
for (Map.Entry<String, String> entry : diskPathLabelMap.entrySet()) {
|
||||
if (!diskPathUuidMap.containsKey(entry.getKey())) {
|
||||
continue;
|
||||
}
|
||||
String diskName = entry.getValue();
|
||||
String export = diskPathUuidMap.get(entry.getKey());
|
||||
// todo: use UUID here as well?
|
||||
String scratchFile = "/var/tmp/scratch-" + export + ".qcow2";
|
||||
xml.append(" <disk name=\"").append(diskName).append("\" type=\"file\" exportname=\"").append(export);
|
||||
if (StringUtils.isNotBlank(fromCheckpointId)) {
|
||||
String exportBitmap = export + "-" + fromCheckpointId.substring(0, 4);
|
||||
xml.append("\" exportbitmap=\"").append(exportBitmap);
|
||||
}
|
||||
xml.append("\">\n");
|
||||
xml.append(" <scratch file=\"").append(scratchFile).append("\"/>\n");
|
||||
xml.append(" </disk>\n");
|
||||
diskIndex++;
|
||||
}
|
||||
|
||||
xml.append(" </disks>\n");
|
||||
|
|
|
|||
|
|
@ -176,9 +176,11 @@ public class IncrementalBackupServiceImpl extends ManagerBase implements Increme
|
|||
backup = backupDao.persist(backup);
|
||||
|
||||
List<VolumeVO> volumes = volumeDao.findByInstance(vmId);
|
||||
Map<String, String> diskVolumePaths = new HashMap<>();
|
||||
Map<String, String> diskPathUuidMap = new HashMap<>();
|
||||
for (Volume vol : volumes) {
|
||||
diskVolumePaths.put(vol.getUuid(), vol.getPath());
|
||||
StoragePoolVO storagePool = primaryDataStoreDao.findById(vol.getPoolId());
|
||||
String volumePath = String.format("/mnt/%s/%s", storagePool.getUuid(), vol.getPath());
|
||||
diskPathUuidMap.put(volumePath, vol.getUuid());
|
||||
}
|
||||
|
||||
Host host = hostDao.findById(vm.getHostId());
|
||||
|
|
@ -187,7 +189,7 @@ public class IncrementalBackupServiceImpl extends ManagerBase implements Increme
|
|||
toCheckpointId,
|
||||
fromCheckpointId,
|
||||
nbdPort,
|
||||
diskVolumePaths,
|
||||
diskPathUuidMap,
|
||||
host.getPrivateIpAddress()
|
||||
);
|
||||
|
||||
|
|
@ -240,20 +242,18 @@ public class IncrementalBackupServiceImpl extends ManagerBase implements Increme
|
|||
throw new CloudRuntimeException("Backup does not belong to VM: " + vmId);
|
||||
}
|
||||
|
||||
// Get VM
|
||||
VMInstanceVO vm = vmInstanceDao.findById(vmId);
|
||||
if (vm == null) {
|
||||
throw new CloudRuntimeException("VM not found: " + vmId);
|
||||
}
|
||||
|
||||
boolean dummyOffering = isDummyOffering(vm.getBackupOfferingId());
|
||||
boolean dummyOffering = isDummyOffering(backup.getBackupOfferingId());
|
||||
|
||||
List<ImageTransferVO> transfers = imageTransferDao.listByBackupId(backupId);
|
||||
if (CollectionUtils.isNotEmpty(transfers)) {
|
||||
throw new CloudRuntimeException("Image transfers not finalized for backup: " + backupId);
|
||||
}
|
||||
|
||||
// Send StopBackupCommand to agent
|
||||
StopBackupCommand stopCmd = new StopBackupCommand(vm.getInstanceName(), vmId, backupId);
|
||||
|
||||
try {
|
||||
|
|
@ -261,7 +261,7 @@ public class IncrementalBackupServiceImpl extends ManagerBase implements Increme
|
|||
if (dummyOffering) {
|
||||
answer = new StopBackupAnswer(stopCmd, true, "Dummy answer");
|
||||
} else {
|
||||
answer = (StopBackupAnswer) agentManager.send(vm.getHostId(), stopCmd);
|
||||
answer = (StopBackupAnswer) agentManager.send(backup.getHostId(), stopCmd);
|
||||
}
|
||||
|
||||
if (!answer.getResult()) {
|
||||
|
|
@ -276,7 +276,7 @@ public class IncrementalBackupServiceImpl extends ManagerBase implements Increme
|
|||
|
||||
// Delete old checkpoint if exists (POC: skip actual libvirt call)
|
||||
if (oldCheckpointId != null) {
|
||||
// In production: send command to delete oldCheckpointId via virsh checkpoint-delete
|
||||
// todo: In production: send command to delete oldCheckpointId via virsh checkpoint-delete
|
||||
logger.debug("Would delete old checkpoint: " + oldCheckpointId);
|
||||
}
|
||||
|
||||
|
|
@ -305,7 +305,8 @@ public class IncrementalBackupServiceImpl extends ManagerBase implements Increme
|
|||
host.getPrivateIpAddress(),
|
||||
volume.getUuid(),
|
||||
backup.getNbdPort(),
|
||||
direction
|
||||
direction,
|
||||
backup.getFromCheckpointId()
|
||||
);
|
||||
|
||||
try {
|
||||
|
|
@ -392,7 +393,8 @@ public class IncrementalBackupServiceImpl extends ManagerBase implements Increme
|
|||
host.getPrivateIpAddress(),
|
||||
volume.getUuid(),
|
||||
nbdPort,
|
||||
direction
|
||||
direction,
|
||||
null
|
||||
);
|
||||
|
||||
EndPoint ssvm = _epSelector.findSsvm(volume.getDataCenterId());
|
||||
|
|
|
|||
|
|
@ -3865,6 +3865,10 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements S
|
|||
payload.put("host", hostIp);
|
||||
payload.put("port", nbdPort);
|
||||
payload.put("export", exportName);
|
||||
String checkpointId = cmd.getCheckpointId();
|
||||
if (checkpointId != null) {
|
||||
payload.put("export_bitmap", exportName + "-" + checkpointId.substring(0, 4));
|
||||
}
|
||||
|
||||
final String json = new GsonBuilder().create().toJson(payload);
|
||||
File dir = new File("/tmp/imagetransfer");
|
||||
|
|
|
|||
Loading…
Reference in New Issue