package org.zstack.compute.cluster; import org.springframework.beans.factory.annotation.Autowire; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Configurable; import org.zstack.core.cascade.CascadeConstant; import org.zstack.core.cascade.CascadeFacade; import org.zstack.core.cloudbus.CloudBus; import org.zstack.core.db.DatabaseFacade; import org.zstack.core.errorcode.ErrorFacade; import org.zstack.header.core.NopeCompletion; import org.zstack.header.core.workflow.*; import org.zstack.header.errorcode.SysErrors; import org.zstack.core.thread.SyncTask; import org.zstack.core.thread.ThreadFacade; import org.zstack.core.workflow.*; import org.zstack.header.cluster.*; import org.zstack.header.core.Completion; import org.zstack.header.errorcode.ErrorCode; import org.zstack.header.message.APIDeleteMessage; import org.zstack.header.message.APIMessage; import org.zstack.header.message.Message; import org.zstack.utils.Utils; import org.zstack.utils.logging.CLogger; import java.util.Arrays; import java.util.List; import java.util.Map; @Configurable(preConstruction = true, autowire = Autowire.BY_TYPE) public class ClusterBase extends AbstractCluster { protected final static CLogger logger = Utils.getLogger(ClusterBase.class); @Autowired protected DatabaseFacade dbf; @Autowired protected CloudBus bus; @Autowired protected ThreadFacade thdf; @Autowired protected ClusterExtensionPointEmitter extpEmitter; @Autowired protected CascadeFacade casf; @Autowired protected ErrorFacade errf; protected ClusterVO self; protected final int threadSyncLevel = 1; public ClusterBase(ClusterVO self) { this.self = self; } protected ClusterInventory changeState(ClusterStateEvent event) { ClusterState currentState = self.getState(); ClusterState next = AbstractCluster.getNextState(self.getState(), event); extpEmitter.beforeChange(self, event); self.setState(next); self = dbf.updateAndRefresh(self); ClusterInventory inv = ClusterInventory.valueOf(self); extpEmitter.afterChange(self, event, currentState); logger.debug("Cluster " + self.getName() + " uuid: " + self.getUuid() + " changed state from " + currentState + " to " + self.getState()); return inv; } protected void handleApiMessage(APIMessage msg) { if (msg.getClass() == APIChangeClusterStateMsg.class) { handle((APIChangeClusterStateMsg) msg); } else if (msg.getClass() == APIDeleteClusterMsg.class) { handle((APIDeleteClusterMsg) msg); } else if (msg instanceof APIUpdateClusterMsg) { handle((APIUpdateClusterMsg) msg); } else { bus.dealWithUnknownMessage(msg); } } private void handle(APIUpdateClusterMsg msg) { boolean update = false; if (msg.getName() != null) { self.setName(msg.getName()); update = true; } if (msg.getDescription() != null) { self.setDescription(msg.getDescription()); update = true; } if (update) { self = dbf.updateAndRefresh(self); } APIUpdateClusterEvent evt = new APIUpdateClusterEvent(msg.getId()); evt.setInventory(ClusterInventory.valueOf(self)); bus.publish(evt); } protected void handle(final APIDeleteClusterMsg msg) { final APIDeleteClusterEvent evt = new APIDeleteClusterEvent(msg.getId()); final String issuer = ClusterVO.class.getSimpleName(); final List<ClusterInventory> ctx = ClusterInventory.valueOf(Arrays.asList(self)); FlowChain chain = FlowChainBuilder.newSimpleFlowChain(); chain.setName(String.format("delete-cluster-%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()); bus.publish(evt); } }).error(new FlowErrorHandler(msg) { @Override public void handle(ErrorCode errCode, Map data) { evt.setError(errf.instantiateErrorCode(SysErrors.DELETE_RESOURCE_ERROR, errCode)); bus.publish(evt); } }).start(); } protected void changeStateByApiMsg(APIChangeClusterStateMsg msg) { APIChangeClusterStateEvent evt = new APIChangeClusterStateEvent(msg.getId()); ClusterStateEvent stateEvent = ClusterStateEvent.valueOf(msg.getStateEvent()); try { extpEmitter.preChange(self, stateEvent); } catch (ClusterException e) { evt.setError(errf.instantiateErrorCode(SysErrors.CHANGE_RESOURCE_STATE_ERROR, e.getMessage())); bus.publish(evt); return; } try { ClusterInventory inv = changeState(stateEvent); evt.setInventory(inv); bus.publish(evt); } catch (Exception e) { bus.logExceptionWithMessageDump(msg, e); bus.replyErrorByMessageType(msg, e); } } protected void handle(final APIChangeClusterStateMsg msg) { thdf.syncSubmit(new SyncTask<Void>() { @Override public String getName() { return "ChangeClusterStateByApi"; } @Override public Void call() throws Exception { changeStateByApiMsg(msg); return null; } @Override public String getSyncSignature() { return self.getUuid(); } @Override public int getSyncLevel() { return threadSyncLevel; } }); } protected void handleLocalMessage(Message msg) { if (msg instanceof ChangeClusterStateMsg) { handle((ChangeClusterStateMsg) msg); } else if (msg instanceof ClusterDeletionMsg) { handle((ClusterDeletionMsg) msg); } else { bus.dealWithUnknownMessage(msg); } } private void handle(ClusterDeletionMsg msg) { ClusterInventory inv = ClusterInventory.valueOf(self); extpEmitter.beforeDelete(inv); deleteHook(); extpEmitter.afterDelete(inv); ClusterDeletionReply reply = new ClusterDeletionReply(); bus.reply(msg ,reply); } protected void changeClusterStateByLocalMsg(ChangeClusterStateMsg msg) { ChangeClusterStateReply reply = new ChangeClusterStateReply(); try { ClusterInventory inv = changeState(ClusterStateEvent.valueOf(msg.getStateEvent())); reply.setInventory(inv); bus.reply(msg, reply); } catch (Exception e) { bus.logExceptionWithMessageDump(msg, e); bus.replyErrorByMessageType(msg, e); } } protected void handle(final ChangeClusterStateMsg msg) { thdf.syncSubmit(new SyncTask<Void>() { @Override public String getName() { return "ChangeClusterStateByLocalMessage"; } @Override public Void call() throws Exception { changeClusterStateByLocalMsg(msg); return null; } @Override public String getSyncSignature() { return self.getUuid(); } @Override public int getSyncLevel() { return threadSyncLevel; } }); } @Override public void handleMessage(Message msg) { try { if (msg instanceof APIMessage) { handleApiMessage((APIMessage) msg); } else { handleLocalMessage(msg); } } catch (Exception e) { bus.logExceptionWithMessageDump(msg, e); bus.replyErrorByMessageType(msg ,e); } } @Override protected void deleteHook() { } }