You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by ro...@apache.org on 2023/07/03 07:30:39 UTC

[cloudstack] branch 4.18 updated: engine-orchestration: fix volume size resource count mismatch (#7666)

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

rohit 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 31dbdd0f5c0 engine-orchestration: fix volume size resource count mismatch (#7666)
31dbdd0f5c0 is described below

commit 31dbdd0f5c0a0eb33e71fa7e7bef0b8b74f408b3
Author: Abhishek Kumar <ab...@gmail.com>
AuthorDate: Mon Jul 3 13:00:32 2023 +0530

    engine-orchestration: fix volume size resource count mismatch (#7666)
    
    Fixes case of account/domain having negative storage count when there is a volume size difference while deploying volumes on certain stroages. In case of Powerflex volume size results in a multiple of 8. If user deploys a volume of 12GB it will result in 16GB in size. But currently CloudStack will not update resource count after deployment.
    
    Signed-off-by: Abhishek Kumar <ab...@gmail.com>
---
 .../engine/orchestration/VolumeOrchestrator.java   |  23 ++++-
 .../orchestration/VolumeOrchestratorTest.java      | 103 +++++++++++++++++++++
 2 files changed, 124 insertions(+), 2 deletions(-)

diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java
index 49c822d25ad..2aa997e13aa 100644
--- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java
+++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java
@@ -28,6 +28,7 @@ import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
 import java.util.UUID;
@@ -83,6 +84,7 @@ import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO;
 import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
 import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.collections.MapUtils;
+import org.apache.commons.lang3.ObjectUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.log4j.Logger;
 import org.jetbrains.annotations.Nullable;
@@ -1656,6 +1658,23 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
         return tasks;
     }
 
+    protected void checkAndUpdateVolumeAccountResourceCount(VolumeVO originalEntry, VolumeVO updateEntry) {
+        if (Objects.equals(originalEntry.getSize(), updateEntry.getSize())) {
+            return;
+        }
+        s_logger.debug(String.format("Size mismatch found for %s after creation, old size: %d, new size: %d. Updating resource count", updateEntry, originalEntry.getSize(), updateEntry.getSize()));
+        if (ObjectUtils.anyNull(originalEntry.getSize(), updateEntry.getSize())) {
+            _resourceLimitMgr.recalculateResourceCount(updateEntry.getAccountId(), updateEntry.getDomainId(),
+                    ResourceType.primary_storage.getOrdinal());
+            return;
+        }
+        if (updateEntry.getSize() > originalEntry.getSize()) {
+            _resourceLimitMgr.incrementResourceCount(updateEntry.getAccountId(), ResourceType.primary_storage, updateEntry.isDisplayVolume(), updateEntry.getSize() - originalEntry.getSize());
+        } else {
+            _resourceLimitMgr.decrementResourceCount(updateEntry.getAccountId(), ResourceType.primary_storage, updateEntry.isDisplayVolume(), originalEntry.getSize() - updateEntry.getSize());
+        }
+    }
+
     private Pair<VolumeVO, DataStore> recreateVolume(VolumeVO vol, VirtualMachineProfile vm, DeployDestination dest) throws StorageUnavailableException, StorageAccessException {
         String volToString = getReflectOnlySelectedFields(vol);
 
@@ -1793,8 +1812,8 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
                 throw new StorageUnavailableException(msg, destPool.getId());
             }
         }
-
-        return new Pair<VolumeVO, DataStore>(newVol, destPool);
+        checkAndUpdateVolumeAccountResourceCount(vol, newVol);
+        return new Pair<>(newVol, destPool);
     }
 
     private VolumeVO setPassphraseForVolumeEncryption(VolumeVO volume) {
diff --git a/engine/orchestration/src/test/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestratorTest.java b/engine/orchestration/src/test/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestratorTest.java
new file mode 100644
index 00000000000..b2f4c436ca3
--- /dev/null
+++ b/engine/orchestration/src/test/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestratorTest.java
@@ -0,0 +1,103 @@
+// 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.engine.orchestration;
+
+import java.util.ArrayList;
+
+import org.apache.commons.lang3.ObjectUtils;
+import org.junit.Assert;
+import org.junit.Before;
+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.configuration.Resource;
+import com.cloud.storage.VolumeVO;
+import com.cloud.user.ResourceLimitService;
+
+@RunWith(MockitoJUnitRunner.class)
+public class VolumeOrchestratorTest {
+
+    @Mock
+    protected ResourceLimitService resourceLimitMgr;
+
+    @Spy
+    @InjectMocks
+    private VolumeOrchestrator volumeOrchestrator = new VolumeOrchestrator();
+
+    private static final Long DEFAULT_ACCOUNT_PS_RESOURCE_COUNT = 100L;
+    private Long accountPSResourceCount;
+
+    @Before
+    public void setUp() throws Exception {
+        accountPSResourceCount = DEFAULT_ACCOUNT_PS_RESOURCE_COUNT;
+        Mockito.when(resourceLimitMgr.recalculateResourceCount(Mockito.anyLong(), Mockito.anyLong(), Mockito.anyInt())).thenReturn(new ArrayList<>());
+        Mockito.doAnswer((Answer<Void>) invocation -> {
+            Resource.ResourceType type = (Resource.ResourceType)invocation.getArguments()[1];
+            Long increment = (Long)invocation.getArguments()[3];
+            if (Resource.ResourceType.primary_storage.equals(type)) {
+                accountPSResourceCount += increment;
+            }
+            return null;
+        }).when(resourceLimitMgr).incrementResourceCount(Mockito.anyLong(), Mockito.any(Resource.ResourceType.class), Mockito.anyBoolean(), Mockito.anyLong());
+        Mockito.doAnswer((Answer<Void>) invocation -> {
+            Resource.ResourceType type = (Resource.ResourceType)invocation.getArguments()[1];
+            Long decrement = (Long)invocation.getArguments()[3];
+            if (Resource.ResourceType.primary_storage.equals(type)) {
+                accountPSResourceCount -= decrement;
+            }
+            return null;
+        }).when(resourceLimitMgr).decrementResourceCount(Mockito.anyLong(), Mockito.any(Resource.ResourceType.class), Mockito.anyBoolean(), Mockito.anyLong());
+    }
+
+    private void runCheckAndUpdateVolumeAccountResourceCountTest(Long originalSize, Long newSize) {
+        VolumeVO v1 = Mockito.mock(VolumeVO.class);
+        Mockito.when(v1.getSize()).thenReturn(originalSize);
+        VolumeVO v2 = Mockito.mock(VolumeVO.class);
+        Mockito.when(v2.getSize()).thenReturn(newSize);
+        volumeOrchestrator.checkAndUpdateVolumeAccountResourceCount(v1, v2);
+        Long expected = ObjectUtils.anyNull(originalSize, newSize) ?
+                DEFAULT_ACCOUNT_PS_RESOURCE_COUNT : DEFAULT_ACCOUNT_PS_RESOURCE_COUNT + (newSize - originalSize);
+        Assert.assertEquals(expected, accountPSResourceCount);
+    }
+
+    @Test
+    public void testCheckAndUpdateVolumeAccountResourceCountSameSize() {
+        runCheckAndUpdateVolumeAccountResourceCountTest(10L, 10L);
+    }
+
+    @Test
+    public void testCheckAndUpdateVolumeAccountResourceCountEitherSizeNull() {
+        runCheckAndUpdateVolumeAccountResourceCountTest(null, 10L);
+        runCheckAndUpdateVolumeAccountResourceCountTest(10L, null);
+    }
+
+    @Test
+    public void testCheckAndUpdateVolumeAccountResourceCountMoreSize() {
+        runCheckAndUpdateVolumeAccountResourceCountTest(10L, 20L);
+    }
+
+    @Test
+    public void testCheckAndUpdateVolumeAccountResourceCountLessSize() {
+        runCheckAndUpdateVolumeAccountResourceCountTest(20L, 10L);
+    }
+}