package org.zstack.network.service.virtualrouter.vip; import org.springframework.beans.factory.annotation.Autowired; import org.zstack.core.cloudbus.CloudBus; import org.zstack.core.cloudbus.CloudBusCallBack; import org.zstack.core.componentloader.PluginRegistry; import org.zstack.core.db.DatabaseFacade; import org.zstack.core.db.SimpleQuery; import org.zstack.core.errorcode.ErrorFacade; import org.zstack.core.timeout.ApiTimeoutManager; import org.zstack.core.workflow.*; import org.zstack.header.core.Completion; import org.zstack.header.core.ReturnValueCompletion; import org.zstack.header.core.workflow.*; import org.zstack.header.errorcode.ErrorCode; import org.zstack.header.errorcode.OperationFailureException; import org.zstack.header.exception.CloudRuntimeException; import org.zstack.header.message.MessageReply; import org.zstack.header.network.l3.L3NetworkInventory; import org.zstack.header.vm.*; import org.zstack.network.service.vip.VipBackend; import org.zstack.network.service.vip.VipInventory; import org.zstack.network.service.virtualrouter.*; import org.zstack.network.service.virtualrouter.VirtualRouterCommands.*; import org.zstack.utils.Utils; import org.zstack.utils.logging.CLogger; import static org.zstack.core.Platform.operr; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import static org.zstack.utils.CollectionDSL.list; public class VirtualRouterVipBackend extends AbstractVirtualRouterBackend implements VipBackend { private static final CLogger logger = Utils.getLogger(VirtualRouterVipBackend.class); @Autowired private CloudBus bus; @Autowired protected DatabaseFacade dbf; @Autowired protected PluginRegistry pluginRgty; @Autowired protected ErrorFacade errf; @Autowired private ApiTimeoutManager apiTimeoutManager; private String getOwnerMac(VirtualRouterVmInventory vr, VipInventory vip) { for (VmNicInventory nic : vr.getVmNics()) { if (nic.getL3NetworkUuid().equals(vip.getL3NetworkUuid())) { return nic.getMac(); } } throw new CloudRuntimeException(String.format("virtual router vm[uuid:%s] has no nic on l3Network[uuid:%s] for vip[uuid:%s, ip:%s]", vr.getUuid(), vip.getL3NetworkUuid(), vip.getUuid(), vip.getIp())); } public void createVipOnVirtualRouterVm(final VirtualRouterVmInventory vr, List<VipInventory> vips, final Completion completion) { final List<VipTO> tos = new ArrayList<VipTO>(vips.size()); for (VipInventory vip : vips) { String mac = getOwnerMac(vr, vip); VipTO to = VipTO.valueOf(vip, mac); tos.add(to); } CreateVipCmd cmd = new CreateVipCmd(); cmd.setVips(tos); VirtualRouterAsyncHttpCallMsg msg = new VirtualRouterAsyncHttpCallMsg(); msg.setVmInstanceUuid(vr.getUuid()); msg.setCommand(cmd); msg.setCommandTimeout(apiTimeoutManager.getTimeout(cmd.getClass(), "30m")); msg.setPath(VirtualRouterConstant.VR_CREATE_VIP); bus.makeTargetServiceIdByResourceUuid(msg, VmInstanceConstant.SERVICE_ID, vr.getUuid()); bus.send(msg, new CloudBusCallBack(completion) { @Override public void run(MessageReply reply) { if (!reply.isSuccess()) { completion.fail(reply.getError()); return; } VirtualRouterAsyncHttpCallReply re = reply.castReply(); CreateVipRsp ret = re.toResponse(CreateVipRsp.class); if (!ret.isSuccess()) { ErrorCode err = operr("failed to create vip%s on virtual router[uuid:%s], because %s", tos, vr.getUuid(), ret.getError()); completion.fail(err); } else { completion.success(); } } }); } public void releaseVipOnVirtualRouterVm(final VirtualRouterVmInventory vr, VipInventory vip, final Completion completion) { List<VipInventory> invs = new ArrayList<VipInventory>(); invs.add(vip); releaseVipOnVirtualRouterVm(vr, invs, completion); } public void releaseVipOnVirtualRouterVm(final VirtualRouterVmInventory vr, List<VipInventory> vips, final Completion completion) { final List<VipTO> tos = new ArrayList<VipTO>(vips.size()); for (VipInventory vip : vips) { String mac = getOwnerMac(vr, vip); VipTO to = VipTO.valueOf(vip, mac); tos.add(to); } RemoveVipCmd cmd = new RemoveVipCmd(); cmd.setVips(tos); VirtualRouterAsyncHttpCallMsg msg = new VirtualRouterAsyncHttpCallMsg(); msg.setPath(VirtualRouterConstant.VR_REMOVE_VIP); msg.setCommand(cmd); msg.setCommandTimeout(apiTimeoutManager.getTimeout(cmd.getClass(), "30m")); msg.setVmInstanceUuid(vr.getUuid()); bus.makeTargetServiceIdByResourceUuid(msg, VmInstanceConstant.SERVICE_ID, vr.getUuid()); bus.send(msg, new CloudBusCallBack(completion) { @Override public void run(MessageReply reply) { if (!reply.isSuccess()) { completion.fail(reply.getError()); return; } VirtualRouterAsyncHttpCallReply re = reply.castReply(); RemoveVipRsp ret = re.toResponse(RemoveVipRsp.class); if (ret.isSuccess()) { completion.success(); } else { ErrorCode err = operr("failed to remove vip%s, because %s", tos, ret.getError()); completion.fail(err); } } }); } public void acquireVipOnVirtualRouterVm(final VirtualRouterVmInventory vr, final VipInventory vip, final Completion completion) { createVipOnVirtualRouterVm(vr, list(vip), new Completion(completion) { @Override public void success() { if (!dbf.isExist(vip.getUuid(), VirtualRouterVipVO.class)) { VirtualRouterVipVO vrvip = new VirtualRouterVipVO(); vrvip.setUuid(vip.getUuid()); vrvip.setVirtualRouterVmUuid(vr.getUuid()); dbf.persist(vrvip); } completion.success(); } @Override public void fail(ErrorCode errorCode) { completion.fail(errorCode); } }); } @Override public void acquireVip(final VipInventory vip, final L3NetworkInventory guestNw, final Completion completion) { VirtualRouterVipVO vipvo = dbf.findByUuid(vip.getUuid(), VirtualRouterVipVO.class); if (vipvo != null) { SimpleQuery<VmInstanceVO> q = dbf.createQuery(VmInstanceVO.class); q.select(VmInstanceVO_.state); q.add(VmInstanceVO_.uuid, SimpleQuery.Op.EQ, vipvo.getVirtualRouterVmUuid()); VmInstanceState vrState = q.findValue(); if (VmInstanceState.Running != vrState) { completion.fail(operr("virtual router[uuid:%s, state:%s] is not running, current HA has not been supported, please manually start this virtual router", vipvo.getVirtualRouterVmUuid(), vrState)); } else { completion.success(); } return; } FlowChain chain = FlowChainBuilder.newSimpleFlowChain(); chain.setName(String.format("prepare-vr-for-vip-%s-%s", vip.getUuid(), vip.getIp())); chain.then(new NoRollbackFlow() { @Override public void run(final FlowTrigger trigger, final Map data) { VirtualRouterStruct s = new VirtualRouterStruct(); s.setL3Network(guestNw); s.setOfferingValidator(new VirtualRouterOfferingValidator() { @Override public void validate(VirtualRouterOfferingInventory offering) throws OperationFailureException { if (!offering.getPublicNetworkUuid().equals(vip.getL3NetworkUuid())) { throw new OperationFailureException(operr("found a virtual router offering[uuid:%s] for L3Network[uuid:%s] in zone[uuid:%s]; however, the network's public network[uuid:%s] is not the same to VIP[uuid:%s]'s; you may need to use system tag" + " guestL3Network::l3NetworkUuid to specify a particular virtual router offering for the L3Network", offering.getUuid(), guestNw.getUuid(), guestNw.getZoneUuid(), vip.getL3NetworkUuid(), vip.getUuid())); } } }); acquireVirtualRouterVm(s, new ReturnValueCompletion<VirtualRouterVmInventory>(trigger){ @Override public void success(VirtualRouterVmInventory returnValue) { data.put(VirtualRouterConstant.Param.VR.toString(), returnValue); trigger.next(); } @Override public void fail(ErrorCode errorCode) { trigger.fail(errorCode); } }); } }).then(new NoRollbackFlow() { @Override public void run(final FlowTrigger trigger, Map data) { final VirtualRouterVmInventory vr = (VirtualRouterVmInventory) data.get(VirtualRouterConstant.Param.VR.toString()); createVipOnVirtualRouterVm(vr, Arrays.asList(vip), 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) { final VirtualRouterVmInventory vr = (VirtualRouterVmInventory) data.get(VirtualRouterConstant.Param.VR.toString()); if (!dbf.isExist(vip.getUuid(), VirtualRouterVipVO.class)) { VirtualRouterVipVO vrvip = new VirtualRouterVipVO(); vrvip.setUuid(vip.getUuid()); vrvip.setVirtualRouterVmUuid(vr.getUuid()); dbf.persist(vrvip); } completion.success(); } }).error(new FlowErrorHandler(completion) { @Override public void handle(ErrorCode errCode, Map data) { completion.fail(errCode); } }).start(); } @Override public void releaseVip(final VipInventory vip, final Completion completion) { final VirtualRouterVipVO vrvip = dbf.findByUuid(vip.getUuid(), VirtualRouterVipVO.class); if (vrvip == null) { completion.success(); return; } if (vrvip.getVirtualRouterVmUuid() == null) { // the vr has been deleted dbf.remove(vrvip); completion.success(); return; } final VirtualRouterVmVO vrvo = dbf.findByUuid(vrvip.getVirtualRouterVmUuid(), VirtualRouterVmVO.class); if (vrvo.getState() != VmInstanceState.Running) { // vr will sync when becomes Running dbf.remove(vrvip); completion.success(); return; } final VirtualRouterVmInventory vr = VirtualRouterVmInventory.valueOf(vrvo); releaseVipOnVirtualRouterVm(vr, vip, new Completion(completion) { @Override public void success() { logger.debug(String.format("successfully released vip[uuid:%s, name:%s, ip:%s] on virtual router vm[uuid:%s]", vip.getUuid(), vip.getName(), vip.getIp(), vrvo.getUuid())); dbf.remove(vrvip); completion.success(); } @Override public void fail(ErrorCode errorCode) { logger.warn(String.format("failed to release vip[uuid:%s, name:%s, ip:%s] on virtual router vm[uuid:%s], because %s", vip.getUuid(), vip.getName(), vip.getIp(), vrvo.getUuid(), errorCode)); completion.fail(errorCode); } }); } @Override public String getServiceProviderTypeForVip() { return VirtualRouterConstant.VIRTUAL_ROUTER_PROVIDER_TYPE; } }