Skip to content

Commit e87ce0c

Browse files
Fix reorder/list pools when cluster details are not set, while deploying vm / attaching volume (apache#8373)
This PR fixes reorder/list pools when cluster details are not set, while deploying vm / attaching volume. Problem: Attach volume to a VM fails, on infra with zone-wide pools & vm.allocation.algorithm=userdispersing as the cluster details are not set (passed as null) while reordering / listing pools by volumes. Solution: Ignore cluster details when not set, while reordering / listing pools by volumes.
1 parent 4f40eae commit e87ce0c

3 files changed

Lines changed: 173 additions & 22 deletions

File tree

engine/schema/src/main/java/com/cloud/storage/dao/VolumeDaoImpl.java

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,9 @@ public class VolumeDaoImpl extends GenericDaoBase<VolumeVO, Long> implements Vol
8383
protected static final String SELECT_HYPERTYPE_FROM_ZONE_VOLUME = "SELECT s.hypervisor from volumes v, storage_pool s where v.pool_id = s.id and v.id = ?";
8484
protected static final String SELECT_POOLSCOPE = "SELECT s.scope from storage_pool s, volumes v where s.id = v.pool_id and v.id = ?";
8585

86-
private static final String ORDER_POOLS_NUMBER_OF_VOLUMES_FOR_ACCOUNT = "SELECT pool.id, SUM(IF(vol.state='Ready' AND vol.account_id = ?, 1, 0)) FROM `cloud`.`storage_pool` pool LEFT JOIN `cloud`.`volumes` vol ON pool.id = vol.pool_id WHERE pool.data_center_id = ? "
87-
+ " AND pool.pod_id = ? AND pool.cluster_id = ? " + " GROUP BY pool.id ORDER BY 2 ASC ";
86+
private static final String ORDER_POOLS_NUMBER_OF_VOLUMES_FOR_ACCOUNT_PART1 = "SELECT pool.id, SUM(IF(vol.state='Ready' AND vol.account_id = ?, 1, 0)) FROM `cloud`.`storage_pool` pool LEFT JOIN `cloud`.`volumes` vol ON pool.id = vol.pool_id WHERE pool.data_center_id = ? ";
87+
private static final String ORDER_POOLS_NUMBER_OF_VOLUMES_FOR_ACCOUNT_PART2 = " GROUP BY pool.id ORDER BY 2 ASC ";
88+
8889
private static final String ORDER_ZONE_WIDE_POOLS_NUMBER_OF_VOLUMES_FOR_ACCOUNT = "SELECT pool.id, SUM(IF(vol.state='Ready' AND vol.account_id = ?, 1, 0)) FROM `cloud`.`storage_pool` pool LEFT JOIN `cloud`.`volumes` vol ON pool.id = vol.pool_id WHERE pool.data_center_id = ? "
8990
+ " AND pool.scope = 'ZONE' AND pool.status='Up' " + " GROUP BY pool.id ORDER BY 2 ASC ";
9091

@@ -612,24 +613,39 @@ public boolean updateState(com.cloud.storage.Volume.State currentState, Event ev
612613
public List<Long> listPoolIdsByVolumeCount(long dcId, Long podId, Long clusterId, long accountId) {
613614
TransactionLegacy txn = TransactionLegacy.currentTxn();
614615
PreparedStatement pstmt = null;
615-
List<Long> result = new ArrayList<Long>();
616+
List<Long> result = new ArrayList<>();
617+
StringBuilder sql = new StringBuilder(ORDER_POOLS_NUMBER_OF_VOLUMES_FOR_ACCOUNT_PART1);
616618
try {
617-
String sql = ORDER_POOLS_NUMBER_OF_VOLUMES_FOR_ACCOUNT;
618-
pstmt = txn.prepareAutoCloseStatement(sql);
619-
pstmt.setLong(1, accountId);
620-
pstmt.setLong(2, dcId);
621-
pstmt.setLong(3, podId);
622-
pstmt.setLong(4, clusterId);
619+
List<Long> resourceIdList = new ArrayList<>();
620+
resourceIdList.add(accountId);
621+
resourceIdList.add(dcId);
622+
623+
if (podId != null) {
624+
sql.append(" AND pool.pod_id = ?");
625+
resourceIdList.add(podId);
626+
}
627+
if (clusterId != null) {
628+
sql.append(" AND pool.cluster_id = ?");
629+
resourceIdList.add(clusterId);
630+
}
631+
sql.append(ORDER_POOLS_NUMBER_OF_VOLUMES_FOR_ACCOUNT_PART2);
632+
633+
pstmt = txn.prepareAutoCloseStatement(sql.toString());
634+
for (int i = 0; i < resourceIdList.size(); i++) {
635+
pstmt.setLong(i + 1, resourceIdList.get(i));
636+
}
623637

624638
ResultSet rs = pstmt.executeQuery();
625639
while (rs.next()) {
626640
result.add(rs.getLong(1));
627641
}
628642
return result;
629643
} catch (SQLException e) {
630-
throw new CloudRuntimeException("DB Exception on: " + ORDER_POOLS_NUMBER_OF_VOLUMES_FOR_ACCOUNT, e);
644+
s_logger.debug("DB Exception on: " + sql.toString(), e);
645+
throw new CloudRuntimeException(e);
631646
} catch (Throwable e) {
632-
throw new CloudRuntimeException("Caught: " + ORDER_POOLS_NUMBER_OF_VOLUMES_FOR_ACCOUNT, e);
647+
s_logger.debug("Caught: " + sql.toString(), e);
648+
throw new CloudRuntimeException(e);
633649
}
634650
}
635651

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
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 com.cloud.storage.dao;
18+
19+
import static org.mockito.ArgumentMatchers.anyInt;
20+
import static org.mockito.ArgumentMatchers.anyLong;
21+
import static org.mockito.ArgumentMatchers.startsWith;
22+
import static org.mockito.Mockito.times;
23+
import static org.mockito.Mockito.verify;
24+
import static org.mockito.Mockito.when;
25+
26+
import java.sql.PreparedStatement;
27+
import java.sql.ResultSet;
28+
import java.sql.SQLException;
29+
30+
import org.junit.AfterClass;
31+
import org.junit.BeforeClass;
32+
import org.junit.Test;
33+
import org.junit.runner.RunWith;
34+
import org.mockito.Mock;
35+
import org.mockito.MockedStatic;
36+
import org.mockito.Mockito;
37+
import org.mockito.junit.MockitoJUnitRunner;
38+
39+
import com.cloud.utils.db.TransactionLegacy;
40+
41+
@RunWith(MockitoJUnitRunner.class)
42+
public class VolumeDaoImplTest {
43+
@Mock
44+
private PreparedStatement preparedStatementMock;
45+
46+
@Mock
47+
private TransactionLegacy transactionMock;
48+
49+
private static MockedStatic<TransactionLegacy> mockedTransactionLegacy;
50+
51+
private final VolumeDaoImpl volumeDao = new VolumeDaoImpl();
52+
53+
@BeforeClass
54+
public static void init() {
55+
mockedTransactionLegacy = Mockito.mockStatic(TransactionLegacy.class);
56+
}
57+
58+
@AfterClass
59+
public static void close() {
60+
mockedTransactionLegacy.close();
61+
}
62+
63+
@Test
64+
public void testListPoolIdsByVolumeCount_with_cluster_details() throws SQLException {
65+
final String ORDER_POOLS_NUMBER_OF_VOLUMES_FOR_ACCOUNT_QUERY_WITH_CLUSTER =
66+
"SELECT pool.id, SUM(IF(vol.state='Ready' AND vol.account_id = ?, 1, 0)) FROM `cloud`.`storage_pool` pool LEFT JOIN `cloud`.`volumes` vol ON pool.id = vol.pool_id WHERE pool.data_center_id = ? AND pool.pod_id = ? AND pool.cluster_id = ? GROUP BY pool.id ORDER BY 2 ASC ";
67+
final long dcId = 1, accountId = 1;
68+
final Long podId = 1L, clusterId = 1L;
69+
70+
when(TransactionLegacy.currentTxn()).thenReturn(transactionMock);
71+
when(transactionMock.prepareAutoCloseStatement(startsWith(ORDER_POOLS_NUMBER_OF_VOLUMES_FOR_ACCOUNT_QUERY_WITH_CLUSTER))).thenReturn(preparedStatementMock);
72+
ResultSet rs = Mockito.mock(ResultSet.class);
73+
when(preparedStatementMock.executeQuery()).thenReturn(rs, rs);
74+
75+
volumeDao.listPoolIdsByVolumeCount(dcId, podId, clusterId, accountId);
76+
77+
verify(transactionMock, times(1)).prepareAutoCloseStatement(ORDER_POOLS_NUMBER_OF_VOLUMES_FOR_ACCOUNT_QUERY_WITH_CLUSTER);
78+
verify(preparedStatementMock, times(1)).setLong(1, accountId);
79+
verify(preparedStatementMock, times(1)).setLong(2, dcId);
80+
verify(preparedStatementMock, times(1)).setLong(3, podId);
81+
verify(preparedStatementMock, times(1)).setLong(4, clusterId);
82+
verify(preparedStatementMock, times(4)).setLong(anyInt(), anyLong());
83+
verify(preparedStatementMock, times(1)).executeQuery();
84+
}
85+
86+
@Test
87+
public void testListPoolIdsByVolumeCount_without_cluster_details() throws SQLException {
88+
final String ORDER_POOLS_NUMBER_OF_VOLUMES_FOR_ACCOUNT_QUERY_WITHOUT_CLUSTER =
89+
"SELECT pool.id, SUM(IF(vol.state='Ready' AND vol.account_id = ?, 1, 0)) FROM `cloud`.`storage_pool` pool LEFT JOIN `cloud`.`volumes` vol ON pool.id = vol.pool_id WHERE pool.data_center_id = ? GROUP BY pool.id ORDER BY 2 ASC ";
90+
final long dcId = 1, accountId = 1;
91+
92+
when(TransactionLegacy.currentTxn()).thenReturn(transactionMock);
93+
when(transactionMock.prepareAutoCloseStatement(startsWith(ORDER_POOLS_NUMBER_OF_VOLUMES_FOR_ACCOUNT_QUERY_WITHOUT_CLUSTER))).thenReturn(preparedStatementMock);
94+
ResultSet rs = Mockito.mock(ResultSet.class);
95+
when(preparedStatementMock.executeQuery()).thenReturn(rs, rs);
96+
97+
volumeDao.listPoolIdsByVolumeCount(dcId, null, null, accountId);
98+
99+
verify(transactionMock, times(1)).prepareAutoCloseStatement(ORDER_POOLS_NUMBER_OF_VOLUMES_FOR_ACCOUNT_QUERY_WITHOUT_CLUSTER);
100+
verify(preparedStatementMock, times(1)).setLong(1, accountId);
101+
verify(preparedStatementMock, times(1)).setLong(2, dcId);
102+
verify(preparedStatementMock, times(2)).setLong(anyInt(), anyLong());
103+
verify(preparedStatementMock, times(1)).executeQuery();
104+
}
105+
}

engine/storage/src/test/java/org/apache/cloudstack/storage/allocator/AbstractStoragePoolAllocatorTest.java

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,13 @@
1717
package org.apache.cloudstack.storage.allocator;
1818

1919

20-
import com.cloud.deploy.DeploymentPlan;
21-
import com.cloud.deploy.DeploymentPlanner;
22-
import com.cloud.storage.Storage;
23-
import com.cloud.storage.StoragePool;
24-
import com.cloud.user.Account;
25-
import com.cloud.vm.DiskProfile;
26-
import com.cloud.vm.VirtualMachineProfile;
20+
import static org.mockito.Mockito.when;
21+
22+
import java.util.ArrayList;
23+
import java.util.HashSet;
24+
import java.util.List;
25+
import java.util.Set;
26+
2727
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
2828
import org.junit.After;
2929
import org.junit.Assert;
@@ -34,10 +34,14 @@
3434
import org.mockito.Mockito;
3535
import org.mockito.junit.MockitoJUnitRunner;
3636

37-
import java.util.ArrayList;
38-
import java.util.HashSet;
39-
import java.util.List;
40-
import java.util.Set;
37+
import com.cloud.deploy.DeploymentPlan;
38+
import com.cloud.deploy.DeploymentPlanner;
39+
import com.cloud.storage.Storage;
40+
import com.cloud.storage.StoragePool;
41+
import com.cloud.storage.dao.VolumeDao;
42+
import com.cloud.user.Account;
43+
import com.cloud.vm.DiskProfile;
44+
import com.cloud.vm.VirtualMachineProfile;
4145

4246
@RunWith(MockitoJUnitRunner.class)
4347
public class AbstractStoragePoolAllocatorTest {
@@ -51,6 +55,9 @@ public class AbstractStoragePoolAllocatorTest {
5155
Account account;
5256
private List<StoragePool> pools;
5357

58+
@Mock
59+
VolumeDao volumeDao;
60+
5461
@Before
5562
public void setUp() {
5663
pools = new ArrayList<>();
@@ -83,6 +90,29 @@ public void reorderStoragePoolsBasedOnAlgorithm_userdispersing() {
8390
Mockito.verify(allocator, Mockito.times(0)).reorderRandomPools(pools);
8491
}
8592

93+
@Test
94+
public void reorderStoragePoolsBasedOnAlgorithm_userdispersing_reorder_check() {
95+
allocator.allocationAlgorithm = "userdispersing";
96+
allocator.volumeDao = volumeDao;
97+
98+
when(plan.getDataCenterId()).thenReturn(1l);
99+
when(plan.getPodId()).thenReturn(1l);
100+
when(plan.getClusterId()).thenReturn(1l);
101+
when(account.getAccountId()).thenReturn(1l);
102+
List<Long> poolIds = new ArrayList<>();
103+
poolIds.add(1l);
104+
poolIds.add(9l);
105+
when(volumeDao.listPoolIdsByVolumeCount(1l,1l,1l,1l)).thenReturn(poolIds);
106+
107+
List<StoragePool> reorderedPools = allocator.reorderStoragePoolsBasedOnAlgorithm(pools, plan, account);
108+
Assert.assertEquals(poolIds.size(),reorderedPools.size());
109+
110+
Mockito.verify(allocator, Mockito.times(0)).reorderPoolsByCapacity(plan, pools);
111+
Mockito.verify(allocator, Mockito.times(1)).reorderPoolsByNumberOfVolumes(plan, pools, account);
112+
Mockito.verify(allocator, Mockito.times(0)).reorderRandomPools(pools);
113+
Mockito.verify(volumeDao, Mockito.times(1)).listPoolIdsByVolumeCount(1l,1l,1l,1l);
114+
}
115+
86116
@Test
87117
public void reorderStoragePoolsBasedOnAlgorithm_firstfitleastconsumed() {
88118
allocator.allocationAlgorithm = "firstfitleastconsumed";

0 commit comments

Comments
 (0)