package org.zstack.compute.vm; import org.apache.commons.lang.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; import org.zstack.compute.allocator.HostAllocatorManager; import org.zstack.core.cascade.CascadeConstant; import org.zstack.core.cascade.CascadeFacade; import org.zstack.core.cloudbus.*; import org.zstack.core.componentloader.PluginRegistry; import org.zstack.core.db.DatabaseFacade; import org.zstack.core.db.SQLBatch; import org.zstack.core.db.SimpleQuery; import org.zstack.core.db.SimpleQuery.Op; import org.zstack.core.defer.Defer; import org.zstack.core.defer.Deferred; import org.zstack.core.jsonlabel.JsonLabel; import org.zstack.core.notification.N; import org.zstack.core.scheduler.SchedulerFacade; import org.zstack.core.thread.ChainTask; import org.zstack.core.thread.SyncTaskChain; import org.zstack.core.thread.ThreadFacade; import org.zstack.core.workflow.FlowChainBuilder; import org.zstack.core.workflow.ShareFlow; import org.zstack.header.allocator.*; import org.zstack.header.apimediator.ApiMessageInterceptionException; import org.zstack.header.cluster.ClusterInventory; import org.zstack.header.cluster.ClusterVO; import org.zstack.header.cluster.ClusterVO_; import org.zstack.header.configuration.*; import org.zstack.header.core.Completion; import org.zstack.header.core.NoErrorCompletion; import org.zstack.header.core.NopeCompletion; import org.zstack.header.core.ReturnValueCompletion; import org.zstack.header.core.scheduler.SchedulerInventory; import org.zstack.header.core.scheduler.SchedulerVO; import org.zstack.header.core.workflow.*; import org.zstack.header.errorcode.ErrorCode; import org.zstack.header.errorcode.OperationFailureException; import org.zstack.header.errorcode.SysErrors; import org.zstack.header.exception.CloudRuntimeException; import org.zstack.header.host.*; import org.zstack.header.image.ImageConstant.ImageMediaType; import org.zstack.header.image.ImageEO; import org.zstack.header.image.ImageInventory; import org.zstack.header.image.ImageStatus; import org.zstack.header.image.ImageVO; import org.zstack.header.message.*; import org.zstack.header.network.l3.*; import org.zstack.header.storage.primary.*; import org.zstack.header.vm.*; import org.zstack.header.vm.ChangeVmMetaDataMsg.AtomicHostUuid; import org.zstack.header.vm.ChangeVmMetaDataMsg.AtomicVmState; import org.zstack.header.vm.VmAbnormalLifeCycleStruct.VmAbnormalLifeCycleOperation; import org.zstack.header.vm.VmInstanceConstant.Capability; import org.zstack.header.vm.VmInstanceConstant.Params; import org.zstack.header.vm.VmInstanceConstant.VmOperation; import org.zstack.header.vm.VmInstanceDeletionPolicyManager.VmInstanceDeletionPolicy; import org.zstack.header.vm.VmInstanceSpec.HostName; import org.zstack.header.vm.VmInstanceSpec.IsoSpec; import org.zstack.header.volume.*; import org.zstack.identity.AccountManager; import org.zstack.tag.SystemTagCreator; import org.zstack.utils.CollectionUtils; import org.zstack.utils.DebugUtils; import org.zstack.utils.ObjectUtils; import org.zstack.utils.Utils; import org.zstack.utils.data.SizeUnit; import org.zstack.utils.function.ForEachFunction; import org.zstack.utils.function.Function; import org.zstack.utils.gson.JSONObjectUtil; import org.zstack.utils.logging.CLogger; import javax.persistence.TypedQuery; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import static org.zstack.core.Platform.err; import static org.zstack.core.Platform.operr; import static org.zstack.utils.CollectionDSL.*; public class VmInstanceBase extends AbstractVmInstance { protected static final CLogger logger = Utils.getLogger(VmInstanceBase.class); @Autowired protected CloudBus bus; @Autowired protected DatabaseFacade dbf; @Autowired protected ThreadFacade thdf; @Autowired protected VmInstanceManager vmMgr; @Autowired protected VmInstanceExtensionPointEmitter extEmitter; @Autowired protected VmInstanceNotifyPointEmitter notifyEmitter; @Autowired protected CascadeFacade casf; @Autowired protected AccountManager acntMgr; @Autowired protected EventFacade evtf; @Autowired protected PluginRegistry pluginRgty; @Autowired protected VmInstanceDeletionPolicyManager deletionPolicyMgr; @Autowired private SchedulerFacade schedulerFacade; @Autowired private HostAllocatorManager hostAllocatorMgr; protected VmInstanceVO self; protected VmInstanceVO originalCopy; protected String syncThreadName; private void checkState(final String hostUuid, final NoErrorCompletion completion) { CheckVmStateOnHypervisorMsg msg = new CheckVmStateOnHypervisorMsg(); msg.setVmInstanceUuids(list(self.getUuid())); msg.setHostUuid(hostUuid); bus.makeTargetServiceIdByResourceUuid(msg, HostConstant.SERVICE_ID, hostUuid); bus.send(msg, new CloudBusCallBack(completion) { @Override public void run(MessageReply reply) { if (!reply.isSuccess()) { N.New(VmInstanceVO.class, self.getUuid()).warn_("unable to check state of the vm[uuid:%s] on the host[uuid:%s], %s;" + "put the VM into the Unknown state", self.getUuid(), hostUuid, reply.getError()); self = dbf.reload(self); changeVmStateInDb(VmInstanceStateEvent.unknown); completion.done(); return; } CheckVmStateOnHypervisorReply r = reply.castReply(); String state = r.getStates().get(self.getUuid()); self = dbf.reload(self); if (state == null) { changeVmStateInDb(VmInstanceStateEvent.unknown); } if (VmInstanceState.Running.toString().equals(state)) { self.setHostUuid(hostUuid); changeVmStateInDb(VmInstanceStateEvent.running); } else if (VmInstanceState.Stopped.toString().equals(state) && self.getState().equals(VmInstanceState.Destroying)) { changeVmStateInDb(VmInstanceStateEvent.destroyed); } else if (VmInstanceState.Stopped.toString().equals(state)) { changeVmStateInDb(VmInstanceStateEvent.stopped); } else { throw new CloudRuntimeException(String.format( "CheckVmStateOnHypervisorMsg should only report states[Running or Stopped]," + "but it reports %s for the vm[uuid:%s] on the host[uuid:%s]", state, self.getUuid(), hostUuid)); } completion.done(); } }); } protected void destroy(final VmInstanceDeletionPolicy deletionPolicy, final Completion completion) { if (deletionPolicy == VmInstanceDeletionPolicy.DBOnly) { completion.success(); return; } final VmInstanceInventory inv = VmInstanceInventory.valueOf(self); VmInstanceSpec spec = buildSpecFromInventory(inv, VmOperation.Destroy); self = changeVmStateInDb(VmInstanceStateEvent.destroying); FlowChain chain = getDestroyVmWorkFlowChain(inv); setFlowMarshaller(chain); chain.setName(String.format("destroy-vm-%s", self.getUuid())); chain.getData().put(VmInstanceConstant.Params.VmInstanceSpec.toString(), spec); chain.getData().put(Params.DeletionPolicy, deletionPolicy); chain.done(new FlowDoneHandler(completion) { @Override public void handle(Map data) { completion.success(); } }).error(new FlowErrorHandler(completion) { @Override public void handle(final ErrorCode errCode, Map data) { if (originalCopy.getState() == VmInstanceState.Running) { checkState(originalCopy.getHostUuid(), new NoErrorCompletion(completion) { @Override public void done() { completion.fail(errCode); } }); } else { self = dbf.reload(self); changeVmStateInDb(VmInstanceStateEvent.unknown); completion.fail(errCode); } } }).start(); } protected VmInstanceVO getSelf() { return self; } protected VmInstanceInventory getSelfInventory() { return VmInstanceInventory.valueOf(self); } public VmInstanceBase(VmInstanceVO vo) { this.self = vo; this.syncThreadName = "Vm-" + vo.getUuid(); this.originalCopy = ObjectUtils.newAndCopy(vo, vo.getClass()); } protected VmInstanceVO refreshVO() { return refreshVO(false); } protected VmInstanceVO refreshVO(boolean noException) { VmInstanceVO vo = self; self = dbf.findByUuid(self.getUuid(), VmInstanceVO.class); if (self == null && noException) { return null; } if (self == null) { throw new OperationFailureException(operr("vm[uuid:%s, name:%s] has been deleted", vo.getUuid(), vo.getName())); } originalCopy = ObjectUtils.newAndCopy(vo, vo.getClass()); return self; } protected FlowChain getCreateVmWorkFlowChain(VmInstanceInventory inv) { return vmMgr.getCreateVmWorkFlowChain(inv); } protected FlowChain getStopVmWorkFlowChain(VmInstanceInventory inv) { return vmMgr.getStopVmWorkFlowChain(inv); } protected FlowChain getRebootVmWorkFlowChain(VmInstanceInventory inv) { return vmMgr.getRebootVmWorkFlowChain(inv); } protected FlowChain getStartVmWorkFlowChain(VmInstanceInventory inv) { return vmMgr.getStartVmWorkFlowChain(inv); } protected FlowChain getDestroyVmWorkFlowChain(VmInstanceInventory inv) { return vmMgr.getDestroyVmWorkFlowChain(inv); } protected FlowChain getExpungeVmWorkFlowChain(VmInstanceInventory inv) { return vmMgr.getExpungeVmWorkFlowChain(inv); } protected FlowChain getMigrateVmWorkFlowChain(VmInstanceInventory inv) { return vmMgr.getMigrateVmWorkFlowChain(inv); } protected FlowChain getAttachUninstantiatedVolumeWorkFlowChain(VmInstanceInventory inv) { return vmMgr.getAttachUninstantiatedVolumeWorkFlowChain(inv); } protected FlowChain getAttachIsoWorkFlowChain(VmInstanceInventory inv) { return vmMgr.getAttachIsoWorkFlowChain(inv); } protected FlowChain getDetachIsoWorkFlowChain(VmInstanceInventory inv) { return vmMgr.getDetachIsoWorkFlowChain(inv); } protected FlowChain getPauseVmWorkFlowChain(VmInstanceInventory inv) { return vmMgr.getPauseWorkFlowChain(inv); } protected FlowChain getResumeVmWorkFlowChain(VmInstanceInventory inv) { return vmMgr.getResumeVmWorkFlowChain(inv); } protected VmInstanceVO changeVmStateInDb(VmInstanceStateEvent stateEvent) { VmInstanceState bs = self.getState(); final VmInstanceState state = self.getState().nextState(stateEvent); if (state == VmInstanceState.Stopped) { // cleanup the hostUuid if the VM is stopped if (self.getHostUuid() != null) { self.setLastHostUuid(self.getHostUuid()); } self.setHostUuid(null); } self.setState(state); self = dbf.updateAndRefresh(self); if (bs != state) { logger.debug(String.format("vm[uuid:%s] changed state from %s to %s in db", self.getUuid(), bs, self.getState())); VmCanonicalEvents.VmStateChangedData data = new VmCanonicalEvents.VmStateChangedData(); data.setVmUuid(self.getUuid()); data.setOldState(bs.toString()); data.setNewState(state.toString()); data.setInventory(getSelfInventory()); evtf.fire(VmCanonicalEvents.VM_FULL_STATE_CHANGED_PATH, data); VmInstanceInventory inv = getSelfInventory(); CollectionUtils.safeForEach(pluginRgty.getExtensionList(VmStateChangedExtensionPoint.class), new ForEachFunction<VmStateChangedExtensionPoint>() { @Override public void run(VmStateChangedExtensionPoint ext) { ext.vmStateChanged(inv, bs, self.getState()); } }); //TODO: remove this notifyEmitter.notifyVmStateChange(VmInstanceInventory.valueOf(self), bs, state); } return self; } @Override @MessageSafe public void handleMessage(final Message msg) { if (msg instanceof APIMessage) { handleApiMessage((APIMessage) msg); } else { handleLocalMessage(msg); } } protected void handleLocalMessage(Message msg) { if (msg instanceof StartNewCreatedVmInstanceMsg) { handle((StartNewCreatedVmInstanceMsg) msg); } else if (msg instanceof StartVmInstanceMsg) { handle((StartVmInstanceMsg) msg); } else if (msg instanceof StopVmInstanceMsg) { handle((StopVmInstanceMsg) msg); } else if (msg instanceof RebootVmInstanceMsg) { handle((RebootVmInstanceMsg) msg); } else if (msg instanceof ChangeVmStateMsg) { handle((ChangeVmStateMsg) msg); } else if (msg instanceof DestroyVmInstanceMsg) { handle((DestroyVmInstanceMsg) msg); } else if (msg instanceof AttachNicToVmMsg) { handle((AttachNicToVmMsg) msg); } else if (msg instanceof CreateTemplateFromVmRootVolumeMsg) { handle((CreateTemplateFromVmRootVolumeMsg) msg); } else if (msg instanceof VmInstanceDeletionMsg) { handle((VmInstanceDeletionMsg) msg); } else if (msg instanceof VmAttachNicMsg) { handle((VmAttachNicMsg) msg); } else if (msg instanceof MigrateVmMsg) { handle((MigrateVmMsg) msg); } else if (msg instanceof DetachDataVolumeFromVmMsg) { handle((DetachDataVolumeFromVmMsg) msg); } else if (msg instanceof AttachDataVolumeToVmMsg) { handle((AttachDataVolumeToVmMsg) msg); } else if (msg instanceof GetVmMigrationTargetHostMsg) { handle((GetVmMigrationTargetHostMsg) msg); } else if (msg instanceof ChangeVmMetaDataMsg) { handle((ChangeVmMetaDataMsg) msg); } else if (msg instanceof LockVmInstanceMsg) { handle((LockVmInstanceMsg) msg); } else if (msg instanceof DetachNicFromVmMsg) { handle((DetachNicFromVmMsg) msg); } else if (msg instanceof VmStateChangedOnHostMsg) { handle((VmStateChangedOnHostMsg) msg); } else if (msg instanceof VmCheckOwnStateMsg) { handle((VmCheckOwnStateMsg) msg); } else if (msg instanceof ExpungeVmMsg) { handle((ExpungeVmMsg) msg); } else if (msg instanceof HaStartVmInstanceMsg) { handle((HaStartVmInstanceMsg) msg); } else { VmInstanceBaseExtensionFactory ext = vmMgr.getVmInstanceBaseExtensionFactory(msg); if (ext != null) { VmInstance v = ext.getVmInstance(self); v.handleMessage(msg); } else { bus.dealWithUnknownMessage(msg); } } } private void handle(final APIGetVmStartingCandidateClustersHostsMsg msg) { thdf.chainSubmit(new ChainTask(msg) { @Override public String getSyncSignature() { return syncThreadName; } @Override public void run(final SyncTaskChain chain) { getStartingCandidateHosts(msg, new NoErrorCompletion(chain) { @Override public void done() { chain.next(); } }); } @Override public String getName() { return "get-starting-candidate-hosts"; } }); } private void getStartingCandidateHosts(final APIGetVmStartingCandidateClustersHostsMsg msg, final NoErrorCompletion completion) { refreshVO(); ErrorCode err = validateOperationByState(msg, self.getState(), SysErrors.OPERATION_ERROR); if (err != null) { throw new OperationFailureException(err); } final DesignatedAllocateHostMsg amsg = new DesignatedAllocateHostMsg(); amsg.setCpuCapacity(self.getCpuNum()); amsg.setMemoryCapacity(self.getMemorySize()); amsg.setVmInstance(VmInstanceInventory.valueOf(self)); amsg.setServiceId(bus.makeLocalServiceId(HostAllocatorConstant.SERVICE_ID)); amsg.setAllocatorStrategy(self.getAllocatorStrategy()); amsg.setVmOperation(VmOperation.Start.toString()); amsg.setL3NetworkUuids(CollectionUtils.transformToList(self.getVmNics(), new Function<String, VmNicVO>() { @Override public String call(VmNicVO arg) { return arg.getL3NetworkUuid(); } })); amsg.setDryRun(true); amsg.setListAllHosts(true); final APIGetVmStartingCandidateClustersHostsReply reply = new APIGetVmStartingCandidateClustersHostsReply(); bus.send(amsg, new CloudBusCallBack(completion) { @Override public void run(MessageReply re) { if (!re.isSuccess()) { if (HostAllocatorError.NO_AVAILABLE_HOST.toString().equals(re.getError().getCode())) { reply.setHostInventories(new ArrayList<>()); reply.setClusterInventories(new ArrayList<>()); } else { reply.setError(re.getError()); } } else { List<HostInventory> hosts = ((AllocateHostDryRunReply) re).getHosts(); if (!hosts.isEmpty()) { List<String> cuuids = CollectionUtils.transformToList(hosts, new Function<String, HostInventory>() { @Override public String call(HostInventory arg) { return arg.getClusterUuid(); } }); SimpleQuery<ClusterVO> cq = dbf.createQuery(ClusterVO.class); cq.add(ClusterVO_.uuid, Op.IN, cuuids); List<ClusterVO> cvos = cq.list(); reply.setClusterInventories(ClusterInventory.valueOf(cvos)); reply.setHostInventories(hosts); } else { reply.setHostInventories(hosts); reply.setClusterInventories(new ArrayList<>()); } } bus.reply(msg, reply); completion.done(); } }); } private void handle(final HaStartVmInstanceMsg msg) { thdf.chainSubmit(new ChainTask(msg) { @Override public String getSyncSignature() { return syncThreadName; } @Override public void run(final SyncTaskChain chain) { refreshVO(); HaStartVmJudger judger; try { Class clz = Class.forName(msg.getJudgerClassName()); judger = (HaStartVmJudger) clz.newInstance(); } catch (Exception e) { throw new CloudRuntimeException(e); } final HaStartVmInstanceReply reply = new HaStartVmInstanceReply(); if (!judger.whetherStartVm(getSelfInventory())) { bus.reply(msg, reply); chain.next(); return; } logger.debug(String.format("HaStartVmJudger[%s] says the VM[uuid:%s, name:%s] is qualified for HA start, now we are starting it", judger.getClass(), self.getUuid(), self.getName())); self.setState(VmInstanceState.Stopped); dbf.update(self); startVm(msg, new Completion(msg, chain) { @Override public void success() { bus.reply(msg, reply); chain.next(); } @Override public void fail(ErrorCode errorCode) { reply.setError(errorCode); bus.reply(msg, reply); chain.next(); } }); } @Override public String getName() { return "ha-start-vm"; } }); } private void changeVmIp(final String l3Uuid, final String ip, final Completion completion) { final VmNicVO targetNic = CollectionUtils.find(self.getVmNics(), new Function<VmNicVO, VmNicVO>() { @Override public VmNicVO call(VmNicVO arg) { return l3Uuid.equals(arg.getL3NetworkUuid()) ? arg : null; } }); if (targetNic == null) { throw new OperationFailureException(operr("the vm[uuid:%s] has no nic on the L3 network[uuid:%s]", self.getUuid(), l3Uuid)); } if (ip.equals(targetNic.getIp())) { completion.success(); return; } final UsedIpInventory oldIp = new UsedIpInventory(); oldIp.setIp(targetNic.getIp()); oldIp.setGateway(targetNic.getGateway()); oldIp.setNetmask(targetNic.getNetmask()); oldIp.setL3NetworkUuid(targetNic.getL3NetworkUuid()); oldIp.setUuid(targetNic.getUsedIpUuid()); final FlowChain chain = FlowChainBuilder.newShareFlowChain(); chain.setName(String.format("change-vm-ip-to-%s-l3-%s-vm-%s", ip, l3Uuid, self.getUuid())); chain.then(new ShareFlow() { UsedIpInventory newIp; String oldIpUuid = targetNic.getUsedIpUuid(); @Override public void setup() { flow(new Flow() { String __name__ = "acquire-new-ip"; @Override public void run(final FlowTrigger trigger, Map data) { AllocateIpMsg amsg = new AllocateIpMsg(); amsg.setL3NetworkUuid(l3Uuid); amsg.setRequiredIp(ip); bus.makeTargetServiceIdByResourceUuid(amsg, L3NetworkConstant.SERVICE_ID, l3Uuid); bus.send(amsg, new CloudBusCallBack(trigger) { @Override public void run(MessageReply reply) { if (!reply.isSuccess()) { trigger.fail(reply.getError()); } else { AllocateIpReply r = reply.castReply(); newIp = r.getIpInventory(); trigger.next(); } } }); } @Override public void rollback(FlowRollback trigger, Map data) { if (newIp != null) { ReturnIpMsg rmsg = new ReturnIpMsg(); rmsg.setL3NetworkUuid(newIp.getL3NetworkUuid()); rmsg.setUsedIpUuid(newIp.getUuid()); bus.makeTargetServiceIdByResourceUuid(rmsg, L3NetworkConstant.SERVICE_ID, newIp.getL3NetworkUuid()); bus.send(rmsg); } trigger.rollback(); } }); flow(new NoRollbackFlow() { String __name__ = "change-ip-in-database"; @Override public void run(FlowTrigger trigger, Map data) { targetNic.setUsedIpUuid(newIp.getUuid()); targetNic.setGateway(newIp.getGateway()); targetNic.setNetmask(newIp.getNetmask()); targetNic.setIp(newIp.getIp()); dbf.update(targetNic); trigger.next(); } }); flow(new NoRollbackFlow() { String __name__ = "return-old-ip"; @Override public void run(FlowTrigger trigger, Map data) { ReturnIpMsg rmsg = new ReturnIpMsg(); rmsg.setUsedIpUuid(oldIpUuid); rmsg.setL3NetworkUuid(targetNic.getL3NetworkUuid()); bus.makeTargetServiceIdByResourceUuid(rmsg, L3NetworkConstant.SERVICE_ID, targetNic.getL3NetworkUuid()); bus.send(rmsg); trigger.next(); } }); done(new FlowDoneHandler(completion) { @Override public void handle(Map data) { final VmInstanceInventory vm = getSelfInventory(); final VmNicInventory nic = VmNicInventory.valueOf(targetNic); CollectionUtils.safeForEach(pluginRgty.getExtensionList(VmIpChangedExtensionPoint.class), new ForEachFunction<VmIpChangedExtensionPoint>() { @Override public void run(VmIpChangedExtensionPoint ext) { ext.vmIpChanged(vm, nic, oldIp, newIp); } }); completion.success(); } }); error(new FlowErrorHandler(completion) { @Override public void handle(ErrorCode errCode, Map data) { completion.fail(errCode); } }); } }).start(); } private void handle(final ExpungeVmMsg msg) { final ExpungeVmReply reply = new ExpungeVmReply(); thdf.chainSubmit(new ChainTask(msg) { @Override public String getSyncSignature() { return syncThreadName; } @Override public void run(final SyncTaskChain chain) { expunge(msg, new Completion(msg, chain) { @Override public void success() { bus.reply(msg, reply); chain.next(); } @Override public void fail(ErrorCode errorCode) { reply.setError(errorCode); bus.reply(msg, reply); chain.next(); } }); } @Override public String getName() { return "expunge-vm"; } }); } private void expunge(Message msg, final Completion completion) { refreshVO(); final VmInstanceInventory inv = getSelfInventory(); CollectionUtils.safeForEach(pluginRgty.getExtensionList(VmBeforeExpungeExtensionPoint.class), new ForEachFunction<VmBeforeExpungeExtensionPoint>() { @Override public void run(VmBeforeExpungeExtensionPoint arg) { arg.vmBeforeExpunge(inv); } }); ErrorCode error = validateOperationByState(msg, self.getState(), SysErrors.OPERATION_ERROR); if (error != null) { throw new OperationFailureException(error); } if (inv.getAllVolumes().size() > 1) { throw new CloudRuntimeException(String.format("why the deleted vm[uuid:%s] has data volumes??? %s", self.getUuid(), JSONObjectUtil.toJsonString(inv.getAllVolumes()))); } VmInstanceSpec spec = buildSpecFromInventory(inv, VmOperation.Expunge); FlowChain chain = getExpungeVmWorkFlowChain(inv); setFlowMarshaller(chain); chain.setName(String.format("expunge-vm-%s", self.getUuid())); chain.getData().put(VmInstanceConstant.Params.VmInstanceSpec.toString(), spec); chain.getData().put(Params.DeletionPolicy, VmInstanceDeletionPolicy.Direct); chain.done(new FlowDoneHandler(completion) { @Override public void handle(Map data) { dbf.removeCollection(self.getVmNics(), VmNicVO.class); dbf.remove(self); logger.debug(String.format("successfully expunged the vm[uuid:%s]", self.getUuid())); completion.success(); } }).error(new FlowErrorHandler(completion) { @Override public void handle(final ErrorCode errCode, Map data) { completion.fail(errCode); } }).start(); } private void handle(final VmCheckOwnStateMsg msg) { thdf.chainSubmit(new ChainTask(msg) { @Override public String getSyncSignature() { return syncThreadName; } @Override public void run(final SyncTaskChain chain) { refreshVO(); final VmCheckOwnStateReply reply = new VmCheckOwnStateReply(); if (self.getHostUuid() == null) { // no way to check bus.reply(msg, reply); chain.next(); return; } final CheckVmStateOnHypervisorMsg cmsg = new CheckVmStateOnHypervisorMsg(); cmsg.setVmInstanceUuids(list(self.getUuid())); cmsg.setHostUuid(self.getHostUuid()); bus.makeTargetServiceIdByResourceUuid(cmsg, HostConstant.SERVICE_ID, self.getHostUuid()); bus.send(cmsg, new CloudBusCallBack(msg, chain) { @Override public void run(MessageReply r) { if (!r.isSuccess()) { reply.setError(r.getError()); bus.reply(msg, r); chain.next(); return; } CheckVmStateOnHypervisorReply cr = r.castReply(); String s = cr.getStates().get(self.getUuid()); VmInstanceState state = VmInstanceState.valueOf(s); if (state != self.getState()) { VmStateChangedOnHostMsg vcmsg = new VmStateChangedOnHostMsg(); vcmsg.setHostUuid(self.getHostUuid()); vcmsg.setVmInstanceUuid(self.getUuid()); vcmsg.setStateOnHost(state); bus.makeTargetServiceIdByResourceUuid(vcmsg, VmInstanceConstant.SERVICE_ID, self.getUuid()); bus.send(vcmsg); } bus.reply(msg, reply); chain.next(); } }); } @Override public String getName() { return "check-state"; } }); } private void handle(final VmStateChangedOnHostMsg msg) { thdf.chainSubmit(new ChainTask(msg) { @Override public String getSyncSignature() { return syncThreadName; } @Override public void run(final SyncTaskChain chain) { vmStateChangeOnHost(msg, new NoErrorCompletion(chain) { @Override public void done() { chain.next(); } }); } @Override public String getName() { return String.format("vm-%s-state-change-on-the-host-%s", self.getUuid(), msg.getHostUuid()); } }); } private VmAbnormalLifeCycleOperation getVmAbnormalLifeCycleOperation(String originalHostUuid, String currentHostUuid, VmInstanceState originalState, VmInstanceState currentState) { if (originalState == VmInstanceState.Stopped && currentState == VmInstanceState.Running) { return VmAbnormalLifeCycleOperation.VmRunningOnTheHost; } if (originalState == VmInstanceState.Running && currentState == VmInstanceState.Stopped && currentHostUuid.equals(originalHostUuid)) { return VmAbnormalLifeCycleOperation.VmStoppedOnTheSameHost; } if (VmInstanceState.intermediateStates.contains(originalState) && currentState == VmInstanceState.Running) { return VmAbnormalLifeCycleOperation.VmRunningFromIntermediateState; } if (VmInstanceState.intermediateStates.contains(originalState) && currentState == VmInstanceState.Stopped) { return VmAbnormalLifeCycleOperation.VmStoppedFromIntermediateState; } if (originalState == VmInstanceState.Running && currentState == VmInstanceState.Paused && currentHostUuid.equals(originalHostUuid)) { return VmAbnormalLifeCycleOperation.VmPausedFromRunningStateHostNotChanged; } if (originalState == VmInstanceState.Unknown && currentState == VmInstanceState.Paused && currentHostUuid.equals(originalHostUuid)) { return VmAbnormalLifeCycleOperation.VmPausedFromUnknownStateHostNotChanged; } if (originalState == VmInstanceState.Unknown && currentState == VmInstanceState.Running && currentHostUuid.equals(originalHostUuid)) { return VmAbnormalLifeCycleOperation.VmRunningFromUnknownStateHostNotChanged; } if (originalState == VmInstanceState.Unknown && currentState == VmInstanceState.Running && !currentHostUuid.equals(originalHostUuid)) { return VmAbnormalLifeCycleOperation.VmRunningFromUnknownStateHostChanged; } if (originalState == VmInstanceState.Unknown && currentState == VmInstanceState.Stopped && currentHostUuid.equals(originalHostUuid)) { return VmAbnormalLifeCycleOperation.VmStoppedOnTheSameHost; } if (originalState == VmInstanceState.Unknown && currentState == VmInstanceState.Stopped && originalHostUuid == null && currentHostUuid.equals(self.getLastHostUuid())) { return VmAbnormalLifeCycleOperation.VmStoppedFromUnknownStateHostNotChanged; } if (originalState == VmInstanceState.Running && originalState == currentState && !currentHostUuid.equals(originalHostUuid)) { return VmAbnormalLifeCycleOperation.VmMigrateToAnotherHost; } if (originalState == VmInstanceState.Paused && currentState == VmInstanceState.Running && currentHostUuid.equals(originalHostUuid)) { return VmAbnormalLifeCycleOperation.VmRunningFromPausedStateHostNotChanged; } if (originalState == VmInstanceState.Paused && currentState == VmInstanceState.Stopped && currentHostUuid.equals(originalHostUuid)) { return VmAbnormalLifeCycleOperation.VmStoppedFromPausedStateHostNotChanged; } throw new CloudRuntimeException(String.format("unknown VM[uuid:%s] abnormal state combination[original state: %s," + " current state: %s, original host:%s, current host:%s]", self.getUuid(), originalState, currentState, originalHostUuid, currentHostUuid)); } private void vmStateChangeOnHost(final VmStateChangedOnHostMsg msg, final NoErrorCompletion completion) { final VmStateChangedOnHostReply reply = new VmStateChangedOnHostReply(); if (refreshVO(true) == null) { // the vm has been deleted reply.setError(operr("the vm has been deleted")); bus.reply(msg, reply); completion.done(); return; } if (msg.getVmStateAtTracingMoment() != null) { // the vm tracer periodically reports vms's state. It catches an old state // before an vm operation(start, stop, reboot, migrate) completes. Ignore this VmInstanceState expected = VmInstanceState.valueOf(msg.getVmStateAtTracingMoment()); if (expected != self.getState()) { bus.reply(msg, reply); completion.done(); return; } } final String originalHostUuid = self.getHostUuid(); final String currentHostUuid = msg.getHostUuid(); final VmInstanceState originalState = self.getState(); final VmInstanceState currentState = VmInstanceState.valueOf(msg.getStateOnHost()); if (originalState == currentState && originalHostUuid != null && currentHostUuid.equals(originalHostUuid)) { logger.debug(String.format("vm[uuid:%s]'s state[%s] is inline with its state on the host[uuid:%s], ignore VmStateChangeOnHostMsg", self.getUuid(), originalState, originalHostUuid)); bus.reply(msg, reply); completion.done(); return; } if (originalState == VmInstanceState.Stopped && currentState == VmInstanceState.Unknown) { bus.reply(msg, reply); completion.done(); return; } final Runnable fireEvent = new Runnable() { @Override public void run() { VmTracerCanonicalEvents.VmStateChangedOnHostData data = new VmTracerCanonicalEvents.VmStateChangedOnHostData(); data.setVmUuid(self.getUuid()); data.setFrom(originalState); data.setTo(self.getState()); data.setOriginalHostUuid(originalHostUuid); data.setCurrentHostUuid(self.getHostUuid()); evtf.fire(VmTracerCanonicalEvents.VM_STATE_CHANGED_PATH, data); } }; if (currentState == VmInstanceState.Unknown) { changeVmStateInDb(VmInstanceStateEvent.unknown); fireEvent.run(); bus.reply(msg, reply); completion.done(); return; } VmAbnormalLifeCycleOperation operation = getVmAbnormalLifeCycleOperation(originalHostUuid, currentHostUuid, originalState, currentState); if (operation == VmAbnormalLifeCycleOperation.VmRunningFromUnknownStateHostNotChanged) { // the vm is detected on the host again. It's largely because the host disconnected before // and now reconnected self.setHostUuid(msg.getHostUuid()); changeVmStateInDb(VmInstanceStateEvent.running); fireEvent.run(); bus.reply(msg, reply); completion.done(); return; } else if (operation == VmAbnormalLifeCycleOperation.VmStoppedFromUnknownStateHostNotChanged) { // the vm comes out of the unknown state to the stopped state // it happens when an operation failure led the vm from the stopped state to the unknown state, // and later on the vm was detected as stopped on the host again self.setHostUuid(null); changeVmStateInDb(VmInstanceStateEvent.stopped); fireEvent.run(); bus.reply(msg, reply); completion.done(); return; } else if (operation == VmAbnormalLifeCycleOperation.VmStoppedFromPausedStateHostNotChanged) { self.setHostUuid(msg.getHostUuid()); changeVmStateInDb(VmInstanceStateEvent.stopped); fireEvent.run(); bus.reply(msg, reply); completion.done(); return; } else if (operation == VmAbnormalLifeCycleOperation.VmPausedFromUnknownStateHostNotChanged) { //some reason led vm to unknown state and the paused vm are detected on the host again self.setHostUuid(msg.getHostUuid()); changeVmStateInDb(VmInstanceStateEvent.paused); fireEvent.run(); bus.reply(msg, reply); completion.done(); return; } else if (operation == VmAbnormalLifeCycleOperation.VmPausedFromRunningStateHostNotChanged) { // just synchronize database self.setHostUuid(msg.getHostUuid()); changeVmStateInDb(VmInstanceStateEvent.paused); fireEvent.run(); bus.reply(msg, reply); completion.done(); return; } else if (operation == VmAbnormalLifeCycleOperation.VmRunningFromPausedStateHostNotChanged) { // just synchronize database self.setHostUuid(msg.getHostUuid()); changeVmStateInDb(VmInstanceStateEvent.running); fireEvent.run(); bus.reply(msg, reply); completion.done(); return; } List<VmAbnormalLifeCycleExtensionPoint> exts = pluginRgty.getExtensionList(VmAbnormalLifeCycleExtensionPoint.class); VmAbnormalLifeCycleStruct struct = new VmAbnormalLifeCycleStruct(); struct.setCurrentHostUuid(currentHostUuid); struct.setCurrentState(currentState); struct.setOriginalHostUuid(originalHostUuid); struct.setOriginalState(originalState); struct.setVmInstance(getSelfInventory()); struct.setOperation(operation); logger.debug(String.format("the vm[uuid:%s]'s state changed abnormally on the host[uuid:%s]," + " ZStack is going to take the operation[%s]," + "[original state: %s, current state: %s, original host: %s, current host:%s]", self.getUuid(), currentHostUuid, operation, originalState, currentState, originalHostUuid, currentHostUuid)); FlowChain chain = FlowChainBuilder.newSimpleFlowChain(); chain.setName(String.format("handle-abnormal-lifecycle-of-vm-%s", self.getUuid())); chain.getData().put(Params.AbnormalLifeCycleStruct, struct); chain.allowEmptyFlow(); for (VmAbnormalLifeCycleExtensionPoint ext : exts) { Flow flow = ext.createVmAbnormalLifeCycleHandlingFlow(struct); chain.then(flow); } chain.done(new FlowDoneHandler(completion) { @Override public void handle(Map data) { if (currentState == VmInstanceState.Running) { self.setHostUuid(currentHostUuid); changeVmStateInDb(VmInstanceStateEvent.running); } else if (currentState == VmInstanceState.Stopped) { self.setHostUuid(null); changeVmStateInDb(VmInstanceStateEvent.stopped); } fireEvent.run(); bus.reply(msg, reply); completion.done(); } }).error(new FlowErrorHandler(completion) { @Override public void handle(ErrorCode errCode, Map data) { N.New(VmInstanceVO.class, self.getUuid()).warn_("failed to handle abnormal lifecycle of the vm[uuid:%s, original state: %s, current state:%s," + "original host: %s, current host: %s], %s", self.getUuid(), originalState, currentState, originalHostUuid, currentHostUuid, errCode); reply.setError(errCode); bus.reply(msg, reply); completion.done(); } }).start(); } private String buildUserdata() { return new UserdataBuilder().buildByVmUuid(self.getUuid()); } private void handle(final DetachNicFromVmMsg msg) { thdf.chainSubmit(new ChainTask(msg) { @Override public String getSyncSignature() { return syncThreadName; } @Override public void run(final SyncTaskChain chain) { final DetachNicFromVmReply reply = new DetachNicFromVmReply(); refreshVO(); if (self.getState() == VmInstanceState.Destroyed) { // the cascade framework may send this message when // the vm has been destroyed VmNicVO nic = CollectionUtils.find(self.getVmNics(), new Function<VmNicVO, VmNicVO>() { @Override public VmNicVO call(VmNicVO arg) { return msg.getVmNicUuid().equals(arg.getUuid()) ? arg : null; } }); if (nic != null) { dbf.remove(nic); } bus.reply(msg, reply); chain.next(); return; } final ErrorCode allowed = validateOperationByState(msg, self.getState(), SysErrors.OPERATION_ERROR); if (allowed != null) { reply.setError(allowed); bus.reply(msg, reply); chain.next(); return; } detachNic(msg.getVmNicUuid(), new Completion(msg, chain) { @Override public void success() { bus.reply(msg, reply); chain.next(); } @Override public void fail(ErrorCode errorCode) { reply.setError(errorCode); bus.reply(msg, reply); chain.next(); } }); } @Override public String getName() { return "detach-nic"; } }); } private void handle(final LockVmInstanceMsg msg) { thdf.chainSubmit(new ChainTask(msg) { @Override public String getSyncSignature() { return syncThreadName; } @Override public void run(final SyncTaskChain chain) { logger.debug(String.format("locked vm[uuid:%s] for %s", self.getUuid(), msg.getReason())); evtf.on(LockResourceMessage.UNLOCK_CANONICAL_EVENT_PATH, new AutoOffEventCallback() { @Override public boolean run(Map tokens, Object data) { if (msg.getUnlockKey().equals(data)) { logger.debug(String.format("unlocked vm[uuid:%s] that was locked by %s", self.getUuid(), msg.getReason())); chain.next(); return true; } return false; } }); LockVmInstanceReply reply = new LockVmInstanceReply(); bus.reply(msg, reply); } @Override public String getName() { return String.format("lock-vm-%s", self.getUuid()); } }); } private void handle(final ChangeVmMetaDataMsg msg) { thdf.chainSubmit(new ChainTask(msg) { @Override public String getSyncSignature() { return syncThreadName; } @Override public void run(final SyncTaskChain chain) { changeMetaData(msg); chain.next(); } @Override public String getName() { return String.format("change-meta-data-of-vm-%s", self.getUuid()); } }); } private void changeMetaData(ChangeVmMetaDataMsg msg) { ChangeVmMetaDataReply reply = new ChangeVmMetaDataReply(); refreshVO(); if (self == null) { bus.reply(msg, reply); return; } AtomicVmState s = msg.getState(); AtomicHostUuid h = msg.getHostUuid(); if (msg.isNeedHostAndStateBothMatch()) { if (s != null && h != null && s.getExpected() == self.getState()) { if ((h.getExpected() == null && self.getHostUuid() == null) || (h.getExpected() != null && h.getExpected().equals(self.getHostUuid()))) { changeVmStateInDb(s.getValue().getDrivenEvent()); reply.setChangeStateDone(true); self.setHostUuid(h.getValue()); dbf.update(self); reply.setChangeHostUuidDone(true); } } } else { if (s != null && s.getExpected() == self.getState()) { changeVmStateInDb(s.getValue().getDrivenEvent()); reply.setChangeStateDone(true); } if (h != null) { if ((h.getExpected() == null && self.getHostUuid() == null) || (h.getExpected() != null && h.getExpected().equals(self.getHostUuid()))) { self.setHostUuid(h.getValue()); dbf.update(self); reply.setChangeHostUuidDone(true); } } } bus.reply(msg, reply); } private void getVmMigrationTargetHost(Message msg, final ReturnValueCompletion<List<HostInventory>> completion) { refreshVO(); ErrorCode allowed = validateOperationByState(msg, self.getState(), VmErrors.MIGRATE_ERROR); if (allowed != null) { completion.fail(allowed); return; } if (self.getVmNics().size() == 0) { completion.fail(operr("cannot get target migration host without any nics on vm")); return; } final DesignatedAllocateHostMsg amsg = new DesignatedAllocateHostMsg(); amsg.setCpuCapacity(self.getCpuNum()); amsg.setMemoryCapacity(self.getMemorySize()); amsg.getAvoidHostUuids().add(self.getHostUuid()); if (msg instanceof GetVmMigrationTargetHostMsg) { GetVmMigrationTargetHostMsg gmsg = (GetVmMigrationTargetHostMsg) msg; if (gmsg.getAvoidHostUuids() != null) { amsg.getAvoidHostUuids().addAll(gmsg.getAvoidHostUuids()); } } amsg.setVmInstance(VmInstanceInventory.valueOf(self)); amsg.setServiceId(bus.makeLocalServiceId(HostAllocatorConstant.SERVICE_ID)); amsg.setAllocatorStrategy(HostAllocatorConstant.MIGRATE_VM_ALLOCATOR_TYPE); amsg.setVmOperation(VmOperation.Migrate.toString()); amsg.setL3NetworkUuids(CollectionUtils.transformToList(self.getVmNics(), new Function<String, VmNicVO>() { @Override public String call(VmNicVO arg) { return arg.getL3NetworkUuid(); } })); amsg.setDryRun(true); amsg.setAllowNoL3Networks(true); bus.send(amsg, new CloudBusCallBack(completion) { @Override public void run(MessageReply re) { if (!re.isSuccess()) { if (HostAllocatorError.NO_AVAILABLE_HOST.toString().equals(re.getError().getCode())) { completion.success(new ArrayList<HostInventory>()); } else { completion.fail(re.getError()); } } else { completion.success(((AllocateHostDryRunReply) re).getHosts()); } } }); } private void handle(final GetVmMigrationTargetHostMsg msg) { thdf.chainSubmit(new ChainTask(msg) { @Override public String getSyncSignature() { return syncThreadName; } @Override public void run(final SyncTaskChain chain) { final GetVmMigrationTargetHostReply reply = new GetVmMigrationTargetHostReply(); getVmMigrationTargetHost(msg, new ReturnValueCompletion<List<HostInventory>>(msg, chain) { @Override public void success(List<HostInventory> returnValue) { reply.setHosts(returnValue); bus.reply(msg, reply); chain.next(); } @Override public void fail(ErrorCode errorCode) { reply.setError(errorCode); bus.reply(msg, reply); chain.next(); } }); } @Override public String getName() { return String.format("get-migration-target-host-for-vm-%s", self.getUuid()); } }); } private void handle(final AttachDataVolumeToVmMsg msg) { thdf.chainSubmit(new ChainTask(msg) { @Override public String getSyncSignature() { return syncThreadName; } @Override public void run(final SyncTaskChain chain) { attachDataVolume(msg, new NoErrorCompletion(chain) { @Override public void done() { chain.next(); } }); } @Override public String getName() { return String.format("attach-volume-%s-to-vm-%s", msg.getVolume().getUuid(), msg.getVmInstanceUuid()); } }); } private void handle(final DetachDataVolumeFromVmMsg msg) { thdf.chainSubmit(new ChainTask(msg) { @Override public String getSyncSignature() { return syncThreadName; } @Override public void run(final SyncTaskChain chain) { detachVolume(msg, new NoErrorCompletion(chain) { @Override public void done() { chain.next(); } }); } @Override public String getName() { return String.format("detach-volume-%s-from-vm-%s", msg.getVolume().getUuid(), msg.getVmInstanceUuid()); } }); } private void handle(final MigrateVmMsg msg) { final MigrateVmReply reply = new MigrateVmReply(); thdf.chainSubmit(new ChainTask(msg) { @Override public String getSyncSignature() { return syncThreadName; } @Override public void run(final SyncTaskChain chain) { migrateVm(msg, new Completion(chain) { @Override public void success() { bus.reply(msg, reply); chain.next(); } @Override public void fail(ErrorCode errorCode) { reply.setError(errorCode); bus.reply(msg, reply); chain.next(); } }); } @Override public String getName() { return String.format("migrate-vm-%s", self.getUuid()); } }); } private void attachNic(final Message msg, final String l3Uuid, final ReturnValueCompletion<VmNicInventory> completion) { thdf.chainSubmit(new ChainTask(completion) { @Override public String getSyncSignature() { return syncThreadName; } @Override @Deferred public void run(final SyncTaskChain chain) { refreshVO(); ErrorCode allowed = validateOperationByState(msg, self.getState(), SysErrors.OPERATION_ERROR); if (allowed != null) { completion.fail(allowed); return; } class SetDefaultL3Network { private boolean isSet = false; void set() { if (self.getDefaultL3NetworkUuid() == null) { self.setDefaultL3NetworkUuid(l3Uuid); self = dbf.updateAndRefresh(self); isSet = true; } } void rollback() { if (isSet) { self.setDefaultL3NetworkUuid(null); dbf.update(self); } } } class SetStaticIp { private boolean isSet = false; void set() { if (!(msg instanceof APIAttachL3NetworkToVmMsg)) { return; } APIAttachL3NetworkToVmMsg amsg = (APIAttachL3NetworkToVmMsg) msg; if (amsg.getStaticIp() == null) { return; } new StaticIpOperator().setStaticIp(self.getUuid(), amsg.getL3NetworkUuid(), amsg.getStaticIp()); isSet = true; } void rollback() { if (isSet) { APIAttachL3NetworkToVmMsg amsg = (APIAttachL3NetworkToVmMsg) msg; new StaticIpOperator().deleteStaticIpByVmUuidAndL3Uuid(self.getUuid(), amsg.getL3NetworkUuid()); } } } final SetDefaultL3Network setDefaultL3Network = new SetDefaultL3Network(); setDefaultL3Network.set(); Defer.guard(new Runnable() { @Override public void run() { setDefaultL3Network.rollback(); } }); final SetStaticIp setStaticIp = new SetStaticIp(); setStaticIp.set(); Defer.guard(new Runnable() { @Override public void run() { setStaticIp.rollback(); } }); final VmInstanceSpec spec = buildSpecFromInventory(getSelfInventory(), VmOperation.AttachNic); spec.setVmInventory(VmInstanceInventory.valueOf(self)); L3NetworkVO l3vo = dbf.findByUuid(l3Uuid, L3NetworkVO.class); final L3NetworkInventory l3 = L3NetworkInventory.valueOf(l3vo); final VmInstanceInventory vm = getSelfInventory(); for (VmPreAttachL3NetworkExtensionPoint ext : pluginRgty.getExtensionList(VmPreAttachL3NetworkExtensionPoint.class)) { ext.vmPreAttachL3Network(vm, l3); } spec.setL3Networks(list(l3)); spec.setDestNics(new ArrayList<VmNicInventory>()); CollectionUtils.safeForEach(pluginRgty.getExtensionList(VmBeforeAttachL3NetworkExtensionPoint.class), new ForEachFunction<VmBeforeAttachL3NetworkExtensionPoint>() { @Override public void run(VmBeforeAttachL3NetworkExtensionPoint arg) { arg.vmBeforeAttachL3Network(vm, l3); } }); FlowChain flowChain = FlowChainBuilder.newSimpleFlowChain(); setFlowMarshaller(flowChain); flowChain.setName(String.format("attachNic-vm-%s-l3-%s", self.getUuid(), l3Uuid)); flowChain.getData().put(VmInstanceConstant.Params.VmInstanceSpec.toString(), spec); flowChain.then(new VmAllocateNicFlow()); flowChain.then(new VmSetDefaultL3NetworkOnAttachingFlow()); if (self.getState() == VmInstanceState.Running) { flowChain.then(new VmInstantiateResourceOnAttachingNicFlow()); flowChain.then(new VmAttachNicOnHypervisorFlow()); } flowChain.done(new FlowDoneHandler(chain) { @Override public void handle(Map data) { CollectionUtils.safeForEach(pluginRgty.getExtensionList(VmAfterAttachL3NetworkExtensionPoint.class), new ForEachFunction<VmAfterAttachL3NetworkExtensionPoint>() { @Override public void run(VmAfterAttachL3NetworkExtensionPoint arg) { arg.vmAfterAttachL3Network(vm, l3); } }); VmNicInventory nic = spec.getDestNics().get(0); completion.success(nic); chain.next(); } }).error(new FlowErrorHandler(chain) { @Override public void handle(final ErrorCode errCode, Map data) { CollectionUtils.safeForEach(pluginRgty.getExtensionList(VmFailToAttachL3NetworkExtensionPoint.class), new ForEachFunction<VmFailToAttachL3NetworkExtensionPoint>() { @Override public void run(VmFailToAttachL3NetworkExtensionPoint arg) { arg.vmFailToAttachL3Network(vm, l3, errCode); } }); setDefaultL3Network.rollback(); setStaticIp.rollback(); completion.fail(errCode); chain.next(); } }).start(); } @Override public String getName() { return String.format("attachNic-vm-%s-l3-%s", self.getUuid(), l3Uuid); } }); } private void handle(final VmAttachNicMsg msg) { final VmAttachNicReply reply = new VmAttachNicReply(); attachNic(msg, msg.getL3NetworkUuid(), new ReturnValueCompletion<VmNicInventory>(msg) { @Override public void success(VmNicInventory nic) { reply.setInventroy(nic); bus.reply(msg, reply); } @Override public void fail(ErrorCode errorCode) { reply.setError(errorCode); bus.reply(msg, reply); } }); } protected void doDestroy(final VmInstanceDeletionPolicy deletionPolicy, final Completion completion) { final VmInstanceInventory inv = VmInstanceInventory.valueOf(self); extEmitter.beforeDestroyVm(inv); destroy(deletionPolicy, new Completion(completion) { @Override public void success() { extEmitter.afterDestroyVm(inv); logger.debug(String.format("successfully deleted vm instance[name:%s, uuid:%s]", self.getName(), self.getUuid())); if (deletionPolicy == VmInstanceDeletionPolicy.Direct) { self = dbf.reload(self); changeVmStateInDb(VmInstanceStateEvent.destroyed); dbf.remove(getSelf()); } else if (deletionPolicy == VmInstanceDeletionPolicy.DBOnly) { new SQLBatch() { @Override protected void scripts() { sql(VmNicVO.class).eq(VmNicVO_.vmInstanceUuid, self.getUuid()).hardDelete(); sql(VolumeVO.class).eq(VolumeVO_.vmInstanceUuid, self.getUuid()) .eq(VolumeVO_.type, VolumeType.Root).hardDelete(); sql(VmInstanceVO.class).eq(VmInstanceVO_.uuid, self.getUuid()).hardDelete(); } }.execute(); } else if (deletionPolicy == VmInstanceDeletionPolicy.Delay) { self = dbf.reload(self); self.setHostUuid(null); changeVmStateInDb(VmInstanceStateEvent.destroyed); } else if (deletionPolicy == VmInstanceDeletionPolicy.Never) { logger.warn(String.format("the vm[uuid:%s] is deleted, but by it's deletion policy[Never]," + " the root volume is not deleted on the primary storage", self.getUuid())); self = dbf.reload(self); self.setHostUuid(null); changeVmStateInDb(VmInstanceStateEvent.destroyed); } completion.success(); } @Override public void fail(ErrorCode errorCode) { extEmitter.failedToDestroyVm(inv, errorCode); logger.debug(String.format("failed to delete vm instance[name:%s, uuid:%s], because %s", self.getName(), self.getUuid(), errorCode)); completion.fail(errorCode); } }); } private VmInstanceDeletionPolicy getVmDeletionPolicy(final VmInstanceDeletionMsg msg) { if (self.getState() == VmInstanceState.Created) { return VmInstanceDeletionPolicy.DBOnly; } return msg.getDeletionPolicy() == null ? deletionPolicyMgr.getDeletionPolicy(self.getUuid()) : VmInstanceDeletionPolicy.valueOf(msg.getDeletionPolicy()); } private void handle(final VmInstanceDeletionMsg msg) { thdf.chainSubmit(new ChainTask(msg) { @Override public String getSyncSignature() { return syncThreadName; } @Override public void run(SyncTaskChain chain) { final VmInstanceDeletionReply r = new VmInstanceDeletionReply(); final VmInstanceDeletionPolicy deletionPolicy = getVmDeletionPolicy(msg); self = dbf.findByUuid(self.getUuid(), VmInstanceVO.class); if (self == null || self.getState() == VmInstanceState.Destroyed) { // the vm has been destroyed, most likely by rollback if (deletionPolicy != VmInstanceDeletionPolicy.DBOnly) { bus.reply(msg, r); chain.next(); return; } } destroyHook(deletionPolicy, new Completion(msg, chain) { @Override public void success() { bus.reply(msg, r); chain.next(); } @Override public void fail(ErrorCode errorCode) { r.setError(errorCode); bus.reply(msg, r); chain.next(); } }); } @Override public String getName() { return "delete-vm"; } }); } protected void destroyHook(VmInstanceDeletionPolicy deletionPolicy, Completion completion) { doDestroy(deletionPolicy, completion); } private void handle(final RebootVmInstanceMsg msg) { thdf.chainSubmit(new ChainTask(msg) { @Override public String getName() { return String.format("reboot-vm-%s", self.getUuid()); } @Override public String getSyncSignature() { return syncThreadName; } @Override public void run(SyncTaskChain chain) { rebootVm(msg, chain); } }); } private void rebootVm(final RebootVmInstanceMsg msg, final SyncTaskChain chain) { rebootVm(msg, new Completion(chain) { @Override public void success() { RebootVmInstanceReply reply = new RebootVmInstanceReply(); VmInstanceInventory inv = VmInstanceInventory.valueOf(self); reply.setInventory(inv); bus.reply(msg, reply); chain.next(); } @Override public void fail(ErrorCode errorCode) { RebootVmInstanceReply reply = new RebootVmInstanceReply(); reply.setError(errf.instantiateErrorCode(VmErrors.REBOOT_ERROR, errorCode)); bus.reply(msg, reply); chain.next(); } }); } private void handle(final StopVmInstanceMsg msg) { thdf.chainSubmit(new ChainTask(msg) { @Override public String getName() { return String.format("stop-vm-%s", self.getUuid()); } @Override public String getSyncSignature() { return syncThreadName; } @Override public void run(SyncTaskChain chain) { stopVm(msg, chain); } }); } private void stopVm(final StopVmInstanceMsg msg, final SyncTaskChain chain) { stopVm(msg, new Completion(chain) { @Override public void success() { StopVmInstanceReply reply = new StopVmInstanceReply(); VmInstanceInventory inv = VmInstanceInventory.valueOf(self); reply.setInventory(inv); bus.reply(msg, reply); chain.next(); } @Override public void fail(ErrorCode errorCode) { StopVmInstanceReply reply = new StopVmInstanceReply(); reply.setError(errf.instantiateErrorCode(VmErrors.STOP_ERROR, errorCode)); bus.reply(msg, reply); chain.next(); } }); } private void handle(final StartVmInstanceMsg msg) { thdf.chainSubmit(new ChainTask(msg) { @Override public String getName() { return String.format("start-vm-%s", self.getUuid()); } @Override public String getSyncSignature() { return syncThreadName; } @Override public void run(SyncTaskChain chain) { startVm(msg, chain); } }); } private void createTemplateFromRootVolume(final CreateTemplateFromVmRootVolumeMsg msg, final SyncTaskChain chain) { boolean callNext = true; try { refreshVO(); ErrorCode allowed = validateOperationByState(msg, self.getState(), SysErrors.OPERATION_ERROR); if (allowed != null) { bus.replyErrorByMessageType(msg, allowed); return; } final CreateTemplateFromVmRootVolumeReply reply = new CreateTemplateFromVmRootVolumeReply(); CreateTemplateFromVolumeOnPrimaryStorageMsg cmsg = new CreateTemplateFromVolumeOnPrimaryStorageMsg(); cmsg.setVolumeInventory(msg.getRootVolumeInventory()); cmsg.setBackupStorageUuid(msg.getBackupStorageUuid()); cmsg.setImageInventory(msg.getImageInventory()); bus.makeTargetServiceIdByResourceUuid(cmsg, PrimaryStorageConstant.SERVICE_ID, msg.getRootVolumeInventory().getPrimaryStorageUuid()); bus.send(cmsg, new CloudBusCallBack(chain) { private void fail(ErrorCode errorCode) { String err = String.format("failed to create template from root volume[uuid:%s] on primary storage[uuid:%s]", msg.getRootVolumeInventory().getUuid(), msg.getRootVolumeInventory().getPrimaryStorageUuid()); logger.warn(err); reply.setError(errf.instantiateErrorCode(SysErrors.OPERATION_ERROR, err, errorCode)); bus.reply(msg, reply); } @Override public void run(MessageReply r) { if (!r.isSuccess()) { fail(r.getError()); } else { CreateTemplateFromVolumeOnPrimaryStorageReply creply = (CreateTemplateFromVolumeOnPrimaryStorageReply) r; reply.setInstallPath(creply.getTemplateBackupStorageInstallPath()); reply.setFormat(creply.getFormat()); bus.reply(msg, reply); } chain.next(); } }); callNext = false; } finally { if (callNext) { chain.next(); } } } private void handle(final CreateTemplateFromVmRootVolumeMsg msg) { thdf.chainSubmit(new ChainTask(msg) { @Override public String getName() { return String.format("create-template-from-%s", self.getUuid()); } @Override public String getSyncSignature() { return syncThreadName; } @Override public void run(SyncTaskChain chain) { createTemplateFromRootVolume(msg, chain); } }); } private void handle(final AttachNicToVmMsg msg) { ErrorCode allowed = validateOperationByState(msg, self.getState(), SysErrors.OPERATION_ERROR); if (allowed != null) { bus.replyErrorByMessageType(msg, allowed); return; } AttachNicToVmOnHypervisorMsg amsg = new AttachNicToVmOnHypervisorMsg(); amsg.setVmUuid(self.getUuid()); amsg.setHostUuid(self.getHostUuid()); amsg.setNics(msg.getNics()); bus.makeTargetServiceIdByResourceUuid(amsg, HostConstant.SERVICE_ID, self.getHostUuid()); bus.send(amsg, new CloudBusCallBack(msg) { @Override public void run(MessageReply reply) { if (self.getDefaultL3NetworkUuid() == null) { self.setDefaultL3NetworkUuid(msg.getNics().get(0).getL3NetworkUuid()); self = dbf.updateAndRefresh(self); logger.debug(String.format("set the VM[uuid: %s]'s default L3 network[uuid:%s], as it doen't have one before", self.getUuid(), self.getDefaultL3NetworkUuid())); } AttachNicToVmReply r = new AttachNicToVmReply(); if (!reply.isSuccess()) { r.setError(errf.instantiateErrorCode(VmErrors.ATTACH_NETWORK_ERROR, r.getError())); } bus.reply(msg, r); } }); } private void handle(final DestroyVmInstanceMsg msg) { final DestroyVmInstanceReply reply = new DestroyVmInstanceReply(); final String issuer = VmInstanceVO.class.getSimpleName(); VmDeletionStruct s = new VmDeletionStruct(); s.setDeletionPolicy(deletionPolicyMgr.getDeletionPolicy(self.getUuid())); s.setInventory(getSelfInventory()); final List<VmDeletionStruct> ctx = list(s); final FlowChain chain = FlowChainBuilder.newSimpleFlowChain(); chain.setName(String.format("destory-vm-%s", self.getUuid())); chain.then(new NoRollbackFlow() { @Override public void run(final FlowTrigger trigger, Map data) { casf.asyncCascade(CascadeConstant.DELETION_FORCE_DELETE_CODE, issuer, ctx, new Completion(trigger) { @Override public void success() { trigger.next(); } @Override public void fail(ErrorCode errorCode) { trigger.fail(errorCode); } }); } }).done(new FlowDoneHandler(msg) { @Override public void handle(Map data) { casf.asyncCascadeFull(CascadeConstant.DELETION_CLEANUP_CODE, issuer, ctx, new NopeCompletion()); bus.reply(msg, reply); } }).error(new FlowErrorHandler(msg) { @Override public void handle(final ErrorCode errCode, Map data) { reply.setError(errCode); bus.reply(msg, reply); } }).start(); } protected void handle(final ChangeVmStateMsg msg) { thdf.chainSubmit(new ChainTask(msg) { @Override public String getName() { return String.format("change-vm-state-%s", self.getUuid()); } @Override public String getSyncSignature() { return syncThreadName; } @Override @Deferred public void run(SyncTaskChain chain) { refreshVO(); Defer.defer(new Runnable() { @Override public void run() { ChangeVmStateReply reply = new ChangeVmStateReply(); bus.reply(msg, reply); } }); if (self == null) { // vm has been deleted by previous request // this happens when delete vm request queued before // change state request from vm tracer. // in this case, ignore change state request logger.debug(String.format("vm[uuid:%s] has been deleted, ignore change vm state request from vm tracer", msg.getVmInstanceUuid())); chain.next(); return; } changeVmStateInDb(VmInstanceStateEvent.valueOf(msg.getStateEvent())); chain.next(); } }); } protected void setFlowMarshaller(FlowChain chain) { chain.setFlowMarshaller(new FlowMarshaller() { @Override public Flow marshalTheNextFlow(String previousFlowClassName, String nextFlowClassName, FlowChain chain, Map data) { Flow nflow = null; for (MarshalVmOperationFlowExtensionPoint mext : pluginRgty.getExtensionList(MarshalVmOperationFlowExtensionPoint.class)) { VmInstanceSpec spec = (VmInstanceSpec) data.get(VmInstanceConstant.Params.VmInstanceSpec.toString()); nflow = mext.marshalVmOperationFlow(previousFlowClassName, nextFlowClassName, chain, spec); if (nflow != null) { logger.debug(String.format("a VM[uuid: %s, operation: %s] operation flow[%s] is changed to the flow[%s] by %s", self.getUuid(), spec.getCurrentVmOperation(), nextFlowClassName, nflow.getClass(), mext.getClass())); break; } } return nflow; } }); } protected void selectBootOrder(VmInstanceSpec spec) { if (spec.getCurrentVmOperation() == null) { throw new CloudRuntimeException("selectBootOrder must be called after VmOperation is set"); } if (spec.getCurrentVmOperation() == VmOperation.NewCreate && spec.getDestIso() != null) { spec.setBootOrders(list(VmBootDevice.CdRom.toString())); } else { String order = VmSystemTags.BOOT_ORDER.getTokenByResourceUuid(self.getUuid(), VmSystemTags.BOOT_ORDER_TOKEN); if (order == null) { spec.setBootOrders(list(VmBootDevice.HardDisk.toString())); } else { spec.setBootOrders(list(order.split(","))); } } } protected void startVmFromNewCreate(final StartNewCreatedVmInstanceMsg msg, final SyncTaskChain taskChain) { refreshVO(); ErrorCode error = validateOperationByState(msg, self.getState(), SysErrors.OPERATION_ERROR); if (error != null) { throw new OperationFailureException(error); } error = extEmitter.preStartNewCreatedVm(msg.getVmInstanceInventory()); if (error != null) { throw new OperationFailureException(error); } StartNewCreatedVmInstanceReply reply = new StartNewCreatedVmInstanceReply(); startVmFromNewCreate(StartVmFromNewCreatedStruct.fromMessage(msg), new Completion(msg, taskChain) { @Override public void success() { self = dbf.reload(self); reply.setVmInventory(getSelfInventory()); bus.reply(msg, reply); taskChain.next(); } @Override public void fail(ErrorCode errorCode) { reply.setError(errorCode); bus.reply(msg, reply); taskChain.next(); } }); } protected void handle(final StartNewCreatedVmInstanceMsg msg) { thdf.chainSubmit(new ChainTask(msg) { @Override public String getName() { return String.format("create-vm-%s", self.getUuid()); } @Override public String getSyncSignature() { return syncThreadName; } @Override public void run(SyncTaskChain chain) { startVmFromNewCreate(msg, chain); } }); } protected void handleApiMessage(APIMessage msg) { if (msg instanceof APIStopVmInstanceMsg) { handle((APIStopVmInstanceMsg) msg); } else if (msg instanceof APICreateStopVmInstanceSchedulerMsg) { handle((APICreateStopVmInstanceSchedulerMsg) msg); } else if (msg instanceof APIRebootVmInstanceMsg) { handle((APIRebootVmInstanceMsg) msg); } else if (msg instanceof APICreateRebootVmInstanceSchedulerMsg) { handle((APICreateRebootVmInstanceSchedulerMsg) msg); } else if (msg instanceof APIDestroyVmInstanceMsg) { handle((APIDestroyVmInstanceMsg) msg); } else if (msg instanceof APIStartVmInstanceMsg) { handle((APIStartVmInstanceMsg) msg); } else if (msg instanceof APICreateStartVmInstanceSchedulerMsg) { handle((APICreateStartVmInstanceSchedulerMsg) msg); } else if (msg instanceof APIMigrateVmMsg) { handle((APIMigrateVmMsg) msg); } else if (msg instanceof APIAttachL3NetworkToVmMsg) { handle((APIAttachL3NetworkToVmMsg) msg); } else if (msg instanceof APIGetVmMigrationCandidateHostsMsg) { handle((APIGetVmMigrationCandidateHostsMsg) msg); } else if (msg instanceof APIGetVmAttachableDataVolumeMsg) { handle((APIGetVmAttachableDataVolumeMsg) msg); } else if (msg instanceof APIUpdateVmInstanceMsg) { handle((APIUpdateVmInstanceMsg) msg); } else if (msg instanceof APIChangeInstanceOfferingMsg) { handle((APIChangeInstanceOfferingMsg) msg); } else if (msg instanceof APIDetachL3NetworkFromVmMsg) { handle((APIDetachL3NetworkFromVmMsg) msg); } else if (msg instanceof APIGetVmAttachableL3NetworkMsg) { handle((APIGetVmAttachableL3NetworkMsg) msg); } else if (msg instanceof APIAttachIsoToVmInstanceMsg) { handle((APIAttachIsoToVmInstanceMsg) msg); } else if (msg instanceof APIDetachIsoFromVmInstanceMsg) { handle((APIDetachIsoFromVmInstanceMsg) msg); } else if (msg instanceof APIExpungeVmInstanceMsg) { handle((APIExpungeVmInstanceMsg) msg); } else if (msg instanceof APIRecoverVmInstanceMsg) { handle((APIRecoverVmInstanceMsg) msg); } else if (msg instanceof APISetVmBootOrderMsg) { handle((APISetVmBootOrderMsg) msg); } else if (msg instanceof APISetVmConsolePasswordMsg) { handle((APISetVmConsolePasswordMsg) msg); } else if (msg instanceof APIGetVmBootOrderMsg) { handle((APIGetVmBootOrderMsg) msg); } else if (msg instanceof APIDeleteVmConsolePasswordMsg) { handle((APIDeleteVmConsolePasswordMsg) msg); } else if (msg instanceof APIGetVmConsolePasswordMsg) { handle((APIGetVmConsolePasswordMsg) msg); } else if (msg instanceof APIGetVmConsoleAddressMsg) { handle((APIGetVmConsoleAddressMsg) msg); } else if (msg instanceof APISetVmHostnameMsg) { handle((APISetVmHostnameMsg) msg); } else if (msg instanceof APIDeleteVmHostnameMsg) { handle((APIDeleteVmHostnameMsg) msg); } else if (msg instanceof APISetVmStaticIpMsg) { handle((APISetVmStaticIpMsg) msg); } else if (msg instanceof APIDeleteVmStaticIpMsg) { handle((APIDeleteVmStaticIpMsg) msg); } else if (msg instanceof APIGetVmHostnameMsg) { handle((APIGetVmHostnameMsg) msg); } else if (msg instanceof APIGetVmStartingCandidateClustersHostsMsg) { handle((APIGetVmStartingCandidateClustersHostsMsg) msg); } else if (msg instanceof APIGetVmCapabilitiesMsg) { handle((APIGetVmCapabilitiesMsg) msg); } else if (msg instanceof APISetVmSshKeyMsg) { handle((APISetVmSshKeyMsg) msg); } else if (msg instanceof APIGetVmSshKeyMsg) { handle((APIGetVmSshKeyMsg) msg); } else if (msg instanceof APIDeleteVmSshKeyMsg) { handle((APIDeleteVmSshKeyMsg) msg); } else if (msg instanceof APIGetCandidateIsoForAttachingVmMsg) { handle((APIGetCandidateIsoForAttachingVmMsg) msg); } else if (msg instanceof APIPauseVmInstanceMsg) { handle((APIPauseVmInstanceMsg) msg); } else if (msg instanceof APIResumeVmInstanceMsg) { handle((APIResumeVmInstanceMsg) msg); } else if (msg instanceof APIReimageVmInstanceMsg) { handle((APIReimageVmInstanceMsg) msg); } else { VmInstanceBaseExtensionFactory ext = vmMgr.getVmInstanceBaseExtensionFactory(msg); if (ext != null) { VmInstance v = ext.getVmInstance(self); v.handleMessage(msg); } else { bus.dealWithUnknownMessage(msg); } } } @Transactional(readOnly = true) private void handle(APIGetCandidateIsoForAttachingVmMsg msg) { APIGetCandidateIsoForAttachingVmReply reply = new APIGetCandidateIsoForAttachingVmReply(); if (self.getState() != VmInstanceState.Running && self.getState() != VmInstanceState.Stopped) { reply.setInventories(new ArrayList<>()); bus.reply(msg, reply); return; } String psUuid = getSelfInventory().getRootVolume().getPrimaryStorageUuid(); PrimaryStorageVO ps = dbf.getEntityManager().find(PrimaryStorageVO.class, psUuid); PrimaryStorageType psType = PrimaryStorageType.valueOf(ps.getType()); List<String> bsUuids = psType.findBackupStorage(psUuid); if (bsUuids == null) { String sql = "select img" + " from ImageVO img, ImageBackupStorageRefVO ref, BackupStorageVO bs" + " where ref.imageUuid = img.uuid" + " and img.mediaType = :imgType" + " and img.status = :status" + " and bs.uuid = ref.backupStorageUuid" + " and bs.type in (:bsTypes)"; TypedQuery<ImageVO> q = dbf.getEntityManager().createQuery(sql, ImageVO.class); q.setParameter("imgType", ImageMediaType.ISO); q.setParameter("status", ImageStatus.Ready); q.setParameter("bsTypes", hostAllocatorMgr.getBackupStorageTypesByPrimaryStorageTypeFromMetrics(ps.getType())); reply.setInventories(ImageInventory.valueOf(q.getResultList())); } else if (!bsUuids.isEmpty()) { String sql = "select img" + " from ImageVO img, ImageBackupStorageRefVO ref, BackupStorageVO bs" + " where ref.imageUuid = img.uuid" + " and img.mediaType = :imgType" + " and img.status = :status" + " and bs.uuid = ref.backupStorageUuid" + " and bs.uuid in (:bsUuids)"; TypedQuery<ImageVO> q = dbf.getEntityManager().createQuery(sql, ImageVO.class); q.setParameter("imgType", ImageMediaType.ISO); q.setParameter("status", ImageStatus.Ready); q.setParameter("bsUuids", bsUuids); reply.setInventories(ImageInventory.valueOf(q.getResultList())); } else { reply.setInventories(new ArrayList<>()); } bus.reply(msg, reply); } private void handle(APIGetVmCapabilitiesMsg msg) { APIGetVmCapabilitiesReply reply = new APIGetVmCapabilitiesReply(); Map<String, Object> ret = new HashMap<>(); checkPrimaryStorageCapabilities(ret); checkImageMediaTypeCapabilities(ret); reply.setCapabilities(ret); bus.reply(msg, reply); } private void checkPrimaryStorageCapabilities(Map<String, Object> ret) { VolumeInventory rootVolume = getSelfInventory().getRootVolume(); if (rootVolume == null) { ret.put(Capability.LiveMigration.toString(), false); ret.put(Capability.VolumeMigration.toString(), false); } else { SimpleQuery<PrimaryStorageVO> q = dbf.createQuery(PrimaryStorageVO.class); q.select(PrimaryStorageVO_.type); q.add(PrimaryStorageVO_.uuid, Op.EQ, rootVolume.getPrimaryStorageUuid()); String type = q.findValue(); PrimaryStorageType psType = PrimaryStorageType.valueOf(type); ret.put(Capability.LiveMigration.toString(), psType.isSupportVmLiveMigration()); ret.put(Capability.VolumeMigration.toString(), psType.isSupportVolumeMigration()); } } private void checkImageMediaTypeCapabilities(Map<String, Object> ret) { ImageVO vo = null; ImageMediaType imageMediaType; if (self.getImageUuid() != null) { vo = dbf.findByUuid(self.getImageUuid(), ImageVO.class); } if (vo == null) { imageMediaType = null; } else { imageMediaType = vo.getMediaType(); } if (imageMediaType == ImageMediaType.ISO || imageMediaType == null) { ret.put(Capability.Reimage.toString(), false); } else { ret.put(Capability.Reimage.toString(), true); } } private void handle(APIGetVmHostnameMsg msg) { String hostname = VmSystemTags.HOSTNAME.getTokenByResourceUuid(self.getUuid(), VmSystemTags.HOSTNAME_TOKEN); APIGetVmHostnameReply reply = new APIGetVmHostnameReply(); reply.setHostname(hostname); bus.reply(msg, reply); } private void handle(final APIDeleteVmStaticIpMsg msg) { thdf.chainSubmit(new ChainTask(msg) { @Override public String getSyncSignature() { return syncThreadName; } @Override public void run(SyncTaskChain chain) { APIDeleteVmStaticIpEvent evt = new APIDeleteVmStaticIpEvent(msg.getId()); new StaticIpOperator().deleteStaticIpByVmUuidAndL3Uuid(self.getUuid(), msg.getL3NetworkUuid()); bus.publish(evt); chain.next(); } @Override public String getName() { return "delete-static-ip"; } }); } private void handle(final APISetVmStaticIpMsg msg) { thdf.chainSubmit(new ChainTask(msg) { @Override public String getSyncSignature() { return syncThreadName; } @Override public void run(final SyncTaskChain chain) { setStaticIp(msg, new NoErrorCompletion(msg, chain) { @Override public void done() { chain.next(); } }); } @Override public String getName() { return "set-static-ip"; } }); } private void setStaticIp(final APISetVmStaticIpMsg msg, final NoErrorCompletion completion) { refreshVO(); ErrorCode error = validateOperationByState(msg, self.getState(), SysErrors.OPERATION_ERROR); if (error != null) { throw new OperationFailureException(error); } final APISetVmStaticIpEvent evt = new APISetVmStaticIpEvent(msg.getId()); changeVmIp(msg.getL3NetworkUuid(), msg.getIp(), new Completion(msg, completion) { @Override public void success() { new StaticIpOperator().setStaticIp(self.getUuid(), msg.getL3NetworkUuid(), msg.getIp()); bus.publish(evt); completion.done(); } @Override public void fail(ErrorCode errorCode) { evt.setError(errorCode); bus.publish(evt); completion.done(); } }); } private void handle(APIDeleteVmHostnameMsg msg) { APIDeleteVmHostnameEvent evt = new APIDeleteVmHostnameEvent(msg.getId()); VmSystemTags.HOSTNAME.delete(self.getUuid()); bus.publish(evt); } private void handle(APISetVmHostnameMsg msg) { if (!VmSystemTags.HOSTNAME.hasTag(self.getUuid())) { SystemTagCreator creator = VmSystemTags.HOSTNAME.newSystemTagCreator(self.getUuid()); creator.setTagByTokens(map( e(VmSystemTags.HOSTNAME_TOKEN, msg.getHostname()) )); creator.create(); } else { VmSystemTags.HOSTNAME.update(self.getUuid(), VmSystemTags.HOSTNAME.instantiateTag( map(e(VmSystemTags.HOSTNAME_TOKEN, msg.getHostname())) )); } APISetVmHostnameEvent evt = new APISetVmHostnameEvent(msg.getId()); bus.publish(evt); } private void handle(final APIGetVmConsoleAddressMsg msg) { ErrorCode error = validateOperationByState(msg, self.getState(), SysErrors.OPERATION_ERROR); if (error != null) { throw new OperationFailureException(error); } final APIGetVmConsoleAddressReply creply = new APIGetVmConsoleAddressReply(); GetVmConsoleAddressFromHostMsg hmsg = new GetVmConsoleAddressFromHostMsg(); hmsg.setHostUuid(self.getHostUuid()); hmsg.setVmInstanceUuid(self.getUuid()); bus.makeTargetServiceIdByResourceUuid(hmsg, HostConstant.SERVICE_ID, self.getHostUuid()); bus.send(hmsg, new CloudBusCallBack(msg) { @Override public void run(MessageReply reply) { if (!reply.isSuccess()) { creply.setError(reply.getError()); } else { GetVmConsoleAddressFromHostReply hr = reply.castReply(); creply.setHostIp(hr.getHostIp()); creply.setPort(hr.getPort()); creply.setProtocol(hr.getProtocol()); } bus.reply(msg, creply); } }); } private void handle(APIGetVmBootOrderMsg msg) { APIGetVmBootOrderReply reply = new APIGetVmBootOrderReply(); String order = VmSystemTags.BOOT_ORDER.getTokenByResourceUuid(self.getUuid(), VmSystemTags.BOOT_ORDER_TOKEN); if (order == null) { reply.setOrder(list(VmBootDevice.HardDisk.toString())); } else { reply.setOrder(list(order.split(","))); } bus.reply(msg, reply); } private void handle(APISetVmBootOrderMsg msg) { APISetVmBootOrderEvent evt = new APISetVmBootOrderEvent(msg.getId()); if (msg.getBootOrder() != null) { SystemTagCreator creator = VmSystemTags.BOOT_ORDER.newSystemTagCreator(self.getUuid()); creator.inherent = true; creator.recreate = true; creator.setTagByTokens(map(e(VmSystemTags.BOOT_ORDER_TOKEN, StringUtils.join(msg.getBootOrder(), ",")))); creator.create(); } else { VmSystemTags.BOOT_ORDER.deleteInherentTag(self.getUuid()); } evt.setInventory(getSelfInventory()); bus.publish(evt); } private void handle(APISetVmConsolePasswordMsg msg) { APISetVmConsolePasswordEvent evt = new APISetVmConsolePasswordEvent(msg.getId()); SystemTagCreator creator = VmSystemTags.CONSOLE_PASSWORD.newSystemTagCreator(self.getUuid()); creator.setTagByTokens(map(e(VmSystemTags.CONSOLE_PASSWORD_TOKEN, msg.getConsolePassword()))); creator.recreate = true; creator.create(); evt.setInventory(getSelfInventory()); bus.publish(evt); } private void handle(APIGetVmConsolePasswordMsg msg) { APIGetVmConsolePasswordReply reply = new APIGetVmConsolePasswordReply(); String consolePassword = VmSystemTags.CONSOLE_PASSWORD.getTokenByResourceUuid(self.getUuid(), VmSystemTags.CONSOLE_PASSWORD_TOKEN); reply.setConsolePassword(consolePassword); bus.reply(msg, reply); } private void handle(APIDeleteVmConsolePasswordMsg msg) { APIDeleteVmConsolePasswordEvent evt = new APIDeleteVmConsolePasswordEvent(msg.getId()); VmSystemTags.CONSOLE_PASSWORD.delete(self.getUuid()); evt.setInventory(getSelfInventory()); bus.publish(evt); } private void handle(APISetVmSshKeyMsg msg) { APISetVmSshKeyEvent evt = new APISetVmSshKeyEvent(msg.getId()); SystemTagCreator creator = VmSystemTags.SSHKEY.newSystemTagCreator(self.getUuid()); creator.setTagByTokens(map(e(VmSystemTags.SSHKEY_TOKEN, msg.getSshKey()))); creator.recreate = true; creator.create(); evt.setInventory(getSelfInventory()); bus.publish(evt); } private void handle(APIGetVmSshKeyMsg msg) { APIGetVmSshKeyReply reply = new APIGetVmSshKeyReply(); String sshKey = VmSystemTags.SSHKEY.getTokenByResourceUuid(self.getUuid(), VmSystemTags.SSHKEY_TOKEN); reply.setSshKey(sshKey); bus.reply(msg, reply); } private void handle(APIDeleteVmSshKeyMsg msg) { APIDeleteVmSshKeyEvent evt = new APIDeleteVmSshKeyEvent(msg.getId()); VmSystemTags.SSHKEY.delete(self.getUuid()); evt.setInventory(getSelfInventory()); bus.publish(evt); } private boolean ipExists(final String l3uuid, final String ipAddress) { SimpleQuery<VmNicVO> q = dbf.createQuery(VmNicVO.class); q.add(VmNicVO_.l3NetworkUuid, Op.EQ, l3uuid); q.add(VmNicVO_.ip, Op.EQ, ipAddress); return q.isExists(); } // If the VM is assigned static IP and it is now occupied, we will // remove the static IP tag so that it can acquire IP dynamically. // c.f. issue #1639 private void checkIpConflict(final String vmUuid) { StaticIpOperator ipo = new StaticIpOperator(); for (Map.Entry<String, String> entry : ipo.getStaticIpbyVmUuid(vmUuid).entrySet()) { if (ipExists(entry.getKey(), entry.getValue())) { ipo.deleteStaticIpByVmUuidAndL3Uuid(vmUuid, entry.getKey()); } } } private void recoverVm(final Completion completion) { final VmInstanceInventory vm = getSelfInventory(); final List<RecoverVmExtensionPoint> exts = pluginRgty.getExtensionList(RecoverVmExtensionPoint.class); for (RecoverVmExtensionPoint ext : exts) { ext.preRecoverVm(vm); } CollectionUtils.forEach(exts, new ForEachFunction<RecoverVmExtensionPoint>() { @Override public void run(RecoverVmExtensionPoint ext) { ext.beforeRecoverVm(vm); } }); FlowChain chain = FlowChainBuilder.newShareFlowChain(); chain.setName(String.format("recover-vm-%s", self.getUuid())); chain.then(new ShareFlow() { @Override public void setup() { flow(new NoRollbackFlow() { String __name__ = "check-ip-conflict"; @Override public void run(FlowTrigger trigger, Map data) { checkIpConflict(vm.getUuid()); trigger.next(); } }); flow(new NoRollbackFlow() { String __name__ = "recover-root-volume"; @Override public void run(final FlowTrigger trigger, Map data) { RecoverVolumeMsg msg = new RecoverVolumeMsg(); msg.setVolumeUuid(self.getRootVolumeUuid()); bus.makeTargetServiceIdByResourceUuid(msg, VolumeConstant.SERVICE_ID, self.getRootVolumeUuid()); bus.send(msg, new CloudBusCallBack(trigger) { @Override public void run(MessageReply reply) { if (!reply.isSuccess()) { trigger.fail(reply.getError()); } else { trigger.next(); } } }); } }); flow(new NoRollbackFlow() { String __name__ = "recover-vm"; @Override public void run(FlowTrigger trigger, Map data) { self = changeVmStateInDb(VmInstanceStateEvent.stopped); CollectionUtils.forEach(exts, new ForEachFunction<RecoverVmExtensionPoint>() { @Override public void run(RecoverVmExtensionPoint ext) { ext.afterRecoverVm(vm); } }); trigger.next(); } }); done(new FlowDoneHandler(completion) { @Override public void handle(Map data) { completion.success(); } }); error(new FlowErrorHandler(completion) { @Override public void handle(ErrorCode errCode, Map data) { completion.fail(errCode); } }); } }).start(); } private void handle(final APIRecoverVmInstanceMsg msg) { thdf.chainSubmit(new ChainTask(msg) { @Override public String getSyncSignature() { return syncThreadName; } @Override public void run(final SyncTaskChain chain) { final APIRecoverVmInstanceEvent evt = new APIRecoverVmInstanceEvent(msg.getId()); refreshVO(); ErrorCode error = validateOperationByState(msg, self.getState(), SysErrors.OPERATION_ERROR); if (error != null) { evt.setError(error); bus.publish(evt); chain.next(); return; } recoverVm(new Completion(msg, chain) { @Override public void success() { evt.setInventory(getSelfInventory()); bus.publish(evt); chain.next(); } @Override public void fail(ErrorCode errorCode) { evt.setError(errorCode); bus.publish(evt); chain.next(); } }); } @Override public String getName() { return "recover-vm"; } }); } private void handle(final APIExpungeVmInstanceMsg msg) { final APIExpungeVmInstanceEvent evt = new APIExpungeVmInstanceEvent(msg.getId()); thdf.chainSubmit(new ChainTask(msg) { @Override public String getSyncSignature() { return syncThreadName; } @Override public void run(final SyncTaskChain chain) { expunge(msg, new Completion(msg, chain) { @Override public void success() { bus.publish(evt); chain.next(); } @Override public void fail(ErrorCode errorCode) { evt.setError(errorCode); bus.publish(evt); chain.next(); } }); } @Override public String getName() { return "expunge-vm-by-api"; } }); } private void handle(final APIDetachIsoFromVmInstanceMsg msg) { thdf.chainSubmit(new ChainTask(msg) { @Override public String getSyncSignature() { return syncThreadName; } @Override public void run(final SyncTaskChain chain) { final APIDetachIsoFromVmInstanceEvent evt = new APIDetachIsoFromVmInstanceEvent(msg.getId()); refreshVO(); ErrorCode allowed = validateOperationByState(msg, self.getState(), SysErrors.OPERATION_ERROR); if (allowed != null) { evt.setError(allowed); bus.publish(evt); chain.next(); return; } detachIso(new Completion(msg, chain) { @Override public void success() { self = dbf.reload(self); evt.setInventory(getSelfInventory()); bus.publish(evt); chain.next(); } @Override public void fail(ErrorCode errorCode) { evt.setError(errorCode); bus.publish(evt); chain.next(); } }); } @Override public String getName() { return String.format("detach-iso-from-vm-%s", self.getUuid()); } }); } private void detachIso(final Completion completion) { if (self.getState() == VmInstanceState.Stopped) { new IsoOperator().detachIsoFromVm(self.getUuid()); completion.success(); return; } if (!new IsoOperator().isIsoAttachedToVm(self.getUuid())) { completion.success(); return; } VmInstanceSpec spec = buildSpecFromInventory(getSelfInventory(), VmOperation.DetachIso); if (spec.getDestIso() == null) { // the image ISO has been deleted from backup storage // try to detach it from the VM anyway String isoUuid = new IsoOperator().getIsoUuidByVmUuid(self.getUuid()); IsoSpec isoSpec = new IsoSpec(); isoSpec.setImageUuid(isoUuid); spec.setDestIso(isoSpec); logger.debug(String.format("the iso[uuid:%s] has been deleted, try to detach it from the VM[uuid:%s] anyway", isoUuid, self.getUuid())); } FlowChain chain = getDetachIsoWorkFlowChain(spec.getVmInventory()); chain.setName(String.format("detach-iso-%s-from-vm-%s", spec.getDestIso().getImageUuid(), self.getUuid())); chain.getData().put(VmInstanceConstant.Params.VmInstanceSpec.toString(), spec); setFlowMarshaller(chain); chain.done(new FlowDoneHandler(completion) { @Override public void handle(Map data) { new IsoOperator().detachIsoFromVm(self.getUuid()); completion.success(); } }).error(new FlowErrorHandler(completion) { @Override public void handle(ErrorCode errCode, Map data) { completion.fail(errCode); } }).start(); } @Transactional(readOnly = true) private List<L3NetworkInventory> getAttachableL3Network(String accountUuid) { List<String> l3Uuids = acntMgr.getResourceUuidsCanAccessByAccount(accountUuid, L3NetworkVO.class); if (l3Uuids != null && l3Uuids.isEmpty()) { return new ArrayList<L3NetworkInventory>(); } String sql; TypedQuery<L3NetworkVO> q; if (self.getVmNics().isEmpty()) { if (l3Uuids == null) { // accessed by a system admin sql = "select l3" + " from L3NetworkVO l3, VmInstanceVO vm, L2NetworkVO l2, L2NetworkClusterRefVO l2ref" + " where vm.uuid = :uuid" + " and vm.clusterUuid = l2ref.clusterUuid" + " and l2ref.l2NetworkUuid = l2.uuid" + " and l2.uuid = l3.l2NetworkUuid" + " and l3.state = :l3State" + " group by l3.uuid"; q = dbf.getEntityManager().createQuery(sql, L3NetworkVO.class); } else { // accessed by a normal account sql = "select l3" + " from L3NetworkVO l3, VmInstanceVO vm, L2NetworkVO l2, L2NetworkClusterRefVO l2ref" + " where vm.uuid = :uuid" + " and vm.clusterUuid = l2ref.clusterUuid" + " and l2ref.l2NetworkUuid = l2.uuid" + " and l2.uuid = l3.l2NetworkUuid" + " and l3.state = :l3State" + " and l3.uuid in (:l3uuids)" + " group by l3.uuid"; q = dbf.getEntityManager().createQuery(sql, L3NetworkVO.class); q.setParameter("l3uuids", l3Uuids); } } else { if (l3Uuids == null) { // accessed by a system admin sql = "select l3" + " from L3NetworkVO l3, VmInstanceVO vm, L2NetworkVO l2, L2NetworkClusterRefVO l2ref" + " where l3.uuid not in" + " (select nic.l3NetworkUuid from VmNicVO nic where nic.vmInstanceUuid = :uuid)" + " and vm.uuid = :uuid" + " and vm.clusterUuid = l2ref.clusterUuid" + " and l2ref.l2NetworkUuid = l2.uuid" + " and l2.uuid = l3.l2NetworkUuid" + " and l3.state = :l3State" + " group by l3.uuid"; q = dbf.getEntityManager().createQuery(sql, L3NetworkVO.class); } else { // accessed by a normal account sql = "select l3" + " from L3NetworkVO l3, VmInstanceVO vm, L2NetworkVO l2, L2NetworkClusterRefVO l2ref" + " where l3.uuid not in" + " (select nic.l3NetworkUuid from VmNicVO nic where nic.vmInstanceUuid = :uuid)" + " and vm.uuid = :uuid" + " and vm.clusterUuid = l2ref.clusterUuid" + " and l2ref.l2NetworkUuid = l2.uuid" + " and l2.uuid = l3.l2NetworkUuid" + " and l3.state = :l3State" + " and l3.uuid in (:l3uuids)" + " group by l3.uuid"; q = dbf.getEntityManager().createQuery(sql, L3NetworkVO.class); q.setParameter("l3uuids", l3Uuids); } } q.setParameter("l3State", L3NetworkState.Enabled); q.setParameter("uuid", self.getUuid()); List<L3NetworkVO> l3s = q.getResultList(); return L3NetworkInventory.valueOf(l3s); } private void handle(APIGetVmAttachableL3NetworkMsg msg) { APIGetVmAttachableL3NetworkReply reply = new APIGetVmAttachableL3NetworkReply(); reply.setInventories(getAttachableL3Network(msg.getSession().getAccountUuid())); bus.reply(msg, reply); } private void handle(final APIAttachIsoToVmInstanceMsg msg) { thdf.chainSubmit(new ChainTask(msg) { @Override public String getSyncSignature() { return syncThreadName; } @Override public void run(final SyncTaskChain chain) { final APIAttachIsoToVmInstanceEvent evt = new APIAttachIsoToVmInstanceEvent(msg.getId()); refreshVO(); ErrorCode allowed = validateOperationByState(msg, self.getState(), SysErrors.OPERATION_ERROR); if (allowed != null) { evt.setError(allowed); bus.publish(evt); chain.next(); return; } attachIso(msg.getIsoUuid(), new Completion(msg, chain) { @Override public void success() { self = dbf.reload(self); evt.setInventory(getSelfInventory()); bus.publish(evt); chain.next(); } @Override public void fail(ErrorCode errorCode) { evt.setError(errorCode); bus.publish(evt); chain.next(); } }); } @Override public String getName() { return String.format("attach-iso-%s-to-vm-%s", msg.getIsoUuid(), self.getUuid()); } }); } private void attachIso(final String isoUuid, final Completion completion) { checkIfIsoAttachable(isoUuid); if (self.getState() == VmInstanceState.Stopped) { new IsoOperator().attachIsoToVm(self.getUuid(), isoUuid); completion.success(); return; } VmInstanceSpec spec = buildSpecFromInventory(getSelfInventory(), VmOperation.AttachIso); IsoSpec isoSpec = new IsoSpec(); isoSpec.setImageUuid(isoUuid); spec.setDestIso(isoSpec); FlowChain chain = getAttachIsoWorkFlowChain(spec.getVmInventory()); chain.setName(String.format("attach-iso-%s-to-vm-%s", isoUuid, self.getUuid())); chain.getData().put(VmInstanceConstant.Params.VmInstanceSpec.toString(), spec); setFlowMarshaller(chain); chain.done(new FlowDoneHandler(completion) { @Override public void handle(Map data) { new IsoOperator().attachIsoToVm(self.getUuid(), isoUuid); completion.success(); } }).error(new FlowErrorHandler(completion) { @Override public void handle(ErrorCode errCode, Map data) { completion.fail(errCode); } }).start(); } @Transactional(readOnly = true) private void checkIfIsoAttachable(String isoUuid) { String psUuid = getSelfInventory().getRootVolume().getPrimaryStorageUuid(); String sql = "select count(i)" + " from ImageCacheVO i" + " where i.primaryStorageUuid = :psUuid" + " and i.imageUuid = :isoUuid"; TypedQuery<Long> q = dbf.getEntityManager().createQuery(sql, Long.class); q.setParameter("psUuid", psUuid); q.setParameter("isoUuid", isoUuid); Long count = q.getSingleResult(); if (count > 0) { // on the same primary storage return; } PrimaryStorageVO psvo = dbf.getEntityManager().find(PrimaryStorageVO.class, psUuid); PrimaryStorageType type = PrimaryStorageType.valueOf(psvo.getType()); List<String> bsUuids = type.findBackupStorage(psUuid); if (bsUuids == null) { List<String> possibleBsTypes = hostAllocatorMgr.getBackupStorageTypesByPrimaryStorageTypeFromMetrics(psvo.getType()); sql = "select count(bs)" + " from BackupStorageVO bs, ImageBackupStorageRefVO ref" + " where bs.uuid = ref.backupStorageUuid" + " and ref.imageUuid = :imgUuid" + " and bs.type in (:bsTypes)"; q = dbf.getEntityManager().createQuery(sql, Long.class); q.setParameter("imgUuid", isoUuid); q.setParameter("bsTypes", possibleBsTypes); count = q.getSingleResult(); if (count > 0) { return; } } else if (!bsUuids.isEmpty()) { sql = "select count(bs)" + " from BackupStorageVO bs, ImageBackupStorageRefVO ref" + " where bs.uuid = ref.backupStorageUuid" + " and ref.imageUuid = :imgUuid" + " and bs.uuid in (:bsUuids)"; q = dbf.getEntityManager().createQuery(sql, Long.class); q.setParameter("imgUuid", isoUuid); q.setParameter("bsUuids", bsUuids); count = q.getSingleResult(); if (count > 0) { return; } } throw new OperationFailureException(operr("the ISO[uuid:%s] is on backup storage that is not compatible of the primary storage[uuid:%s]" + " where the VM[name:%s, uuid:%s] is on", isoUuid, psUuid, self.getName(), self.getUuid())); } private void handle(final APIDetachL3NetworkFromVmMsg msg) { thdf.chainSubmit(new ChainTask(msg) { @Override public String getSyncSignature() { return syncThreadName; } @Override public void run(final SyncTaskChain chain) { final APIDetachL3NetworkFromVmEvent evt = new APIDetachL3NetworkFromVmEvent(msg.getId()); refreshVO(); ErrorCode allowed = validateOperationByState(msg, self.getState(), SysErrors.OPERATION_ERROR); if (allowed != null) { evt.setError(allowed); bus.publish(evt); chain.next(); return; } detachNic(msg.getVmNicUuid(), new Completion(msg, chain) { @Override public void success() { self = dbf.reload(self); evt.setInventory(getSelfInventory()); bus.publish(evt); chain.next(); } @Override public void fail(ErrorCode errorCode) { evt.setError(errorCode); bus.publish(evt); chain.next(); } }); } @Override public String getName() { return "detach-nic"; } }); } // switch vm default nic if vm current default nic is input parm nic protected void selectDefaultL3(VmNicInventory nic) { if (self.getDefaultL3NetworkUuid() != null && !self.getDefaultL3NetworkUuid().equals(nic.getL3NetworkUuid())) { return; } final VmInstanceInventory vm = getSelfInventory(); final String previousDefaultL3 = vm.getDefaultL3NetworkUuid(); // the nic has been removed, reload self = dbf.reload(self); final VmNicVO candidate = CollectionUtils.find(self.getVmNics(), new Function<VmNicVO, VmNicVO>() { @Override public VmNicVO call(VmNicVO arg) { return arg.getUuid().equals(nic.getUuid()) ? null : arg; } }); if (candidate != null) { CollectionUtils.safeForEach( pluginRgty.getExtensionList(VmDefaultL3NetworkChangedExtensionPoint.class), new ForEachFunction<VmDefaultL3NetworkChangedExtensionPoint>() { @Override public void run(VmDefaultL3NetworkChangedExtensionPoint ext) { ext.vmDefaultL3NetworkChanged(vm, previousDefaultL3, candidate.getL3NetworkUuid()); } }); self.setDefaultL3NetworkUuid(candidate.getL3NetworkUuid()); logger.debug(String.format( "after detaching the nic[uuid:%s, L3 uuid:%s], change the default L3 of the VM[uuid:%s]" + " to the L3 network[uuid: %s]", nic.getUuid(), nic.getL3NetworkUuid(), self.getUuid(), candidate.getL3NetworkUuid())); } else { self.setDefaultL3NetworkUuid(null); logger.debug(String.format( "after detaching the nic[uuid:%s, L3 uuid:%s], change the default L3 of the VM[uuid:%s]" + " to null, as the VM has no other nics", nic.getUuid(), nic.getL3NetworkUuid(), self.getUuid())); } self = dbf.updateAndRefresh(self); } private void detachNic(final String nicUuid, final Completion completion) { final VmNicInventory nic = VmNicInventory.valueOf( CollectionUtils.find(self.getVmNics(), new Function<VmNicVO, VmNicVO>() { @Override public VmNicVO call(VmNicVO arg) { return arg.getUuid().equals(nicUuid) ? arg : null; } }) ); for (VmDetachNicExtensionPoint ext : pluginRgty.getExtensionList(VmDetachNicExtensionPoint.class)) { ext.preDetachNic(nic); } CollectionUtils.safeForEach(pluginRgty.getExtensionList(VmDetachNicExtensionPoint.class), new ForEachFunction<VmDetachNicExtensionPoint>() { @Override public void run(VmDetachNicExtensionPoint arg) { arg.beforeDetachNic(nic); } }); final VmInstanceSpec spec = buildSpecFromInventory(getSelfInventory(), VmOperation.DetachNic); spec.setVmInventory(VmInstanceInventory.valueOf(self)); spec.setDestNics(list(nic)); spec.setL3Networks(list(L3NetworkInventory.valueOf(dbf.findByUuid(nic.getL3NetworkUuid(), L3NetworkVO.class)))); FlowChain flowChain = FlowChainBuilder.newSimpleFlowChain(); flowChain.setName(String.format("detachNic-vm-%s-nic-%s", self.getUuid(), nicUuid)); setFlowMarshaller(flowChain); flowChain.getData().put(VmInstanceConstant.Params.VmInstanceSpec.toString(), spec); if (self.getState() == VmInstanceState.Running) { flowChain.then(new VmDetachNicOnHypervisorFlow()); } flowChain.then(new VmReleaseResourceOnDetachingNicFlow()); flowChain.then(new VmDetachNicFlow()); flowChain.done(new FlowDoneHandler(completion) { @Override public void handle(Map data) { selectDefaultL3(nic); removeStaticIp(); CollectionUtils.safeForEach(pluginRgty.getExtensionList(VmDetachNicExtensionPoint.class), new ForEachFunction<VmDetachNicExtensionPoint>() { @Override public void run(VmDetachNicExtensionPoint arg) { arg.afterDetachNic(nic); } }); completion.success(); } private void removeStaticIp() { new StaticIpOperator().deleteStaticIpByVmUuidAndL3Uuid(self.getUuid(), nic.getL3NetworkUuid()); } }).error(new FlowErrorHandler(completion) { @Override public void handle(final ErrorCode errCode, Map data) { CollectionUtils.safeForEach(pluginRgty.getExtensionList(VmDetachNicExtensionPoint.class), new ForEachFunction<VmDetachNicExtensionPoint>() { @Override public void run(VmDetachNicExtensionPoint arg) { arg.failedToDetachNic(nic, errCode); } }); completion.fail(errCode); } }).start(); } private void handle(final APIChangeInstanceOfferingMsg msg) { thdf.chainSubmit(new ChainTask(msg) { @Override public String getSyncSignature() { return syncThreadName; } @Override public void run(SyncTaskChain chain) { APIChangeInstanceOfferingEvent evt = new APIChangeInstanceOfferingEvent(msg.getId()); refreshVO(); ErrorCode allowed = validateOperationByState(msg, self.getState(), SysErrors.OPERATION_ERROR); if (allowed != null) { throw new OperationFailureException(operr("operation is not allowed to vm[uuid:%s] which is in the state of state:%s", self.getUuid(), self.getState())); } changeOffering(msg, new Completion(msg, chain) { @Override public void success() { refreshVO(); evt.setInventory(getSelfInventory()); bus.publish(evt); chain.next(); } @Override public void fail(ErrorCode errorCode) { evt.setError(errorCode); bus.publish(evt); chain.next(); } }); } @Override public String getName() { return "change-instance-offering"; } }); } private void changeOffering(APIChangeInstanceOfferingMsg msg, final Completion completion) { final InstanceOfferingVO newOfferingVO = dbf.findByUuid(msg.getInstanceOfferingUuid(), InstanceOfferingVO.class); final InstanceOfferingInventory inv = InstanceOfferingInventory.valueOf(newOfferingVO); final VmInstanceInventory vm = getSelfInventory(); List<ChangeInstanceOfferingExtensionPoint> exts = pluginRgty.getExtensionList(ChangeInstanceOfferingExtensionPoint.class); for (ChangeInstanceOfferingExtensionPoint ext : exts) { ext.preChangeInstanceOffering(vm, inv); } CollectionUtils.safeForEach(exts, new ForEachFunction<ChangeInstanceOfferingExtensionPoint>() { @Override public void run(ChangeInstanceOfferingExtensionPoint arg) { arg.beforeChangeInstanceOffering(vm, inv); } }); if (self.getState() == VmInstanceState.Stopped) { changeCpuAndMemoryForStoppedVm(newOfferingVO.getCpuNum(), newOfferingVO.getMemorySize()); completion.success(); } else if (self.getState() == VmInstanceState.Running) { changeCpuAndMemoryForRunningVm(newOfferingVO.getCpuNum(), newOfferingVO.getMemorySize(), completion); } CollectionUtils.safeForEach(exts, new ForEachFunction<ChangeInstanceOfferingExtensionPoint>() { @Override public void run(ChangeInstanceOfferingExtensionPoint arg) { arg.afterChangeInstanceOffering(vm, inv); } }); } private void changeCpuAndMemoryForStoppedVm(final int cpuNum, final long memorySize) { self.setCpuNum(cpuNum); self.setMemorySize(memorySize); self = dbf.updateAndRefresh(self); } private void changeCpuAndMemoryForRunningVm(final int cpuNum, final long memorySize, final Completion completion) { final int oldCpuNum = self.getCpuNum(); final long oldMemorySize = self.getMemorySize(); class AlignmentStruct { long alignedMemory; } final AlignmentStruct struct = new AlignmentStruct(); struct.alignedMemory = memorySize; FlowChain chain = FlowChainBuilder.newSimpleFlowChain(); chain.setName(String.format("change-cpu-and-memory-of-vm-%s", self.getUuid())); chain.then(new NoRollbackFlow() { String __name__ = "align-memory"; @Override public void run(FlowTrigger chain, Map data) { // align memory long increaseMemory = memorySize - oldMemorySize; long remainderMemory = increaseMemory % SizeUnit.MEGABYTE.toByte(128); if (increaseMemory != 0 && remainderMemory != 0) { if (remainderMemory > SizeUnit.MEGABYTE.toByte(128) / 2) { struct.alignedMemory = (memorySize / SizeUnit.MEGABYTE.toByte(128) + 1) * SizeUnit.MEGABYTE.toByte(128); } else { struct.alignedMemory = memorySize / SizeUnit.MEGABYTE.toByte(128) * SizeUnit.MEGABYTE.toByte(128); } N.New(VmInstanceVO.class, self.getUuid()).info_("automatically align memory from %s to %s", memorySize, struct.alignedMemory); } chain.next(); } }).then(new Flow() { String __name__ = String.format("allocate-host-capacity-on-host-%s", self.getHostUuid()); boolean result = false; @Override public void run(FlowTrigger chain, Map data) { DesignatedAllocateHostMsg msg = new DesignatedAllocateHostMsg(); msg.setCpuCapacity(cpuNum - oldCpuNum); msg.setMemoryCapacity(struct.alignedMemory - oldMemorySize); msg.setAllocatorStrategy(HostAllocatorConstant.DESIGNATED_HOST_ALLOCATOR_STRATEGY_TYPE); msg.setVmInstance(VmInstanceInventory.valueOf(self)); msg.setHostUuid(self.getHostUuid()); msg.setL3NetworkUuids(CollectionUtils.transformToList(self.getVmNics(), new Function<String, VmNicVO>() { @Override public String call(VmNicVO arg) { return arg.getL3NetworkUuid(); } })); msg.setServiceId(bus.makeLocalServiceId(HostAllocatorConstant.SERVICE_ID)); bus.send(msg, new CloudBusCallBack(chain) { @Override public void run(MessageReply reply) { if (!reply.isSuccess()) { chain.fail(reply.getError()); } else { result = true; logger.debug(String.format("reserve memory %s bytes and cpu %s on host[uuid:%s]", memorySize - self.getMemorySize(), cpuNum - self.getCpuNum(), self.getHostUuid())); chain.next(); } } }); } @Override public void rollback(FlowRollback chain, Map data) { if (result) { ReturnHostCapacityMsg msg = new ReturnHostCapacityMsg(); msg.setCpuCapacity(cpuNum - oldCpuNum); msg.setMemoryCapacity(struct.alignedMemory - oldMemorySize); msg.setHostUuid(self.getHostUuid()); msg.setServiceId(bus.makeLocalServiceId(HostAllocatorConstant.SERVICE_ID)); bus.send(msg); } chain.rollback(); } }).then(new NoRollbackFlow() { String __name__ = String.format("change-cpu-of-vm-%s", self.getUuid()); @Override public void run(FlowTrigger chain, Map data) { if (cpuNum != self.getCpuNum()) { IncreaseVmCpuMsg msg = new IncreaseVmCpuMsg(); msg.setVmInstanceUuid(self.getUuid()); msg.setHostUuid(self.getHostUuid()); msg.setCpuNum(cpuNum); bus.makeTargetServiceIdByResourceUuid(msg, HostConstant.SERVICE_ID, self.getHostUuid()); bus.send(msg, new CloudBusCallBack(chain) { @Override public void run(MessageReply reply) { if (!reply.isSuccess()) { logger.error("failed to update cpu"); chain.fail(reply.getError()); } else { IncreaseVmCpuReply r = reply.castReply(); self.setCpuNum(r.getCpuNum()); chain.next(); } } }); } else { chain.next(); } } }).then(new NoRollbackFlow() { String __name__ = String.format("change-memory-of-vm-%s", self.getUuid()); @Override public void run(FlowTrigger chain, Map data) { if (memorySize != self.getMemorySize()) { IncreaseVmMemoryMsg msg = new IncreaseVmMemoryMsg(); msg.setVmInstanceUuid(self.getUuid()); msg.setHostUuid(self.getHostUuid()); msg.setMemorySize(struct.alignedMemory); bus.makeTargetServiceIdByResourceUuid(msg, HostConstant.SERVICE_ID, self.getHostUuid()); bus.send(msg, new CloudBusCallBack(chain) { @Override public void run(MessageReply reply) { if (!reply.isSuccess()) { logger.error("failed to update memory"); chain.fail(reply.getError()); } else { IncreaseVmMemoryReply r = reply.castReply(); self.setMemorySize(r.getMemorySize()); chain.next(); } } }); } else { chain.next(); } } }).done(new FlowDoneHandler(completion) { @Override public void handle(Map data) { dbf.update(self); completion.success(); } }).error(new FlowErrorHandler(completion) { @Override public void handle(ErrorCode errCode, Map data) { completion.fail(errCode); } }).start(); } private void handle(final APIUpdateVmInstanceMsg msg) { thdf.chainSubmit(new ChainTask(msg) { @Override public String getSyncSignature() { return syncThreadName; } @Override public void run(SyncTaskChain chain) { APIUpdateVmInstanceEvent evt = new APIUpdateVmInstanceEvent(msg.getId()); refreshVO(); List<Runnable> extensions = new ArrayList<Runnable>(); final VmInstanceInventory vm = getSelfInventory(); boolean update = false; if (msg.getName() != null) { self.setName(msg.getName()); update = true; } if (msg.getDescription() != null) { self.setDescription(msg.getDescription()); update = true; } if (msg.getState() != null) { self.setState(VmInstanceState.valueOf(msg.getState())); update = true; if (!vm.getState().equals(msg.getState())) { extensions.add(new Runnable() { @Override public void run() { logger.debug(String.format("vm[uuid:%s] changed state from %s to %s", self.getUuid(), vm.getState(), msg.getState())); VmCanonicalEvents.VmStateChangedData data = new VmCanonicalEvents.VmStateChangedData(); data.setVmUuid(self.getUuid()); data.setOldState(vm.getState()); data.setNewState(msg.getState()); data.setInventory(getSelfInventory()); evtf.fire(VmCanonicalEvents.VM_FULL_STATE_CHANGED_PATH, data); } }); } } if (msg.getDefaultL3NetworkUuid() != null) { self.setDefaultL3NetworkUuid(msg.getDefaultL3NetworkUuid()); update = true; if (!msg.getDefaultL3NetworkUuid().equals(vm.getDefaultL3NetworkUuid())) { extensions.add(new Runnable() { @Override public void run() { for (VmDefaultL3NetworkChangedExtensionPoint ext : pluginRgty.getExtensionList(VmDefaultL3NetworkChangedExtensionPoint.class)) { ext.vmDefaultL3NetworkChanged(vm, vm.getDefaultL3NetworkUuid(), msg.getDefaultL3NetworkUuid()); } } }); } } if (msg.getPlatform() != null) { self.setPlatform(msg.getPlatform()); update = true; } if (update) { dbf.update(self); } CollectionUtils.safeForEach(extensions, new ForEachFunction<Runnable>() { @Override public void run(Runnable arg) { arg.run(); } }); if (msg.getCpuNum() != null || msg.getMemorySize() != null) { changeCpuAndMemory(msg, new Completion(msg, chain) { @Override public void success() { refreshVO(); evt.setInventory(getSelfInventory()); bus.publish(evt); chain.next(); } @Override public void fail(ErrorCode errorCode) { evt.setError(errorCode); bus.publish(evt); chain.next(); } }); } else { evt.setInventory(getSelfInventory()); bus.publish(evt); chain.next(); } } @Override public String getName() { return "update-vm-info"; } }); } @Transactional(readOnly = true) private List<VolumeVO> getAttachableVolume(String accountUuid) { List<String> volUuids = acntMgr.getResourceUuidsCanAccessByAccount(accountUuid, VolumeVO.class); if (volUuids != null && volUuids.isEmpty()) { return new ArrayList<>(); } List<String> formats = VolumeFormat.getVolumeFormatSupportedByHypervisorTypeInString(self.getHypervisorType()); if (formats.isEmpty()) { throw new CloudRuntimeException(String.format("cannot find volume formats for the hypervisor type[%s]", self.getHypervisorType())); } String sql; List<VolumeVO> vos; if (volUuids == null) { // accessed by a system admin sql = "select vol" + " from VolumeVO vol, VmInstanceVO vm, PrimaryStorageClusterRefVO ref" + " where vol.type = :type" + " and vol.state = :volState" + " and vol.status = :volStatus" + " and vol.format in (:formats)" + " and vol.vmInstanceUuid is null" + " and vm.clusterUuid = ref.clusterUuid" + " and ref.primaryStorageUuid = vol.primaryStorageUuid" + " and vm.uuid = :vmUuid" + " group by vol.uuid"; TypedQuery<VolumeVO> q = dbf.getEntityManager().createQuery(sql, VolumeVO.class); q.setParameter("volState", VolumeState.Enabled); q.setParameter("volStatus", VolumeStatus.Ready); q.setParameter("formats", formats); q.setParameter("vmUuid", self.getUuid()); q.setParameter("type", VolumeType.Data); vos = q.getResultList(); sql = "select vol" + " from VolumeVO vol" + " where vol.type = :type" + " and vol.status = :volStatus" + " and vol.state = :volState" + " group by vol.uuid"; q = dbf.getEntityManager().createQuery(sql, VolumeVO.class); q.setParameter("type", VolumeType.Data); q.setParameter("volState", VolumeState.Enabled); q.setParameter("volStatus", VolumeStatus.NotInstantiated); vos.addAll(q.getResultList()); } else { // accessed by a normal account sql = "select vol" + " from VolumeVO vol, VmInstanceVO vm, PrimaryStorageClusterRefVO ref" + " where vol.type = :type" + " and vol.state = :volState" + " and vol.status = :volStatus" + " and vol.format in (:formats)" + " and vol.vmInstanceUuid is null" + " and vm.clusterUuid = ref.clusterUuid" + " and ref.primaryStorageUuid = vol.primaryStorageUuid" + " and vol.uuid in (:volUuids)" + " and vm.uuid = :vmUuid" + " group by vol.uuid"; TypedQuery<VolumeVO> q = dbf.getEntityManager().createQuery(sql, VolumeVO.class); q.setParameter("volState", VolumeState.Enabled); q.setParameter("volStatus", VolumeStatus.Ready); q.setParameter("vmUuid", self.getUuid()); q.setParameter("formats", formats); q.setParameter("type", VolumeType.Data); q.setParameter("volUuids", volUuids); vos = q.getResultList(); sql = "select vol" + " from VolumeVO vol" + " where vol.type = :type" + " and vol.status = :volStatus" + " and vol.state = :volState" + " and vol.uuid in (:volUuids)" + " group by vol.uuid"; q = dbf.getEntityManager().createQuery(sql, VolumeVO.class); q.setParameter("type", VolumeType.Data); q.setParameter("volState", VolumeState.Enabled); q.setParameter("volUuids", volUuids); q.setParameter("volStatus", VolumeStatus.NotInstantiated); vos.addAll(q.getResultList()); } for (GetAttachableVolumeExtensionPoint ext : pluginRgty.getExtensionList(GetAttachableVolumeExtensionPoint.class)) { if (!vos.isEmpty()) { vos = ext.returnAttachableVolumes(getSelfInventory(), vos); } } return vos; } private void changeCpuAndMemory(APIUpdateVmInstanceMsg msg, Completion completion) { // add some systemTag and can be appliance for the next start int cpuNum = msg.getCpuNum() == null ? self.getCpuNum() : msg.getCpuNum(); long memory = msg.getMemorySize() == null ? self.getMemorySize() : msg.getMemorySize(); if (self.getState().equals(VmInstanceState.Running)) { changeCpuAndMemoryForRunningVm(cpuNum, memory, completion); } else if (self.getState().equals(VmInstanceState.Stopped)) { changeCpuAndMemoryForStoppedVm(cpuNum, memory); completion.success(); } } private void handle(APIGetVmAttachableDataVolumeMsg msg) { APIGetVmAttachableDataVolumeReply reply = new APIGetVmAttachableDataVolumeReply(); reply.setInventories(VolumeInventory.valueOf(getAttachableVolume(msg.getSession().getAccountUuid()))); bus.reply(msg, reply); } private void handle(final APIGetVmMigrationCandidateHostsMsg msg) { final APIGetVmMigrationCandidateHostsReply reply = new APIGetVmMigrationCandidateHostsReply(); getVmMigrationTargetHost(msg, new ReturnValueCompletion<List<HostInventory>>(msg) { @Override public void success(List<HostInventory> returnValue) { reply.setInventories(returnValue); bus.reply(msg, reply); } @Override public void fail(ErrorCode errorCode) { reply.setError(errorCode); bus.reply(msg, reply); } }); } private void handle(final APIAttachL3NetworkToVmMsg msg) { final APIAttachL3NetworkToVmEvent evt = new APIAttachL3NetworkToVmEvent(msg.getId()); attachNic(msg, msg.getL3NetworkUuid(), new ReturnValueCompletion<VmNicInventory>(msg) { @Override public void success(VmNicInventory returnValue) { self = dbf.reload(self); evt.setInventory(VmInstanceInventory.valueOf(self)); bus.publish(evt); } @Override public void fail(ErrorCode errorCode) { evt.setError(errorCode); bus.publish(evt); } }); } private void detachVolume(final DetachDataVolumeFromVmMsg msg, final NoErrorCompletion completion) { final DetachDataVolumeFromVmReply reply = new DetachDataVolumeFromVmReply(); refreshVO(true); if (self == null || VmInstanceState.Destroyed == self.getState()) { // the vm is destroyed, the data volume must have been detached bus.reply(msg, reply); completion.done(); return; } ErrorCode allowed = validateOperationByState(msg, self.getState(), VmErrors.DETACH_VOLUME_ERROR); if (allowed != null) { throw new OperationFailureException(allowed); } final VolumeInventory volume = msg.getVolume(); extEmitter.preDetachVolume(getSelfInventory(), volume); extEmitter.beforeDetachVolume(getSelfInventory(), volume); if (self.getState() == VmInstanceState.Stopped) { extEmitter.afterDetachVolume(getSelfInventory(), volume); bus.reply(msg, reply); completion.done(); return; } // VmInstanceState.Running String hostUuid = self.getHostUuid(); DetachVolumeFromVmOnHypervisorMsg dmsg = new DetachVolumeFromVmOnHypervisorMsg(); dmsg.setVmInventory(VmInstanceInventory.valueOf(self)); dmsg.setInventory(volume); dmsg.setHostUuid(hostUuid); bus.makeTargetServiceIdByResourceUuid(dmsg, HostConstant.SERVICE_ID, hostUuid); bus.send(dmsg, new CloudBusCallBack(msg, completion) { @Override public void run(final MessageReply r) { if (!r.isSuccess()) { reply.setError(r.getError()); extEmitter.failedToDetachVolume(getSelfInventory(), volume, r.getError()); } else { extEmitter.afterDetachVolume(getSelfInventory(), volume); } bus.reply(msg, reply); completion.done(); } }); } protected void attachDataVolume(final AttachDataVolumeToVmMsg msg, final NoErrorCompletion completion) { final AttachDataVolumeToVmReply reply = new AttachDataVolumeToVmReply(); refreshVO(); ErrorCode err = validateOperationByState(msg, self.getState(), VmErrors.ATTACH_VOLUME_ERROR); if (err != null) { throw new OperationFailureException(err); } final VolumeInventory volume = msg.getVolume(); extEmitter.preAttachVolume(getSelfInventory(), volume); extEmitter.beforeAttachVolume(getSelfInventory(), volume); VmInstanceSpec spec = new VmInstanceSpec(); spec.setMessage(msg); spec.setVmInventory(VmInstanceInventory.valueOf(self)); spec.setCurrentVmOperation(VmOperation.AttachVolume); spec.setDestDataVolumes(list(volume)); FlowChain chain; if (volume.getStatus().equals(VolumeStatus.Ready.toString())) { chain = FlowChainBuilder.newSimpleFlowChain(); chain.then(new VmAssignDeviceIdToAttachingVolumeFlow()); chain.then(new VmAttachVolumeOnHypervisorFlow()); } else { chain = getAttachUninstantiatedVolumeWorkFlowChain(spec.getVmInventory()); } setFlowMarshaller(chain); chain.setName(String.format("vm-%s-attach-volume-%s", self.getUuid(), volume.getUuid())); chain.getData().put(VmInstanceConstant.Params.VmInstanceSpec.toString(), spec); chain.getData().put(VmInstanceConstant.Params.AttachingVolumeInventory.toString(), volume); chain.done(new FlowDoneHandler(msg, completion) { @Override public void handle(Map data) { extEmitter.afterAttachVolume(getSelfInventory(), volume); reply.setHypervisorType(self.getHypervisorType()); bus.reply(msg, reply); completion.done(); } }).error(new FlowErrorHandler(msg, completion) { @Override public void handle(final ErrorCode errCode, Map data) { extEmitter.failedToAttachVolume(getSelfInventory(), volume, errCode); reply.setError(errf.instantiateErrorCode(VmErrors.ATTACH_VOLUME_ERROR, errCode)); bus.reply(msg, reply); completion.done(); } }).start(); } protected void migrateVm(final Message msg, final Completion completion) { refreshVO(); ErrorCode allowed = validateOperationByState(msg, self.getState(), VmErrors.MIGRATE_ERROR); if (allowed != null) { completion.fail(allowed); return; } VmInstanceInventory pinv = getSelfInventory(); for (VmPreMigrationExtensionPoint ext : pluginRgty.getExtensionList(VmPreMigrationExtensionPoint.class)) { ext.preVmMigration(pinv); } VmInstanceInventory inv = VmInstanceInventory.valueOf(self); final VmInstanceSpec spec = buildSpecFromInventory(inv, VmOperation.Migrate); final VmInstanceState originState = self.getState(); changeVmStateInDb(VmInstanceStateEvent.migrating); spec.setMessage(msg); FlowChain chain = getMigrateVmWorkFlowChain(inv); setFlowMarshaller(chain); chain.setName(String.format("migrate-vm-%s", self.getUuid())); chain.getData().put(VmInstanceConstant.Params.VmInstanceSpec.toString(), spec); chain.done(new FlowDoneHandler(completion) { @Override public void handle(final Map data) { VmInstanceSpec spec = (VmInstanceSpec) data.get(VmInstanceConstant.Params.VmInstanceSpec.toString()); HostInventory host = spec.getDestHost(); self.setZoneUuid(host.getZoneUuid()); self.setClusterUuid(host.getClusterUuid()); self.setLastHostUuid(self.getHostUuid()); self.setHostUuid(host.getUuid()); self = changeVmStateInDb(VmInstanceStateEvent.running); VmInstanceInventory vm = VmInstanceInventory.valueOf(self); extEmitter.afterMigrateVm(vm, vm.getLastHostUuid()); completion.success(); } }).error(new FlowErrorHandler(completion) { @Override public void handle(final ErrorCode errCode, Map data) { extEmitter.failedToMigrateVm(VmInstanceInventory.valueOf(self), spec.getDestHost().getUuid(), errCode); if (HostErrors.FAILED_TO_MIGRATE_VM_ON_HYPERVISOR.isEqual(errCode.getCode())) { checkState(originalCopy.getHostUuid(), new NoErrorCompletion(completion) { @Override public void done() { completion.fail(errCode); } }); } else { self.setState(originState); self = dbf.updateAndRefresh(self); completion.fail(errCode); } } }).start(); } protected void handle(final APIMigrateVmMsg msg) { thdf.chainSubmit(new ChainTask(msg) { @Override public String getName() { return String.format("migrate-vm-%s", self.getUuid()); } @Override public String getSyncSignature() { return syncThreadName; } @Override public void run(final SyncTaskChain chain) { migrateVm(msg, new Completion(chain) { @Override public void success() { APIMigrateVmEvent evt = new APIMigrateVmEvent(msg.getId()); evt.setInventory(VmInstanceInventory.valueOf(self)); bus.publish(evt); chain.next(); } @Override public void fail(ErrorCode errorCode) { APIMigrateVmEvent evt = new APIMigrateVmEvent(msg.getId()); evt.setError(errorCode); bus.publish(evt); chain.next(); } }); } }); } protected void startVm(final Message msg, final Completion completion) { refreshVO(); ErrorCode allowed = validateOperationByState(msg, self.getState(), null); if (allowed != null) { completion.fail(allowed); return; } if (self.getState() == VmInstanceState.Created) { StartVmFromNewCreatedStruct struct = new JsonLabel().get( StartVmFromNewCreatedStruct.makeLabelKey(self.getUuid()), StartVmFromNewCreatedStruct.class); startVmFromNewCreate(struct, completion); return; } VmInstanceInventory inv = VmInstanceInventory.valueOf(self); ErrorCode preStart = extEmitter.preStartVm(inv); if (preStart != null) { completion.fail(preStart); return; } final VmInstanceSpec spec = buildSpecFromInventory(inv, VmOperation.Start); spec.setMessage(msg); if (msg instanceof APIStartVmInstanceMsg) { APIStartVmInstanceMsg amsg = (APIStartVmInstanceMsg) msg; spec.setRequiredClusterUuid(amsg.getClusterUuid()); spec.setRequiredHostUuid(amsg.getHostUuid()); } if (spec.getDestNics().isEmpty()) { throw new OperationFailureException(operr("unable to start the vm[uuid:%s]." + " It doesn't have any nic, please attach a nic and try again", self.getUuid())); } final VmInstanceState originState = self.getState(); changeVmStateInDb(VmInstanceStateEvent.starting); extEmitter.beforeStartVm(VmInstanceInventory.valueOf(self)); FlowChain chain = getStartVmWorkFlowChain(inv); setFlowMarshaller(chain); String oldHostUuid = self.getHostUuid(); chain.setName(String.format("start-vm-%s", self.getUuid())); chain.getData().put(VmInstanceConstant.Params.VmInstanceSpec.toString(), spec); chain.done(new FlowDoneHandler(completion) { @Override public void handle(final Map data) { VmInstanceSpec spec = (VmInstanceSpec) data.get(VmInstanceConstant.Params.VmInstanceSpec.toString()); // reload self because some nics may have been deleted in start phase because a former L3Network deletion. // reload to avoid JPA EntityNotFoundException self = dbf.reload(self); self.setLastHostUuid(oldHostUuid); self.setHostUuid(spec.getDestHost().getUuid()); self.setClusterUuid(spec.getDestHost().getClusterUuid()); self.setZoneUuid(spec.getDestHost().getZoneUuid()); self = changeVmStateInDb(VmInstanceStateEvent.running); logger.debug(String.format("vm[uuid:%s] is running ..", self.getUuid())); VmInstanceInventory inv = VmInstanceInventory.valueOf(self); extEmitter.afterStartVm(inv); completion.success(); } }).error(new FlowErrorHandler(completion) { @Override public void handle(final ErrorCode errCode, Map data) { // reload self because some nics may have been deleted in start phase because a former L3Network deletion. // reload to avoid JPA EntityNotFoundException self = dbf.reload(self); extEmitter.failedToStartVm(VmInstanceInventory.valueOf(self), errCode); VmInstanceSpec spec = (VmInstanceSpec) data.get(VmInstanceConstant.Params.VmInstanceSpec.toString()); if (HostErrors.FAILED_TO_START_VM_ON_HYPERVISOR.isEqual(errCode.getCode())) { checkState(spec.getDestHost().getUuid(), new NoErrorCompletion(completion) { @Override public void done() { completion.fail(errCode); } }); } else { self.setState(originState); self = dbf.updateAndRefresh(self); completion.fail(errCode); } } }).start(); } private void startVmFromNewCreate(StartVmFromNewCreatedStruct struct, Completion completion) { VmInstanceInventory inv = getSelfInventory(); final VmInstanceSpec spec = new VmInstanceSpec(); spec.setRequiredPrimaryStorageUuidForRootVolume(struct.getPrimaryStorageUuidForRootVolume()); spec.setVmInventory(inv); if (struct.getL3NetworkUuids() != null && !struct.getL3NetworkUuids().isEmpty()) { SimpleQuery<L3NetworkVO> nwquery = dbf.createQuery(L3NetworkVO.class); nwquery.add(L3NetworkVO_.uuid, Op.IN, struct.getL3NetworkUuids()); List<L3NetworkVO> vos = nwquery.list(); List<L3NetworkInventory> nws = L3NetworkInventory.valueOf(vos); // order L3 networks by the order they specified in the API List<L3NetworkInventory> l3s = new ArrayList<>(nws.size()); for (final String l3Uuid : struct.getL3NetworkUuids()) { L3NetworkInventory l3 = CollectionUtils.find(nws, new Function<L3NetworkInventory, L3NetworkInventory>() { @Override public L3NetworkInventory call(L3NetworkInventory arg) { return arg.getUuid().equals(l3Uuid) ? arg : null; } }); if(l3 == null){ completion.fail(err( SysErrors.OPERATION_ERROR, "Unable to find L3Network[uuid:%s] to start the current vm, it may have been deleted, Operation suggestion: delete this vm, recreate a new vm", l3Uuid)); return; } DebugUtils.Assert(l3 != null, "where is the L3???"); l3s.add(l3); } spec.setL3Networks(l3s); } else { spec.setL3Networks(new ArrayList<>()); } if (struct.getDataDiskOfferingUuids() != null && !struct.getDataDiskOfferingUuids().isEmpty()) { SimpleQuery<DiskOfferingVO> dquery = dbf.createQuery(DiskOfferingVO.class); dquery.add(DiskOfferingVO_.uuid, SimpleQuery.Op.IN, struct.getDataDiskOfferingUuids()); List<DiskOfferingVO> vos = dquery.list(); // allow create multiple data volume from the same disk offering List<DiskOfferingInventory> disks = new ArrayList<>(); for (final String duuid : struct.getDataDiskOfferingUuids()) { DiskOfferingVO dvo = CollectionUtils.find(vos, new Function<DiskOfferingVO, DiskOfferingVO>() { @Override public DiskOfferingVO call(DiskOfferingVO arg) { if (duuid.equals(arg.getUuid())) { return arg; } return null; } }); disks.add(DiskOfferingInventory.valueOf(dvo)); } spec.setDataDiskOfferings(disks); } else { spec.setDataDiskOfferings(new ArrayList<>()); } if (struct.getRootDiskOfferingUuid() != null) { DiskOfferingVO rootDisk = dbf.findByUuid(struct.getRootDiskOfferingUuid(), DiskOfferingVO.class); spec.setRootDiskOffering(DiskOfferingInventory.valueOf(rootDisk)); } ImageVO imvo = dbf.findByUuid(spec.getVmInventory().getImageUuid(), ImageVO.class); if (imvo.getMediaType() == ImageMediaType.ISO) { new IsoOperator().attachIsoToVm(self.getUuid(), imvo.getUuid()); IsoSpec isoSpec = new IsoSpec(); isoSpec.setImageUuid(imvo.getUuid()); spec.setDestIso(isoSpec); } spec.getImageSpec().setInventory(ImageInventory.valueOf(imvo)); spec.setCurrentVmOperation(VmOperation.NewCreate); if (self.getZoneUuid() != null || self.getClusterUuid() != null || self.getHostUuid() != null) { spec.setHostAllocatorStrategy(HostAllocatorConstant.DESIGNATED_HOST_ALLOCATOR_STRATEGY_TYPE); } buildHostname(spec); spec.setUserdata(buildUserdata()); selectBootOrder(spec); spec.setConsolePassword(VmSystemTags.CONSOLE_PASSWORD. getTokenByResourceUuid(self.getUuid(), VmSystemTags.CONSOLE_PASSWORD_TOKEN)); changeVmStateInDb(VmInstanceStateEvent.starting); CollectionUtils.safeForEach(pluginRgty.getExtensionList(BeforeStartNewCreatedVmExtensionPoint.class), new ForEachFunction<BeforeStartNewCreatedVmExtensionPoint>() { @Override public void run(BeforeStartNewCreatedVmExtensionPoint ext) { ext.beforeStartNewCreatedVm(spec); } }); extEmitter.beforeStartNewCreatedVm(VmInstanceInventory.valueOf(self)); FlowChain chain = getCreateVmWorkFlowChain(inv); setFlowMarshaller(chain); chain.setName(String.format("create-vm-%s", self.getUuid())); chain.getData().put(VmInstanceConstant.Params.VmInstanceSpec.toString(), spec); chain.done(new FlowDoneHandler(completion) { @Override public void handle(final Map data) { VmInstanceSpec spec = (VmInstanceSpec) data.get(VmInstanceConstant.Params.VmInstanceSpec.toString()); self.setLastHostUuid(spec.getDestHost().getUuid()); self.setHostUuid(spec.getDestHost().getUuid()); self.setClusterUuid(spec.getDestHost().getClusterUuid()); self.setZoneUuid(spec.getDestHost().getZoneUuid()); self.setHypervisorType(spec.getDestHost().getHypervisorType()); self.setRootVolumeUuid(spec.getDestRootVolume().getUuid()); changeVmStateInDb(VmInstanceStateEvent.running); logger.debug(String.format("vm[uuid:%s] is running ..", self.getUuid())); VmInstanceInventory inv = VmInstanceInventory.valueOf(self); extEmitter.afterStartNewCreatedVm(inv); completion.success(); } }).error(new FlowErrorHandler(completion) { @Override public void handle(final ErrorCode errCode, Map data) { extEmitter.failedToStartNewCreatedVm(VmInstanceInventory.valueOf(self), errCode); dbf.remove(self); // clean up EO, otherwise API-retry may cause conflict if // the resource uuid is set dbf.eoCleanup(VmInstanceVO.class); completion.fail(errf.instantiateErrorCode(SysErrors.OPERATION_ERROR, errCode)); } }).start(); } protected void startVm(final StartVmInstanceMsg msg, final SyncTaskChain taskChain) { startVm(msg, new Completion(taskChain) { @Override public void success() { VmInstanceInventory inv = VmInstanceInventory.valueOf(self); StartVmInstanceReply reply = new StartVmInstanceReply(); reply.setInventory(inv); bus.reply(msg, reply); taskChain.next(); } @Override public void fail(ErrorCode errorCode) { StartVmInstanceReply reply = new StartVmInstanceReply(); reply.setError(errf.instantiateErrorCode(VmErrors.START_ERROR, errorCode)); bus.reply(msg, reply); taskChain.next(); } }); } protected void startVm(final APIStartVmInstanceMsg msg, final SyncTaskChain taskChain) { startVm(msg, new Completion(taskChain) { @Override public void success() { VmInstanceInventory inv = VmInstanceInventory.valueOf(self); APIStartVmInstanceEvent evt = new APIStartVmInstanceEvent(msg.getId()); evt.setInventory(inv); bus.publish(evt); taskChain.next(); } @Override public void fail(ErrorCode errorCode) { APIStartVmInstanceEvent evt = new APIStartVmInstanceEvent(msg.getId()); evt.setError(errf.instantiateErrorCode(VmErrors.START_ERROR, errorCode)); bus.publish(evt); taskChain.next(); } }); } protected void handle(final APIStartVmInstanceMsg msg) { thdf.chainSubmit(new ChainTask(msg) { @Override public String getName() { return String.format("start-vm-%s", self.getUuid()); } @Override public String getSyncSignature() { return syncThreadName; } @Override public void run(SyncTaskChain chain) { startVm(msg, chain); } }); } protected void handle(final APIDestroyVmInstanceMsg msg) { final APIDestroyVmInstanceEvent evt = new APIDestroyVmInstanceEvent(msg.getId()); destroyVm(msg, new Completion(msg) { @Override public void success() { bus.publish(evt); } @Override public void fail(ErrorCode errorCode) { evt.setError(errorCode); bus.publish(evt); } }); } private void destroyVm(APIDestroyVmInstanceMsg msg, final Completion completion) { final String issuer = VmInstanceVO.class.getSimpleName(); final List<VmDeletionStruct> ctx = new ArrayList<VmDeletionStruct>(); VmDeletionStruct s = new VmDeletionStruct(); s.setInventory(getSelfInventory()); s.setDeletionPolicy(deletionPolicyMgr.getDeletionPolicy(self.getUuid())); ctx.add(s); FlowChain chain = FlowChainBuilder.newSimpleFlowChain(); chain.setName(String.format("delete-vm-%s", msg.getUuid())); if (msg.getDeletionMode() == APIDeleteMessage.DeletionMode.Permissive) { chain.then(new NoRollbackFlow() { @Override public void run(final FlowTrigger trigger, Map data) { casf.asyncCascade(CascadeConstant.DELETION_CHECK_CODE, issuer, ctx, new Completion(trigger) { @Override public void success() { trigger.next(); } @Override public void fail(ErrorCode errorCode) { trigger.fail(errorCode); } }); } }).then(new NoRollbackFlow() { @Override public void run(final FlowTrigger trigger, Map data) { casf.asyncCascade(CascadeConstant.DELETION_DELETE_CODE, issuer, ctx, new Completion(trigger) { @Override public void success() { trigger.next(); } @Override public void fail(ErrorCode errorCode) { trigger.fail(errorCode); } }); } }); } else { chain.then(new NoRollbackFlow() { @Override public void run(final FlowTrigger trigger, Map data) { casf.asyncCascade(CascadeConstant.DELETION_FORCE_DELETE_CODE, issuer, ctx, new Completion(trigger) { @Override public void success() { trigger.next(); } @Override public void fail(ErrorCode errorCode) { trigger.fail(errorCode); } }); } }); } chain.done(new FlowDoneHandler(msg) { @Override public void handle(Map data) { casf.asyncCascadeFull(CascadeConstant.DELETION_CLEANUP_CODE, issuer, ctx, new NopeCompletion()); completion.success(); } }).error(new FlowErrorHandler(msg) { @Override public void handle(ErrorCode errCode, Map data) { completion.fail(errf.instantiateErrorCode(SysErrors.DELETE_RESOURCE_ERROR, errCode)); } }).start(); } protected void buildHostname(VmInstanceSpec spec) { String defaultHostname = VmSystemTags.HOSTNAME.getTag(self.getUuid()); if (defaultHostname == null) { return; } HostName dhname = new HostName(); dhname.setL3NetworkUuid(self.getDefaultL3NetworkUuid()); dhname.setHostname(VmSystemTags.HOSTNAME.getTokenByTag(defaultHostname, VmSystemTags.HOSTNAME_TOKEN)); spec.getHostnames().add(dhname); } protected VmInstanceSpec buildSpecFromInventory(VmInstanceInventory inv, VmOperation operation) { VmInstanceSpec spec = new VmInstanceSpec(); spec.setUserdata(buildUserdata()); // for L3Network that has been deleted List<String> nicUuidToDel = CollectionUtils.transformToList(inv.getVmNics(), new Function<String, VmNicInventory>() { @Override public String call(VmNicInventory arg) { return arg.getL3NetworkUuid() == null ? arg.getUuid() : null; } }); if (!nicUuidToDel.isEmpty()) { dbf.removeByPrimaryKeys(nicUuidToDel, VmNicVO.class); self = dbf.findByUuid(inv.getUuid(), VmInstanceVO.class); inv = VmInstanceInventory.valueOf(self); } spec.setDestNics(inv.getVmNics()); List<String> l3Uuids = CollectionUtils.transformToList(inv.getVmNics(), new Function<String, VmNicInventory>() { @Override public String call(VmNicInventory arg) { return arg.getL3NetworkUuid(); } }); spec.setL3Networks(L3NetworkInventory.valueOf(dbf.listByPrimaryKeys(l3Uuids, L3NetworkVO.class))); String huuid = inv.getHostUuid() == null ? inv.getLastHostUuid() : inv.getHostUuid(); if (huuid != null) { HostVO hvo = dbf.findByUuid(huuid, HostVO.class); if (hvo != null) { spec.setDestHost(HostInventory.valueOf(hvo)); } } List<VolumeInventory> dataVols = new ArrayList<VolumeInventory>(); for (VolumeInventory vol : inv.getAllVolumes()) { if (vol.getUuid().equals(inv.getRootVolumeUuid())) { spec.setDestRootVolume(vol); } else { dataVols.add(vol); } } List<BuildVolumeSpecExtensionPoint> exts = pluginRgty.getExtensionList( BuildVolumeSpecExtensionPoint.class); String vmUuid = inv.getUuid(); exts.forEach(e -> dataVols.addAll(e.supplyAdditionalVolumesForVmInstance(vmUuid))); spec.setDestDataVolumes(dataVols); // When starting an imported VM, we might not have an image UUID. if (inv.getImageUuid() != null) { ImageVO imgvo = dbf.findByUuid(inv.getImageUuid(), ImageVO.class); ImageInventory imginv = null; if (imgvo == null) { // the image has been deleted, use EO instead ImageEO imgeo = dbf.findByUuid(inv.getImageUuid(), ImageEO.class); imginv = ImageInventory.valueOf(imgeo); } else { imginv = ImageInventory.valueOf(imgvo); } spec.getImageSpec().setInventory(imginv); } spec.setVmInventory(inv); buildHostname(spec); String isoUuid = new IsoOperator().getIsoUuidByVmUuid(inv.getUuid()); if (isoUuid != null) { if (dbf.isExist(isoUuid, ImageVO.class)) { IsoSpec isoSpec = new IsoSpec(); isoSpec.setImageUuid(isoUuid); spec.setDestIso(isoSpec); } else { //TODO logger.warn(String.format("iso[uuid:%s] is deleted, however, the VM[uuid:%s] still has it attached", isoUuid, self.getUuid())); } } spec.setCurrentVmOperation(operation); selectBootOrder(spec); spec.setConsolePassword(VmSystemTags.CONSOLE_PASSWORD. getTokenByResourceUuid(self.getUuid(), VmSystemTags.CONSOLE_PASSWORD_TOKEN)); return spec; } protected void rebootVm(final Message msg, final Completion completion) { refreshVO(); ErrorCode allowed = validateOperationByState(msg, self.getState(), null); if (allowed != null) { completion.fail(allowed); return; } VmInstanceInventory inv = VmInstanceInventory.valueOf(self); ErrorCode preReboot = extEmitter.preRebootVm(inv); if (preReboot != null) { completion.fail(preReboot); return; } final VmInstanceSpec spec = buildSpecFromInventory(inv, VmOperation.Reboot); spec.setDestHost(HostInventory.valueOf(dbf.findByUuid(self.getHostUuid(), HostVO.class))); final VmInstanceState originState = self.getState(); changeVmStateInDb(VmInstanceStateEvent.rebooting); extEmitter.beforeRebootVm(VmInstanceInventory.valueOf(self)); spec.setMessage(msg); FlowChain chain = getRebootVmWorkFlowChain(inv); setFlowMarshaller(chain); chain.setName(String.format("reboot-vm-%s", self.getUuid())); chain.getData().put(VmInstanceConstant.Params.VmInstanceSpec.toString(), spec); chain.done(new FlowDoneHandler(completion) { @Override public void handle(Map data) { self = changeVmStateInDb(VmInstanceStateEvent.running); VmInstanceInventory inv = VmInstanceInventory.valueOf(self); extEmitter.afterRebootVm(inv); completion.success(); } }).error(new FlowErrorHandler(completion) { @Override public void handle(final ErrorCode errCode, Map data) { extEmitter.failedToRebootVm(VmInstanceInventory.valueOf(self), errCode); if (HostErrors.FAILED_TO_STOP_VM_ON_HYPERVISOR.isEqual(errCode.getCode()) || HostErrors.FAILED_TO_START_VM_ON_HYPERVISOR.isEqual(errCode.getCode())) { checkState(originalCopy.getHostUuid(), new NoErrorCompletion(completion) { @Override public void done() { completion.fail(errCode); } }); } else { self.setState(originState); self = dbf.updateAndRefresh(self); completion.fail(errCode); } } }).start(); } protected void rebootVm(final APIRebootVmInstanceMsg msg, final SyncTaskChain taskChain) { rebootVm(msg, new Completion(taskChain) { @Override public void success() { APIRebootVmInstanceEvent evt = new APIRebootVmInstanceEvent(msg.getId()); VmInstanceInventory inv = VmInstanceInventory.valueOf(self); evt.setInventory(inv); bus.publish(evt); taskChain.next(); } @Override public void fail(ErrorCode errorCode) { APIRebootVmInstanceEvent evt = new APIRebootVmInstanceEvent(msg.getId()); evt.setError(errf.instantiateErrorCode(VmErrors.REBOOT_ERROR, errorCode)); bus.publish(evt); taskChain.next(); } }); } protected void handle(final APIRebootVmInstanceMsg msg) { thdf.chainSubmit(new ChainTask(msg) { @Override public String getName() { return String.format("reboot-vm-%s", self.getUuid()); } @Override public String getSyncSignature() { return syncThreadName; } @Override public void run(SyncTaskChain chain) { rebootVm(msg, chain); } }); } protected void stopVm(final APIStopVmInstanceMsg msg, final SyncTaskChain taskChain) { stopVm(msg, new Completion(taskChain) { @Override public void success() { APIStopVmInstanceEvent evt = new APIStopVmInstanceEvent(msg.getId()); VmInstanceInventory inv = VmInstanceInventory.valueOf(self); evt.setInventory(inv); bus.publish(evt); taskChain.next(); } @Override public void fail(ErrorCode errorCode) { APIStopVmInstanceEvent evt = new APIStopVmInstanceEvent(msg.getId()); evt.setError(errf.instantiateErrorCode(VmErrors.STOP_ERROR, errorCode)); bus.publish(evt); taskChain.next(); } }); } private void stopVm(final Message msg, final Completion completion) { refreshVO(); ErrorCode allowed = validateOperationByState(msg, self.getState(), null); if (allowed != null) { completion.fail(allowed); return; } if (self.getState() == VmInstanceState.Stopped) { completion.success(); return; } VmInstanceInventory inv = VmInstanceInventory.valueOf(self); ErrorCode preStop = extEmitter.preStopVm(inv); if (preStop != null) { completion.fail(preStop); return; } final VmInstanceSpec spec = buildSpecFromInventory(inv, VmOperation.Stop); spec.setMessage(msg); if (msg instanceof StopVmInstanceMsg) { spec.setGcOnStopFailure(((StopVmInstanceMsg) msg).isGcOnFailure()); } final VmInstanceState originState = self.getState(); changeVmStateInDb(VmInstanceStateEvent.stopping); extEmitter.beforeStopVm(VmInstanceInventory.valueOf(self)); FlowChain chain = getStopVmWorkFlowChain(inv); setFlowMarshaller(chain); chain.setName(String.format("stop-vm-%s", self.getUuid())); chain.getData().put(VmInstanceConstant.Params.VmInstanceSpec.toString(), spec); chain.done(new FlowDoneHandler(completion) { @Override public void handle(Map data) { self.setLastHostUuid(self.getHostUuid()); self.setHostUuid(null); self = changeVmStateInDb(VmInstanceStateEvent.stopped); VmInstanceInventory inv = VmInstanceInventory.valueOf(self); extEmitter.afterStopVm(inv); completion.success(); } }).error(new FlowErrorHandler(completion) { @Override public void handle(final ErrorCode errCode, Map data) { VmInstanceInventory inv = VmInstanceInventory.valueOf(self); extEmitter.failedToStopVm(inv, errCode); if (HostErrors.FAILED_TO_STOP_VM_ON_HYPERVISOR.isEqual(errCode.getCode())) { checkState(originalCopy.getHostUuid(), new NoErrorCompletion(completion) { @Override public void done() { completion.fail(errCode); } }); } else { self.setState(originState); self = dbf.updateAndRefresh(self); completion.fail(errCode); } } }).start(); } protected void handle(final APIStopVmInstanceMsg msg) { thdf.chainSubmit(new ChainTask(msg) { @Override public String getName() { return String.format("stop-vm-%s", self.getUuid()); } @Override public String getSyncSignature() { return syncThreadName; } @Override public void run(SyncTaskChain chain) { stopVm(msg, chain); } }); } protected void handle(final APICreateStopVmInstanceSchedulerMsg msg) { APICreateStopVmInstanceSchedulerEvent evt = new APICreateStopVmInstanceSchedulerEvent(msg.getId()); StopVmInstanceJob job = new StopVmInstanceJob(msg); job.setVmUuid(msg.getVmInstanceUuid()); job.setTargetResourceUuid(msg.getVmInstanceUuid()); SchedulerVO schedulerVO = schedulerFacade.runScheduler(job); acntMgr.createAccountResourceRef(msg.getSession().getAccountUuid(), schedulerVO.getUuid(), SchedulerVO.class); if (schedulerVO != null) { schedulerVO = dbf.reload(schedulerVO); SchedulerInventory sinv = SchedulerInventory.valueOf(schedulerVO); evt.setInventory(sinv); } bus.publish(evt); } protected void handle(final APICreateStartVmInstanceSchedulerMsg msg) { APICreateStartVmInstanceSchedulerEvent evt = new APICreateStartVmInstanceSchedulerEvent(msg.getId()); StartVmInstanceJob job = new StartVmInstanceJob(msg); job.setVmUuid(msg.getVmInstanceUuid()); job.setTargetResourceUuid(msg.getVmInstanceUuid()); SchedulerVO schedulerVO = schedulerFacade.runScheduler(job); acntMgr.createAccountResourceRef(msg.getSession().getAccountUuid(), schedulerVO.getUuid(), SchedulerVO.class); if (schedulerVO != null) { schedulerVO = dbf.reload(schedulerVO); SchedulerInventory sinv = SchedulerInventory.valueOf(schedulerVO); evt.setInventory(sinv); } bus.publish(evt); } protected void handle(final APICreateRebootVmInstanceSchedulerMsg msg) { APICreateRebootVmInstanceSchedulerEvent evt = new APICreateRebootVmInstanceSchedulerEvent(msg.getId()); RebootVmInstanceJob job = new RebootVmInstanceJob(msg); job.setVmUuid(msg.getVmInstanceUuid()); job.setTargetResourceUuid(msg.getVmInstanceUuid()); SchedulerVO schedulerVO = schedulerFacade.runScheduler(job); acntMgr.createAccountResourceRef(msg.getSession().getAccountUuid(), schedulerVO.getUuid(), SchedulerVO.class); if (schedulerVO != null) { schedulerVO = dbf.reload(schedulerVO); SchedulerInventory sinv = SchedulerInventory.valueOf(schedulerVO); evt.setInventory(sinv); } bus.publish(evt); } protected void pauseVm(final APIPauseVmInstanceMsg msg, final SyncTaskChain taskChain) { pauseVm(msg, new Completion(taskChain) { @Override public void success() { APIPauseVmInstanceEvent evt = new APIPauseVmInstanceEvent(msg.getId()); VmInstanceInventory inv = VmInstanceInventory.valueOf(self); evt.setInventory(inv); bus.publish(evt); taskChain.next(); } @Override public void fail(ErrorCode errorCode) { APIPauseVmInstanceEvent evt = new APIPauseVmInstanceEvent(msg.getId()); evt.setError(errf.instantiateErrorCode(VmErrors.SUSPEND_ERROR, errorCode)); bus.publish(evt); taskChain.next(); } }); } protected void pauseVm(final Message msg, Completion completion) { refreshVO(); ErrorCode allowed = validateOperationByState(msg, self.getState(), null); if (allowed != null) { completion.fail(allowed); return; } if (self.getState() == VmInstanceState.Paused) { completion.success(); return; } VmInstanceInventory inv = VmInstanceInventory.valueOf(self); final VmInstanceSpec spec = buildSpecFromInventory(inv, VmOperation.Pause); spec.setMessage(msg); final VmInstanceState originState = self.getState(); changeVmStateInDb(VmInstanceStateEvent.pausing); FlowChain chain = getPauseVmWorkFlowChain(inv); setFlowMarshaller(chain); chain.setName(String.format("pause-vm-%s", self.getUuid())); chain.getData().put(VmInstanceConstant.Params.VmInstanceSpec.toString(), spec); chain.done(new FlowDoneHandler(completion) { @Override public void handle(Map Data) { self = changeVmStateInDb(VmInstanceStateEvent.paused); completion.success(); } }).error(new FlowErrorHandler(completion) { @Override public void handle(final ErrorCode errCode, Map data) { self.setState(originState); self = dbf.updateAndRefresh(self); completion.fail(errCode); } }).start(); } protected void handle(final APIPauseVmInstanceMsg msg) { thdf.chainSubmit(new ChainTask(msg) { @Override public String getSyncSignature() { return syncThreadName; } @Override public void run(SyncTaskChain chain) { pauseVm(msg, chain); } @Override public String getName() { return String.format("pause-vm-%s", msg.getVmInstanceUuid()); } }); } protected void resumeVm(final APIResumeVmInstanceMsg msg, final SyncTaskChain taskChain) { resumeVm(msg, new Completion(taskChain) { @Override public void success() { APIResumeVmInstanceEvent evt = new APIResumeVmInstanceEvent(msg.getId()); VmInstanceInventory inv = VmInstanceInventory.valueOf(self); evt.setInventory(inv); bus.publish(evt); taskChain.next(); } @Override public void fail(ErrorCode errorCode) { APIResumeVmInstanceEvent evt = new APIResumeVmInstanceEvent(msg.getId()); evt.setError(errf.instantiateErrorCode(VmErrors.RESUME_ERROR, errorCode)); bus.publish(evt); taskChain.next(); } }); } protected void resumeVm(final Message msg, Completion completion) { refreshVO(); ErrorCode allowed = validateOperationByState(msg, self.getState(), null); if (allowed != null) { completion.fail(allowed); return; } VmInstanceInventory inv = VmInstanceInventory.valueOf(self); final VmInstanceSpec spec = buildSpecFromInventory(inv, VmOperation.Resume); spec.setMessage(msg); final VmInstanceState originState = self.getState(); changeVmStateInDb(VmInstanceStateEvent.resuming); FlowChain chain = getResumeVmWorkFlowChain(inv); setFlowMarshaller(chain); chain.setName(String.format("resume-vm-%s", self.getUuid())); chain.getData().put(VmInstanceConstant.Params.VmInstanceSpec.toString(), spec); chain.done(new FlowDoneHandler(completion) { @Override public void handle(Map Data) { self = changeVmStateInDb(VmInstanceStateEvent.running); completion.success(); } }).error(new FlowErrorHandler(completion) { @Override public void handle(final ErrorCode errCode, Map data) { self.setState(originState); self = dbf.updateAndRefresh(self); completion.fail(errCode); } }).start(); } protected void handle(final APIResumeVmInstanceMsg msg) { thdf.chainSubmit(new ChainTask(msg) { @Override public String getSyncSignature() { return syncThreadName; } @Override public void run(SyncTaskChain chain) { resumeVm(msg, chain); } @Override public String getName() { return String.format("resume-vm-%s", msg.getVmInstanceUuid()); } }); } private void handle(final APIReimageVmInstanceMsg msg) { thdf.chainSubmit(new ChainTask(msg) { @Override public String getSyncSignature() { return syncThreadName; } @Override public void run(final SyncTaskChain chain) { reimageVmInstance(msg, new NoErrorCompletion(chain) { @Override public void done() { chain.next(); } }); } @Override public String getName() { return "reimage-vminstance"; } }); } private void reimageVmInstance(final APIReimageVmInstanceMsg msg, NoErrorCompletion completion) { final APIReimageVmInstanceEvent evt = new APIReimageVmInstanceEvent(msg.getId()); self = refreshVO(); VolumeVO rootVolume = dbf.findByUuid(self.getRootVolumeUuid(), VolumeVO.class); VolumeInventory rootVolumeInventory = VolumeInventory.valueOf(rootVolume); // check vm stopped { if (self.getState() != VmInstanceState.Stopped) { throw new ApiMessageInterceptionException(errf.instantiateErrorCode( VmErrors.RE_IMAGE_VM_NOT_IN_STOPPED_STATE, String.format("unable to reset volume[uuid:%s] to origin image[uuid:%s]," + " the vm[uuid:%s] volume attached to is not in Stopped state, current state is %s", rootVolume.getUuid(), rootVolume.getRootImageUuid(), rootVolume.getVmInstanceUuid(), self.getState()) )); } } // check image cache to ensure image type is not ISO { SimpleQuery<ImageCacheVO> q = dbf.createQuery(ImageCacheVO.class); q.select(ImageCacheVO_.mediaType); q.add(ImageCacheVO_.imageUuid, Op.EQ, rootVolume.getRootImageUuid()); q.setLimit(1); ImageMediaType imageMediaType = q.findValue(); if (imageMediaType == null) { throw new OperationFailureException(errf.instantiateErrorCode( VmErrors.RE_IMAGE_CANNOT_FIND_IMAGE_CACHE, String.format("unable to reset volume[uuid:%s] to origin image[uuid:%s]," + " cannot find image cache.", rootVolume.getUuid(), rootVolume.getRootImageUuid()) )); } if (imageMediaType.toString().equals("ISO")) { throw new OperationFailureException(errf.instantiateErrorCode( VmErrors.RE_IMAGE_IMAGE_MEDIA_TYPE_SHOULD_NOT_BE_ISO, String.format("unable to reset volume[uuid:%s] to origin image[uuid:%s]," + " for image type is ISO", rootVolume.getUuid(), rootVolume.getRootImageUuid()) )); } } // do the re-image op FlowChain chain = FlowChainBuilder.newShareFlowChain(); chain.setName(String.format("reset-root-volume-%s-from-image-%s", rootVolume.getUuid(), rootVolume.getRootImageUuid())); chain.then(new ShareFlow() { String newVolumeInstallPath; @Override public void setup() { flow(new NoRollbackFlow() { String __name__ = "reset-root-volume-from-image-on-primary-storage"; @Override public void run(final FlowTrigger trigger, Map data) { ReInitRootVolumeFromTemplateOnPrimaryStorageMsg rmsg = new ReInitRootVolumeFromTemplateOnPrimaryStorageMsg(); rmsg.setVolume(rootVolumeInventory); bus.makeTargetServiceIdByResourceUuid(rmsg, PrimaryStorageConstant.SERVICE_ID, rootVolumeInventory.getPrimaryStorageUuid()); bus.send(rmsg, new CloudBusCallBack(trigger) { @Override public void run(MessageReply reply) { if (reply.isSuccess()) { ReInitRootVolumeFromTemplateOnPrimaryStorageReply re = (ReInitRootVolumeFromTemplateOnPrimaryStorageReply) reply; newVolumeInstallPath = re.getNewVolumeInstallPath(); trigger.next(); } else { trigger.fail(reply.getError()); } } }); } }); done(new FlowDoneHandler(msg, completion) { @Override public void handle(Map data) { rootVolume.setInstallPath(newVolumeInstallPath); dbf.update(rootVolume); List<AfterReimageVmInstanceExtensionPoint> list = pluginRgty.getExtensionList( AfterReimageVmInstanceExtensionPoint.class); for (AfterReimageVmInstanceExtensionPoint ext : list) { ext.afterReimageVmInstance(rootVolumeInventory); } self = dbf.reload(self); evt.setInventory(VmInstanceInventory.valueOf(self)); bus.publish(evt); completion.done(); } }); error(new FlowErrorHandler(msg, completion) { @Override public void handle(ErrorCode errCode, Map data) { logger.warn(String.format("failed to restore volume[uuid:%s] to image[uuid:%s], %s", rootVolumeInventory.getUuid(), rootVolumeInventory.getRootImageUuid(), errCode)); evt.setError(errCode); bus.publish(evt); completion.done(); } }); } }).start(); } }