package org.zstack.kvm; import org.springframework.beans.factory.annotation.Autowired; import org.zstack.compute.vm.VmTracer; import org.zstack.core.cloudbus.CloudBus; import org.zstack.core.cloudbus.CloudBusCallBack; import org.zstack.core.db.DatabaseFacade; import org.zstack.core.db.SimpleQuery; import org.zstack.core.db.SimpleQuery.Op; import org.zstack.core.errorcode.ErrorFacade; import org.zstack.core.notification.N; import org.zstack.core.thread.ChainTask; import org.zstack.core.thread.SyncTaskChain; import org.zstack.core.thread.ThreadFacade; import org.zstack.core.timeout.ApiTimeoutManager; import org.zstack.header.Component; import org.zstack.header.core.Completion; import org.zstack.header.core.NoErrorCompletion; import org.zstack.header.core.NopeCompletion; import org.zstack.header.core.workflow.Flow; import org.zstack.header.core.workflow.FlowTrigger; import org.zstack.header.core.workflow.NoRollbackFlow; import org.zstack.header.errorcode.ErrorCode; import org.zstack.header.host.*; import org.zstack.header.message.MessageReply; import org.zstack.header.rest.RESTFacade; import org.zstack.header.rest.SyncHttpCallHandler; import org.zstack.header.vm.*; import org.zstack.kvm.KVMAgentCommands.ReportVmStateCmd; import org.zstack.kvm.KVMAgentCommands.VmSyncCmd; import org.zstack.kvm.KVMAgentCommands.VmSyncResponse; import org.zstack.kvm.KVMConstant.KvmVmState; import org.zstack.utils.Utils; import org.zstack.utils.logging.CLogger; import java.util.HashMap; import java.util.Map; import static org.zstack.core.Platform.operr; public class KvmVmSyncPingTask extends VmTracer implements KVMPingAgentNoFailureExtensionPoint, KVMHostConnectExtensionPoint, HostConnectionReestablishExtensionPoint, HostAfterConnectedExtensionPoint, Component { private static final CLogger logger = Utils.getLogger(KvmVmSyncPingTask.class); @Autowired private RESTFacade restf; @Autowired private ErrorFacade errf; @Autowired private DatabaseFacade dbf; @Autowired private CloudBus bus; @Autowired private ThreadFacade thdf; @Autowired private ApiTimeoutManager timeoutMgr; private void syncVm(final HostInventory host, final Completion completion) { KVMHostAsyncHttpCallMsg msg = new KVMHostAsyncHttpCallMsg(); VmSyncCmd cmd = new VmSyncCmd(); msg.setCommand(cmd); msg.setCommandTimeout(timeoutMgr.getTimeout(cmd.getClass(), "5m")); msg.setNoStatusCheck(true); msg.setHostUuid(host.getUuid()); msg.setPath(KVMConstant.KVM_VM_SYNC_PATH); bus.makeTargetServiceIdByResourceUuid(msg, HostConstant.SERVICE_ID, host.getUuid()); bus.send(msg, new CloudBusCallBack(completion) { @Override public void run(MessageReply reply) { if (!reply.isSuccess()) { completion.fail(reply.getError()); return; } KVMHostAsyncHttpCallReply r = reply.castReply(); VmSyncResponse ret = r.toResponse(VmSyncResponse.class); if (ret.isSuccess()) { Map<String, VmInstanceState> states = new HashMap<String, VmInstanceState>(ret.getStates().size()); for (Map.Entry<String, String> e : ret.getStates().entrySet()) { VmInstanceState state = KvmVmState.valueOf(e.getValue()).toVmInstanceState(); if (state == VmInstanceState.Running || state == VmInstanceState.Paused || state == VmInstanceState.Unknown) { states.put(e.getKey(), state); } } reportVmState(host.getUuid(), states); completion.success(); } else { ErrorCode errorCode = operr("unable to do vm sync on host[uuid:%s, ip:%s] because %s", host.getUuid(), host.getManagementIp(), ret.getError()); completion.fail(errorCode); } } }); } @Override public void connectionReestablished(HostInventory inv) throws HostException { syncVm(inv, new NopeCompletion()); } @Override public HypervisorType getHypervisorTypeForReestablishExtensionPoint() { return HypervisorType.valueOf(KVMConstant.KVM_HYPERVISOR_TYPE); } @Override public boolean start() { restf.registerSyncHttpCallHandler(KVMConstant.KVM_REPORT_VM_STATE, ReportVmStateCmd.class, new SyncHttpCallHandler<ReportVmStateCmd>() { private void reportState(final ReportVmStateCmd cmd) { thdf.chainSubmit(new ChainTask(null) { @Override public String getSyncSignature() { return String.format("report-state-of-vm-%s", cmd.vmUuid); } @Override public void run(final SyncTaskChain chain) { VmInstanceState state = KvmVmState.valueOf(cmd.vmState).toVmInstanceState(); SimpleQuery<VmInstanceVO> q = dbf.createQuery(VmInstanceVO.class); q.select(VmInstanceVO_.state); q.add(VmInstanceVO_.uuid, Op.EQ, cmd.vmUuid); VmInstanceState stateInDb = q.findValue(); if (stateInDb == null) { N.New(HostVO.class, cmd.hostUuid).warn_("an anonymous VM[uuid:%s, state:%s] is detected on the host[uuid:%s]", cmd.hostUuid, state, cmd.hostUuid); chain.next(); return; } VmStateChangedOnHostMsg msg = new VmStateChangedOnHostMsg(); msg.setVmStateAtTracingMoment(stateInDb); msg.setVmInstanceUuid(cmd.vmUuid); msg.setStateOnHost(state); msg.setHostUuid(cmd.hostUuid); bus.makeTargetServiceIdByResourceUuid(msg, VmInstanceConstant.SERVICE_ID, cmd.vmUuid); bus.send(msg, new CloudBusCallBack(chain) { @Override public void run(MessageReply reply) { if (!reply.isSuccess()) { N.New(HostVO.class, cmd.hostUuid).warn_("failed to report state[%s] of the vm[uuid:%s] on the host[uuid:%s], %s", cmd.vmState, cmd.vmUuid, cmd.hostUuid, reply.getError()); N.New(VmInstanceVO.class, cmd.vmUuid).warn_("failed to report state[%s] of the vm[uuid:%s] on the host[uuid:%s], %s", cmd.vmState, cmd.vmUuid, cmd.hostUuid, reply.getError()); } chain.next(); } }); } @Override public String getName() { return "report-vm-state"; } }); } @Override public String handleSyncHttpCall(ReportVmStateCmd cmd) { reportState(cmd); return null; } }); return true; } @Override public boolean stop() { return true; } @Override public void afterHostConnected(final HostInventory host) { //syncVm has done the same work, so abandon it } @Override public Flow createKvmHostConnectingFlow(final KVMHostConnectedContext context) { return new NoRollbackFlow() { @Override public void run(final FlowTrigger trigger, Map data) { syncVm(context.getInventory(), new Completion(trigger) { String __name__ = "sync-vm-state"; @Override public void success() { trigger.next(); } @Override public void fail(ErrorCode errorCode) { trigger.fail(errorCode); } }); } }; } @Override public void kvmPingAgentNoFailure(KVMHostInventory host, NoErrorCompletion completion) { if (!KVMGlobalConfig.VM_SYNC_ON_HOST_PING.value(Boolean.class)) { completion.done(); return; } syncVm(host, new Completion(completion) { @Override public void success() { completion.done(); } @Override public void fail(ErrorCode errorCode) { N.New(HostVO.class, host.getUuid()).warn_("failed to sync VM states on the host[uuid:%s, name:%s], %s", host.getUuid(), host.getName(), errorCode); completion.done(); } }); } }