Skip to content

Commit 31dbdd0

Browse files
authored
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 <abhishek.mrt22@gmail.com>
1 parent 7082013 commit 31dbdd0

2 files changed

Lines changed: 124 additions & 2 deletions

File tree

engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import java.util.HashSet;
2929
import java.util.List;
3030
import java.util.Map;
31+
import java.util.Objects;
3132
import java.util.Optional;
3233
import java.util.Set;
3334
import java.util.UUID;
@@ -83,6 +84,7 @@
8384
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
8485
import org.apache.commons.collections.CollectionUtils;
8586
import org.apache.commons.collections.MapUtils;
87+
import org.apache.commons.lang3.ObjectUtils;
8688
import org.apache.commons.lang3.StringUtils;
8789
import org.apache.log4j.Logger;
8890
import org.jetbrains.annotations.Nullable;
@@ -1656,6 +1658,23 @@ private List<VolumeTask> getTasks(List<VolumeVO> vols, Map<Volume, StoragePool>
16561658
return tasks;
16571659
}
16581660

1661+
protected void checkAndUpdateVolumeAccountResourceCount(VolumeVO originalEntry, VolumeVO updateEntry) {
1662+
if (Objects.equals(originalEntry.getSize(), updateEntry.getSize())) {
1663+
return;
1664+
}
1665+
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()));
1666+
if (ObjectUtils.anyNull(originalEntry.getSize(), updateEntry.getSize())) {
1667+
_resourceLimitMgr.recalculateResourceCount(updateEntry.getAccountId(), updateEntry.getDomainId(),
1668+
ResourceType.primary_storage.getOrdinal());
1669+
return;
1670+
}
1671+
if (updateEntry.getSize() > originalEntry.getSize()) {
1672+
_resourceLimitMgr.incrementResourceCount(updateEntry.getAccountId(), ResourceType.primary_storage, updateEntry.isDisplayVolume(), updateEntry.getSize() - originalEntry.getSize());
1673+
} else {
1674+
_resourceLimitMgr.decrementResourceCount(updateEntry.getAccountId(), ResourceType.primary_storage, updateEntry.isDisplayVolume(), originalEntry.getSize() - updateEntry.getSize());
1675+
}
1676+
}
1677+
16591678
private Pair<VolumeVO, DataStore> recreateVolume(VolumeVO vol, VirtualMachineProfile vm, DeployDestination dest) throws StorageUnavailableException, StorageAccessException {
16601679
String volToString = getReflectOnlySelectedFields(vol);
16611680

@@ -1793,8 +1812,8 @@ private Pair<VolumeVO, DataStore> recreateVolume(VolumeVO vol, VirtualMachinePro
17931812
throw new StorageUnavailableException(msg, destPool.getId());
17941813
}
17951814
}
1796-
1797-
return new Pair<VolumeVO, DataStore>(newVol, destPool);
1815+
checkAndUpdateVolumeAccountResourceCount(vol, newVol);
1816+
return new Pair<>(newVol, destPool);
17981817
}
17991818

18001819
private VolumeVO setPassphraseForVolumeEncryption(VolumeVO volume) {
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
package org.apache.cloudstack.engine.orchestration;
18+
19+
import java.util.ArrayList;
20+
21+
import org.apache.commons.lang3.ObjectUtils;
22+
import org.junit.Assert;
23+
import org.junit.Before;
24+
import org.junit.Test;
25+
import org.junit.runner.RunWith;
26+
import org.mockito.InjectMocks;
27+
import org.mockito.Mock;
28+
import org.mockito.Mockito;
29+
import org.mockito.Spy;
30+
import org.mockito.junit.MockitoJUnitRunner;
31+
import org.mockito.stubbing.Answer;
32+
33+
import com.cloud.configuration.Resource;
34+
import com.cloud.storage.VolumeVO;
35+
import com.cloud.user.ResourceLimitService;
36+
37+
@RunWith(MockitoJUnitRunner.class)
38+
public class VolumeOrchestratorTest {
39+
40+
@Mock
41+
protected ResourceLimitService resourceLimitMgr;
42+
43+
@Spy
44+
@InjectMocks
45+
private VolumeOrchestrator volumeOrchestrator = new VolumeOrchestrator();
46+
47+
private static final Long DEFAULT_ACCOUNT_PS_RESOURCE_COUNT = 100L;
48+
private Long accountPSResourceCount;
49+
50+
@Before
51+
public void setUp() throws Exception {
52+
accountPSResourceCount = DEFAULT_ACCOUNT_PS_RESOURCE_COUNT;
53+
Mockito.when(resourceLimitMgr.recalculateResourceCount(Mockito.anyLong(), Mockito.anyLong(), Mockito.anyInt())).thenReturn(new ArrayList<>());
54+
Mockito.doAnswer((Answer<Void>) invocation -> {
55+
Resource.ResourceType type = (Resource.ResourceType)invocation.getArguments()[1];
56+
Long increment = (Long)invocation.getArguments()[3];
57+
if (Resource.ResourceType.primary_storage.equals(type)) {
58+
accountPSResourceCount += increment;
59+
}
60+
return null;
61+
}).when(resourceLimitMgr).incrementResourceCount(Mockito.anyLong(), Mockito.any(Resource.ResourceType.class), Mockito.anyBoolean(), Mockito.anyLong());
62+
Mockito.doAnswer((Answer<Void>) invocation -> {
63+
Resource.ResourceType type = (Resource.ResourceType)invocation.getArguments()[1];
64+
Long decrement = (Long)invocation.getArguments()[3];
65+
if (Resource.ResourceType.primary_storage.equals(type)) {
66+
accountPSResourceCount -= decrement;
67+
}
68+
return null;
69+
}).when(resourceLimitMgr).decrementResourceCount(Mockito.anyLong(), Mockito.any(Resource.ResourceType.class), Mockito.anyBoolean(), Mockito.anyLong());
70+
}
71+
72+
private void runCheckAndUpdateVolumeAccountResourceCountTest(Long originalSize, Long newSize) {
73+
VolumeVO v1 = Mockito.mock(VolumeVO.class);
74+
Mockito.when(v1.getSize()).thenReturn(originalSize);
75+
VolumeVO v2 = Mockito.mock(VolumeVO.class);
76+
Mockito.when(v2.getSize()).thenReturn(newSize);
77+
volumeOrchestrator.checkAndUpdateVolumeAccountResourceCount(v1, v2);
78+
Long expected = ObjectUtils.anyNull(originalSize, newSize) ?
79+
DEFAULT_ACCOUNT_PS_RESOURCE_COUNT : DEFAULT_ACCOUNT_PS_RESOURCE_COUNT + (newSize - originalSize);
80+
Assert.assertEquals(expected, accountPSResourceCount);
81+
}
82+
83+
@Test
84+
public void testCheckAndUpdateVolumeAccountResourceCountSameSize() {
85+
runCheckAndUpdateVolumeAccountResourceCountTest(10L, 10L);
86+
}
87+
88+
@Test
89+
public void testCheckAndUpdateVolumeAccountResourceCountEitherSizeNull() {
90+
runCheckAndUpdateVolumeAccountResourceCountTest(null, 10L);
91+
runCheckAndUpdateVolumeAccountResourceCountTest(10L, null);
92+
}
93+
94+
@Test
95+
public void testCheckAndUpdateVolumeAccountResourceCountMoreSize() {
96+
runCheckAndUpdateVolumeAccountResourceCountTest(10L, 20L);
97+
}
98+
99+
@Test
100+
public void testCheckAndUpdateVolumeAccountResourceCountLessSize() {
101+
runCheckAndUpdateVolumeAccountResourceCountTest(20L, 10L);
102+
}
103+
}

0 commit comments

Comments
 (0)