diff --git a/engine/schema/src/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDao.java b/engine/schema/src/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDao.java index 2398e91c90c7..870948fb905c 100644 --- a/engine/schema/src/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDao.java +++ b/engine/schema/src/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDao.java @@ -75,9 +75,9 @@ public interface PrimaryDataStoreDao extends GenericDao { * details to match. All must match for the pool to be returned. * @return List of StoragePoolVO */ - List findPoolsByDetails(long dcId, long podId, Long clusterId, Map details, ScopeType scope); + List findPoolsByDetails(long dcId, Long podId, Long clusterId, Map details, ScopeType scope); - List findPoolsByTags(long dcId, long podId, Long clusterId, String[] tags); + List findPoolsByTags(long dcId, Long podId, Long clusterId, String[] tags); List findDisabledPoolsByScope(long dcId, Long podId, Long clusterId, ScopeType scope); @@ -112,7 +112,7 @@ public interface PrimaryDataStoreDao extends GenericDao { List listPoolsByCluster(long clusterId); - List findLocalStoragePoolsByTags(long dcId, long podId, Long clusterId, String[] tags); + List findLocalStoragePoolsByTags(long dcId, Long podId, Long clusterId, String[] tags); List findZoneWideStoragePoolsByTags(long dcId, String[] tags); diff --git a/engine/schema/src/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDaoImpl.java b/engine/schema/src/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDaoImpl.java index 5a3c229a94c1..606f21e017ea 100644 --- a/engine/schema/src/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDaoImpl.java +++ b/engine/schema/src/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDaoImpl.java @@ -53,6 +53,7 @@ public class PrimaryDataStoreDaoImpl extends GenericDaoBase protected final SearchBuilder AllFieldSearch; protected final SearchBuilder DcPodSearch; protected final SearchBuilder DcPodAnyClusterSearch; + protected final SearchBuilder DcAnyPodAnyClusterSearch; protected final SearchBuilder DeleteLvmSearch; protected final SearchBuilder DcLocalStorageSearch; protected final GenericSearchBuilder StatusCountSearch; @@ -65,14 +66,14 @@ public class PrimaryDataStoreDaoImpl extends GenericDaoBase protected StoragePoolTagsDao _tagsDao; protected final String DetailsSqlPrefix = - "SELECT storage_pool.* from storage_pool LEFT JOIN storage_pool_details ON storage_pool.id = storage_pool_details.pool_id WHERE storage_pool.removed is null and storage_pool.status = 'Up' and storage_pool.data_center_id = ? and (storage_pool.pod_id = ? or storage_pool.pod_id is null) and storage_pool.scope = ? and ("; + "SELECT storage_pool.* from storage_pool LEFT JOIN storage_pool_details ON storage_pool.id = storage_pool_details.pool_id WHERE storage_pool.removed is null and storage_pool.status = 'Up' and storage_pool.data_center_id = ? and storage_pool.scope = ? and ("; protected final String DetailsSqlSuffix = ") GROUP BY storage_pool_details.pool_id HAVING COUNT(storage_pool_details.name) >= ?"; protected final String ZoneWideTagsSqlPrefix = "SELECT storage_pool.* from storage_pool LEFT JOIN storage_pool_tags ON storage_pool.id = storage_pool_tags.pool_id WHERE storage_pool.removed is null and storage_pool.status = 'Up' and storage_pool.data_center_id = ? and storage_pool.scope = ? and ("; protected final String ZoneWideTagsSqlSuffix = ") GROUP BY storage_pool_tags.pool_id HAVING COUNT(storage_pool_tags.tag) >= ?"; // Storage tags are now separate from storage_pool_details, leaving only details on that table - protected final String TagsSqlPrefix = "SELECT storage_pool.* from storage_pool LEFT JOIN storage_pool_tags ON storage_pool.id = storage_pool_tags.pool_id WHERE storage_pool.removed is null and storage_pool.status = 'Up' and storage_pool.data_center_id = ? and (storage_pool.pod_id = ? or storage_pool.pod_id is null) and storage_pool.scope = ? and ("; + protected final String TagsSqlPrefix = "SELECT storage_pool.* from storage_pool LEFT JOIN storage_pool_tags ON storage_pool.id = storage_pool_tags.pool_id WHERE storage_pool.removed is null and storage_pool.status = 'Up' and storage_pool.data_center_id = ? and storage_pool.scope = ? and ("; protected final String TagsSqlSuffix = ") GROUP BY storage_pool_tags.pool_id HAVING COUNT(storage_pool_tags.tag) >= ?"; protected final String FindPoolTags = "SELECT storage_pool_tags.tag FROM storage_pool_tags WHERE pool_id = ?"; @@ -119,6 +120,12 @@ public PrimaryDataStoreDaoImpl() { DcPodAnyClusterSearch.cp(); DcPodAnyClusterSearch.done(); + DcAnyPodAnyClusterSearch = createSearchBuilder(); + DcAnyPodAnyClusterSearch.and("datacenterId", DcAnyPodAnyClusterSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); + DcAnyPodAnyClusterSearch.and("status", DcAnyPodAnyClusterSearch.entity().getStatus(), SearchCriteria.Op.EQ); + DcAnyPodAnyClusterSearch.and("scope", DcAnyPodAnyClusterSearch.entity().getScope(), SearchCriteria.Op.EQ); + DcAnyPodAnyClusterSearch.done(); + DeleteLvmSearch = createSearchBuilder(); DeleteLvmSearch.and("ids", DeleteLvmSearch.entity().getId(), SearchCriteria.Op.IN); DeleteLvmSearch.and().op("LVM", DeleteLvmSearch.entity().getPoolType(), SearchCriteria.Op.EQ); @@ -244,13 +251,19 @@ public List listBy(long datacenterId, Long podId, Long clusterId, sc.setParameters("cluster", clusterId); return listBy(sc); - } else { + } else if (podId != null) { SearchCriteria sc = DcPodAnyClusterSearch.create(); sc.setParameters("datacenterId", datacenterId); sc.setParameters("podId", podId); sc.setParameters("status", Status.Up); sc.setParameters("scope", scope); return listBy(sc); + } else { + SearchCriteria sc = DcAnyPodAnyClusterSearch.create(); + sc.setParameters("datacenterId", datacenterId); + sc.setParameters("status", Status.Up); + sc.setParameters("scope", scope); + return listBy(sc); } } @@ -300,10 +313,10 @@ public StoragePoolVO persist(StoragePoolVO pool, Map details, Li * @param valuesLength values length * @return list of storage pools matching conditions */ - protected List findPoolsByDetailsOrTagsInternal(long dcId, long podId, Long clusterId, ScopeType scope, String sqlValues, ValueType valuesType, int valuesLength) { + protected List findPoolsByDetailsOrTagsInternal(long dcId, Long podId, Long clusterId, ScopeType scope, String sqlValues, ValueType valuesType, int valuesLength) { String sqlPrefix = valuesType.equals(ValueType.DETAILS) ? DetailsSqlPrefix : TagsSqlPrefix; String sqlSuffix = valuesType.equals(ValueType.DETAILS) ? DetailsSqlSuffix : TagsSqlSuffix; - String sql = getSqlPreparedStatement(sqlPrefix, sqlSuffix, sqlValues, clusterId); + String sql = getSqlPreparedStatement(sqlPrefix, sqlSuffix, sqlValues, clusterId, podId); return searchStoragePoolsPreparedStatement(sql, dcId, podId, clusterId, scope, valuesLength); } @@ -325,10 +338,10 @@ protected List searchStoragePoolsPreparedStatement(String sql, lo if (pstmt != null) { int i = 1; pstmt.setLong(i++, dcId); + pstmt.setString(i++, scope.toString()); if (podId != null) { pstmt.setLong(i++, podId); } - pstmt.setString(i++, scope.toString()); if (clusterId != null) { pstmt.setLong(i++, clusterId); } @@ -355,8 +368,11 @@ protected List searchStoragePoolsPreparedStatement(String sql, lo * @param clusterId cluster id * @return sql prepared statement */ - protected String getSqlPreparedStatement(String sqlPrefix, String sqlSuffix, String sqlValues, Long clusterId) { + protected String getSqlPreparedStatement(String sqlPrefix, String sqlSuffix, String sqlValues, Long clusterId, Long podId) { StringBuilder sql = new StringBuilder(sqlPrefix); + if (podId != null) { + sql.append("storage_pool.pod_id = ? OR storage_pool.pod_id is null) AND ("); + } if (clusterId != null) { sql.append("storage_pool.cluster_id = ? OR storage_pool.cluster_id IS NULL) AND ("); } @@ -405,13 +421,13 @@ protected String getSqlValuesFromStorageTags(String[] tags) throws NullPointerEx @DB @Override - public List findPoolsByDetails(long dcId, long podId, Long clusterId, Map details, ScopeType scope) { + public List findPoolsByDetails(long dcId, Long podId, Long clusterId, Map details, ScopeType scope) { String sqlValues = getSqlValuesFromDetails(details); return findPoolsByDetailsOrTagsInternal(dcId, podId, clusterId, scope, sqlValues, ValueType.DETAILS, details.size()); } @Override - public List findPoolsByTags(long dcId, long podId, Long clusterId, String[] tags) { + public List findPoolsByTags(long dcId, Long podId, Long clusterId, String[] tags) { List storagePools = null; if (tags == null || tags.length == 0) { storagePools = listBy(dcId, podId, clusterId, ScopeType.CLUSTER); @@ -444,7 +460,7 @@ public List findDisabledPoolsByScope(long dcId, Long podId, Long } @Override - public List findLocalStoragePoolsByTags(long dcId, long podId, Long clusterId, String[] tags) { + public List findLocalStoragePoolsByTags(long dcId, Long podId, Long clusterId, String[] tags) { List storagePools = null; if (tags == null || tags.length == 0) { storagePools = listBy(dcId, podId, clusterId, ScopeType.HOST); @@ -498,7 +514,7 @@ public List findZoneWideStoragePoolsByTags(long dcId, String[] ta return sc.list(); } else { String sqlValues = getSqlValuesFromStorageTags(tags); - String sql = getSqlPreparedStatement(ZoneWideTagsSqlPrefix, ZoneWideTagsSqlSuffix, sqlValues, null); + String sql = getSqlPreparedStatement(ZoneWideTagsSqlPrefix, ZoneWideTagsSqlSuffix, sqlValues, null, null); return searchStoragePoolsPreparedStatement(sql, dcId, null, null, ScopeType.ZONE, tags.length); } } diff --git a/engine/schema/test/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDaoImplTest.java b/engine/schema/test/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDaoImplTest.java index 5b3d5d3c11ad..83909bff2ba6 100755 --- a/engine/schema/test/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDaoImplTest.java +++ b/engine/schema/test/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDaoImplTest.java @@ -118,14 +118,14 @@ public void testGetSqlValuesFromDetailsEmptyDetailss() { @Test public void testGetSqlPreparedStatementNullClusterId() { - String sqlPreparedStatement = primaryDataStoreDao.getSqlPreparedStatement(SQL_PREFIX, SQL_SUFFIX, SQL_VALUES, null); + String sqlPreparedStatement = primaryDataStoreDao.getSqlPreparedStatement(SQL_PREFIX, SQL_SUFFIX, SQL_VALUES, null, null); assertEquals(SQL_PREFIX + SQL_VALUES + SQL_SUFFIX, sqlPreparedStatement); } @Test public void testGetSqlPreparedStatementNotNullClusterId() { String clusterSql = "storage_pool.cluster_id = ? OR storage_pool.cluster_id IS NULL) AND ("; - String sqlPreparedStatement = primaryDataStoreDao.getSqlPreparedStatement(SQL_PREFIX, SQL_SUFFIX, SQL_VALUES, 1l); + String sqlPreparedStatement = primaryDataStoreDao.getSqlPreparedStatement(SQL_PREFIX, SQL_SUFFIX, SQL_VALUES, 1l, null); assertEquals(SQL_PREFIX + clusterSql + SQL_VALUES + SQL_SUFFIX, sqlPreparedStatement); } @@ -134,7 +134,7 @@ public void testFindPoolsByDetailsOrTagsInternalStorageTagsType() { List storagePools = primaryDataStoreDao.findPoolsByDetailsOrTagsInternal(DATACENTER_ID, POD_ID, CLUSTER_ID, SCOPE, SQL_VALUES, ValueType.TAGS, STORAGE_TAGS_ARRAY.length); assertEquals(Arrays.asList(storagePoolVO), storagePools); verify(primaryDataStoreDao).getSqlPreparedStatement( - primaryDataStoreDao.TagsSqlPrefix, primaryDataStoreDao.TagsSqlSuffix, SQL_VALUES, CLUSTER_ID); + primaryDataStoreDao.TagsSqlPrefix, primaryDataStoreDao.TagsSqlSuffix, SQL_VALUES, CLUSTER_ID, null); String expectedSql = primaryDataStoreDao.TagsSqlPrefix + SQL_VALUES + primaryDataStoreDao.TagsSqlSuffix; verify(primaryDataStoreDao).searchStoragePoolsPreparedStatement(expectedSql, DATACENTER_ID, POD_ID, CLUSTER_ID, SCOPE, STORAGE_TAGS_ARRAY.length); } @@ -144,7 +144,7 @@ public void testFindPoolsByDetailsOrTagsInternalDetailsType() { List storagePools = primaryDataStoreDao.findPoolsByDetailsOrTagsInternal(DATACENTER_ID, POD_ID, CLUSTER_ID, SCOPE, SQL_VALUES, ValueType.DETAILS, STORAGE_POOL_DETAILS.size()); assertEquals(Arrays.asList(storagePoolVO), storagePools); verify(primaryDataStoreDao).getSqlPreparedStatement( - primaryDataStoreDao.DetailsSqlPrefix, primaryDataStoreDao.DetailsSqlSuffix, SQL_VALUES, CLUSTER_ID); + primaryDataStoreDao.DetailsSqlPrefix, primaryDataStoreDao.DetailsSqlSuffix, SQL_VALUES, CLUSTER_ID, null); String expectedSql = primaryDataStoreDao.DetailsSqlPrefix + SQL_VALUES + primaryDataStoreDao.DetailsSqlSuffix; verify(primaryDataStoreDao).searchStoragePoolsPreparedStatement(expectedSql, DATACENTER_ID, POD_ID, CLUSTER_ID, SCOPE, STORAGE_POOL_DETAILS.size()); } diff --git a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/PrimaryDataStoreImpl.java b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/PrimaryDataStoreImpl.java index 81966784be05..d322ce62f14d 100644 --- a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/PrimaryDataStoreImpl.java +++ b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/PrimaryDataStoreImpl.java @@ -203,8 +203,7 @@ public String getUuid() { @Override public String getName() { - // TODO Auto-generated method stub - return null; + return pdsv.getName(); } @Override diff --git a/server/src/com/cloud/server/ManagementServerImpl.java b/server/src/com/cloud/server/ManagementServerImpl.java index 79bc3d5bccfb..81b706e275c2 100644 --- a/server/src/com/cloud/server/ManagementServerImpl.java +++ b/server/src/com/cloud/server/ManagementServerImpl.java @@ -37,6 +37,7 @@ import javax.inject.Inject; import javax.naming.ConfigurationException; +import com.cloud.storage.ScopeType; import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.affinity.AffinityGroupProcessor; import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao; @@ -1383,8 +1384,10 @@ public Pair, List> listStorag List storagePools = null; if (srcVolumePool.getClusterId() == null) { storagePools = _poolDao.findZoneWideStoragePoolsByTags(volume.getDataCenterId(), null); - } else { + } else if (vm != null) { storagePools = _poolDao.findPoolsByTags(volume.getDataCenterId(), srcVolumePool.getPodId(), srcVolumePool.getClusterId(), null); + } else { + storagePools = _poolDao.findPoolsByTags(volume.getDataCenterId(), null, null, null); } storagePools.remove(srcVolumePool); @@ -1399,12 +1402,30 @@ public Pair, List> listStorag final ExcludeList avoid = new ExcludeList(); avoid.addPool(srcVolumePool.getId()); - // Volume stays in the same cluster after migration. - final DataCenterDeployment plan = new DataCenterDeployment(volume.getDataCenterId(), srcVolumePool.getPodId(), srcVolumePool.getClusterId(), null, null, null); + if(vm != null) { + // Volume stays in the same cluster after migration. + addSuitablePools(suitablePools, avoid, vm, volume, vm.getHypervisorType(), srcVolumePool.getDataCenterId(), srcVolumePool.getPodId(), srcVolumePool.getClusterId()); + } else { + final HypervisorType volHypervisorType = _volumeDao.getHypervisorType(volumeId); + if(srcVolumePool.getScope() == ScopeType.CLUSTER) { + for (HostPodVO podVo : _hostPodDao.listByDataCenterId(volume.getDataCenterId())) { + addSuitablePools(suitablePools, avoid, vm, volume, volHypervisorType, volume.getDataCenterId(), podVo.getId(), null); + } + } else if (srcVolumePool.getScope() == ScopeType.ZONE) { + addSuitablePools(suitablePools, avoid, vm, volume, volHypervisorType, volume.getDataCenterId(), null, null); + } + } + + return new Pair, List>(allPools, suitablePools); + } + + private void addSuitablePools(List suitablePools, ExcludeList avoid, VMInstanceVO vm, VolumeVO volume, HypervisorType volHypervisorType, + long dcId, Long podId, Long clusterId) { + final DataCenterDeployment plan = new DataCenterDeployment(dcId, podId, clusterId, null, null, null); final VirtualMachineProfile profile = new VirtualMachineProfileImpl(vm); final DiskOfferingVO diskOffering = _diskOfferingDao.findById(volume.getDiskOfferingId()); - final DiskProfile diskProfile = new DiskProfile(volume, diskOffering, profile.getHypervisorType()); + final DiskProfile diskProfile = new DiskProfile(volume, diskOffering, volHypervisorType); // Call the storage pool allocator to find the list of storage pools. for (final StoragePoolAllocator allocator : _storagePoolAllocators) { @@ -1414,8 +1435,6 @@ public Pair, List> listStorag break; } } - - return new Pair, List>(allPools, suitablePools); } private Pair, Integer> searchForServers(final Long startIndex, final Long pageSize, final Object name, final Object type, final Object state, final Object zone, final Object pod, final Object cluster, diff --git a/server/src/com/cloud/storage/VolumeApiServiceImpl.java b/server/src/com/cloud/storage/VolumeApiServiceImpl.java index 3330cc74a260..2a017542c90a 100644 --- a/server/src/com/cloud/storage/VolumeApiServiceImpl.java +++ b/server/src/com/cloud/storage/VolumeApiServiceImpl.java @@ -29,6 +29,8 @@ import com.cloud.dc.DataCenter; import com.cloud.dc.DataCenterVO; import com.cloud.dc.dao.DataCenterDao; +import com.cloud.deploy.DataCenterDeployment; +import com.cloud.deploy.DeploymentPlanner; import com.cloud.domain.Domain; import com.cloud.event.ActionEvent; import com.cloud.event.EventTypes; @@ -83,12 +85,15 @@ import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.fsm.NoTransitionException; import com.cloud.utils.fsm.StateMachine2; +import com.cloud.vm.DiskProfile; import com.cloud.vm.UserVmManager; import com.cloud.vm.UserVmService; import com.cloud.vm.UserVmVO; import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachine.State; +import com.cloud.vm.VirtualMachineProfile; +import com.cloud.vm.VirtualMachineProfileImpl; import com.cloud.vm.VmDetailConstants; import com.cloud.vm.VmWork; import com.cloud.vm.VmWorkAttachVolume; @@ -1970,6 +1975,41 @@ public Volume migrateVolume(MigrateVolumeCmd cmd) { throw new InvalidParameterValueException("Migration of volume from local storage pool is not supported"); } + // Add all the pool except destPool in avoid list + final DeploymentPlanner.ExcludeList avoids = new DeploymentPlanner.ExcludeList(); + List allPools = _storagePoolDao.listAll(); + for (StoragePoolVO pool : allPools) { + if(destPool.getId() != pool.getId()) { + avoids.addPool(pool.getId()); + } + } + + // Volume will move to destPool after migration. + final DataCenterDeployment plan = new DataCenterDeployment(vol.getDataCenterId(), destPool.getPodId(), destPool.getClusterId(), null, null, null); + final VirtualMachineProfile profile = new VirtualMachineProfileImpl(vm); + + final DiskOfferingVO diskOffering = _diskOfferingDao.findById(vol.getDiskOfferingId()); + final DiskProfile diskProfile; + if(vm == null) { + diskProfile = new DiskProfile(vol, diskOffering, _volsDao.getHypervisorType(volumeId)); + } else { + diskProfile = new DiskProfile(vol, diskOffering, profile.getHypervisorType()); + } + + boolean isPoolSuitable = false; + // Call the storage pool allocators to find whether destPool is suitable or not + for (final StoragePoolAllocator allocator : _storagePoolAllocators) { + final List pools = allocator.allocateToPool(diskProfile, profile, plan, avoids, StoragePoolAllocator.RETURN_UPTO_ALL); + if (pools != null && !pools.isEmpty()) { + isPoolSuitable = true; + break; + } + } + + if(!isPoolSuitable) { + throw new InvalidParameterValueException("Failed to migrate Volume: " + vol.getName() + ". Specified pool: " + destPool.getName() + " doesn't have enough resources."); + } + if (vm != null) { // serialize VM operation AsyncJobExecutionContext jobContext = AsyncJobExecutionContext.getCurrentExecutionContext(); diff --git a/ui/scripts/storage.js b/ui/scripts/storage.js index 9638f1e5e2ae..66640a9b0386 100644 --- a/ui/scripts/storage.js +++ b/ui/scripts/storage.js @@ -1463,16 +1463,16 @@ }, select: function(args) { $.ajax({ - url: createURL("listStoragePools&zoneid=" + args.context.volumes[0].zoneid), + url: createURL("findStoragePoolsForMigration&id=" + args.context.volumes[0].id), dataType: "json", async: true, success: function(json) { - var pools = json.liststoragepoolsresponse.storagepool; + var pools = json.findstoragepoolsformigrationresponse.storagepool; var items = []; $(pools).each(function() { items.push({ id: this.id, - description: this.name + description: this.name + " (" + (this.suitableformigration ? "Suitable" : "Not Suitable") + ")" }); }); args.response.success({