sureshanaparti commented on code in PR #10381: URL: https://github.com/apache/cloudstack/pull/10381#discussion_r2075322057
########## server/src/main/java/com/cloud/resource/ResourceManagerImpl.java: ########## @@ -1917,6 +1936,741 @@ private void updateHostGuestOSCategory(Long hostId, Long guestOSCategoryId) { } } + private void removeStorageAccessGroupsOnPodsInZone(long zoneId, List<String> newStoragePoolTags, List<String> tagsToDeleteOnZone) { + List<HostPodVO> pods = _podDao.listByDataCenterId(zoneId); + for (HostPodVO pod : pods) { + removeStorageAccessGroupsOnClustersInPod(pod.getId(), newStoragePoolTags, tagsToDeleteOnZone); + updateStorageAccessGroupsToBeAddedOnPodInZone(pod.getId(), newStoragePoolTags); + } + } + + private void removeStorageAccessGroupsOnClustersInPod(long podId, List<String> newStoragePoolTags, List<String> tagsToDeleteOnPod) { + List<ClusterVO> clusters = _clusterDao.listByPodId(podId); + for (ClusterVO cluster : clusters) { + updateStorageAccessGroupsToBeDeletedOnHostsInCluster(cluster.getId(), tagsToDeleteOnPod); + updateStorageAccessGroupsToBeAddedOnHostsInCluster(cluster.getId(), newStoragePoolTags); + updateStorageAccessGroupsToBeAddedOnClustersInPod(cluster.getId(), newStoragePoolTags); + } + } + + private void updateStorageAccessGroupsToBeDeletedOnHostsInCluster(long clusterId, List<String> storageAccessGroupsToDeleteOnCluster) { + if (CollectionUtils.isEmpty(storageAccessGroupsToDeleteOnCluster)) { + return; + } + + List<HostVO> hosts = _hostDao.findByClusterId(clusterId); + List<Long> hostIdsUsingStorageAccessGroups = listOfHostIdsUsingTheStorageAccessGroups(storageAccessGroupsToDeleteOnCluster, clusterId, null, null); + for (HostVO host : hosts) { + String hostStorageAccessGroups = host.getStorageAccessGroups(); + if (hostIdsUsingStorageAccessGroups != null && hostIdsUsingStorageAccessGroups.contains(host.getId())) { + Set<String> mergedSet = hostStorageAccessGroups != null + ? new HashSet<>(Arrays.asList(hostStorageAccessGroups.split(","))) + : new HashSet<>(); + mergedSet.addAll(storageAccessGroupsToDeleteOnCluster); + host.setStorageAccessGroups(String.join(",", mergedSet)); + _hostDao.update(host.getId(), host); + } else { + if (hostStorageAccessGroups != null) { + List<String> hostTagsList = new ArrayList<>(Arrays.asList(hostStorageAccessGroups.split(","))); + hostTagsList.removeAll(storageAccessGroupsToDeleteOnCluster); + String updatedClusterStoragePoolTags = hostTagsList.isEmpty() ? null : String.join(",", hostTagsList); + host.setStorageAccessGroups(updatedClusterStoragePoolTags); + _hostDao.update(host.getId(), host); + } + } + } + } + + private void updateStorageAccessGroupsToBeAddedOnHostsInCluster(long clusterId, List<String> tagsAddedOnCluster) { + if (CollectionUtils.isEmpty(tagsAddedOnCluster)) { + return; + } + + List<HostVO> hosts = _hostDao.findByClusterId(clusterId); + for (HostVO host : hosts) { + String hostStoragePoolTags = host.getStorageAccessGroups(); + Set<String> hostStoragePoolTagsSet = hostStoragePoolTags != null + ? new HashSet<>(Arrays.asList(hostStoragePoolTags.split(","))) + : new HashSet<>(); + + hostStoragePoolTagsSet.removeIf(tagsAddedOnCluster::contains); + host.setStorageAccessGroups(hostStoragePoolTagsSet.isEmpty() ? null : String.join(",", hostStoragePoolTagsSet)); + _hostDao.update(host.getId(), host); + } + } + + private void updateStorageAccessGroupsToBeAddedOnClustersInPod(long clusterId, List<String> tagsAddedOnPod) { + if (CollectionUtils.isEmpty(tagsAddedOnPod)) { + return; + } + + ClusterVO cluster = _clusterDao.findById(clusterId); + String clusterStoragePoolTags = cluster.getStorageAccessGroups(); + if (clusterStoragePoolTags != null) { + List<String> clusterTagsList = new ArrayList<>(Arrays.asList(clusterStoragePoolTags.split(","))); + clusterTagsList.removeAll(tagsAddedOnPod); + String updatedClusterStoragePoolTags = clusterTagsList.isEmpty() ? null : String.join(",", clusterTagsList); + cluster.setStorageAccessGroups(updatedClusterStoragePoolTags); + _clusterDao.update(cluster.getId(), cluster); + } + } + + private void updateStorageAccessGroupsToBeAddedOnPodInZone(long podId, List<String> tagsAddedOnZone) { + if (CollectionUtils.isEmpty(tagsAddedOnZone)) { + return; + } + + HostPodVO pod = _podDao.findById(podId); + String podStoragePoolTags = pod.getStorageAccessGroups(); + if (podStoragePoolTags != null) { + List<String> podTagsList = new ArrayList<>(Arrays.asList(podStoragePoolTags.split(","))); + podTagsList.removeAll(tagsAddedOnZone); + String updatedClusterStoragePoolTags = podTagsList.isEmpty() ? null : String.join(",", podTagsList); + pod.setStorageAccessGroups(updatedClusterStoragePoolTags); + _podDao.update(pod.getId(), pod); + } + } + + public List<Long> listOfHostIdsUsingTheStorageAccessGroups(List<String> storageAccessGroups, Long clusterId, Long podId, Long datacenterId) { + GenericSearchBuilder<VMInstanceVO, Long> vmInstanceSearch = _vmDao.createSearchBuilder(Long.class); + vmInstanceSearch.select(null, Func.DISTINCT, vmInstanceSearch.entity().getHostId()); + vmInstanceSearch.and("hostId", vmInstanceSearch.entity().getHostId(), Op.NNULL); + vmInstanceSearch.and("removed", vmInstanceSearch.entity().getRemoved(), Op.NULL); + + GenericSearchBuilder<VolumeVO, Long> volumeSearch = volumeDao.createSearchBuilder(Long.class); + volumeSearch.selectFields(volumeSearch.entity().getInstanceId()); + volumeSearch.and("state", volumeSearch.entity().getState(), Op.NIN); + + GenericSearchBuilder<StoragePoolVO, Long> storagePoolSearch = _storagePoolDao.createSearchBuilder(Long.class); + storagePoolSearch.and("clusterId", storagePoolSearch.entity().getClusterId(), Op.EQ); + storagePoolSearch.and("podId", storagePoolSearch.entity().getPodId(), Op.EQ); + storagePoolSearch.and("datacenterId", storagePoolSearch.entity().getDataCenterId(), Op.EQ); + storagePoolSearch.selectFields(storagePoolSearch.entity().getId()); + + GenericSearchBuilder<StoragePoolAndAccessGroupMapVO, Long> storageAccessGroupSearch = _storagePoolAccessGroupMapDao.createSearchBuilder(Long.class); + storageAccessGroupSearch.and("sag", storageAccessGroupSearch.entity().getStorageAccessGroup(), Op.IN); + + storagePoolSearch.join("storageAccessGroupSearch", storageAccessGroupSearch, storagePoolSearch.entity().getId(), storageAccessGroupSearch.entity().getPoolId(), JoinBuilder.JoinType.INNER); + storageAccessGroupSearch.done(); + + volumeSearch.join("storagePoolSearch", storagePoolSearch, volumeSearch.entity().getPoolId(), storagePoolSearch.entity().getId(), JoinBuilder.JoinType.INNER); + storagePoolSearch.done(); + + vmInstanceSearch.join("volumeSearch", volumeSearch, vmInstanceSearch.entity().getId(), volumeSearch.entity().getInstanceId(), JoinBuilder.JoinType.INNER); + volumeSearch.done(); + + vmInstanceSearch.done(); + + SearchCriteria<Long> sc = vmInstanceSearch.create(); + sc.setJoinParameters("storageAccessGroupSearch", "sag", storageAccessGroups.toArray()); + sc.setJoinParameters("volumeSearch", "state", new String[]{"Destroy", "Error", "Expunging", "Expunged"}); + if (clusterId != null) { + sc.setParameters("storagePoolSearch", "clusterId", clusterId); + } + if (podId != null) { + sc.setParameters("storagePoolSearch", "podId", podId); + } + if (datacenterId != null) { + sc.setParameters("storagePoolSearch", "datacenterId", datacenterId); + } + + return _vmDao.customSearch(sc, null); + } + + public List<Long> listOfHostIdsUsingTheStoragePool(Long storagePoolId) { + GenericSearchBuilder<VMInstanceVO, Long> vmInstanceSearch = _vmDao.createSearchBuilder(Long.class); + vmInstanceSearch.select(null, Func.DISTINCT, vmInstanceSearch.entity().getHostId()); + vmInstanceSearch.and("hostId", vmInstanceSearch.entity().getHostId(), Op.NNULL); + vmInstanceSearch.and("removed", vmInstanceSearch.entity().getRemoved(), Op.NULL); + + GenericSearchBuilder<VolumeVO, Long> volumeSearch = volumeDao.createSearchBuilder(Long.class); + volumeSearch.selectFields(volumeSearch.entity().getInstanceId()); + volumeSearch.and("state", volumeSearch.entity().getState(), Op.NIN); + + GenericSearchBuilder<StoragePoolVO, Long> storagePoolSearch = _storagePoolDao.createSearchBuilder(Long.class); + storagePoolSearch.selectFields(storagePoolSearch.entity().getId()); + storagePoolSearch.and("poolId", storagePoolSearch.entity().getId(), Op.EQ); + + volumeSearch.join("storagePoolSearch", storagePoolSearch, volumeSearch.entity().getPoolId(), storagePoolSearch.entity().getId(), JoinBuilder.JoinType.INNER); + storagePoolSearch.done(); + + vmInstanceSearch.join("volumeSearch", volumeSearch, vmInstanceSearch.entity().getId(), volumeSearch.entity().getInstanceId(), JoinBuilder.JoinType.INNER); + volumeSearch.done(); + + vmInstanceSearch.done(); + + SearchCriteria<Long> sc = vmInstanceSearch.create(); + sc.setJoinParameters("storagePoolSearch", "poolId", storagePoolId); + sc.setJoinParameters("volumeSearch", "state", new String[]{"Destroy", "Error", "Expunging", "Expunged"}); + + return _vmDao.customSearch(sc, null); + } + + public List<VolumeVO> listOfVolumesUsingTheStorageAccessGroups(List<String> storageAccessGroups, Long hostId, Long clusterId, Long podId, Long datacenterId) { + SearchBuilder<VolumeVO> volumeSearch = volumeDao.createSearchBuilder(); + volumeSearch.and("state", volumeSearch.entity().getState(), Op.NIN); + + GenericSearchBuilder<VMInstanceVO, Long> vmInstanceSearch = _vmDao.createSearchBuilder(Long.class); + vmInstanceSearch.selectFields(vmInstanceSearch.entity().getId()); + vmInstanceSearch.and("hostId", vmInstanceSearch.entity().getHostId(), Op.EQ); + vmInstanceSearch.and("removed", vmInstanceSearch.entity().getRemoved(), Op.NULL); + + GenericSearchBuilder<StoragePoolVO, Long> storagePoolSearch = _storagePoolDao.createSearchBuilder(Long.class); + storagePoolSearch.and("clusterId", storagePoolSearch.entity().getClusterId(), Op.EQ); + storagePoolSearch.and("podId", storagePoolSearch.entity().getPodId(), Op.EQ); + storagePoolSearch.and("datacenterId", storagePoolSearch.entity().getDataCenterId(), Op.EQ); + storagePoolSearch.selectFields(storagePoolSearch.entity().getId()); + + GenericSearchBuilder<StoragePoolAndAccessGroupMapVO, Long> storageAccessGroupSearch = _storagePoolAccessGroupMapDao.createSearchBuilder(Long.class); + storageAccessGroupSearch.and("sag", storageAccessGroupSearch.entity().getStorageAccessGroup(), Op.IN); + + storagePoolSearch.join("storageAccessGroupSearch", storageAccessGroupSearch, storagePoolSearch.entity().getId(), storageAccessGroupSearch.entity().getPoolId(), JoinBuilder.JoinType.INNER); + + volumeSearch.join("storagePoolSearch", storagePoolSearch, volumeSearch.entity().getPoolId(), storagePoolSearch.entity().getId(), JoinBuilder.JoinType.INNER); + + volumeSearch.join("vmInstanceSearch", vmInstanceSearch, volumeSearch.entity().getInstanceId(), vmInstanceSearch.entity().getId(), JoinBuilder.JoinType.INNER); + + storageAccessGroupSearch.done(); + storagePoolSearch.done(); + vmInstanceSearch.done(); + volumeSearch.done(); + + SearchCriteria<VolumeVO> sc = volumeSearch.create(); + sc.setParameters( "state", new String[]{"Destroy", "Error", "Expunging", "Expunged"}); + sc.setJoinParameters("storageAccessGroupSearch", "sag", storageAccessGroups.toArray()); + if (hostId != null) { + sc.setJoinParameters("vmInstanceSearch", "hostId", hostId); + } + if (clusterId != null) { + sc.setJoinParameters("storagePoolSearch", "clusterId", clusterId); + } + if (podId != null) { + sc.setJoinParameters("storagePoolSearch", "podId", podId); + } + if (datacenterId != null) { + sc.setJoinParameters("storagePoolSearch", "datacenterId", datacenterId); + } + + return volumeDao.customSearch(sc, null); + } + + private List<Long> listOfStoragePoolIDsUsedByHost(long hostId) { + GenericSearchBuilder<VMInstanceVO, Long> vmInstanceSearch = _vmDao.createSearchBuilder(Long.class); + vmInstanceSearch.selectFields(vmInstanceSearch.entity().getId()); + vmInstanceSearch.and("hostId", vmInstanceSearch.entity().getHostId(), Op.EQ); + + GenericSearchBuilder<VolumeVO, Long> volumeSearch = volumeDao.createSearchBuilder(Long.class); + volumeSearch.selectFields(volumeSearch.entity().getPoolId()); + volumeSearch.and("state", volumeSearch.entity().getState(), Op.EQ); + + volumeSearch.join("vmInstanceSearch", vmInstanceSearch, volumeSearch.entity().getInstanceId(), vmInstanceSearch.entity().getId(), JoinBuilder.JoinType.INNER); + vmInstanceSearch.done(); + + GenericSearchBuilder<StoragePoolVO, Long> storagePoolSearch = _storagePoolDao.createSearchBuilder(Long.class); + storagePoolSearch.select(null, Func.DISTINCT, storagePoolSearch.entity().getId()); + + storagePoolSearch.join("volumeSearch", volumeSearch, storagePoolSearch.entity().getId(), volumeSearch.entity().getPoolId(), JoinBuilder.JoinType.INNER); + volumeSearch.done(); + + storagePoolSearch.done(); + + SearchCriteria<Long> sc = storagePoolSearch.create(); + sc.setJoinParameters("vmInstanceSearch", "hostId", hostId); + sc.setJoinParameters("volumeSearch", "state", "Ready"); + + List<Long> storagePoolsInUse = _storagePoolDao.customSearch(sc, null); + return storagePoolsInUse; + } + + @Override + public void updateStoragePoolConnectionsOnHosts(Long poolId, List<String> storageAccessGroups) { + StoragePoolVO storagePool = _storagePoolDao.findById(poolId); + List<HostVO> hosts = new ArrayList<>(); + + if (storagePool.getScope().equals(ScopeType.CLUSTER)) { + List<HostVO> hostsInCluster = listAllUpHosts(Host.Type.Routing, storagePool.getClusterId(), storagePool.getPodId(), storagePool.getDataCenterId()); + hosts.addAll(hostsInCluster); + } else if (storagePool.getScope().equals(ScopeType.ZONE)) { + List<HostVO> hostsInZone = listAllUpHosts(Host.Type.Routing, null, null, storagePool.getDataCenterId()); + hosts.addAll(hostsInZone); + } + + List<HostVO> hostsToConnect = new ArrayList<>(); + List<HostVO> hostsToDisconnect = new ArrayList<>(); + boolean storagePoolHasAccessGroups = CollectionUtils.isNotEmpty(storageAccessGroups); + + for (HostVO host : hosts) { + String[] storageAccessGroupsOnHost = _storageMgr.getStorageAccessGroups(null, null, null, host.getId()); + List<String> listOfStorageAccessGroupsOnHost = Arrays.asList(storageAccessGroupsOnHost); + StoragePoolHostVO hostPoolRecord = _storagePoolHostDao.findByPoolHost(storagePool.getId(), host.getId()); + + if (storagePoolHasAccessGroups) { + List<String> intersection = new ArrayList<>(listOfStorageAccessGroupsOnHost); + intersection.retainAll(storageAccessGroups); + if (CollectionUtils.isNotEmpty(intersection)) { + if (hostPoolRecord == null) { + hostsToConnect.add(host); + } + } else { + hostsToDisconnect.add(host); + } + } else { + if (hostPoolRecord == null) { + hostsToConnect.add(host); + } + } + } + + if (CollectionUtils.isNotEmpty(hostsToDisconnect)) { + List<Long> hostIdsUsingTheStoragePool = listOfHostIdsUsingTheStoragePool(poolId); + List<Long> hostIdsToDisconnect = hostsToDisconnect.stream() + .map(HostVO::getId) + .collect(Collectors.toList()); + List<Long> conflictingHostIds = new ArrayList<>(CollectionUtils.intersection(hostIdsToDisconnect, hostIdsUsingTheStoragePool)); + if (CollectionUtils.isNotEmpty(conflictingHostIds)) { + Map<HostVO, List<VolumeVO>> hostVolumeMap = new HashMap<>(); + List<VolumeVO> volumesInPool = volumeDao.findByPoolId(poolId); + Map<Long, VMInstanceVO> vmInstanceCache = new HashMap<>(); + + for (Long hostId : conflictingHostIds) { + HostVO host = _hostDao.findById(hostId); + List<VolumeVO> matchingVolumes = volumesInPool.stream() + .filter(volume -> { + Long vmId = volume.getInstanceId(); + if (vmId == null) return false; + + VMInstanceVO vmInstance = vmInstanceCache.computeIfAbsent(vmId, _vmDao::findById); + return vmInstance != null && hostId.equals(vmInstance.getHostId()); + }) + .collect(Collectors.toList()); + if (!matchingVolumes.isEmpty()) { + hostVolumeMap.put(host, matchingVolumes); + } + } + + logger.error(String.format("Conflict detected: Hosts using the storage pool that need to be disconnected or " + + "connected to the pool: Host IDs and volumes: %s", hostVolumeMap)); + throw new CloudRuntimeException("Storage access groups cannot be updated as they are currently in use by some hosts. Please check the logs."); + } + } + + if (!hostsToConnect.isEmpty()) { + logger.debug(String.format("Hosts to connect to storage pool [%s]: %s", storagePool.getUuid(), hostsToConnect)); Review Comment: _hostsToConnect_ logged will be huge list if there are more hosts, better keep it inside loop (same for _hostsToDisconnect_) -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: commits-unsubscr...@cloudstack.apache.org For queries about this service, please contact Infrastructure at: us...@infra.apache.org