package org.zstack.compute.allocator; import org.springframework.beans.factory.annotation.Autowire; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Configurable; import org.springframework.transaction.annotation.Transactional; import org.zstack.core.db.DatabaseFacade; import org.zstack.core.db.SimpleQuery; import org.zstack.core.db.SimpleQuery.Op; import org.zstack.core.errorcode.ErrorFacade; import org.zstack.header.allocator.*; import org.zstack.header.errorcode.OperationFailureException; import org.zstack.header.host.HostVO; import org.zstack.header.image.ImageBackupStorageRefInventory; import org.zstack.header.image.ImageStatus; import org.zstack.header.storage.backup.*; import org.zstack.header.storage.primary.ImageCacheVO; import org.zstack.header.storage.primary.ImageCacheVO_; import org.zstack.header.storage.primary.PrimaryStorageClusterRefVO; import org.zstack.header.storage.primary.PrimaryStorageClusterRefVO_; import org.zstack.header.vm.VmInstanceConstant.VmOperation; import org.zstack.utils.CollectionUtils; import org.zstack.utils.function.Function; import static org.zstack.core.Platform.operr; import javax.persistence.TypedQuery; import java.util.List; /** */ @Configurable(preConstruction = true, autowire = Autowire.BY_TYPE) public class ImageBackupStorageAllocatorFlow extends AbstractHostAllocatorFlow { @Autowired private DatabaseFacade dbf; @Autowired private ErrorFacade errf; private boolean checkIfNeedBackupStorageToDownloadImage(HostAllocatorSpec spec, List<HostVO> candidates) { List<String> clusterUuids = CollectionUtils.transformToList(candidates, new Function<String, HostVO>() { @Override public String call(HostVO arg) { return arg.getClusterUuid(); } }); SimpleQuery<PrimaryStorageClusterRefVO> pq = dbf.createQuery(PrimaryStorageClusterRefVO.class); pq.select(PrimaryStorageClusterRefVO_.primaryStorageUuid); pq.add(PrimaryStorageClusterRefVO_.clusterUuid, Op.IN, clusterUuids); List<String> psUuids = pq.listValue(); if (psUuids.isEmpty()) { return true; } SimpleQuery<ImageCacheVO> cq = dbf.createQuery(ImageCacheVO.class); cq.add(ImageCacheVO_.imageUuid, Op.EQ, spec.getImage().getUuid()); cq.add(ImageCacheVO_.primaryStorageUuid, Op.IN, psUuids); cq.groupBy(ImageCacheVO_.primaryStorageUuid); long count = cq.count(); return count != psUuids.size(); } @Override public void allocate() { if (!VmOperation.NewCreate.toString().equals(spec.getVmOperation())) { next(candidates); return; } throwExceptionIfIAmTheFirstFlow(); if (!checkIfNeedBackupStorageToDownloadImage(spec, candidates)) { next(candidates); return; } List<String> bsUuids = CollectionUtils.transformToList(spec.getImage().getBackupStorageRefs(), new Function<String, ImageBackupStorageRefInventory>() { @Override public String call(ImageBackupStorageRefInventory arg) { return ImageStatus.Deleted.toString().equals(arg.getStatus()) ? null : arg.getBackupStorageUuid(); } }); if (bsUuids.isEmpty()) { throw new OperationFailureException(operr( "the image[uuid:%s, name:%s] is deleted on all backup storage", spec.getImage().getUuid(), spec.getImage().getName() )); } SimpleQuery<BackupStorageVO> bq = dbf.createQuery(BackupStorageVO.class); bq.select(BackupStorageVO_.uuid); bq.add(BackupStorageVO_.state, Op.EQ, BackupStorageState.Enabled); bq.add(BackupStorageVO_.status, Op.EQ, BackupStorageStatus.Connected); bq.add(BackupStorageVO_.uuid, Op.IN, bsUuids); bsUuids = bq.listValue(); if (bsUuids.isEmpty()) { // we stop allocation on purpose, to prevent further pagination proceeding throw new OperationFailureException(errf.instantiateErrorCode(HostAllocatorError.NO_AVAILABLE_HOST, String.format("all backup storage that image[uuid:%s] is on can not satisfy conditions[state = %s, status = %s]", spec.getImage().getUuid(), BackupStorageState.Enabled, BackupStorageStatus.Connected) )); } SimpleQuery<BackupStorageZoneRefVO> q = dbf.createQuery(BackupStorageZoneRefVO.class); q.select(BackupStorageZoneRefVO_.zoneUuid); q.add(BackupStorageZoneRefVO_.backupStorageUuid, Op.IN, bsUuids); final List<String> zoneUuids = q.listValue(); candidates = CollectionUtils.transformToList(candidates, new Function<HostVO, HostVO>() { @Override public HostVO call(HostVO arg) { if (zoneUuids.contains(arg.getZoneUuid())) { return arg; } return null; } }); if (candidates.isEmpty()) { fail(String.format("no host found in zones[uuids:%s] that attaches to backup storage where image[%s] is on", zoneUuids, spec.getImage().getUuid())); } else { next(candidates); } } }