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.componentloader.PluginRegistry;
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.AbstractHostAllocatorFlow;
import org.zstack.header.allocator.HostAllocatorError;
import org.zstack.header.errorcode.ErrorCode;
import org.zstack.header.errorcode.OperationFailureException;
import org.zstack.header.exception.CloudRuntimeException;
import org.zstack.header.host.HostVO;
import org.zstack.header.storage.backup.BackupStorageType;
import org.zstack.header.storage.backup.BackupStorageVO;
import org.zstack.header.storage.backup.BackupStorageVO_;
import org.zstack.utils.Utils;
import org.zstack.utils.logging.CLogger;
import static org.zstack.core.Platform.operr;
import javax.persistence.TypedQuery;
import java.util.List;
/**
* Created by xing5 on 2016/8/17.
*/
@Configurable(preConstruction = true, autowire = Autowire.BY_TYPE)
public class BackupStorageSelectPrimaryStorageAllocatorFlow extends AbstractHostAllocatorFlow {
private static final CLogger logger = Utils.getLogger(BackupStorageSelectPrimaryStorageAllocatorFlow.class);
@Autowired
private DatabaseFacade dbf;
@Autowired
private PluginRegistry pluginRgty;
@Autowired
private ErrorFacade errf;
@Override
public void allocate() {
try {
throwExceptionIfIAmTheFirstFlow();
} catch (CloudRuntimeException e) {
logger.warn(e.getMessage());
ErrorCode errorCode = new ErrorCode();
errorCode.setCode(HostAllocatorError.NO_AVAILABLE_NIC.toString());
errorCode.setDetails("host cannot be allocated without L2Networks");
throw new OperationFailureException(errorCode);
}
if (spec.getRequiredBackupStorageUuid() == null) {
next(candidates);
return;
}
SimpleQuery<BackupStorageVO> q = dbf.createQuery(BackupStorageVO.class);
q.select(BackupStorageVO_.type);
q.add(BackupStorageVO_.uuid, Op.EQ, spec.getRequiredBackupStorageUuid());
String type = q.findValue();
BackupStorageType bsType = BackupStorageType.valueOf(type);
List<String> psUuids = bsType.findRelatedPrimaryStorage(spec.getRequiredBackupStorageUuid());
List<HostVO> result;
if (psUuids == null) {
List<String> possiblePrimaryStorageTypes = spec.getBackupStoragePrimaryStorageMetrics().get(type);
if (possiblePrimaryStorageTypes == null) {
throw new OperationFailureException(errf.stringToInternalError(
String.format("the image[uuid:%s] is on the backup storage[uuid:%s, type:%s] that doesn't have metrics defined" +
" in conf/springConfigXml/HostAllocatorManager.xml. The developer should add its primary storage metrics",
spec.getImage().getUuid(), spec.getRequiredBackupStorageUuid(), type)
));
}
result = findHostsByPrimaryStorageTypes(possiblePrimaryStorageTypes);
if (result.isEmpty()) {
String name = spec.getImage().getName();
throw new OperationFailureException(operr(
"The image[uuid:%s, name:%s] is on the backup storage[uuid:%s, type:%s] that requires to work with primary storage[types:%s]," +
"however, no host found suitable to work with those primary storage", spec.getImage().getUuid(),spec.getImage().getType(),
spec.getRequiredBackupStorageUuid(), name, possiblePrimaryStorageTypes
));
}
} else if (!psUuids.isEmpty()) {
result = findHostsByPrimaryStorageUuids(psUuids);
if (result.isEmpty()) {
throw new OperationFailureException(operr(
"The image[uuid:%s] is on the backup storage[uuid:%s, type:%s] that requires to work with primary storage[uuids:%s]," +
"however, no host found suitable to work with those primary storage", spec.getImage().getUuid(),
spec.getRequiredBackupStorageUuid(), type, psUuids)
);
}
} else {
throw new OperationFailureException(operr("the backup storage[uuid:%s, type:%s] requires bound" +
" primary storage, however, the primary storage has not been added", spec.getRequiredBackupStorageUuid(), bsType));
}
next(result);
}
@Transactional(readOnly = true)
private List<HostVO> findHostsByPrimaryStorageUuids(List<String> psUuids) {
String sql = "select h" +
" from HostVO h, PrimaryStorageClusterRefVO ref, PrimaryStorageVO ps" +
" where ref.clusterUuid = h.clusterUuid" +
" and ref.primaryStorageUuid = ps.uuid" +
" and ps.uuid in (:psUuids)" +
" and h.uuid in (:huuids)";
TypedQuery<HostVO> q = dbf.getEntityManager().createQuery(sql, HostVO.class);
q.setParameter("psUuids", psUuids);
q.setParameter("huuids", getHostUuidsFromCandidates());
return q.getResultList();
}
@Transactional(readOnly = true)
private List<HostVO> findHostsByPrimaryStorageTypes(List<String> psTypes) {
String sql = "select h" +
" from HostVO h, PrimaryStorageClusterRefVO ref, PrimaryStorageVO ps" +
" where ref.clusterUuid = h.clusterUuid" +
" and ref.primaryStorageUuid = ps.uuid" +
" and ps.type in (:psTypes)" +
" and h.uuid in (:huuids)";
TypedQuery<HostVO> q = dbf.getEntityManager().createQuery(sql, HostVO.class);
q.setParameter("psTypes", psTypes);
q.setParameter("huuids", getHostUuidsFromCandidates());
return q.getResultList();
}
}