package org.zstack.compute.vm;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
import org.zstack.compute.allocator.HostAllocatorManager;
import org.zstack.core.asyncbatch.AsyncLoop;
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.configuration.DiskOfferingInventory;
import org.zstack.header.core.Completion;
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.errorcode.ErrorCode;
import org.zstack.header.host.HostInventory;
import org.zstack.header.image.ImageConstant.ImageMediaType;
import org.zstack.header.image.ImageInventory;
import org.zstack.header.message.MessageReply;
import org.zstack.header.storage.backup.BackupStorageVO;
import org.zstack.header.storage.backup.BackupStorageVO_;
import org.zstack.header.storage.primary.*;
import org.zstack.header.vm.VmInstanceConstant;
import org.zstack.header.vm.VmInstanceSpec;
import org.zstack.header.vm.VmInstanceSpec.VolumeSpec;
import org.zstack.utils.DebugUtils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
@Configurable(preConstruction = true, autowire = Autowire.BY_TYPE)
public class VmAllocatePrimaryStorageFlow implements Flow {
@Autowired
protected DatabaseFacade dbf;
@Autowired
protected CloudBus bus;
@Autowired
protected ErrorFacade errf;
@Autowired
protected HostAllocatorManager hostAllocatorMgr;
@Override
public void run(final FlowTrigger trigger, final Map data) {
final List<AllocatePrimaryStorageMsg> msgs = new ArrayList<>();
final VmInstanceSpec spec = (VmInstanceSpec) data.get(VmInstanceConstant.Params.VmInstanceSpec.toString());
HostInventory destHost = spec.getDestHost();
final ImageInventory iminv = spec.getImageSpec().getInventory();
// get ps types from bs
SimpleQuery<BackupStorageVO> q = dbf.createQuery(BackupStorageVO.class);
q.select(BackupStorageVO_.type);
q.add(BackupStorageVO_.uuid, Op.EQ, spec.getImageSpec().getSelectedBackupStorage().getBackupStorageUuid());
String bsType = q.findValue();
List<String> primaryStorageTypes = hostAllocatorMgr.getBackupStoragePrimaryStorageMetrics().get(bsType);
DebugUtils.Assert(primaryStorageTypes != null, "why primaryStorageTypes is null");
// allocate ps for root volume
AllocatePrimaryStorageMsg rmsg = new AllocatePrimaryStorageMsg();
rmsg.setRequiredPrimaryStorageUuid(spec.getRequiredPrimaryStorageUuidForRootVolume());
rmsg.setVmInstanceUuid(spec.getVmInventory().getUuid());
rmsg.setImageUuid(spec.getImageSpec().getInventory().getUuid());
if (ImageMediaType.ISO.toString().equals(iminv.getMediaType())) {
rmsg.setSize(spec.getRootDiskOffering().getDiskSize());
rmsg.setAllocationStrategy(spec.getRootDiskOffering().getAllocatorStrategy());
rmsg.setRequiredHostUuid(destHost.getUuid());
rmsg.setDiskOfferingUuid(spec.getRootDiskOffering().getUuid());
} else {
//TODO: find a way to allow specifying strategy for root disk
rmsg.setSize(iminv.getSize());
rmsg.setRequiredHostUuid(destHost.getUuid());
}
rmsg.setPurpose(PrimaryStorageAllocationPurpose.CreateNewVm.toString());
rmsg.setRequiredPrimaryStorageTypes(primaryStorageTypes);
bus.makeLocalServiceId(rmsg, PrimaryStorageConstant.SERVICE_ID);
msgs.add(rmsg);
// allocate ps for data volumes
for (DiskOfferingInventory dinv : spec.getDataDiskOfferings()) {
AllocatePrimaryStorageMsg amsg = new AllocatePrimaryStorageMsg();
amsg.setSize(dinv.getDiskSize());
amsg.setRequiredHostUuid(destHost.getUuid());
amsg.setAllocationStrategy(dinv.getAllocatorStrategy());
amsg.setDiskOfferingUuid(dinv.getUuid());
amsg.setRequiredPrimaryStorageTypes(primaryStorageTypes);
bus.makeLocalServiceId(amsg, PrimaryStorageConstant.SERVICE_ID);
msgs.add(amsg);
}
new AsyncLoop<AllocatePrimaryStorageMsg>(trigger) {
@Override
protected Collection<AllocatePrimaryStorageMsg> collectionForLoop() {
return msgs;
}
@Override
protected void run(AllocatePrimaryStorageMsg msg, Completion completion) {
bus.send(msg, new CloudBusCallBack(completion) {
@Override
public void run(MessageReply reply) {
if (!reply.isSuccess()) {
completion.fail(reply.getError());
return;
}
VolumeSpec volumeSpec = new VolumeSpec();
AllocatePrimaryStorageReply ar = reply.castReply();
volumeSpec.setPrimaryStorageInventory(ar.getPrimaryStorageInventory());
volumeSpec.setSize(ar.getSize());
volumeSpec.setRoot(msg.getImageUuid() != null);
if (!volumeSpec.isRoot()) {
volumeSpec.setDiskOfferingUuid(msg.getDiskOfferingUuid());
}
spec.getVolumeSpecs().add(volumeSpec);
completion.success();
}
});
}
@Override
protected void done() {
trigger.next();
}
@Override
protected void error(ErrorCode errorCode) {
trigger.fail(errorCode);
}
}.start();
}
@Override
public void rollback(FlowRollback chain, Map data) {
final VmInstanceSpec spec = (VmInstanceSpec) data.get(VmInstanceConstant.Params.VmInstanceSpec.toString());
for (VolumeSpec vspec : spec.getVolumeSpecs()) {
if (vspec.isVolumeCreated()) {
// don't return capacity as it has been returned when the volume is deleted
continue;
}
IncreasePrimaryStorageCapacityMsg msg = new IncreasePrimaryStorageCapacityMsg();
msg.setDiskSize(vspec.getSize());
msg.setPrimaryStorageUuid(vspec.getPrimaryStorageInventory().getUuid());
bus.makeTargetServiceIdByResourceUuid(msg, PrimaryStorageConstant.SERVICE_ID, vspec.getPrimaryStorageInventory().getUuid());
bus.send(msg);
}
chain.rollback();
}
}