This is an automated email from the ASF dual-hosted git repository.

dahn pushed a commit to branch 4.18
in repository https://gitbox.apache.org/repos/asf/cloudstack.git


The following commit(s) were added to refs/heads/4.18 by this push:
     new 2d6a0698121 server: improve storage GC to skip expunging possible 
duplicate volumes (#7313)
2d6a0698121 is described below

commit 2d6a06981217c8f399aef4ba0a3b41b5655f1a64
Author: Abhishek Kumar <[email protected]>
AuthorDate: Mon Jun 5 13:33:24 2023 +0530

    server: improve storage GC to skip expunging possible duplicate volumes 
(#7313)
    
    Signed-off-by: Abhishek Kumar <[email protected]>
---
 .../java/com/cloud/storage/StorageManagerImpl.java | 38 ++++++++++--
 .../com/cloud/storage/StorageManagerImplTest.java  | 72 +++++++++++++++++++++-
 2 files changed, 104 insertions(+), 6 deletions(-)

diff --git a/server/src/main/java/com/cloud/storage/StorageManagerImpl.java 
b/server/src/main/java/com/cloud/storage/StorageManagerImpl.java
index c1d17cc8f70..7bafbcc77b8 100644
--- a/server/src/main/java/com/cloud/storage/StorageManagerImpl.java
+++ b/server/src/main/java/com/cloud/storage/StorageManagerImpl.java
@@ -19,6 +19,7 @@ package com.cloud.storage;
 import static com.cloud.utils.NumbersUtil.toHumanReadableSize;
 
 import java.math.BigDecimal;
+import java.math.BigInteger;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.net.UnknownHostException;
@@ -37,6 +38,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Random;
 import java.util.Set;
+import java.util.UUID;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
@@ -45,8 +47,6 @@ import java.util.stream.Collectors;
 
 import javax.inject.Inject;
 
-import com.google.common.collect.Sets;
-import com.cloud.vm.UserVmManager;
 import org.apache.cloudstack.annotation.AnnotationService;
 import org.apache.cloudstack.annotation.dao.AnnotationDao;
 import org.apache.cloudstack.api.ApiConstants;
@@ -229,11 +229,11 @@ import com.cloud.utils.db.TransactionLegacy;
 import com.cloud.utils.db.TransactionStatus;
 import com.cloud.utils.exception.CloudRuntimeException;
 import com.cloud.vm.DiskProfile;
+import com.cloud.vm.UserVmManager;
 import com.cloud.vm.VMInstanceVO;
 import com.cloud.vm.VirtualMachine.State;
 import com.cloud.vm.dao.VMInstanceDao;
-import java.math.BigInteger;
-import java.util.UUID;
+import com.google.common.collect.Sets;
 
 @Component
 public class StorageManagerImpl extends ManagerBase implements StorageManager, 
ClusterManagerListener, Configurable {
@@ -1336,7 +1336,10 @@ public class StorageManagerImpl extends ManagerBase 
implements StorageManager, C
                                  continue;
                              }
                         }
-
+                        if (isVolumeSuspectedDestroyDuplicateOfVmVolume(vol)) {
+                            s_logger.warn(String.format("Skipping cleaning up 
%s as it could be a duplicate for another volume on same pool", vol));
+                            continue;
+                        }
                         try {
                             // If this fails, just log a warning. It's ideal 
if we clean up the host-side clustered file
                             // system, but not necessary.
@@ -1468,6 +1471,31 @@ public class StorageManagerImpl extends ManagerBase 
implements StorageManager, C
         }
     }
 
+    protected boolean isVolumeSuspectedDestroyDuplicateOfVmVolume(VolumeVO 
gcVolume) {
+        if (gcVolume.getPath() == null) {
+            return false;
+        }
+        if (gcVolume.getPoolId() == null) {
+            return false;
+        }
+        Long vmId = gcVolume.getInstanceId();
+        if (vmId == null) {
+            return false;
+        }
+        VMInstanceVO vm = _vmInstanceDao.findById(vmId);
+        if (vm == null) {
+            return false;
+        }
+        List<VolumeVO> vmUsableVolumes = 
_volumeDao.findUsableVolumesForInstance(vmId);
+        for (VolumeVO vol : vmUsableVolumes) {
+            if (gcVolume.getPoolId().equals(vol.getPoolId()) && 
gcVolume.getPath().equals(vol.getPath())) {
+                s_logger.debug(String.format("%s meant for garbage collection 
could a possible duplicate for %s", gcVolume, vol));
+                return true;
+            }
+        }
+        return false;
+    }
+
     /**
      * This method only applies for managed storage.
      *
diff --git a/server/src/test/java/com/cloud/storage/StorageManagerImplTest.java 
b/server/src/test/java/com/cloud/storage/StorageManagerImplTest.java
index dc79ac512fa..e7004ba7c5d 100644
--- a/server/src/test/java/com/cloud/storage/StorageManagerImplTest.java
+++ b/server/src/test/java/com/cloud/storage/StorageManagerImplTest.java
@@ -16,20 +16,35 @@
 // under the License.
 package com.cloud.storage;
 
+import java.util.ArrayList;
+import java.util.List;
+
 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.runners.MockitoJUnitRunner;
+import org.mockito.junit.MockitoJUnitRunner;
 
 import com.cloud.agent.api.StoragePoolInfo;
 import com.cloud.host.Host;
+import com.cloud.storage.dao.VolumeDao;
+import com.cloud.vm.VMInstanceVO;
+import com.cloud.vm.dao.VMInstanceDao;
 
 @RunWith(MockitoJUnitRunner.class)
 public class StorageManagerImplTest {
 
+    @Mock
+    VolumeDao _volumeDao;
+
+    @Mock
+    VMInstanceDao vmInstanceDao;
+
     @Spy
+    @InjectMocks
     private StorageManagerImpl storageManagerImpl;
 
     @Test
@@ -58,4 +73,59 @@ public class StorageManagerImplTest {
         String localStoragePoolName = 
storageManagerImpl.createLocalStoragePoolName(hostMock, storagePoolInfoMock);
         Assert.assertEquals(expectedLocalStorageName, localStoragePoolName);
     }
+
+    private VolumeVO mockVolumeForIsVolumeSuspectedDestroyDuplicateTest() {
+        VolumeVO volumeVO = new VolumeVO("data", 1L, 1L, 1L, 1L, 1L, "data", 
"data", Storage.ProvisioningType.THIN, 1, null, null, "data", 
Volume.Type.DATADISK);
+        volumeVO.setPoolId(1L);
+        return volumeVO;
+    }
+
+    @Test
+    public void testIsVolumeSuspectedDestroyDuplicateNoPool() {
+        VolumeVO volume = mockVolumeForIsVolumeSuspectedDestroyDuplicateTest();
+        volume.setPoolId(null);
+        
Assert.assertFalse(storageManagerImpl.isVolumeSuspectedDestroyDuplicateOfVmVolume(volume));
+    }
+
+    @Test
+    public void testIsVolumeSuspectedDestroyDuplicateNoPath() {
+        VolumeVO volume = mockVolumeForIsVolumeSuspectedDestroyDuplicateTest();
+        
Assert.assertFalse(storageManagerImpl.isVolumeSuspectedDestroyDuplicateOfVmVolume(volume));
+    }
+
+    @Test
+    public void testIsVolumeSuspectedDestroyDuplicateNoVmId() {
+        VolumeVO volume = mockVolumeForIsVolumeSuspectedDestroyDuplicateTest();
+        volume.setInstanceId(null);
+        
Assert.assertFalse(storageManagerImpl.isVolumeSuspectedDestroyDuplicateOfVmVolume(volume));
+    }
+
+    @Test
+    public void testIsVolumeSuspectedDestroyDuplicateNoVm() {
+        VolumeVO volume = mockVolumeForIsVolumeSuspectedDestroyDuplicateTest();
+        
Assert.assertFalse(storageManagerImpl.isVolumeSuspectedDestroyDuplicateOfVmVolume(volume));
+    }
+
+    @Test
+    public void testIsVolumeSuspectedDestroyDuplicateNoVmVolumes() {
+        VolumeVO volume = mockVolumeForIsVolumeSuspectedDestroyDuplicateTest();
+        
Mockito.when(vmInstanceDao.findById(1L)).thenReturn(Mockito.mock(VMInstanceVO.class));
+        
Mockito.when(_volumeDao.findUsableVolumesForInstance(1L)).thenReturn(new 
ArrayList<>());
+        
Assert.assertFalse(storageManagerImpl.isVolumeSuspectedDestroyDuplicateOfVmVolume(volume));
+    }
+
+    @Test
+    public void testIsVolumeSuspectedDestroyDuplicateTrue() {
+        Long poolId = 1L;
+        String path = "data";
+        VolumeVO volume = mockVolumeForIsVolumeSuspectedDestroyDuplicateTest();
+        volume.setPoolId(poolId);
+        
Mockito.when(vmInstanceDao.findById(1L)).thenReturn(Mockito.mock(VMInstanceVO.class));
+        VolumeVO volumeVO = Mockito.mock(VolumeVO.class);
+        Mockito.when(volumeVO.getPoolId()).thenReturn(poolId);
+        Mockito.when(volumeVO.getPath()).thenReturn(path);
+        
Mockito.when(_volumeDao.findUsableVolumesForInstance(1L)).thenReturn(List.of(volumeVO,
 Mockito.mock(VolumeVO.class)));
+        
Assert.assertTrue(storageManagerImpl.isVolumeSuspectedDestroyDuplicateOfVmVolume(volume));
+    }
+
 }

Reply via email to