CLOUDSTACK-3237: acknowledge the behind-back VMDK disk consolidation happend in vCenter after storage live migration
Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/834fdc88 Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/834fdc88 Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/834fdc88 Branch: refs/heads/4.2 Commit: 834fdc8859a92989a818fac2f24af97c87d5ed55 Parents: d9016635 Author: Kelven Yang <[email protected]> Authored: Fri Aug 23 17:38:17 2013 -0700 Committer: Kelven Yang <[email protected]> Committed: Fri Aug 23 17:38:34 2013 -0700 ---------------------------------------------------------------------- .../storage/snapshot/SnapshotObject.java | 14 ++++ .../manager/VmwareStorageManagerImpl.java | 3 +- .../resource/VmwareStorageProcessor.java | 84 +++++++++++++++++--- .../hypervisor/vmware/mo/VirtualMachineMO.java | 29 ++++++- 4 files changed, 115 insertions(+), 15 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cloudstack/blob/834fdc88/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotObject.java ---------------------------------------------------------------------- diff --git a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotObject.java b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotObject.java index 3d67d38..e69881c 100644 --- a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotObject.java +++ b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotObject.java @@ -38,6 +38,7 @@ import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.storage.DataStoreRole; import com.cloud.storage.Snapshot; import com.cloud.storage.SnapshotVO; +import com.cloud.storage.VolumeVO; import com.cloud.storage.dao.SnapshotDao; import com.cloud.storage.dao.VolumeDao; import com.cloud.utils.component.ComponentContext; @@ -262,6 +263,19 @@ public class SnapshotObject implements SnapshotInfo { snapshotStore.setParentSnapshotId(0L); } this.snapshotStoreDao.update(snapshotStore.getId(), snapshotStore); + + // update side-effect of snapshot operation + if(snapshotTO.getVolume().getPath() != null) { + VolumeVO vol = this.volumeDao.findByUuid(snapshotTO.getVolume().getUuid()); + if(vol != null) { + s_logger.info("Update volume path change due to snapshot operation, volume " + vol.getId() + " path: " + + vol.getPath() + "->" + snapshotTO.getVolume().getPath()); + vol.setPath(snapshotTO.getVolume().getPath()); + this.volumeDao.update(vol.getId(), vol); + } else { + s_logger.error("Cound't find the original volume with uuid: " + snapshotTO.getVolume().getUuid()); + } + } } else { throw new CloudRuntimeException("Unknown answer: " + answer.getClass()); } http://git-wip-us.apache.org/repos/asf/cloudstack/blob/834fdc88/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageManagerImpl.java ---------------------------------------------------------------------- diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageManagerImpl.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageManagerImpl.java index e7fbb04..955b111 100644 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageManagerImpl.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageManagerImpl.java @@ -292,6 +292,7 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager { } @Override + @Deprecated public Answer execute(VmwareHostService hostService, BackupSnapshotCommand cmd) { Long accountId = cmd.getAccountId(); Long volumeId = cmd.getVolumeId(); @@ -362,7 +363,7 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager { } finally { if(vmMo != null){ ManagedObjectReference snapshotMor = vmMo.getSnapshotMor(snapshotUuid); - if (snapshotMor != null){ + if (snapshotMor != null) { vmMo.removeSnapshot(snapshotUuid, false); } } http://git-wip-us.apache.org/repos/asf/cloudstack/blob/834fdc88/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageProcessor.java ---------------------------------------------------------------------- diff --git a/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageProcessor.java b/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageProcessor.java index 138f725..c2e55fb 100644 --- a/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageProcessor.java +++ b/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageProcessor.java @@ -58,6 +58,7 @@ import com.cloud.hypervisor.vmware.manager.VmwareStorageMount; import com.cloud.hypervisor.vmware.mo.ClusterMO; import com.cloud.hypervisor.vmware.mo.CustomFieldConstants; import com.cloud.hypervisor.vmware.mo.DatacenterMO; +import com.cloud.hypervisor.vmware.mo.DatastoreFile; import com.cloud.hypervisor.vmware.mo.DatastoreMO; import com.cloud.hypervisor.vmware.mo.HostMO; import com.cloud.hypervisor.vmware.mo.HypervisorHostHelper; @@ -949,7 +950,8 @@ public class VmwareStorageProcessor implements StorageProcessor { } } - private void exportVolumeToSecondaryStroage(VirtualMachineMO vmMo, String volumePath, + // return Pair<String(divice bus name), String[](disk chain)> + private Pair<String, String[]> exportVolumeToSecondaryStroage(VirtualMachineMO vmMo, String volumePath, String secStorageUrl, String secStorageDir, String exportName, String workerVmName) throws Exception { @@ -978,7 +980,7 @@ public class VmwareStorageProcessor implements StorageProcessor { } // 4 MB is the minimum requirement for VM memory in VMware - vmMo.cloneFromCurrentSnapshot(workerVmName, 0, 4, volumeDeviceInfo.second(), + String disks[] = vmMo.cloneFromCurrentSnapshot(workerVmName, 0, 4, volumeDeviceInfo.second(), VmwareHelper.getDiskDeviceDatastore(volumeDeviceInfo.first())); clonedVm = vmMo.getRunningHost().findVmOnHyperHost(workerVmName); if(clonedVm == null) { @@ -988,6 +990,8 @@ public class VmwareStorageProcessor implements StorageProcessor { } clonedVm.exportVm(exportPath, exportName, false, false); + + return new Pair<String, String[]>(volumeDeviceInfo.second(), disks); } finally { if(clonedVm != null) { clonedVm.detachAllDisks(); @@ -996,15 +1000,15 @@ public class VmwareStorageProcessor implements StorageProcessor { } } - - private String backupSnapshotToSecondaryStorage(VirtualMachineMO vmMo, String installPath, + // Ternary<String(backup uuid in secondary storage), String(device bus name), String[](original disk chain in the snapshot)> + private Ternary<String, String, String[]> backupSnapshotToSecondaryStorage(VirtualMachineMO vmMo, String installPath, String volumePath, String snapshotUuid, String secStorageUrl, String prevSnapshotUuid, String prevBackupUuid, String workerVmName) throws Exception { String backupUuid = UUID.randomUUID().toString(); - exportVolumeToSecondaryStroage(vmMo, volumePath, secStorageUrl, + Pair<String, String[]> snapshotInfo = exportVolumeToSecondaryStroage(vmMo, volumePath, secStorageUrl, installPath, backupUuid, workerVmName); - return backupUuid + "/" + backupUuid; + return new Ternary<String, String, String[]>(backupUuid + "/" + backupUuid, snapshotInfo.first(), snapshotInfo.second()); } @Override @@ -1033,6 +1037,9 @@ public class VmwareStorageProcessor implements StorageProcessor { 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; @@ -1041,6 +1048,8 @@ public class VmwareStorageProcessor implements StorageProcessor { VmwareHypervisorHost hyperHost = hostService.getHyperHost(context, cmd); morDs = HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(hyperHost, primaryStore.getUuid()); + CopyCmdAnswer answer = null; + try { if (vmName != null) { vmMo = hyperHost.findVmOnHyperHost(vmName); @@ -1067,31 +1076,84 @@ public class VmwareStorageProcessor implements StorageProcessor { // 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); } - snapshotBackupUuid = backupSnapshotToSecondaryStorage(vmMo, destSnapshot.getPath(), srcSnapshot.getVolume().getPath(), snapshotUuid, secondaryStorageUrl, prevSnapshotUuid, prevBackupUuid, + 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."; - return new CopyCmdAnswer(details); + 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); - return new CopyCmdAnswer(newSnapshot); + answer = new CopyCmdAnswer(newSnapshot); } } finally { if(vmMo != null){ ManagedObjectReference snapshotMor = vmMo.getSnapshotMor(snapshotUuid); - if (snapshotMor != null){ + 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 ?!"); } } @@ -1105,6 +1167,8 @@ public class VmwareStorageProcessor implements StorageProcessor { s_logger.warn("Failed to destroy worker VM: " + workerVMName); } } + + return answer; } catch (Throwable e) { if (e instanceof RemoteException) { hostService.invalidateServiceContext(context); http://git-wip-us.apache.org/repos/asf/cloudstack/blob/834fdc88/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java ---------------------------------------------------------------------- diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java b/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java index 8e3df50..6a16ffa 100644 --- a/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java +++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java @@ -1551,11 +1551,13 @@ public class VirtualMachineMO extends BaseMO { return diskDsFullPaths.toArray(new String[0]); } - public void cloneFromCurrentSnapshot(String clonedVmName, int cpuSpeedMHz, int memoryMb, String diskDevice, + // return the disk chain (VMDK datastore paths) for cloned snapshot + public String[] cloneFromCurrentSnapshot(String clonedVmName, int cpuSpeedMHz, int memoryMb, String diskDevice, ManagedObjectReference morDs) throws Exception { assert(morDs != null); String[] disks = getCurrentSnapshotDiskChainDatastorePaths(diskDevice); cloneFromDiskChain(clonedVmName, cpuSpeedMHz, memoryMb, disks, morDs); + return disks; } public void cloneFromDiskChain(String clonedVmName, int cpuSpeedMHz, int memoryMb, @@ -1576,7 +1578,6 @@ public class VirtualMachineMO extends BaseMO { boolean bSuccess = false; try { VirtualMachineConfigSpec vmConfigSpec = new VirtualMachineConfigSpec(); - //VirtualDeviceConfigSpec[] deviceConfigSpecArray = new VirtualDeviceConfigSpec[1]; VirtualDeviceConfigSpec deviceConfigSpec = new VirtualDeviceConfigSpec(); VirtualDevice device = VmwareHelper.prepareDiskDevice(clonedVmMo, -1, disks, morDs, -1, 1); @@ -1611,7 +1612,6 @@ public class VirtualMachineMO extends BaseMO { public void plugDevice(VirtualDevice device) throws Exception { VirtualMachineConfigSpec vmConfigSpec = new VirtualMachineConfigSpec(); - //VirtualDeviceConfigSpec[] deviceConfigSpecArray = new VirtualDeviceConfigSpec[1]; VirtualDeviceConfigSpec deviceConfigSpec = new VirtualDeviceConfigSpec(); deviceConfigSpec.setDevice(device); deviceConfigSpec.setOperation(VirtualDeviceConfigSpecOperation.ADD); @@ -1624,7 +1624,6 @@ public class VirtualMachineMO extends BaseMO { public void tearDownDevice(VirtualDevice device) throws Exception { VirtualMachineConfigSpec vmConfigSpec = new VirtualMachineConfigSpec(); - //VirtualDeviceConfigSpec[] deviceConfigSpecArray = new VirtualDeviceConfigSpec[1]; VirtualDeviceConfigSpec deviceConfigSpec = new VirtualDeviceConfigSpec(); deviceConfigSpec.setDevice(device); deviceConfigSpec.setOperation(VirtualDeviceConfigSpecOperation.REMOVE); @@ -1826,6 +1825,28 @@ public class VirtualMachineMO extends BaseMO { return null; } + + public String getDiskCurrentTopBackingFileInChain(String deviceBusName) throws Exception { + List<VirtualDevice> devices = (List<VirtualDevice>)_context.getVimClient().getDynamicProperty(_mor, "config.hardware.device"); + if(devices != null && devices.size() > 0) { + for(VirtualDevice device : devices) { + if(device instanceof VirtualDisk) { + s_logger.info("Test against disk device, controller key: " + device.getControllerKey() + ", unit number: " + device.getUnitNumber()); + + VirtualDeviceBackingInfo backingInfo = ((VirtualDisk)device).getBacking(); + if(backingInfo instanceof VirtualDiskFlatVer2BackingInfo) { + VirtualDiskFlatVer2BackingInfo diskBackingInfo = (VirtualDiskFlatVer2BackingInfo)backingInfo; + + String deviceNumbering = getDeviceBusName(devices, device); + if(deviceNumbering.equals(deviceBusName)) + return diskBackingInfo.getFileName(); + } + } + } + } + + return null; + } @Deprecated public List<Pair<String, ManagedObjectReference>> getDiskDatastorePathChain(VirtualDisk disk, boolean followChain) throws Exception {
