SnapshotObjectTO srcSnapshot = (SnapshotObjectTO)cmd.getSrcTO();
PrimaryDataStoreTO primaryStore = (PrimaryDataStoreTO)srcSnapshot.getDataStore();
SnapshotObjectTO destSnapshot = (SnapshotObjectTO)cmd.getDestTO();
DataStoreTO destStore = destSnapshot.getDataStore();
if (!(destStore instanceof NfsTO)) {
return new CopyCmdAnswer("unsupported protocol");
}
NfsTO destNfsStore = (NfsTO)destStore;
String secondaryStorageUrl = destNfsStore.getUrl();
String snapshotUuid = srcSnapshot.getPath();
String prevSnapshotUuid = srcSnapshot.getParentSnapshotPath();
String prevBackupUuid = destSnapshot.getParentSnapshotPath();
VirtualMachineMO workerVm=null;
String workerVMName = null;
String volumePath = srcSnapshot.getVolume().getPath();
ManagedObjectReference morDs = null;
DatastoreMO dsMo=null;
// By default assume failure
String details = null;
boolean success = false;
String snapshotBackupUuid = null;
boolean hasOwnerVm = false;
Ternary<String, String, String[]> backupResult = null;
VmwareContext context = hostService.getServiceContext(cmd);
VirtualMachineMO vmMo = null;
String vmName = srcSnapshot.getVmName();
try {
VmwareHypervisorHost hyperHost = hostService.getHyperHost(context, cmd);
morDs = HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(hyperHost, primaryStore.getUuid());
CopyCmdAnswer answer = null;
try {
if (vmName != null) {
vmMo = hyperHost.findVmOnHyperHost(vmName);
}
if (vmMo == null) {
if(s_logger.isDebugEnabled()) {
s_logger.debug("Unable to find owner VM for BackupSnapshotCommand on host " + hyperHost.getHyperHostName() + ", will try within datacenter");
}
if (vmName != null) {
vmMo = hyperHost.findVmOnPeerHyperHost(vmName);
}
if(vmMo == null) {
dsMo = new DatastoreMO(hyperHost.getContext(), morDs);
workerVMName = hostService.getWorkerName(context, cmd, 0);
vmMo = HypervisorHostHelper.createWorkerVM(hyperHost, dsMo, workerVMName);
if (vmMo == null) {
throw new Exception("Failed to find the newly create or relocated VM. vmName: " + workerVMName);
}
workerVm = vmMo;
// attach volume to worker VM
String datastoreVolumePath = dsMo.getDatastorePath(volumePath + ".vmdk");
vmMo.attachDisk(new String[] { datastoreVolumePath }, morDs);
} else {
s_logger.info("Using owner VM " + vmName + " for snapshot operation");
hasOwnerVm = true;
}
} else {
s_logger.info("Using owner VM " + vmName + " for snapshot operation");
hasOwnerVm = true;
}
if (!vmMo.createSnapshot(snapshotUuid, "Snapshot taken for " + srcSnapshot.getName(), false, false)) {
throw new Exception("Failed to take snapshot " + srcSnapshot.getName() + " on vm: " + vmName);
}
backupResult = backupSnapshotToSecondaryStorage(vmMo, destSnapshot.getPath(), srcSnapshot.getVolume().getPath(), snapshotUuid, secondaryStorageUrl, prevSnapshotUuid, prevBackupUuid,
hostService.getWorkerName(context, cmd, 1));
snapshotBackupUuid = backupResult.first();
success = (snapshotBackupUuid != null);
if (!success) {
details = "Failed to backUp the snapshot with uuid: " + snapshotUuid + " to secondary storage.";
answer = new CopyCmdAnswer(details);
} else {
details = "Successfully backedUp the snapshot with Uuid: " + snapshotUuid + " to secondary storage.";
SnapshotObjectTO newSnapshot = new SnapshotObjectTO();
newSnapshot.setPath(destSnapshot.getPath() + "/" + snapshotBackupUuid);
answer = new CopyCmdAnswer(newSnapshot);
}
} finally {
if(vmMo != null){
ManagedObjectReference snapshotMor = vmMo.getSnapshotMor(snapshotUuid);
if (snapshotMor != null) {
vmMo.removeSnapshot(snapshotUuid, false);
// Snapshot operation may cause disk consolidation in VMware, when this happens
// we need to update CloudStack DB
//
// TODO: this post operation fixup is not atomic and not safe when management server stops
// in the middle
if(backupResult != null && hasOwnerVm) {
s_logger.info("Check if we have disk consolidation after snapshot operation");
boolean chainConsolidated = false;
for(String vmdkDsFilePath : backupResult.third()) {
s_logger.info("Validate disk chain file:" + vmdkDsFilePath);
if(vmMo.getDiskDevice(vmdkDsFilePath, false) == null) {
s_logger.info("" + vmdkDsFilePath + " no longer exists, consolidation detected");
chainConsolidated = true;
break;
} else {
s_logger.info("" + vmdkDsFilePath + " is found still in chain");
}
}
if(chainConsolidated) {
String topVmdkFilePath = null;
try {
topVmdkFilePath = vmMo.getDiskCurrentTopBackingFileInChain(backupResult.second());
} catch(Exception e) {
s_logger.error("Unexpected exception", e);
}
s_logger.info("Disk has been consolidated, top VMDK is now: " + topVmdkFilePath);
if(topVmdkFilePath != null) {
DatastoreFile file = new DatastoreFile(topVmdkFilePath);
SnapshotObjectTO snapshotInfo = (SnapshotObjectTO)answer.getNewData();
VolumeObjectTO vol = new VolumeObjectTO();
vol.setUuid(srcSnapshot.getVolume().getUuid());
vol.setPath(file.getFileBaseName());
snapshotInfo.setVolume(vol);
} else {
s_logger.error("Disk has been consolidated, but top VMDK is not found ?!");
}
}
}
} else {
s_logger.error("Can not find the snapshot we just used ?!");
}
}
try {
if (workerVm != null) {
// detach volume and destroy worker vm
workerVm.detachAllDisks();
workerVm.destroy();
}
} catch (Throwable e) {
s_logger.warn("Failed to destroy worker VM: " + workerVMName);
}
}
return answer;
} catch (Throwable e) {
if (e instanceof RemoteException) {
hostService.invalidateServiceContext(context);
}
s_logger.error("Unexpecpted exception ", e);
details = "backup snapshot exception: " + VmwareHelper.getExceptionMessage(e);
return new CopyCmdAnswer(details);
}
}