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.core.cloudbus.CloudBus; import org.zstack.core.cloudbus.CloudBusCallBack; import org.zstack.core.db.DatabaseFacade; import org.zstack.core.errorcode.ErrorFacade; import org.zstack.header.allocator.*; import org.zstack.header.configuration.DiskOfferingInventory; import org.zstack.header.configuration.DiskOfferingVO; 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.exception.CloudRuntimeException; 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.network.l3.L3NetworkInventory; import org.zstack.header.vm.VmInstanceConstant; import org.zstack.header.vm.VmInstanceConstant.VmOperation; import org.zstack.header.vm.VmInstanceSpec; import org.zstack.header.vm.VmInstanceVO; import org.zstack.utils.CollectionUtils; import org.zstack.utils.function.Function; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; import static org.zstack.core.progress.ProgressReportService.taskProgress; @Configurable(preConstruction = true, autowire = Autowire.BY_TYPE) public class VmAllocateHostFlow implements Flow { @Autowired protected DatabaseFacade dbf; @Autowired protected CloudBus bus; @Autowired protected ErrorFacade errf; private long getTotalDataDiskSize(VmInstanceSpec spec) { long size = 0; for (DiskOfferingInventory dinv : spec.getDataDiskOfferings()) { size += dinv.getDiskSize(); } return size; } private AllocateHostMsg prepareMsg(Map<String, Object> ctx) { VmInstanceSpec spec = (VmInstanceSpec) ctx.get(VmInstanceConstant.Params.VmInstanceSpec.toString()); DesignatedAllocateHostMsg msg = new DesignatedAllocateHostMsg(); List<DiskOfferingInventory> diskOfferings = new ArrayList<>(); ImageInventory image = spec.getImageSpec().getInventory(); long diskSize; if (image.getMediaType().equals(ImageMediaType.ISO.toString())) { DiskOfferingVO dvo = dbf.findByUuid(spec.getRootDiskOffering().getUuid(), DiskOfferingVO.class); diskSize = dvo.getDiskSize(); diskOfferings.add(DiskOfferingInventory.valueOf(dvo)); } else { diskSize = image.getSize(); } diskSize += getTotalDataDiskSize(spec); diskOfferings.addAll(spec.getDataDiskOfferings()); msg.setDiskOfferings(diskOfferings); msg.setDiskSize(diskSize); msg.setCpuCapacity(spec.getVmInventory().getCpuNum()); msg.setMemoryCapacity(spec.getVmInventory().getMemorySize()); msg.setL3NetworkUuids(CollectionUtils.transformToList(spec.getL3Networks(), new Function<String, L3NetworkInventory>() { @Override public String call(L3NetworkInventory arg) { return arg.getUuid(); } })); msg.setImage(image); msg.setVmOperation(spec.getCurrentVmOperation().toString()); if (spec.getVmInventory().getZoneUuid() != null) { msg.setZoneUuid(spec.getVmInventory().getZoneUuid()); } if (spec.getVmInventory().getClusterUuid() != null) { msg.setClusterUuid(spec.getVmInventory().getClusterUuid()); } if (spec.getVmInventory().getHostUuid() != null) { msg.setHostUuid(spec.getVmInventory().getHostUuid()); } if (spec.getHostAllocatorStrategy() != null) { msg.setAllocatorStrategy(spec.getHostAllocatorStrategy()); } else { msg.setAllocatorStrategy(spec.getVmInventory().getAllocatorStrategy()); } if (spec.getRequiredPrimaryStorageUuidForRootVolume() != null) { msg.setRequiredPrimaryStorageUuid(spec.getRequiredPrimaryStorageUuidForRootVolume()); } msg.setServiceId(bus.makeLocalServiceId(HostAllocatorConstant.SERVICE_ID)); msg.setTimeout(TimeUnit.MINUTES.toMillis(60)); msg.setVmInstance(spec.getVmInventory()); msg.setRequiredBackupStorageUuid(spec.getImageSpec().getSelectedBackupStorage().getBackupStorageUuid()); return msg; } @Override public void run(final FlowTrigger chain, Map data) { taskProgress("allocate candidate hosts"); final VmInstanceSpec spec = (VmInstanceSpec) data.get(VmInstanceConstant.Params.VmInstanceSpec.toString()); if (VmOperation.NewCreate != spec.getCurrentVmOperation()) { throw new CloudRuntimeException("VmAllocateHostFlow is only for creating new VM"); } AllocateHostMsg msg = this.prepareMsg(data); bus.send(msg, new CloudBusCallBack(chain) { @Override public void run(MessageReply reply) { if (reply.isSuccess()) { AllocateHostReply areply = (AllocateHostReply) reply; spec.setDestHost(areply.getHost()); // update the vm's host uuid and hypervisor type so even if the management node died later and the vm's state // is stuck in Starting, we know which host it's created on and can check its state on the host VmInstanceVO vmvo = dbf.findByUuid(spec.getVmInventory().getUuid(), VmInstanceVO.class); vmvo.setClusterUuid(spec.getDestHost().getClusterUuid()); vmvo.setLastHostUuid(vmvo.getHostUuid()); vmvo.setHostUuid(spec.getDestHost().getUuid()); vmvo.setHypervisorType(spec.getDestHost().getHypervisorType()); dbf.update(vmvo); chain.next(); } else { chain.fail(reply.getError()); } } }); } @Override public void rollback(FlowRollback chain, Map data) { VmInstanceSpec spec = (VmInstanceSpec) data.get(VmInstanceConstant.Params.VmInstanceSpec.toString()); HostInventory host = spec.getDestHost(); if (host != null) { ReturnHostCapacityMsg msg = new ReturnHostCapacityMsg(); msg.setCpuCapacity(spec.getVmInventory().getCpuNum()); msg.setMemoryCapacity(spec.getVmInventory().getMemorySize()); msg.setHostUuid(host.getUuid()); msg.setServiceId(bus.makeLocalServiceId(HostAllocatorConstant.SERVICE_ID)); bus.send(msg); } chain.rollback(); } }