package org.zstack.storage.primary.local;
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.cloudbus.CloudBus;
import org.zstack.core.cloudbus.CloudBusCallBack;
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.core.workflow.Flow;
import org.zstack.header.core.workflow.FlowRollback;
import org.zstack.header.core.workflow.FlowTrigger;
import org.zstack.header.host.HostInventory;
import org.zstack.header.host.HostVO;
import org.zstack.header.message.MessageReply;
import org.zstack.header.storage.primary.*;
import org.zstack.header.vm.VmInstanceConstant;
import org.zstack.header.vm.VmInstanceSpec;
import org.zstack.header.volume.VolumeInventory;
import javax.persistence.Tuple;
import javax.persistence.TypedQuery;
import java.util.List;
import java.util.Map;
/**
* Created by frank on 7/5/2015.
*/
@Configurable(preConstruction = true, autowire = Autowire.BY_TYPE)
public class LocalStorageAllocateCapacityForAttachingVolumeFlow implements Flow {
@Autowired
protected DatabaseFacade dbf;
@Autowired
protected CloudBus bus;
@Autowired
protected ErrorFacade errf;
@Transactional(readOnly = true)
private boolean isThereNetworkSharedStorageForTheHost(String hostUuid, String localStorageUuid) {
String sql = "select count(pri)" +
" from PrimaryStorageVO pri, PrimaryStorageClusterRefVO ref, HostVO host" +
" where pri.uuid = ref.primaryStorageUuid" +
" and ref.clusterUuid = host.clusterUuid" +
" and host.uuid = :huuid" +
" and pri.uuid != :puuid" +
" and pri.type != :type";
TypedQuery<Long> q = dbf.getEntityManager().createQuery(sql, Long.class);
q.setParameter("huuid", hostUuid);
q.setParameter("puuid", localStorageUuid);
q.setParameter("type", LocalStorageConstants.LOCAL_STORAGE_TYPE);
return q.getSingleResult() > 0;
}
@Override
public void run(final FlowTrigger trigger, final Map data) {
final VmInstanceSpec spec = (VmInstanceSpec) data.get(VmInstanceConstant.Params.VmInstanceSpec.toString());
VolumeInventory volume = spec.getDestDataVolumes().get(0);
SimpleQuery<LocalStorageResourceRefVO> q = dbf.createQuery(LocalStorageResourceRefVO.class);
q.select(LocalStorageResourceRefVO_.hostUuid, LocalStorageResourceRefVO_.primaryStorageUuid);
q.add(LocalStorageResourceRefVO_.resourceUuid, Op.EQ, spec.getVmInventory().getRootVolumeUuid());
Tuple t = q.findTuple();
final String hostUuid = t.get(0, String.class);
String priUuid = t.get(1, String.class);
AllocatePrimaryStorageMsg msg = new AllocatePrimaryStorageMsg();
if (isThereNetworkSharedStorageForTheHost(hostUuid, priUuid)) {
// use network-shared primary storage
msg.addExcludeAllocatorStrategy(LocalStorageConstants.LOCAL_STORAGE_ALLOCATOR_STRATEGY);
SimpleQuery<LocalStorageHostRefVO> sq = dbf.createQuery(LocalStorageHostRefVO.class);
sq.add(LocalStorageHostRefVO_.hostUuid, Op.EQ, hostUuid);
List<LocalStorageHostRefVO> localStorageHostRefVOList = sq.list();
if (localStorageHostRefVOList != null && !localStorageHostRefVOList.isEmpty()) {
localStorageHostRefVOList.forEach(r -> msg.addExcludePrimaryStorageUuid(r.getPrimaryStorageUuid()));
}
} else {
msg.setAllocationStrategy(LocalStorageConstants.LOCAL_STORAGE_ALLOCATOR_STRATEGY);
msg.setRequiredPrimaryStorageUuid(spec.getVmInventory().getRootVolume().getPrimaryStorageUuid());
}
msg.setRequiredHostUuid(hostUuid);
msg.setVmInstanceUuid(spec.getVmInventory().getUuid());
msg.setSize(volume.getSize());
msg.setPurpose(PrimaryStorageAllocationPurpose.CreateVolume.toString());
bus.makeLocalServiceId(msg, PrimaryStorageConstant.SERVICE_ID);
bus.send(msg, new CloudBusCallBack(trigger) {
@Override
public void run(MessageReply reply) {
if (!reply.isSuccess()) {
trigger.fail(reply.getError());
return;
}
spec.setDestHost(HostInventory.valueOf(dbf.findByUuid(hostUuid, HostVO.class)));
AllocatePrimaryStorageReply ar = (AllocatePrimaryStorageReply) reply;
data.put(VmInstanceConstant.Params.DestPrimaryStorageInventoryForAttachingVolume.toString(), ar.getPrimaryStorageInventory());
data.put(LocalStorageAllocateCapacityForAttachingVolumeFlow.class, ar.getSize());
trigger.next();
}
});
}
@Override
public void rollback(FlowRollback trigger, Map data) {
Long size = (Long) data.get(LocalStorageAllocateCapacityForAttachingVolumeFlow.class);
if (size != null) {
PrimaryStorageInventory pri = (PrimaryStorageInventory) data.get(
VmInstanceConstant.Params.DestPrimaryStorageInventoryForAttachingVolume.toString());
IncreasePrimaryStorageCapacityMsg imsg = new IncreasePrimaryStorageCapacityMsg();
imsg.setPrimaryStorageUuid(pri.getUuid());
imsg.setDiskSize(size);
bus.makeTargetServiceIdByResourceUuid(imsg, PrimaryStorageConstant.SERVICE_ID, pri.getUuid());
bus.send(imsg);
}
trigger.rollback();
}
}