mirror of https://github.com/apache/cloudstack.git
Implement backend for delete vm checkpoint
This commit is contained in:
parent
b84ff6b99a
commit
dc480e07d3
|
|
@ -0,0 +1,60 @@
|
|||
//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
|
||||
//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.util.Map;
|
||||
|
||||
import com.cloud.agent.api.Command;
|
||||
|
||||
public class DeleteVmCheckpointCommand extends Command {
|
||||
private String vmName;
|
||||
private String checkpointId;
|
||||
private Map<String, String> diskPathUuidMap;
|
||||
private boolean stoppedVM;
|
||||
|
||||
public DeleteVmCheckpointCommand() {
|
||||
}
|
||||
|
||||
public DeleteVmCheckpointCommand(String vmName, String checkpointId, Map<String, String> diskPathUuidMap, boolean stoppedVM) {
|
||||
this.vmName = vmName;
|
||||
this.checkpointId = checkpointId;
|
||||
this.diskPathUuidMap = diskPathUuidMap;
|
||||
this.stoppedVM = stoppedVM;
|
||||
}
|
||||
|
||||
public String getVmName() {
|
||||
return vmName;
|
||||
}
|
||||
|
||||
public String getCheckpointId() {
|
||||
return checkpointId;
|
||||
}
|
||||
|
||||
public Map<String, String> getDiskPathUuidMap() {
|
||||
return diskPathUuidMap;
|
||||
}
|
||||
|
||||
public boolean isStoppedVM() {
|
||||
return stoppedVM;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean executeInSequence() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
//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
|
||||
//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 java.util.Map;
|
||||
|
||||
import org.apache.cloudstack.backup.DeleteVmCheckpointCommand;
|
||||
|
||||
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;
|
||||
|
||||
@ResourceWrapper(handles = DeleteVmCheckpointCommand.class)
|
||||
public class LibvirtDeleteVmCheckpointCommandWrapper extends CommandWrapper<DeleteVmCheckpointCommand, Answer, LibvirtComputingResource> {
|
||||
|
||||
@Override
|
||||
public Answer execute(DeleteVmCheckpointCommand cmd, LibvirtComputingResource resource) {
|
||||
if (cmd.isStoppedVM()) {
|
||||
return deleteBitmapsOnDisks(cmd);
|
||||
}
|
||||
return deleteDomainCheckpoint(cmd);
|
||||
}
|
||||
|
||||
private Answer deleteDomainCheckpoint(DeleteVmCheckpointCommand cmd) {
|
||||
String vmName = cmd.getVmName();
|
||||
String checkpointId = cmd.getCheckpointId();
|
||||
String virshCmd = String.format("virsh checkpoint-delete %s %s", vmName, checkpointId);
|
||||
Script script = new Script("/bin/bash");
|
||||
script.add("-c");
|
||||
script.add(virshCmd);
|
||||
String result = script.execute();
|
||||
if (result != null) {
|
||||
return new Answer(cmd, false, "Failed to delete checkpoint: " + result);
|
||||
}
|
||||
return new Answer(cmd, true, "Checkpoint deleted");
|
||||
}
|
||||
|
||||
/**
|
||||
* Stopped VM: persistent bitmaps on disk images ({@code qemu-img bitmap --remove}), matching {@link LibvirtStartBackupCommandWrapper} bitmap --add.
|
||||
*/
|
||||
private Answer deleteBitmapsOnDisks(DeleteVmCheckpointCommand cmd) {
|
||||
String checkpointId = cmd.getCheckpointId();
|
||||
Map<String, String> diskPathUuidMap = cmd.getDiskPathUuidMap();
|
||||
if (diskPathUuidMap == null || diskPathUuidMap.isEmpty()) {
|
||||
return new Answer(cmd, false, "No disks provided for bitmap removal");
|
||||
}
|
||||
for (Map.Entry<String, String> entry : diskPathUuidMap.entrySet()) {
|
||||
String diskPath = entry.getKey();
|
||||
Script script = new Script("sudo");
|
||||
script.add("qemu-img");
|
||||
script.add("bitmap");
|
||||
script.add("--remove");
|
||||
script.add(diskPath);
|
||||
script.add(checkpointId);
|
||||
String result = script.execute();
|
||||
if (result != null) {
|
||||
return new Answer(cmd, false,
|
||||
"Failed to remove bitmap " + checkpointId + " from disk " + diskPath + ": " + result);
|
||||
}
|
||||
}
|
||||
return new Answer(cmd, true, "Checkpoint bitmap removed from disks");
|
||||
}
|
||||
}
|
||||
|
|
@ -1712,6 +1712,7 @@ public class ServerAdapter extends ManagerBase {
|
|||
DeleteVmCheckpointCmd cmd = new DeleteVmCheckpointCmd();
|
||||
ComponentContext.inject(cmd);
|
||||
cmd.setVmId(vo.getId());
|
||||
cmd.setCheckpointId(checkpointId);
|
||||
kvmBackupExportService.deleteVmCheckpoint(cmd);
|
||||
} catch (Exception e) {
|
||||
throw new CloudRuntimeException("Failed to delete checkpoint: " + e.getMessage(), e);
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@ import com.cloud.user.User;
|
|||
import com.cloud.utils.NumbersUtil;
|
||||
import com.cloud.utils.component.ManagerBase;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.cloud.vm.VMInstanceDetailVO;
|
||||
import com.cloud.vm.VMInstanceVO;
|
||||
import com.cloud.vm.VirtualMachine.State;
|
||||
import com.cloud.vm.VmDetailConstants;
|
||||
|
|
@ -203,6 +204,15 @@ public class KVMBackupExportServiceImpl extends ManagerBase implements KVMBackup
|
|||
}
|
||||
long hostId = backup.getHostId();
|
||||
|
||||
VMInstanceDetailVO lastCheckpointId = vmInstanceDetailsDao.findDetail(vmId, VmDetailConstants.LAST_CHECKPOINT_ID);
|
||||
if (lastCheckpointId != null) {
|
||||
try {
|
||||
sendDeleteCheckpointCommand(vm, lastCheckpointId.getValue());
|
||||
} catch (CloudRuntimeException e) {
|
||||
logger.warn("Failed to delete last checkpoint {} for VM {}, proceeding with backup start", lastCheckpointId.getValue(), vmId, e);
|
||||
}
|
||||
}
|
||||
|
||||
Host host = hostDao.findById(hostId);
|
||||
Map<String, String> vmDetails = vmInstanceDetailsDao.listDetailsKeyPairs(vmId);
|
||||
String activeCkpCreateTimeStr = vmDetails.get(VmDetailConstants.ACTIVE_CHECKPOINT_CREATE_TIME);
|
||||
|
|
@ -724,9 +734,39 @@ public class KVMBackupExportServiceImpl extends ManagerBase implements KVMBackup
|
|||
return responses;
|
||||
}
|
||||
|
||||
private void sendDeleteCheckpointCommand(VMInstanceVO vm, String checkpointId) {
|
||||
Long hostId = vm.getHostId() != null ? vm.getHostId() : vm.getLastHostId();
|
||||
|
||||
Map<String, String> diskPathUuidMap = new HashMap<>();
|
||||
if (vm.getState() == State.Stopped) {
|
||||
List<VolumeVO> volumes = volumeDao.findByInstance(vm.getId());
|
||||
for (Volume vol : volumes) {
|
||||
diskPathUuidMap.put(getVolumePathForFileBasedBackend(vol), vol.getUuid());
|
||||
}
|
||||
}
|
||||
|
||||
DeleteVmCheckpointCommand deleteCmd = new DeleteVmCheckpointCommand(
|
||||
vm.getInstanceName(),
|
||||
checkpointId,
|
||||
diskPathUuidMap,
|
||||
vm.getState() == State.Stopped);
|
||||
|
||||
Answer answer;
|
||||
try {
|
||||
answer = agentManager.send(hostId, deleteCmd);
|
||||
} catch (AgentUnavailableException | OperationTimedoutException e) {
|
||||
logger.error("Failed to communicate with agent to delete checkpoint for VM {}", vm.getId(), e);
|
||||
throw new CloudRuntimeException("Failed to communicate with agent", e);
|
||||
}
|
||||
|
||||
if (answer == null || !answer.getResult()) {
|
||||
String err = answer != null ? answer.getDetails() : "null answer";
|
||||
throw new CloudRuntimeException("Failed to delete checkpoint: " + err);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deleteVmCheckpoint(DeleteVmCheckpointCmd cmd) {
|
||||
// Todo : backend support?
|
||||
VMInstanceVO vm = vmInstanceDao.findById(cmd.getVmId());
|
||||
if (vm == null) {
|
||||
throw new CloudRuntimeException("VM not found: " + cmd.getVmId());
|
||||
|
|
@ -736,12 +776,38 @@ public class KVMBackupExportServiceImpl extends ManagerBase implements KVMBackup
|
|||
" backup provider. Either set backup.framework.enabled to false or set the Zone level config backup.framework.provider.plugin to \"dummy\".");
|
||||
}
|
||||
|
||||
if (vm.getState() != State.Running && vm.getState() != State.Stopped) {
|
||||
throw new CloudRuntimeException("VM must be running or stopped to delete checkpoint");
|
||||
}
|
||||
|
||||
long vmId = cmd.getVmId();
|
||||
vmInstanceDetailsDao.removeDetail(vmId, VmDetailConstants.ACTIVE_CHECKPOINT_ID);
|
||||
vmInstanceDetailsDao.removeDetail(vmId, VmDetailConstants.ACTIVE_CHECKPOINT_CREATE_TIME);
|
||||
Map<String, String> details = vmInstanceDetailsDao.listDetailsKeyPairs(vmId);
|
||||
String activeCheckpointId = details.get(VmDetailConstants.ACTIVE_CHECKPOINT_ID);
|
||||
if (activeCheckpointId == null || !activeCheckpointId.equals(cmd.getCheckpointId())) {
|
||||
logger.error("Checkpoint ID {} to delete does not match active checkpoint ID for VM {}", cmd.getCheckpointId(), vmId);
|
||||
return true;
|
||||
}
|
||||
|
||||
sendDeleteCheckpointCommand(vm, activeCheckpointId);
|
||||
revertVmCheckpointDetailsAfterActiveDelete(vmId, details);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void revertVmCheckpointDetailsAfterActiveDelete(long vmId, Map<String, String> detailsBeforeDelete) {
|
||||
String lastId = detailsBeforeDelete.get(VmDetailConstants.LAST_CHECKPOINT_ID);
|
||||
String lastTime = detailsBeforeDelete.get(VmDetailConstants.LAST_CHECKPOINT_CREATE_TIME);
|
||||
if (lastId != null) {
|
||||
vmInstanceDetailsDao.addDetail(vmId, VmDetailConstants.ACTIVE_CHECKPOINT_ID, lastId, false);
|
||||
vmInstanceDetailsDao.addDetail(vmId, VmDetailConstants.ACTIVE_CHECKPOINT_CREATE_TIME, lastTime, false);
|
||||
vmInstanceDetailsDao.removeDetail(vmId, VmDetailConstants.LAST_CHECKPOINT_ID);
|
||||
vmInstanceDetailsDao.removeDetail(vmId, VmDetailConstants.LAST_CHECKPOINT_CREATE_TIME);
|
||||
} else {
|
||||
vmInstanceDetailsDao.removeDetail(vmId, VmDetailConstants.ACTIVE_CHECKPOINT_ID);
|
||||
vmInstanceDetailsDao.removeDetail(vmId, VmDetailConstants.ACTIVE_CHECKPOINT_CREATE_TIME);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Class<?>> getCommands() {
|
||||
List<Class<?>> cmdList = new ArrayList<>();
|
||||
|
|
|
|||
Loading…
Reference in New Issue