package org.zstack.network.service.vip; 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.cloudbus.MessageSafe; import org.zstack.core.db.DatabaseFacade; import org.zstack.core.errorcode.ErrorFacade; 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.core.Completion; import org.zstack.header.core.NopeCompletion; 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.message.APIDeleteMessage; import org.zstack.header.message.APIMessage; import org.zstack.header.message.Message; import org.zstack.header.network.l3.L3NetworkConstant; import org.zstack.header.network.l3.ReturnIpMsg; import org.zstack.utils.Utils; import org.zstack.utils.logging.CLogger; import static org.zstack.core.Platform.operr; import java.util.Arrays; import java.util.List; import java.util.Map; /** * Created by xing5 on 2016/11/19. */ @Configurable(preConstruction = true, autowire = Autowire.BY_TYPE) public class VipBase { protected static final CLogger logger = Utils.getLogger(VipBase.class); protected VipVO self; @Autowired protected DatabaseFacade dbf; @Autowired protected CloudBus bus; @Autowired protected ThreadFacade thdf; @Autowired protected CascadeFacade casf; @Autowired protected ErrorFacade errf; @Autowired protected VipManager vipMgr; protected String getThreadSyncSignature() { return String.format("vip-%s-%s", self.getName(), self.getUuid()); } protected VipVO getSelf() { return self; } protected VipInventory getSelfInventory() { return VipInventory.valueOf(getSelf()); } public VipBase(VipVO self) { this.self = self; } protected void refresh() { VipVO vo = dbf.reload(self); if (vo == null) { throw new OperationFailureException(errf.instantiateErrorCode(SysErrors.RESOURCE_NOT_FOUND, String.format("cannot find the vip[name:%s, uuid:%s, ip:%s], it may have been deleted", self.getName(), self.getUuid(), self.getIp()) )); } self = vo; } @MessageSafe public void handleMessage(Message msg) { if (msg instanceof APIMessage) { handleApiMessage((APIMessage) msg); } else { handleLocalMessage(msg); } } protected void passToBackend(Message msg) { if (self.getServiceProvider() == null) { bus.dealWithUnknownMessage(msg); return; } VipFactory f = vipMgr.getVipFactory(self.getServiceProvider()); VipBaseBackend bkd = f.getVip(getSelf()); bkd.handleBackendSpecificMessage(msg); } protected void handleLocalMessage(Message msg) { if (msg instanceof VipDeletionMsg) { handle((VipDeletionMsg) msg); } else if (msg instanceof AcquireVipMsg) { handle((AcquireVipMsg) msg); } else if (msg instanceof ReleaseVipMsg) { handle((ReleaseVipMsg) msg); } else if (msg instanceof ModifyVipAttributesMsg) { handle((ModifyVipAttributesMsg) msg); } else if (msg instanceof DeleteVipFromBackendMsg) { handle((DeleteVipFromBackendMsg) msg); } else { passToBackend(msg); } } private void handle(DeleteVipFromBackendMsg msg) { // DO NOT put it in the sync queue // DeleteVipMsg may cause DeleteVipFromBackendMsg, putting DeleteVipFromBackendMsg // in the queue will lead to a deadlock DeleteVipFromBackendReply reply = new DeleteVipFromBackendReply(); deleteFromBackend(new Completion(msg) { @Override public void success() { bus.reply(msg, reply); } @Override public void fail(ErrorCode errorCode) { reply.setError(errorCode); bus.reply(msg, reply); } }); } private void deleteFromBackend(Completion completion) { if (self.getServiceProvider() == null) { // this VIP has not bean created on backend yet completion.success(); return; } VipFactory f = vipMgr.getVipFactory(self.getServiceProvider()); VipBaseBackend vip = f.getVip(getSelf()); vip.releaseVipOnBackend(new Completion(completion) { @Override public void success() { logger.debug(String.format("successfully released vip[uuid:%s, name:%s, ip:%s] on service[%s]", self.getUuid(), self.getName(), self.getIp(), self.getServiceProvider())); completion.success(); } @Override public void fail(ErrorCode errorCode) { logger.warn(String.format("failed to release vip[uuid:%s, name:%s, ip:%s] on service[%s], its garbage collector should" + " handle this", self.getUuid(), self.getName(), self.getIp(), self.getServiceProvider())); completion.fail(errorCode); } }); } private interface Recover { void recover(); } protected Recover modifyAttributes(ModifyVipAttributesStruct s) { VipVO origin = dbf.findByUuid(self.getUuid(), VipVO.class); if (s.isServiceProvider()) { if (self.getServiceProvider() != null && s.getServiceProvider() != null && !s.getServiceProvider().equals(self.getServiceProvider())) { throw new OperationFailureException(operr("service provider of the vip[uuid:%s, name:%s, ip: %s] has been set to %s", self.getUuid(), self.getName(), self.getIp(), self.getServiceProvider())); } self.setServiceProvider(s.getServiceProvider()); } if (s.isUserFor()) { if (self.getUseFor() != null && s.getUseFor() != null && !self.getUseFor().equals(s.getUseFor())) { throw new OperationFailureException(operr("the field 'useFor' of the vip[uuid:%s, name:%s, ip: %s] has been set to %s", self.getUuid(), self.getName(), self.getIp(), self.getUseFor())); } self.setUseFor(s.getUseFor()); } if (s.isPeerL3NetworkUuid()) { if (self.getPeerL3NetworkUuid() != null && s.getPeerL3NetworkUuid() != null && !self.getPeerL3NetworkUuid().equals(s.getPeerL3NetworkUuid())) { throw new OperationFailureException(operr("the field 'peerL3NetworkUuid' of the vip[uuid:%s, name:%s, ip: %s] has been set to %s", self.getUuid(), self.getName(), self.getIp(), self.getPeerL3NetworkUuid())); } self.setPeerL3NetworkUuid(s.getPeerL3NetworkUuid()); } dbf.update(self); return () -> dbf.update(origin); } private void handle(ModifyVipAttributesMsg msg) { thdf.chainSubmit(new ChainTask(msg) { @Override public String getSyncSignature() { return getThreadSyncSignature(); } @Override public void run(SyncTaskChain chain) { refresh(); ModifyVipAttributesStruct current = new ModifyVipAttributesStruct(); current.setPeerL3NetworkUuid(self.getPeerL3NetworkUuid()); current.setUseFor(self.getUseFor()); current.setServiceProvider(self.getServiceProvider()); ModifyVipAttributesReply reply = new ModifyVipAttributesReply(); reply.setStruct(current); modifyAttributes(msg.getStruct()); bus.reply(msg, reply); chain.next(); } @Override public String getName() { return "modify-vip-attributes"; } }); } private void handle(ReleaseVipMsg msg) { ReleaseVipReply reply = new ReleaseVipReply(); thdf.chainSubmit(new ChainTask(msg) { @Override public String getSyncSignature() { return getThreadSyncSignature(); } @Override public void run(SyncTaskChain chain) { if (!msg.isDeleteOnBackend()) { modifyAttributes(msg.getStruct()); bus.reply(msg, reply); chain.next(); return; } releaseVip(msg.getStruct(), 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 "release-vip"; } }); } protected void releaseVip(ModifyVipAttributesStruct s, Completion completion) { if (self.getServiceProvider() == null) { // the vip has been released by other descendant network service logger.debug(String.format("the serviceProvider field is null, the vip[uuid:%s, name:%s, ip:%s] has been released" + " by other service", self.getUuid(), self.getName(), self.getIp())); modifyAttributes(s); completion.success(); return; } VipFactory f = vipMgr.getVipFactory(self.getServiceProvider()); VipBaseBackend vip = f.getVip(getSelf()); vip.releaseVipOnBackend(new Completion(completion) { @Override public void success() { logger.debug(String.format("successfully released vip[uuid:%s, name:%s, ip:%s] on service[%s]", self.getUuid(), self.getName(), self.getIp(), self.getServiceProvider())); modifyAttributes(s); completion.success(); } @Override public void fail(ErrorCode errorCode) { logger.warn(String.format("failed to release vip[uuid:%s, name:%s, ip:%s] on service[%s], its garbage collector should" + " handle this", self.getUuid(), self.getName(), self.getIp(), self.getServiceProvider())); completion.fail(errorCode); } }); } protected void handle(AcquireVipMsg msg) { AcquireVipReply reply = new AcquireVipReply(); thdf.chainSubmit(new ChainTask(msg) { @Override public String getSyncSignature() { return getThreadSyncSignature(); } @Override public void run(SyncTaskChain chain) { refresh(); if (!msg.isCreateOnBackend()) { // no need to really create the VIP on network devices, // just mark it in database modifyAttributes(msg.getStruct()); bus.reply(msg, reply); chain.next(); return; } acquireVip(msg.getStruct(), 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 "acquire-vip"; } }); } protected void acquireVip(ModifyVipAttributesStruct s, Completion completion) { Recover recover = modifyAttributes(s); VipFactory f = vipMgr.getVipFactory(s.getServiceProvider()); VipBaseBackend vip = f.getVip(getSelf()); vip.acquireVipOnBackend(new Completion(completion) { @Override public void success() { logger.debug(String.format("successfully acquired vip[uuid:%s, name:%s, ip:%s] on service[%s]", self.getUuid(), self.getName(), self.getIp(), s.getServiceProvider())); completion.success(); } @Override public void fail(ErrorCode errorCode) { recover.recover(); completion.fail(errorCode); } }); } protected void handle(VipDeletionMsg msg) { VipDeletionReply reply = new VipDeletionReply(); thdf.chainSubmit(new ChainTask(msg) { @Override public String getSyncSignature() { return getThreadSyncSignature(); } @Override public void run(SyncTaskChain chain) { deleteVip(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 "delete-vip"; } }); } protected void returnVip() { ReturnIpMsg msg = new ReturnIpMsg(); msg.setL3NetworkUuid(self.getL3NetworkUuid()); msg.setUsedIpUuid(self.getUsedIpUuid()); bus.makeTargetServiceIdByResourceUuid(msg, L3NetworkConstant.SERVICE_ID, self.getL3NetworkUuid()); bus.send(msg); } protected void deleteVip(Completion completion) { refresh(); if (self.getUseFor() == null) { dbf.remove(self); returnVip(); logger.debug(String.format("'useFor' is not set, released vip[uuid:%s, ip:%s] on l3Network[uuid:%s]", self.getUuid(), self.getIp(), self.getL3NetworkUuid())); completion.success(); return; } FlowChain chain = FlowChainBuilder.newShareFlowChain(); chain.setName(String.format("delete-vip-uuid-%s-ip-%s-name-%s", self.getUuid(), self.getIp(), self.getName())); chain.then(new ShareFlow() { @Override public void setup() { flow(new NoRollbackFlow() { String __name__ = "release-services-on-vip"; @Override public void run(FlowTrigger trigger, Map data) { VipReleaseExtensionPoint ext = vipMgr.getVipReleaseExtensionPoint(self.getUseFor()); ext.releaseServicesOnVip(getSelfInventory(), new Completion(trigger) { @Override public void success() { trigger.next(); } @Override public void fail(ErrorCode errorCode) { trigger.fail(errorCode); } }); } }); flow(new NoRollbackFlow() { String __name__ = "delete-vip"; @Override public void run(FlowTrigger trigger, Map data) { ModifyVipAttributesStruct s = new ModifyVipAttributesStruct(); s.setPeerL3NetworkUuid(null); s.setUseFor(null); releaseVip(s, new Completion(trigger) { @Override public void success() { trigger.next(); } @Override public void fail(ErrorCode errorCode) { trigger.fail(errorCode); } }); } }); done(new FlowDoneHandler(completion) { @Override public void handle(Map data) { dbf.remove(self); returnVip(); completion.success(); } }); error(new FlowErrorHandler(completion) { @Override public void handle(ErrorCode errCode, Map data) { completion.fail(errCode); } }); } }).start(); } protected void handleApiMessage(APIMessage msg) { if (msg instanceof APIChangeVipStateMsg) { handle((APIChangeVipStateMsg) msg); } else if (msg instanceof APIDeleteVipMsg) { handle((APIDeleteVipMsg) msg); } else if (msg instanceof APIUpdateVipMsg) { handle((APIUpdateVipMsg) msg); } else { passToBackend(msg); } } protected void handle(APIUpdateVipMsg msg) { VipVO vo = dbf.findByUuid(msg.getUuid(), VipVO.class); boolean update = false; if (msg.getName() != null) { vo.setName(msg.getName()); update = true; } if (msg.getDescription() != null) { vo.setDescription(msg.getDescription()); update = true; } if (update) { vo = dbf.updateAndRefresh(vo); } APIUpdateVipEvent evt = new APIUpdateVipEvent(msg.getId()); evt.setInventory(VipInventory.valueOf(vo)); bus.publish(evt); } protected void handle(APIDeleteVipMsg msg) { final APIDeleteVipEvent evt = new APIDeleteVipEvent(msg.getId()); final String issuer = VipVO.class.getSimpleName(); final List<VipInventory> ctx = Arrays.asList(VipInventory.valueOf(self)); FlowChain chain = FlowChainBuilder.newShareFlowChain(); chain.setName(String.format("delete-vip-%s", self.getUuid())); chain.then(new ShareFlow() { @Override public void setup() { if (msg.getDeletionMode() == APIDeleteMessage.DeletionMode.Permissive) { flow(new NoRollbackFlow() { String __name__ = "delete-vip-permissive-check"; @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); } }); } }); flow(new NoRollbackFlow() { String __name__ = "delete-vip-permissive-delete"; @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 { flow(new NoRollbackFlow() { String __name__ = "delete-vip-force-delete"; @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.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 handle(APIChangeVipStateMsg msg) { VipVO vip = dbf.findByUuid(msg.getUuid(), VipVO.class); VipStateEvent sevt = VipStateEvent.valueOf(msg.getStateEvent()); vip.setState(vip.getState().nextState(sevt)); vip = dbf.updateAndRefresh(vip); APIChangeVipStateEvent evt = new APIChangeVipStateEvent(msg.getId()); evt.setInventory(VipInventory.valueOf(vip)); bus.publish(evt); } }