package org.zstack.network.service.virtualrouter.eip; import org.springframework.beans.factory.annotation.Autowire; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Configurable; import org.springframework.transaction.annotation.Transactional; 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.errorcode.ErrorFacade; import org.zstack.core.timeout.ApiTimeoutManager; import org.zstack.header.core.workflow.Flow; import org.zstack.header.core.workflow.FlowRollback; import org.zstack.header.core.workflow.FlowTrigger; import org.zstack.header.errorcode.ErrorCode; import org.zstack.header.message.MessageReply; import org.zstack.header.vm.VmInstanceConstant; import org.zstack.header.vm.VmInstanceState; import org.zstack.header.vm.VmNicInventory; import org.zstack.network.service.eip.EipConstant; import org.zstack.network.service.eip.EipGlobalConfig; import org.zstack.network.service.virtualrouter.*; import org.zstack.network.service.virtualrouter.VirtualRouterCommands.SyncEipRsp; import org.zstack.network.service.virtualrouter.VirtualRouterConstant.Param; import org.zstack.utils.CollectionUtils; import org.zstack.utils.DebugUtils; import org.zstack.utils.Utils; import org.zstack.utils.function.Function; import org.zstack.utils.logging.CLogger; import static org.zstack.core.Platform.operr; import javax.persistence.Tuple; import javax.persistence.TypedQuery; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.Callable; /** */ @Configurable(preConstruction = true, autowire = Autowire.BY_TYPE) public class VirtualRouterSyncEipOnStartFlow implements Flow { private static CLogger logger = Utils.getLogger(VirtualRouterSyncEipOnStartFlow.class); @Autowired private DatabaseFacade dbf; @Autowired private VirtualRouterManager vrMgr; @Autowired private CloudBus bus; @Autowired private ErrorFacade errf; @Autowired private ApiTimeoutManager apiTimeoutManager; @Transactional(readOnly = true) private List<EipTO> findEipOnThisRouter(VirtualRouterVmInventory vr, List<String> eipUuids) { String sql = "select vip.ip, nic.l3NetworkUuid, nic.ip from EipVO eip, VipVO vip, VmNicVO nic, VmInstanceVO vm where nic.vmInstanceUuid = vm.uuid and vm.state = :vmState and eip.vipUuid = vip.uuid and eip.vmNicUuid = nic.uuid and eip.uuid in (:euuids)"; TypedQuery<Tuple> q = dbf.getEntityManager().createQuery(sql, Tuple.class); q.setParameter("euuids", eipUuids); q.setParameter("vmState", VmInstanceState.Running); List<Tuple> tuples = q.getResultList(); List<EipTO> ret = new ArrayList<EipTO>(); for (Tuple t : tuples) { String vipIp = t.get(0, String.class); final String l3Uuid = t.get(1, String.class); String guestIp = t.get(2, String.class); String privMac = CollectionUtils.find(vr.getVmNics(), new Function<String, VmNicInventory>() { @Override public String call(VmNicInventory arg) { if (arg.getL3NetworkUuid().equals(l3Uuid)) { return arg.getMac(); } return null; } }); DebugUtils.Assert(privMac!=null, String.format("cannot find private nic[l3NetworkUuid:%s] on virtual router[uuid:%s]", l3Uuid, vr.getUuid())); EipTO to = new EipTO(); to.setVipIp(vipIp); to.setGuestIp(guestIp); to.setPrivateMac(privMac); to.setSnatInboundTraffic(EipGlobalConfig.SNAT_INBOUND_TRAFFIC.value(Boolean.class)); ret.add(to); } return ret; } private List<EipTO> findEipOnThisRouter(final VirtualRouterVmInventory vr, Map<String, Object> data, boolean isNewCreated) { List<String> eipUuids; if (isNewCreated) { final VmNicInventory guestNic = vr.getGuestNic(); final VmNicInventory publicNic = vr.getPublicNic(); eipUuids = new Callable<List<String>>() { @Override @Transactional(readOnly = true) public List<String> call() { String sql = "select eip.uuid from EipVO eip, VipVO vip, VmNicVO nic, VmInstanceVO vm where vm.uuid = nic.vmInstanceUuid and vm.state = :vmState and eip.vipUuid = vip.uuid and eip.vmNicUuid = nic.uuid and vip.l3NetworkUuid = :vipL3Uuid and nic.l3NetworkUuid = :guestL3Uuid"; TypedQuery<String> q = dbf.getEntityManager().createQuery(sql, String.class); q.setParameter("vipL3Uuid", publicNic.getL3NetworkUuid()); q.setParameter("guestL3Uuid", guestNic.getL3NetworkUuid()); q.setParameter("vmState", VmInstanceState.Running); return q.getResultList(); } }.call(); if (!eipUuids.isEmpty()) { List<VirtualRouterEipRefVO> refs = new ArrayList<VirtualRouterEipRefVO>(); for (String eipUuid : eipUuids) { VirtualRouterEipRefVO ref = new VirtualRouterEipRefVO(); ref.setEipUuid(eipUuid); ref.setVirtualRouterVmUuid(vr.getUuid()); refs.add(ref); } dbf.persistCollection(refs); data.put(VirtualRouterSyncEipOnStartFlow.class.getName(), refs); } } else { SimpleQuery<VirtualRouterEipRefVO> q = dbf.createQuery(VirtualRouterEipRefVO.class); q.select(VirtualRouterEipRefVO_.eipUuid); q.add(VirtualRouterEipRefVO_.virtualRouterVmUuid, SimpleQuery.Op.EQ, vr.getUuid()); eipUuids = q.listValue(); } if (eipUuids.isEmpty()) { return new ArrayList<>(); } return findEipOnThisRouter(vr, eipUuids); } public void run(final FlowTrigger trigger, Map data) { final VirtualRouterVmInventory vr = (VirtualRouterVmInventory) data.get(VirtualRouterConstant.Param.VR.toString()); VmNicInventory guestNic = vr.getGuestNic(); if (!vrMgr.isL3NetworkNeedingNetworkServiceByVirtualRouter(guestNic.getL3NetworkUuid(), EipConstant.EIP_NETWORK_SERVICE_TYPE)) { trigger.next(); return; } if (VirtualRouterSystemTags.DEDICATED_ROLE_VR.hasTag(vr.getUuid()) && !VirtualRouterSystemTags.VR_EIP_ROLE.hasTag(vr.getUuid())) { trigger.next(); return; } new VirtualRouterRoleManager().makeEipRole(vr.getUuid()); boolean isNewCreated = data.containsKey(Param.IS_NEW_CREATED.toString()); List<EipTO> eips = findEipOnThisRouter(vr, data, isNewCreated); if (eips.isEmpty()) { trigger.next(); return; } VirtualRouterCommands.SyncEipCmd cmd = new VirtualRouterCommands.SyncEipCmd(); cmd.setEips(eips); VirtualRouterAsyncHttpCallMsg msg = new VirtualRouterAsyncHttpCallMsg(); msg.setPath(VirtualRouterConstant.VR_SYNC_EIP); 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(trigger) { @Override public void run(MessageReply reply) { if (!reply.isSuccess()) { trigger.fail(reply.getError()); return; } VirtualRouterAsyncHttpCallReply re = reply.castReply(); SyncEipRsp ret = re.toResponse(SyncEipRsp.class); if (!ret.isSuccess()) { ErrorCode err = operr("failed to sync eip on virtual router[uuid:%s], %s", vr.getUuid(), ret.getError()); trigger.fail(err); } else { String info = String.format("failed to sync eip on virtual router[uuid:%s]", vr.getUuid()); logger.debug(info); trigger.next(); } } }); } @Override public void rollback(FlowRollback trigger, Map data) { List<VirtualRouterEipRefVO> refs = (List<VirtualRouterEipRefVO>) data.get(VirtualRouterSyncEipOnStartFlow.class.getName()); if (refs != null) { dbf.removeCollection(refs, VirtualRouterEipRefVO.class); } trigger.rollback(); } }