mirror of
				https://github.com/apache/cloudstack.git
				synced 2025-10-26 08:42:29 +01:00 
			
		
		
		
	Fix reorder/list pools when cluster details are not set, while deploying vm / attaching volume (#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.
This commit is contained in:
		
							parent
							
								
									4f40eae1c4
								
							
						
					
					
						commit
						e87ce0c723
					
				| @ -83,8 +83,9 @@ public class VolumeDaoImpl extends GenericDaoBase<VolumeVO, Long> implements Vol | |||||||
|     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 = ?"; |     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 = ?"; | ||||||
|     protected static final String SELECT_POOLSCOPE = "SELECT s.scope from storage_pool s, volumes v where s.id = v.pool_id and v.id = ?"; |     protected static final String SELECT_POOLSCOPE = "SELECT s.scope from storage_pool s, volumes v where s.id = v.pool_id and v.id = ?"; | ||||||
| 
 | 
 | ||||||
|     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 = ? " |     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 = ? "; | ||||||
|             + " AND pool.pod_id = ? AND pool.cluster_id = ? " + " GROUP BY pool.id ORDER BY 2 ASC "; |     private static final String ORDER_POOLS_NUMBER_OF_VOLUMES_FOR_ACCOUNT_PART2 = " GROUP BY pool.id ORDER BY 2 ASC "; | ||||||
|  | 
 | ||||||
|     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 = ? " |     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 = ? " | ||||||
|             + " AND pool.scope = 'ZONE' AND pool.status='Up' " + " GROUP BY pool.id ORDER BY 2 ASC "; |             + " AND pool.scope = 'ZONE' AND pool.status='Up' " + " GROUP BY pool.id ORDER BY 2 ASC "; | ||||||
| 
 | 
 | ||||||
| @ -612,14 +613,27 @@ public class VolumeDaoImpl extends GenericDaoBase<VolumeVO, Long> implements Vol | |||||||
|     public List<Long> listPoolIdsByVolumeCount(long dcId, Long podId, Long clusterId, long accountId) { |     public List<Long> listPoolIdsByVolumeCount(long dcId, Long podId, Long clusterId, long accountId) { | ||||||
|         TransactionLegacy txn = TransactionLegacy.currentTxn(); |         TransactionLegacy txn = TransactionLegacy.currentTxn(); | ||||||
|         PreparedStatement pstmt = null; |         PreparedStatement pstmt = null; | ||||||
|         List<Long> result = new ArrayList<Long>(); |         List<Long> result = new ArrayList<>(); | ||||||
|  |         StringBuilder sql = new StringBuilder(ORDER_POOLS_NUMBER_OF_VOLUMES_FOR_ACCOUNT_PART1); | ||||||
|         try { |         try { | ||||||
|             String sql = ORDER_POOLS_NUMBER_OF_VOLUMES_FOR_ACCOUNT; |             List<Long> resourceIdList = new ArrayList<>(); | ||||||
|             pstmt = txn.prepareAutoCloseStatement(sql); |             resourceIdList.add(accountId); | ||||||
|             pstmt.setLong(1, accountId); |             resourceIdList.add(dcId); | ||||||
|             pstmt.setLong(2, dcId); | 
 | ||||||
|             pstmt.setLong(3, podId); |             if (podId != null) { | ||||||
|             pstmt.setLong(4, clusterId); |                 sql.append(" AND pool.pod_id = ?"); | ||||||
|  |                 resourceIdList.add(podId); | ||||||
|  |             } | ||||||
|  |             if (clusterId != null) { | ||||||
|  |                 sql.append(" AND pool.cluster_id = ?"); | ||||||
|  |                 resourceIdList.add(clusterId); | ||||||
|  |             } | ||||||
|  |             sql.append(ORDER_POOLS_NUMBER_OF_VOLUMES_FOR_ACCOUNT_PART2); | ||||||
|  | 
 | ||||||
|  |             pstmt = txn.prepareAutoCloseStatement(sql.toString()); | ||||||
|  |             for (int i = 0; i < resourceIdList.size(); i++) { | ||||||
|  |                 pstmt.setLong(i + 1, resourceIdList.get(i)); | ||||||
|  |             } | ||||||
| 
 | 
 | ||||||
|             ResultSet rs = pstmt.executeQuery(); |             ResultSet rs = pstmt.executeQuery(); | ||||||
|             while (rs.next()) { |             while (rs.next()) { | ||||||
| @ -627,9 +641,11 @@ public class VolumeDaoImpl extends GenericDaoBase<VolumeVO, Long> implements Vol | |||||||
|             } |             } | ||||||
|             return result; |             return result; | ||||||
|         } catch (SQLException e) { |         } catch (SQLException e) { | ||||||
|             throw new CloudRuntimeException("DB Exception on: " + ORDER_POOLS_NUMBER_OF_VOLUMES_FOR_ACCOUNT, e); |             s_logger.debug("DB Exception on: " + sql.toString(), e); | ||||||
|  |             throw new CloudRuntimeException(e); | ||||||
|         } catch (Throwable e) { |         } catch (Throwable e) { | ||||||
|             throw new CloudRuntimeException("Caught: " + ORDER_POOLS_NUMBER_OF_VOLUMES_FOR_ACCOUNT, e); |             s_logger.debug("Caught: " + sql.toString(), e); | ||||||
|  |             throw new CloudRuntimeException(e); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -0,0 +1,105 @@ | |||||||
|  | // 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 com.cloud.storage.dao; | ||||||
|  | 
 | ||||||
|  | import static org.mockito.ArgumentMatchers.anyInt; | ||||||
|  | import static org.mockito.ArgumentMatchers.anyLong; | ||||||
|  | import static org.mockito.ArgumentMatchers.startsWith; | ||||||
|  | import static org.mockito.Mockito.times; | ||||||
|  | import static org.mockito.Mockito.verify; | ||||||
|  | import static org.mockito.Mockito.when; | ||||||
|  | 
 | ||||||
|  | import java.sql.PreparedStatement; | ||||||
|  | import java.sql.ResultSet; | ||||||
|  | import java.sql.SQLException; | ||||||
|  | 
 | ||||||
|  | import org.junit.AfterClass; | ||||||
|  | import org.junit.BeforeClass; | ||||||
|  | import org.junit.Test; | ||||||
|  | import org.junit.runner.RunWith; | ||||||
|  | import org.mockito.Mock; | ||||||
|  | import org.mockito.MockedStatic; | ||||||
|  | import org.mockito.Mockito; | ||||||
|  | import org.mockito.junit.MockitoJUnitRunner; | ||||||
|  | 
 | ||||||
|  | import com.cloud.utils.db.TransactionLegacy; | ||||||
|  | 
 | ||||||
|  | @RunWith(MockitoJUnitRunner.class) | ||||||
|  | public class VolumeDaoImplTest { | ||||||
|  |     @Mock | ||||||
|  |     private PreparedStatement preparedStatementMock; | ||||||
|  | 
 | ||||||
|  |     @Mock | ||||||
|  |     private TransactionLegacy transactionMock; | ||||||
|  | 
 | ||||||
|  |     private static MockedStatic<TransactionLegacy> mockedTransactionLegacy; | ||||||
|  | 
 | ||||||
|  |     private final VolumeDaoImpl volumeDao = new VolumeDaoImpl(); | ||||||
|  | 
 | ||||||
|  |     @BeforeClass | ||||||
|  |     public static void init() { | ||||||
|  |         mockedTransactionLegacy = Mockito.mockStatic(TransactionLegacy.class); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @AfterClass | ||||||
|  |     public static void close() { | ||||||
|  |         mockedTransactionLegacy.close(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     public void testListPoolIdsByVolumeCount_with_cluster_details() throws SQLException { | ||||||
|  |         final String ORDER_POOLS_NUMBER_OF_VOLUMES_FOR_ACCOUNT_QUERY_WITH_CLUSTER = | ||||||
|  |                 "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 "; | ||||||
|  |         final long dcId = 1, accountId = 1; | ||||||
|  |         final Long podId = 1L, clusterId = 1L; | ||||||
|  | 
 | ||||||
|  |         when(TransactionLegacy.currentTxn()).thenReturn(transactionMock); | ||||||
|  |         when(transactionMock.prepareAutoCloseStatement(startsWith(ORDER_POOLS_NUMBER_OF_VOLUMES_FOR_ACCOUNT_QUERY_WITH_CLUSTER))).thenReturn(preparedStatementMock); | ||||||
|  |         ResultSet rs = Mockito.mock(ResultSet.class); | ||||||
|  |         when(preparedStatementMock.executeQuery()).thenReturn(rs, rs); | ||||||
|  | 
 | ||||||
|  |         volumeDao.listPoolIdsByVolumeCount(dcId, podId, clusterId, accountId); | ||||||
|  | 
 | ||||||
|  |         verify(transactionMock, times(1)).prepareAutoCloseStatement(ORDER_POOLS_NUMBER_OF_VOLUMES_FOR_ACCOUNT_QUERY_WITH_CLUSTER); | ||||||
|  |         verify(preparedStatementMock, times(1)).setLong(1, accountId); | ||||||
|  |         verify(preparedStatementMock, times(1)).setLong(2, dcId); | ||||||
|  |         verify(preparedStatementMock, times(1)).setLong(3, podId); | ||||||
|  |         verify(preparedStatementMock, times(1)).setLong(4, clusterId); | ||||||
|  |         verify(preparedStatementMock, times(4)).setLong(anyInt(), anyLong()); | ||||||
|  |         verify(preparedStatementMock, times(1)).executeQuery(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     public void testListPoolIdsByVolumeCount_without_cluster_details() throws SQLException { | ||||||
|  |         final String ORDER_POOLS_NUMBER_OF_VOLUMES_FOR_ACCOUNT_QUERY_WITHOUT_CLUSTER = | ||||||
|  |                 "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 "; | ||||||
|  |         final long dcId = 1, accountId = 1; | ||||||
|  | 
 | ||||||
|  |         when(TransactionLegacy.currentTxn()).thenReturn(transactionMock); | ||||||
|  |         when(transactionMock.prepareAutoCloseStatement(startsWith(ORDER_POOLS_NUMBER_OF_VOLUMES_FOR_ACCOUNT_QUERY_WITHOUT_CLUSTER))).thenReturn(preparedStatementMock); | ||||||
|  |         ResultSet rs = Mockito.mock(ResultSet.class); | ||||||
|  |         when(preparedStatementMock.executeQuery()).thenReturn(rs, rs); | ||||||
|  | 
 | ||||||
|  |         volumeDao.listPoolIdsByVolumeCount(dcId, null, null, accountId); | ||||||
|  | 
 | ||||||
|  |         verify(transactionMock, times(1)).prepareAutoCloseStatement(ORDER_POOLS_NUMBER_OF_VOLUMES_FOR_ACCOUNT_QUERY_WITHOUT_CLUSTER); | ||||||
|  |         verify(preparedStatementMock, times(1)).setLong(1, accountId); | ||||||
|  |         verify(preparedStatementMock, times(1)).setLong(2, dcId); | ||||||
|  |         verify(preparedStatementMock, times(2)).setLong(anyInt(), anyLong()); | ||||||
|  |         verify(preparedStatementMock, times(1)).executeQuery(); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -17,13 +17,13 @@ | |||||||
| package org.apache.cloudstack.storage.allocator; | package org.apache.cloudstack.storage.allocator; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| import com.cloud.deploy.DeploymentPlan; | import static org.mockito.Mockito.when; | ||||||
| import com.cloud.deploy.DeploymentPlanner; | 
 | ||||||
| import com.cloud.storage.Storage; | import java.util.ArrayList; | ||||||
| import com.cloud.storage.StoragePool; | import java.util.HashSet; | ||||||
| import com.cloud.user.Account; | import java.util.List; | ||||||
| import com.cloud.vm.DiskProfile; | import java.util.Set; | ||||||
| import com.cloud.vm.VirtualMachineProfile; | 
 | ||||||
| import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; | import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; | ||||||
| import org.junit.After; | import org.junit.After; | ||||||
| import org.junit.Assert; | import org.junit.Assert; | ||||||
| @ -34,10 +34,14 @@ import org.mockito.Mock; | |||||||
| import org.mockito.Mockito; | import org.mockito.Mockito; | ||||||
| import org.mockito.junit.MockitoJUnitRunner; | import org.mockito.junit.MockitoJUnitRunner; | ||||||
| 
 | 
 | ||||||
| import java.util.ArrayList; | import com.cloud.deploy.DeploymentPlan; | ||||||
| import java.util.HashSet; | import com.cloud.deploy.DeploymentPlanner; | ||||||
| import java.util.List; | import com.cloud.storage.Storage; | ||||||
| import java.util.Set; | import com.cloud.storage.StoragePool; | ||||||
|  | import com.cloud.storage.dao.VolumeDao; | ||||||
|  | import com.cloud.user.Account; | ||||||
|  | import com.cloud.vm.DiskProfile; | ||||||
|  | import com.cloud.vm.VirtualMachineProfile; | ||||||
| 
 | 
 | ||||||
| @RunWith(MockitoJUnitRunner.class) | @RunWith(MockitoJUnitRunner.class) | ||||||
| public class AbstractStoragePoolAllocatorTest { | public class AbstractStoragePoolAllocatorTest { | ||||||
| @ -51,6 +55,9 @@ public class AbstractStoragePoolAllocatorTest { | |||||||
|     Account account; |     Account account; | ||||||
|     private List<StoragePool> pools; |     private List<StoragePool> pools; | ||||||
| 
 | 
 | ||||||
|  |     @Mock | ||||||
|  |     VolumeDao volumeDao; | ||||||
|  | 
 | ||||||
|     @Before |     @Before | ||||||
|     public void setUp() { |     public void setUp() { | ||||||
|         pools = new ArrayList<>(); |         pools = new ArrayList<>(); | ||||||
| @ -83,6 +90,29 @@ public class AbstractStoragePoolAllocatorTest { | |||||||
|         Mockito.verify(allocator, Mockito.times(0)).reorderRandomPools(pools); |         Mockito.verify(allocator, Mockito.times(0)).reorderRandomPools(pools); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     @Test | ||||||
|  |     public void reorderStoragePoolsBasedOnAlgorithm_userdispersing_reorder_check() { | ||||||
|  |         allocator.allocationAlgorithm = "userdispersing"; | ||||||
|  |         allocator.volumeDao = volumeDao; | ||||||
|  | 
 | ||||||
|  |         when(plan.getDataCenterId()).thenReturn(1l); | ||||||
|  |         when(plan.getPodId()).thenReturn(1l); | ||||||
|  |         when(plan.getClusterId()).thenReturn(1l); | ||||||
|  |         when(account.getAccountId()).thenReturn(1l); | ||||||
|  |         List<Long> poolIds = new ArrayList<>(); | ||||||
|  |         poolIds.add(1l); | ||||||
|  |         poolIds.add(9l); | ||||||
|  |         when(volumeDao.listPoolIdsByVolumeCount(1l,1l,1l,1l)).thenReturn(poolIds); | ||||||
|  | 
 | ||||||
|  |         List<StoragePool> reorderedPools = allocator.reorderStoragePoolsBasedOnAlgorithm(pools, plan, account); | ||||||
|  |         Assert.assertEquals(poolIds.size(),reorderedPools.size()); | ||||||
|  | 
 | ||||||
|  |         Mockito.verify(allocator, Mockito.times(0)).reorderPoolsByCapacity(plan, pools); | ||||||
|  |         Mockito.verify(allocator, Mockito.times(1)).reorderPoolsByNumberOfVolumes(plan, pools, account); | ||||||
|  |         Mockito.verify(allocator, Mockito.times(0)).reorderRandomPools(pools); | ||||||
|  |         Mockito.verify(volumeDao, Mockito.times(1)).listPoolIdsByVolumeCount(1l,1l,1l,1l); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     @Test |     @Test | ||||||
|     public void reorderStoragePoolsBasedOnAlgorithm_firstfitleastconsumed() { |     public void reorderStoragePoolsBasedOnAlgorithm_firstfitleastconsumed() { | ||||||
|         allocator.allocationAlgorithm = "firstfitleastconsumed"; |         allocator.allocationAlgorithm = "firstfitleastconsumed"; | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user