diff --git a/core/src/main/java/org/apache/cloudstack/storage/to/PrimaryDataStoreTO.java b/core/src/main/java/org/apache/cloudstack/storage/to/PrimaryDataStoreTO.java index 9df2a6c955b..a6a74176c13 100644 --- a/core/src/main/java/org/apache/cloudstack/storage/to/PrimaryDataStoreTO.java +++ b/core/src/main/java/org/apache/cloudstack/storage/to/PrimaryDataStoreTO.java @@ -55,6 +55,8 @@ public class PrimaryDataStoreTO implements DataStoreTO { private Boolean diskProvisioningStrictnessFlag; private final boolean isManaged; + private final StoragePoolType parentPoolType; + public PrimaryDataStoreTO(PrimaryDataStore dataStore) { this.uuid = dataStore.getUuid(); this.name = dataStore.getName(); @@ -66,6 +68,7 @@ public class PrimaryDataStoreTO implements DataStoreTO { this.url = dataStore.getUri(); this.details = dataStore.getDetails(); this.isManaged = dataStore.isManaged(); + this.parentPoolType = dataStore.getParentPoolType(); } public long getId() { @@ -172,4 +175,8 @@ public class PrimaryDataStoreTO implements DataStoreTO { public void setDiskProvisioningStrictnessFlag(Boolean diskProvisioningStrictnessFlag) { this.diskProvisioningStrictnessFlag = diskProvisioningStrictnessFlag; } + + public StoragePoolType getParentPoolType() { + return parentPoolType; + } } diff --git a/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStore.java b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStore.java index 5546571ba6b..718f63e77f5 100644 --- a/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStore.java +++ b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStore.java @@ -22,6 +22,8 @@ import java.util.List; import org.apache.cloudstack.engine.subsystem.api.storage.disktype.DiskFormat; +import com.cloud.storage.Storage; + public interface PrimaryDataStore extends DataStore, PrimaryDataStoreInfo { DataObject create(DataObject dataObject, String configuration); @@ -38,4 +40,6 @@ public interface PrimaryDataStore extends DataStore, PrimaryDataStoreInfo { SnapshotInfo getSnapshot(long snapshotId); DiskFormat getDefaultDiskType(); + + Storage.StoragePoolType getParentPoolType(); } diff --git a/engine/storage/snapshot/src/main/java/org/apache/cloudstack/storage/vmsnapshot/DefaultVMSnapshotStrategy.java b/engine/storage/snapshot/src/main/java/org/apache/cloudstack/storage/vmsnapshot/DefaultVMSnapshotStrategy.java index 90f6ccd1f9b..e2815c005c4 100644 --- a/engine/storage/snapshot/src/main/java/org/apache/cloudstack/storage/vmsnapshot/DefaultVMSnapshotStrategy.java +++ b/engine/storage/snapshot/src/main/java/org/apache/cloudstack/storage/vmsnapshot/DefaultVMSnapshotStrategy.java @@ -25,14 +25,14 @@ import java.util.Map; import javax.inject.Inject; import javax.naming.ConfigurationException; -import com.cloud.event.UsageEventVO; -import org.apache.log4j.Logger; - import org.apache.cloudstack.engine.subsystem.api.storage.StrategyPriority; import org.apache.cloudstack.engine.subsystem.api.storage.VMSnapshotOptions; import org.apache.cloudstack.engine.subsystem.api.storage.VMSnapshotStrategy; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.to.VolumeObjectTO; +import org.apache.commons.lang3.StringUtils; +import org.apache.log4j.Logger; import com.cloud.agent.AgentManager; import com.cloud.agent.api.Answer; @@ -45,6 +45,7 @@ import com.cloud.agent.api.RevertToVMSnapshotCommand; import com.cloud.agent.api.VMSnapshotTO; import com.cloud.event.EventTypes; import com.cloud.event.UsageEventUtils; +import com.cloud.event.UsageEventVO; import com.cloud.exception.AgentUnavailableException; import com.cloud.exception.OperationTimedoutException; import com.cloud.host.HostVO; @@ -53,6 +54,7 @@ import com.cloud.storage.DiskOfferingVO; import com.cloud.storage.GuestOSHypervisorVO; import com.cloud.storage.GuestOSVO; import com.cloud.storage.Storage.ImageFormat; +import com.cloud.storage.StoragePool; import com.cloud.storage.VolumeVO; import com.cloud.storage.dao.DiskOfferingDao; import com.cloud.storage.dao.GuestOSDao; @@ -97,6 +99,8 @@ public class DefaultVMSnapshotStrategy extends ManagerBase implements VMSnapshot DiskOfferingDao diskOfferingDao; @Inject HostDao hostDao; + @Inject + PrimaryDataStoreDao primaryDataStoreDao; @Override public boolean configure(String name, Map params) throws ConfigurationException { @@ -323,14 +327,26 @@ public class DefaultVMSnapshotStrategy extends ManagerBase implements VMSnapshot vmSnapshotDao.persist(vmSnapshot); } - private void updateVolumePath(List volumeTOs) { + protected void updateVolumePath(List volumeTOs) { for (VolumeObjectTO volume : volumeTOs) { - if (volume.getPath() != null) { - VolumeVO volumeVO = volumeDao.findById(volume.getId()); - volumeVO.setPath(volume.getPath()); - volumeVO.setVmSnapshotChainSize(volume.getSize()); - volumeDao.persist(volumeVO); + if (StringUtils.isAllEmpty(volume.getDataStoreUuid(), volume.getPath(), volume.getChainInfo())) { + continue; } + VolumeVO volumeVO = volumeDao.findById(volume.getId()); + if (StringUtils.isNotEmpty(volume.getDataStoreUuid())) { + StoragePool pool = primaryDataStoreDao.findPoolByUUID(volume.getDataStoreUuid()); + if (pool != null && pool.getId() != volumeVO.getPoolId()) { + volumeVO.setPoolId(pool.getId()); + } + } + if (StringUtils.isNotEmpty(volume.getPath())) { + volumeVO.setPath(volume.getPath()); + } + if (StringUtils.isNotEmpty(volume.getChainInfo())) { + volumeVO.setChainInfo(volume.getChainInfo()); + } + volumeVO.setVmSnapshotChainSize(volume.getSize()); + volumeDao.persist(volumeVO); } } diff --git a/engine/storage/snapshot/src/test/java/org/apache/cloudstack/storage/vmsnapshot/DefaultVMSnapshotStrategyTest.java b/engine/storage/snapshot/src/test/java/org/apache/cloudstack/storage/vmsnapshot/DefaultVMSnapshotStrategyTest.java new file mode 100644 index 00000000000..da377f96ec3 --- /dev/null +++ b/engine/storage/snapshot/src/test/java/org/apache/cloudstack/storage/vmsnapshot/DefaultVMSnapshotStrategyTest.java @@ -0,0 +1,106 @@ +/* + * 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 org.apache.cloudstack.storage.vmsnapshot; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; +import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; +import org.apache.cloudstack.storage.to.VolumeObjectTO; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; +import org.mockito.stubbing.Answer; + +import com.cloud.storage.Storage; +import com.cloud.storage.Volume; +import com.cloud.storage.VolumeVO; +import com.cloud.storage.dao.VolumeDao; + +@RunWith(MockitoJUnitRunner.class) +public class DefaultVMSnapshotStrategyTest { + @Mock + VolumeDao volumeDao; + @Mock + PrimaryDataStoreDao primaryDataStoreDao; + + @Spy + @InjectMocks + private final DefaultVMSnapshotStrategy defaultVMSnapshotStrategy = new DefaultVMSnapshotStrategy(); + + protected List persistedVolumes = new ArrayList<>(); + + + private void setupVolumeDaoPersistMock() { + persistedVolumes.clear(); + Mockito.when(volumeDao.persist(Mockito.any())).thenAnswer((Answer) invocation -> { + VolumeVO volume = (VolumeVO)invocation.getArguments()[0]; + persistedVolumes.add(volume); + return volume; + }); + } + + @Test + public void testUpdateVolumePath() { + setupVolumeDaoPersistMock(); + VolumeObjectTO vol1 = Mockito.mock(VolumeObjectTO.class); + Mockito.when(vol1.getDataStoreUuid()).thenReturn(null); + Mockito.when(vol1.getPath()).thenReturn(null); + Mockito.when(vol1.getChainInfo()).thenReturn(null); + VolumeObjectTO vol2 = Mockito.mock(VolumeObjectTO.class); + Long volumeId = 1L; + String newDSUuid = UUID.randomUUID().toString(); + String oldVolPath = "old"; + String newVolPath = "new"; + String oldVolChain = "old-chain"; + String newVolChain = "new-chain"; + Long vmSnapshotChainSize = 1000L; + Long oldPoolId = 1L; + Long newPoolId = 2L; + Mockito.when(vol2.getDataStoreUuid()).thenReturn(newDSUuid); + Mockito.when(vol2.getPath()).thenReturn(newVolPath); + Mockito.when(vol2.getChainInfo()).thenReturn(newVolChain); + Mockito.when(vol2.getSize()).thenReturn(vmSnapshotChainSize); + Mockito.when(vol2.getId()).thenReturn(volumeId); + VolumeVO volumeVO = new VolumeVO("name", 0l, 0l, 0l, 0l, 0l, "folder", "path", Storage.ProvisioningType.THIN, 0l, Volume.Type.ROOT); + volumeVO.setPoolId(oldPoolId); + volumeVO.setChainInfo(oldVolChain); + volumeVO.setPath(oldVolPath); + Mockito.when(volumeDao.findById(volumeId)).thenReturn(volumeVO); + StoragePoolVO storagePoolVO = Mockito.mock(StoragePoolVO.class); + Mockito.when(storagePoolVO.getId()).thenReturn(newPoolId); + Mockito.when(primaryDataStoreDao.findPoolByUUID(newDSUuid)).thenReturn(storagePoolVO); + Mockito.when(volumeDao.findById(volumeId)).thenReturn(volumeVO); + defaultVMSnapshotStrategy.updateVolumePath(List.of(vol1, vol2)); + Assert.assertEquals(1, persistedVolumes.size()); + VolumeVO persistedVolume = persistedVolumes.get(0); + Assert.assertNotNull(persistedVolume); + Assert.assertEquals(newPoolId, persistedVolume.getPoolId()); + Assert.assertEquals(newVolPath, persistedVolume.getPath()); + Assert.assertEquals(vmSnapshotChainSize, persistedVolume.getVmSnapshotChainSize()); + Assert.assertEquals(newVolChain, persistedVolume.getChainInfo()); + } +} diff --git a/engine/storage/snapshot/src/test/java/org/apache/cloudstack/storage/vmsnapshot/VMSnapshotStrategyTest.java b/engine/storage/snapshot/src/test/java/org/apache/cloudstack/storage/vmsnapshot/VMSnapshotStrategyTest.java index 4420c19d6a0..5fd6dee9083 100644 --- a/engine/storage/snapshot/src/test/java/org/apache/cloudstack/storage/vmsnapshot/VMSnapshotStrategyTest.java +++ b/engine/storage/snapshot/src/test/java/org/apache/cloudstack/storage/vmsnapshot/VMSnapshotStrategyTest.java @@ -27,6 +27,7 @@ import javax.inject.Inject; import org.apache.cloudstack.engine.subsystem.api.storage.VMSnapshotStrategy; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.to.VolumeObjectTO; import org.apache.cloudstack.test.utils.SpringUtils; import org.junit.Before; @@ -92,6 +93,8 @@ public class VMSnapshotStrategyTest extends TestCase { VMSnapshotDao vmSnapshotDao; @Inject HostDao hostDao; + @Inject + PrimaryDataStoreDao primaryDataStoreDao; @Override @Before @@ -304,5 +307,10 @@ public class VMSnapshotStrategyTest extends TestCase { public HostDao hostDao() { return Mockito.mock(HostDao.class); } + + @Bean + public PrimaryDataStoreDao primaryDataStoreDao() { + return Mockito.mock(PrimaryDataStoreDao.class); + } } } diff --git a/engine/storage/volume/src/main/java/org/apache/cloudstack/storage/datastore/PrimaryDataStoreImpl.java b/engine/storage/volume/src/main/java/org/apache/cloudstack/storage/datastore/PrimaryDataStoreImpl.java index 7f9f2bee993..cf0bca9cfe2 100644 --- a/engine/storage/volume/src/main/java/org/apache/cloudstack/storage/datastore/PrimaryDataStoreImpl.java +++ b/engine/storage/volume/src/main/java/org/apache/cloudstack/storage/datastore/PrimaryDataStoreImpl.java @@ -72,6 +72,7 @@ public class PrimaryDataStoreImpl implements PrimaryDataStore { protected PrimaryDataStoreDriver driver; protected StoragePoolVO pdsv; + protected StoragePoolVO parentStoragePool; @Inject protected PrimaryDataStoreDao dataStoreDao; protected PrimaryDataStoreLifeCycle lifeCycle; @@ -104,6 +105,9 @@ public class PrimaryDataStoreImpl implements PrimaryDataStore { this.provider = provider; this.uuid = pdsv.getUuid(); this.name = pdsv.getName(); + if (pdsv.getParent() != null && pdsv.getParent() > 0L) { + this.parentStoragePool = dataStoreDao.findById(pdsv.getParent()); + } } public static PrimaryDataStoreImpl createDataStore(StoragePoolVO pdsv, PrimaryDataStoreDriver driver, DataStoreProvider provider) { @@ -452,4 +456,12 @@ public class PrimaryDataStoreImpl implements PrimaryDataStore { } return to; } + + @Override + public StoragePoolType getParentPoolType() { + if (this.parentStoragePool != null) { + return this.parentStoragePool.getPoolType(); + } + return null; + } } diff --git a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/manager/VmwareHostService.java b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/manager/VmwareHostService.java index d9ee3c5f5ca..4f680963035 100644 --- a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/manager/VmwareHostService.java +++ b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/manager/VmwareHostService.java @@ -20,6 +20,7 @@ import com.cloud.agent.api.Command; import com.cloud.hypervisor.vmware.mo.DatastoreMO; import com.cloud.hypervisor.vmware.mo.VmwareHypervisorHost; import com.cloud.hypervisor.vmware.util.VmwareContext; +import com.cloud.storage.resource.VmwareStorageProcessor; public interface VmwareHostService { VmwareContext getServiceContext(Command cmd); @@ -31,4 +32,6 @@ public interface VmwareHostService { String getWorkerName(VmwareContext context, Command cmd, int workerSequence, DatastoreMO dsMo) throws Exception; String createLogMessageException(Throwable e, Command command); + + VmwareStorageProcessor getStorageProcessor(); } diff --git a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/manager/VmwareStorageManagerImpl.java b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/manager/VmwareStorageManagerImpl.java index 3ed9ffffb06..1f57e00979d 100644 --- a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/manager/VmwareStorageManagerImpl.java +++ b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/manager/VmwareStorageManagerImpl.java @@ -28,20 +28,10 @@ import java.util.Map; import java.util.Properties; import java.util.UUID; -import org.apache.log4j.Logger; - -import com.cloud.hypervisor.vmware.mo.DatastoreFile; -import com.vmware.vim25.FileInfo; -import com.vmware.vim25.FileQueryFlags; -import com.vmware.vim25.HostDatastoreBrowserSearchResults; -import com.vmware.vim25.HostDatastoreBrowserSearchSpec; -import com.vmware.vim25.ManagedObjectReference; -import com.vmware.vim25.TaskInfo; -import com.vmware.vim25.TaskInfoState; -import com.vmware.vim25.VirtualDisk; - +import org.apache.cloudstack.storage.to.PrimaryDataStoreTO; import org.apache.cloudstack.storage.to.TemplateObjectTO; import org.apache.cloudstack.storage.to.VolumeObjectTO; +import org.apache.log4j.Logger; import com.cloud.agent.api.Answer; import com.cloud.agent.api.BackupSnapshotAnswer; @@ -65,10 +55,12 @@ import com.cloud.agent.api.storage.PrimaryStorageDownloadCommand; import com.cloud.agent.api.to.DataObjectType; import com.cloud.agent.api.to.DataStoreTO; import com.cloud.agent.api.to.DataTO; +import com.cloud.agent.api.to.DiskTO; import com.cloud.agent.api.to.NfsTO; import com.cloud.agent.api.to.StorageFilerTO; 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.HostDatastoreBrowserMO; import com.cloud.hypervisor.vmware.mo.HostMO; @@ -78,9 +70,11 @@ import com.cloud.hypervisor.vmware.mo.VmwareHypervisorHost; import com.cloud.hypervisor.vmware.util.VmwareContext; import com.cloud.hypervisor.vmware.util.VmwareHelper; import com.cloud.storage.JavaStorageLayer; +import com.cloud.storage.Storage; import com.cloud.storage.Storage.ImageFormat; import com.cloud.storage.StorageLayer; import com.cloud.storage.Volume; +import com.cloud.storage.resource.VmwareStorageProcessor; import com.cloud.storage.template.OVAProcessor; import com.cloud.utils.NumbersUtil; import com.cloud.utils.Pair; @@ -89,6 +83,14 @@ import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.script.Script; import com.cloud.vm.VirtualMachine; import com.cloud.vm.snapshot.VMSnapshot; +import com.vmware.vim25.FileInfo; +import com.vmware.vim25.FileQueryFlags; +import com.vmware.vim25.HostDatastoreBrowserSearchResults; +import com.vmware.vim25.HostDatastoreBrowserSearchSpec; +import com.vmware.vim25.ManagedObjectReference; +import com.vmware.vim25.TaskInfo; +import com.vmware.vim25.TaskInfoState; +import com.vmware.vim25.VirtualDisk; public class VmwareStorageManagerImpl implements VmwareStorageManager { @@ -1134,6 +1136,28 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager { return size; } + private boolean isVolumeOnDatastoreCluster(VolumeObjectTO volumeObjectTO) { + DataStoreTO dsTO = volumeObjectTO.getDataStore(); + if (!(dsTO instanceof PrimaryDataStoreTO)) { + return false; + } + PrimaryDataStoreTO primaryDataStoreTO = (PrimaryDataStoreTO)dsTO; + return Storage.StoragePoolType.DatastoreCluster.equals(primaryDataStoreTO.getPoolType()) || + Storage.StoragePoolType.DatastoreCluster.equals(primaryDataStoreTO.getParentPoolType()); + } + + private void syncVolume(VmwareHostService hostService, VirtualMachineMO virtualMachineMO, VmwareContext context, + VmwareHypervisorHost hypervisorHost, VolumeObjectTO volumeTO) throws Exception { + if (hostService.getStorageProcessor() == null) return; + VmwareStorageProcessor storageProcessor = hostService.getStorageProcessor(); + DiskTO disk = new DiskTO(); + Map map = new HashMap<>(); + map.put(DiskTO.PROTOCOL_TYPE, Storage.StoragePoolType.DatastoreCluster.toString()); + disk.setDetails(map); + disk.setData(volumeTO); + storageProcessor.getSyncedVolume(virtualMachineMO, context, hypervisorHost, disk, volumeTO); + } + @Override public CreateVMSnapshotAnswer execute(VmwareHostService hostService, CreateVMSnapshotCommand cmd) { List volumeTOs = cmd.getVolumeTOs(); @@ -1181,15 +1205,13 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager { return new CreateVMSnapshotAnswer(cmd, false, "Unable to create snapshot due to esxi internal failed"); } - Map mapNewDisk = getNewDiskMap(vmMo); - - setVolumeToPathAndSize(volumeTOs, mapNewDisk, context, hyperHost, cmd.getVmName()); + setVolumeToPathAndSize(volumeTOs, vmMo, hostService, context, hyperHost); return new CreateVMSnapshotAnswer(cmd, cmd.getTarget(), volumeTOs); } } catch (Exception e) { String msg = e.getMessage(); - s_logger.error("failed to create snapshot for vm:" + vmName + " due to " + msg); + s_logger.error("failed to create snapshot for vm:" + vmName + " due to " + msg, e); try { if (vmMo.getSnapshotMor(vmSnapshotName) != null) { @@ -1248,29 +1270,37 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager { return mapNewDisk; } - private void setVolumeToPathAndSize(List volumeTOs, Map mapNewDisk, VmwareContext context, VmwareHypervisorHost hyperHost, String vmName) + private void setVolumeToPathAndSize(List volumeTOs, VirtualMachineMO vmMo, VmwareHostService hostService, VmwareContext context, VmwareHypervisorHost hyperHost) throws Exception { + String vmName = vmMo.getVmName(); for (VolumeObjectTO volumeTO : volumeTOs) { - String oldPath = volumeTO.getPath(); + String path = volumeTO.getPath(); + String baseName; + String datastoreUuid = volumeTO.getDataStore().getUuid(); - final String baseName; - - // if this is managed storage - if (oldPath.startsWith("[-iqn.")) { // ex. [-iqn.2010-01.com.company:3y8w.vol-10.64-0] -iqn.2010-01.com.company:3y8w.vol-10.64-0-000001.vmdk - oldPath = oldPath.split(" ")[0]; // ex. [-iqn.2010-01.com.company:3y8w.vol-10.64-0] - - // remove '[' and ']' - baseName = oldPath.substring(1, oldPath.length() - 1); - } else { + if (isVolumeOnDatastoreCluster(volumeTO)) { + syncVolume(hostService, vmMo, context, hyperHost, volumeTO); + path = volumeTO.getPath(); baseName = VmwareHelper.trimSnapshotDeltaPostfix(volumeTO.getPath()); + datastoreUuid = volumeTO.getDataStoreUuid(); + } else { + Map mapNewDisk = getNewDiskMap(vmMo); + // if this is managed storage + if (path.startsWith("[-iqn.")) { // ex. [-iqn.2010-01.com.company:3y8w.vol-10.64-0] -iqn.2010-01.com.company:3y8w.vol-10.64-0-000001.vmdk + path = path.split(" ")[0]; // ex. [-iqn.2010-01.com.company:3y8w.vol-10.64-0] + + // remove '[' and ']' + baseName = path.substring(1, path.length() - 1); + } else { + baseName = VmwareHelper.trimSnapshotDeltaPostfix(path); + } + path = mapNewDisk.get(baseName); + volumeTO.setPath(path); } - String newPath = mapNewDisk.get(baseName); - // get volume's chain size for this VM snapshot; exclude current volume vdisk - DataStoreTO store = volumeTO.getDataStore(); - ManagedObjectReference morDs = getDatastoreAsManagedObjectReference(baseName, hyperHost, store); - long size = getVMSnapshotChainSize(context, hyperHost, baseName + "-*.vmdk", morDs, newPath, vmName); + ManagedObjectReference morDs = getDatastoreAsManagedObjectReference(baseName, hyperHost, datastoreUuid); + long size = getVMSnapshotChainSize(context, hyperHost, baseName + "-*.vmdk", morDs, path, vmName); if (volumeTO.getVolumeType() == Volume.Type.ROOT) { // add memory snapshot size @@ -1278,11 +1308,10 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager { } volumeTO.setSize(size); - volumeTO.setPath(newPath); } } - private ManagedObjectReference getDatastoreAsManagedObjectReference(String baseName, VmwareHypervisorHost hyperHost, DataStoreTO store) throws Exception { + private ManagedObjectReference getDatastoreAsManagedObjectReference(String baseName, VmwareHypervisorHost hyperHost, String storeUuid) throws Exception { try { // if baseName equates to a datastore name, this should be managed storage ManagedObjectReference morDs = hyperHost.findDatastoreByName(baseName); @@ -1295,7 +1324,7 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager { } // not managed storage, so use the standard way of getting a ManagedObjectReference for a datastore - return HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(hyperHost, store.getUuid()); + return HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(hyperHost, storeUuid); } @Override @@ -1335,15 +1364,13 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager { // after removed snapshot, the volumes' paths have been changed for the VM, needs to report new paths to manager - Map mapNewDisk = getNewDiskMap(vmMo); - - setVolumeToPathAndSize(listVolumeTo, mapNewDisk, context, hyperHost, cmd.getVmName()); + setVolumeToPathAndSize(listVolumeTo, vmMo, hostService, context, hyperHost); return new DeleteVMSnapshotAnswer(cmd, listVolumeTo); } } catch (Exception e) { String msg = e.getMessage(); - s_logger.error("failed to delete vm snapshot " + vmSnapshotName + " of vm " + vmName + " due to " + msg); + s_logger.error("failed to delete vm snapshot " + vmSnapshotName + " of vm " + vmName + " due to " + msg, e); return new DeleteVMSnapshotAnswer(cmd, false, msg); } @@ -1403,9 +1430,7 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager { } if (result) { - Map mapNewDisk = getNewDiskMap(vmMo); - - setVolumeToPathAndSize(listVolumeTo, mapNewDisk, context, hyperHost, cmd.getVmName()); + setVolumeToPathAndSize(listVolumeTo, vmMo, hostService, context, hyperHost); if (!snapshotMemory) { vmState = VirtualMachine.PowerState.PowerOff; @@ -1418,7 +1443,7 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager { } } catch (Exception e) { String msg = "revert vm " + vmName + " to snapshot " + snapshotName + " failed due to " + e.getMessage(); - s_logger.error(msg); + s_logger.error(msg, e); return new RevertToVMSnapshotAnswer(cmd, false, msg); } diff --git a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/resource/VmwareResource.java b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/resource/VmwareResource.java index e831f6228e0..04e9dd4cc96 100644 --- a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/resource/VmwareResource.java +++ b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/resource/VmwareResource.java @@ -7671,4 +7671,9 @@ public class VmwareResource extends ServerResourceBase implements StoragePoolRes s_logger.error(String.format("Failed to log command %s due to: [%s].", cmd.getClass().getSimpleName(), e.getMessage()), e); } } + + @Override + public VmwareStorageProcessor getStorageProcessor() { + return _storageProcessor; + } } diff --git a/plugins/hypervisors/vmware/src/main/java/com/cloud/storage/resource/VmwareSecondaryStorageResourceHandler.java b/plugins/hypervisors/vmware/src/main/java/com/cloud/storage/resource/VmwareSecondaryStorageResourceHandler.java index 44bcbf31e1a..68947ef6904 100644 --- a/plugins/hypervisors/vmware/src/main/java/com/cloud/storage/resource/VmwareSecondaryStorageResourceHandler.java +++ b/plugins/hypervisors/vmware/src/main/java/com/cloud/storage/resource/VmwareSecondaryStorageResourceHandler.java @@ -18,14 +18,10 @@ package com.cloud.storage.resource; import java.util.List; -import org.apache.log4j.Logger; -import org.apache.log4j.NDC; - -import com.google.gson.Gson; -import com.vmware.vim25.ManagedObjectReference; - import org.apache.cloudstack.storage.command.StorageSubSystemCommand; import org.apache.cloudstack.storage.resource.SecondaryStorageResourceHandler; +import org.apache.log4j.Logger; +import org.apache.log4j.NDC; import com.cloud.agent.api.Answer; import com.cloud.agent.api.BackupSnapshotCommand; @@ -52,6 +48,8 @@ import com.cloud.serializer.GsonHelper; import com.cloud.utils.NumbersUtil; import com.cloud.utils.Pair; import com.cloud.utils.StringUtils; +import com.google.gson.Gson; +import com.vmware.vim25.ManagedObjectReference; public class VmwareSecondaryStorageResourceHandler implements SecondaryStorageResourceHandler, VmwareHostService, VmwareStorageMount { private static final Logger s_logger = Logger.getLogger(VmwareSecondaryStorageResourceHandler.class); @@ -326,4 +324,9 @@ public class VmwareSecondaryStorageResourceHandler implements SecondaryStorageRe return message; } + + @Override + public VmwareStorageProcessor getStorageProcessor() { + return null; + } } diff --git a/plugins/hypervisors/vmware/src/main/java/com/cloud/storage/resource/VmwareStorageProcessor.java b/plugins/hypervisors/vmware/src/main/java/com/cloud/storage/resource/VmwareStorageProcessor.java index bdb8eca20ed..fd541cca80a 100644 --- a/plugins/hypervisors/vmware/src/main/java/com/cloud/storage/resource/VmwareStorageProcessor.java +++ b/plugins/hypervisors/vmware/src/main/java/com/cloud/storage/resource/VmwareStorageProcessor.java @@ -2081,33 +2081,17 @@ public class VmwareStorageProcessor implements StorageProcessor { datastoreVolumePath = dsMo.getDatastorePath((vmdkPath != null ? vmdkPath : dsMo.getName()) + ".vmdk"); } else { String datastoreUUID = primaryStore.getUuid(); - if (disk.getDetails().get(DiskTO.PROTOCOL_TYPE) != null && disk.getDetails().get(DiskTO.PROTOCOL_TYPE).equalsIgnoreCase("DatastoreCluster")) { - VirtualMachineDiskInfo matchingExistingDisk = getMatchingExistingDisk(hyperHost, context, vmMo, disk); - VirtualMachineDiskInfoBuilder diskInfoBuilder = vmMo.getDiskInfoBuilder(); - if (diskInfoBuilder != null && matchingExistingDisk != null) { - String[] diskChain = matchingExistingDisk.getDiskChain(); - assert (diskChain.length > 0); - DatastoreFile file = new DatastoreFile(diskChain[0]); - if (!file.getFileBaseName().equalsIgnoreCase(volumePath)) { - if (s_logger.isInfoEnabled()) - s_logger.info("Detected disk-chain top file change on volume: " + volumeTO.getId() + " " + volumePath + " -> " + file.getFileBaseName()); - volumePathChangeObserved = true; - volumePath = file.getFileBaseName(); - volumeTO.setPath(volumePath); - chainInfo = _gson.toJson(matchingExistingDisk); - } - - DatastoreMO diskDatastoreMofromVM = getDiskDatastoreMofromVM(hyperHost, context, vmMo, disk, diskInfoBuilder); - if (diskDatastoreMofromVM != null) { - String actualPoolUuid = diskDatastoreMofromVM.getCustomFieldValue(CustomFieldConstants.CLOUD_UUID); - if (!actualPoolUuid.equalsIgnoreCase(primaryStore.getUuid())) { - s_logger.warn(String.format("Volume %s found to be in a different storage pool %s", volumePath, actualPoolUuid)); - datastoreChangeObserved = true; - datastoreUUID = actualPoolUuid; - chainInfo = _gson.toJson(matchingExistingDisk); - } - } - } + Pair changes = getSyncedVolume(vmMo, context, hyperHost, disk, volumeTO); + volumePathChangeObserved = changes.first(); + datastoreChangeObserved = changes.second(); + if (datastoreChangeObserved) { + datastoreUUID = volumeTO.getDataStoreUuid(); + } + if (volumePathChangeObserved) { + volumePath = volumeTO.getPath(); + } + if ((volumePathChangeObserved || datastoreChangeObserved) && StringUtils.isNotEmpty(volumeTO.getChainInfo())) { + chainInfo = volumeTO.getChainInfo(); } if (storagePort == DEFAULT_NFS_PORT) { morDs = HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(hyperHost, isManaged ? VmwareResource.getDatastoreName(diskUuid) : datastoreUUID); @@ -3862,17 +3846,50 @@ public class VmwareStorageProcessor implements StorageProcessor { } } + public Pair getSyncedVolume(VirtualMachineMO vmMo, VmwareContext context,VmwareHypervisorHost hyperHost, DiskTO disk, VolumeObjectTO volumeTO) throws Exception { + DataStoreTO primaryStore = volumeTO.getDataStore(); + boolean datastoreChangeObserved = false; + boolean volumePathChangeObserved = false; + if (!"DatastoreCluster".equalsIgnoreCase(disk.getDetails().get(DiskTO.PROTOCOL_TYPE))) { + return new Pair<>(volumePathChangeObserved, datastoreChangeObserved); + } + VirtualMachineDiskInfo matchingExistingDisk = getMatchingExistingDisk(hyperHost, context, vmMo, disk); + VirtualMachineDiskInfoBuilder diskInfoBuilder = vmMo.getDiskInfoBuilder(); + if (diskInfoBuilder != null && matchingExistingDisk != null) { + String[] diskChain = matchingExistingDisk.getDiskChain(); + assert (diskChain.length > 0); + DatastoreFile file = new DatastoreFile(diskChain[0]); + String volumePath = volumeTO.getPath(); + if (!file.getFileBaseName().equalsIgnoreCase(volumePath)) { + if (s_logger.isInfoEnabled()) { + s_logger.info("Detected disk-chain top file change on volume: " + volumeTO.getId() + " " + volumePath + " -> " + file.getFileBaseName()); + } + volumePathChangeObserved = true; + volumePath = file.getFileBaseName(); + volumeTO.setPath(volumePath); + volumeTO.setChainInfo(_gson.toJson(matchingExistingDisk)); + } + + DatastoreMO diskDatastoreMoFromVM = getDiskDatastoreMofromVM(hyperHost, context, vmMo, disk, diskInfoBuilder); + if (diskDatastoreMoFromVM != null) { + String actualPoolUuid = diskDatastoreMoFromVM.getCustomFieldValue(CustomFieldConstants.CLOUD_UUID); + if (!actualPoolUuid.equalsIgnoreCase(primaryStore.getUuid())) { + s_logger.warn(String.format("Volume %s found to be in a different storage pool %s", volumePath, actualPoolUuid)); + datastoreChangeObserved = true; + volumeTO.setDataStoreUuid(actualPoolUuid); + volumeTO.setChainInfo(_gson.toJson(matchingExistingDisk)); + } + } + } + return new Pair<>(volumePathChangeObserved, datastoreChangeObserved); + } + @Override public Answer syncVolumePath(SyncVolumePathCommand cmd) { DiskTO disk = cmd.getDisk(); VolumeObjectTO volumeTO = (VolumeObjectTO)disk.getData(); - DataStoreTO primaryStore = volumeTO.getDataStore(); - String volumePath = volumeTO.getPath(); String vmName = volumeTO.getVmName(); - boolean datastoreChangeObserved = false; - boolean volumePathChangeObserved = false; - String chainInfo = null; try { VmwareContext context = hostService.getServiceContext(null); VmwareHypervisorHost hyperHost = hostService.getHyperHost(context, null); @@ -3885,46 +3902,19 @@ public class VmwareStorageProcessor implements StorageProcessor { throw new Exception(msg); } } - - String datastoreUUID = primaryStore.getUuid(); - if (disk.getDetails().get(DiskTO.PROTOCOL_TYPE) != null && disk.getDetails().get(DiskTO.PROTOCOL_TYPE).equalsIgnoreCase("DatastoreCluster")) { - VirtualMachineDiskInfo matchingExistingDisk = getMatchingExistingDisk(hyperHost, context, vmMo, disk); - VirtualMachineDiskInfoBuilder diskInfoBuilder = vmMo.getDiskInfoBuilder(); - if (diskInfoBuilder != null && matchingExistingDisk != null) { - String[] diskChain = matchingExistingDisk.getDiskChain(); - assert (diskChain.length > 0); - DatastoreFile file = new DatastoreFile(diskChain[0]); - if (!file.getFileBaseName().equalsIgnoreCase(volumePath)) { - if (s_logger.isInfoEnabled()) - s_logger.info("Detected disk-chain top file change on volume: " + volumeTO.getId() + " " + volumePath + " -> " + file.getFileBaseName()); - volumePathChangeObserved = true; - volumePath = file.getFileBaseName(); - volumeTO.setPath(volumePath); - chainInfo = _gson.toJson(matchingExistingDisk); - } - - DatastoreMO diskDatastoreMofromVM = getDiskDatastoreMofromVM(hyperHost, context, vmMo, disk, diskInfoBuilder); - if (diskDatastoreMofromVM != null) { - String actualPoolUuid = diskDatastoreMofromVM.getCustomFieldValue(CustomFieldConstants.CLOUD_UUID); - if (!actualPoolUuid.equalsIgnoreCase(primaryStore.getUuid())) { - s_logger.warn(String.format("Volume %s found to be in a different storage pool %s", volumePath, actualPoolUuid)); - datastoreChangeObserved = true; - datastoreUUID = actualPoolUuid; - chainInfo = _gson.toJson(matchingExistingDisk); - } - } - } - } + Pair changes = getSyncedVolume(vmMo, context, hyperHost, disk, volumeTO); + boolean volumePathChangeObserved = changes.first(); + boolean datastoreChangeObserved = changes.second(); SyncVolumePathAnswer answer = new SyncVolumePathAnswer(disk); if (datastoreChangeObserved) { - answer.setContextParam("datastoreName", datastoreUUID); + answer.setContextParam("datastoreName", volumeTO.getDataStoreUuid()); } if (volumePathChangeObserved) { - answer.setContextParam("volumePath", volumePath); + answer.setContextParam("volumePath", volumeTO.getPath()); } - if (chainInfo != null && !chainInfo.isEmpty()) { - answer.setContextParam("chainInfo", chainInfo); + if ((volumePathChangeObserved || datastoreChangeObserved) && StringUtils.isNotEmpty(volumeTO.getChainInfo())) { + answer.setContextParam("chainInfo", volumeTO.getChainInfo()); } return answer;