package org.zstack.network.service.virtualrouter.nat; import org.springframework.beans.factory.annotation.Autowired; import org.zstack.core.cloudbus.CloudBus; import org.zstack.core.cloudbus.CloudBusCallBack; import org.zstack.core.errorcode.ErrorFacade; import org.zstack.core.timeout.ApiTimeoutManager; import org.zstack.header.core.Completion; import org.zstack.header.core.NoErrorCompletion; import org.zstack.header.core.ReturnValueCompletion; import org.zstack.header.errorcode.ErrorCode; import org.zstack.header.errorcode.OperationFailureException; import org.zstack.header.message.MessageReply; import org.zstack.header.network.l3.L3NetworkInventory; import org.zstack.header.network.service.NetworkServiceProviderType; import org.zstack.header.network.service.NetworkServiceSnatBackend; import org.zstack.header.network.service.SnatStruct; import org.zstack.header.vm.VmInstanceConstant; import org.zstack.header.vm.VmInstanceSpec; import org.zstack.header.vm.VmNicInventory; import org.zstack.network.service.virtualrouter.*; import org.zstack.network.service.virtualrouter.VirtualRouterCommands.RemoveSNATRsp; import org.zstack.network.service.virtualrouter.VirtualRouterCommands.SetSNATRsp; 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.gson.JSONObjectUtil; import org.zstack.utils.logging.CLogger; import static org.zstack.core.Platform.operr; import java.util.Arrays; import java.util.Iterator; import java.util.List; /** * Created with IntelliJ IDEA. * User: frank * Time: 10:37 PM * To change this template use File | Settings | File Templates. */ public class VirtualRouterSnatBackend extends AbstractVirtualRouterBackend implements NetworkServiceSnatBackend { private static final CLogger logger = Utils.getLogger(VirtualRouterSnatBackend.class); @Autowired private ErrorFacade errf; @Autowired private CloudBus bus; @Autowired private ApiTimeoutManager apiTimeoutManager; @Override public NetworkServiceProviderType getProviderType() { return VirtualRouterConstant.PROVIDER_TYPE; } private void applySnat(final Iterator<SnatStruct> it, final VmInstanceSpec spec, final Completion completion) { if (!it.hasNext()) { completion.success(); return; } final SnatStruct struct = it.next(); final L3NetworkInventory guestL3 = struct.getL3Network(); VirtualRouterStruct s = new VirtualRouterStruct(); s.setL3Network(guestL3); s.setOfferingValidator(new VirtualRouterOfferingValidator() { @Override public void validate(VirtualRouterOfferingInventory offering) throws OperationFailureException { if (offering.getPublicNetworkUuid().equals(guestL3.getUuid())) { throw new OperationFailureException(operr("guest l3Network[uuid:%s, name:%s] needs SNAT service provided by virtual router, but public l3Network[uuid:%s] of virtual router offering[uuid: %s, name:%s] is the same to this guest l3Network", guestL3.getUuid(), guestL3.getName(), offering.getPublicNetworkUuid(), offering.getUuid(), offering.getName())); } } }); acquireVirtualRouterVm(s, new ReturnValueCompletion<VirtualRouterVmInventory>(completion) { @Override public void success(final VirtualRouterVmInventory vr) { final VirtualRouterCommands.SNATInfo info = new VirtualRouterCommands.SNATInfo(); VmNicInventory privateNic = CollectionUtils.find(vr.getVmNics(), new Function<VmNicInventory, VmNicInventory>() { @Override public VmNicInventory call(VmNicInventory arg) { if (arg.getIp().equals(struct.getGuestGateway())) { return arg; } return null; } }); DebugUtils.Assert(privateNic!=null, String.format("cannot find private nic[ip:%s] on virtual router[uuid:%s, name:%s]", struct.getGuestGateway(), vr.getUuid(), vr.getName())); info.setPrivateNicIp(privateNic.getIp()); info.setPrivateNicMac(privateNic.getMac()); info.setPublicNicMac(vr.getPublicNic().getMac()); info.setPublicIp(vr.getPublicNic().getIp()); info.setSnatNetmask(struct.getGuestNetmask()); VirtualRouterCommands.SetSNATCmd cmd = new VirtualRouterCommands.SetSNATCmd(); cmd.setSnat(info); VirtualRouterAsyncHttpCallMsg msg = new VirtualRouterAsyncHttpCallMsg(); msg.setVmInstanceUuid(vr.getUuid()); msg.setPath(VirtualRouterConstant.VR_SET_SNAT_PATH); msg.setCommand(cmd); msg.setCommandTimeout(apiTimeoutManager.getTimeout(cmd.getClass(), "30m")); msg.setCheckStatus(true); 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(); SetSNATRsp ret = re.toResponse(SetSNATRsp.class); if (!ret.isSuccess()) { new VirtualRouterRoleManager().makeSnatRole(vr.getUuid()); ErrorCode err = operr("virtual router[uuid:%s, ip:%s] failed to apply snat[%s] for vm[uuid:%s, name:%s] on L3Network[uuid:%s, name:%s], because %s", vr.getUuid(), vr.getManagementNic().getIp(), JSONObjectUtil.toJsonString(info), spec.getVmInventory().getUuid(), spec.getVmInventory().getName(), struct.getL3Network().getUuid(), struct.getL3Network().getName(), ret.getError()); completion.fail(err); } else { applySnat(it, spec, completion); } } }); } @Override public void fail(ErrorCode errorCode) { completion.fail(errorCode); } }); } @Override public void applySnatService(List<SnatStruct> snatStructList, VmInstanceSpec spec, Completion completion) { if (snatStructList.isEmpty()) { completion.success(); return; } applySnat(snatStructList.iterator(), spec, completion); } private void releaseSnat(final Iterator<SnatStruct> it, final VmInstanceSpec spec, final NoErrorCompletion completion) { if (!it.hasNext()) { completion.done(); return; } final SnatStruct struct = it.next(); final VirtualRouterVmInventory vr = vrMgr.getVirtualRouterVm(struct.getL3Network()); VmNicInventory privateNic = CollectionUtils.find(vr.getVmNics(), new Function<VmNicInventory, VmNicInventory>() { @Override public VmNicInventory call(VmNicInventory arg) { if (arg.getIp().equals(struct.getGuestGateway())) { return arg; } return null; } }); DebugUtils.Assert(privateNic!=null, String.format("cannot find private nic[ip:%s] on virtual router[uuid:%s, name:%s]", struct.getGuestGateway(), vr.getUuid(), vr.getName())); final VirtualRouterCommands.SNATInfo info = new VirtualRouterCommands.SNATInfo(); info.setPrivateNicIp(privateNic.getIp()); info.setPrivateNicMac(privateNic.getMac()); info.setPublicNicMac(vr.getPublicNic().getMac()); info.setPublicIp(vr.getPublicNic().getIp()); info.setSnatNetmask(struct.getGuestNetmask()); VirtualRouterCommands.RemoveSNATCmd cmd = new VirtualRouterCommands.RemoveSNATCmd(); cmd.setNatInfo(Arrays.asList(info)); VirtualRouterAsyncHttpCallMsg msg = new VirtualRouterAsyncHttpCallMsg(); msg.setCheckStatus(true); msg.setVmInstanceUuid(vr.getUuid()); msg.setCommand(cmd); msg.setCommandTimeout(apiTimeoutManager.getTimeout(cmd.getClass(), "30m")); msg.setPath(VirtualRouterConstant.VR_REMOVE_SNAT_PATH); bus.makeTargetServiceIdByResourceUuid(msg, VirtualRouterConstant.SERVICE_ID, vr.getUuid()); bus.send(msg, new CloudBusCallBack(completion) { @Override public void run(MessageReply reply) { if (!reply.isSuccess()) { logger.warn(String.format("failed to release snat[%s] on virtual router[name:%s, uuid:%s] for vm[uuid: %s, name: %s], %s", struct, vr.getName(), vr.getUuid(), spec.getVmInventory().getUuid(), spec.getVmInventory().getName(), reply.getError())); //TODO GC } else { VirtualRouterAsyncHttpCallReply re = reply.castReply(); RemoveSNATRsp ret = re.toResponse(RemoveSNATRsp.class); if (!ret.isSuccess()) { String err = String.format( "virtual router[uuid:%s, ip:%s] failed to release snat[%s] for vm[uuid:%s, name:%s] on L3Network[uuid:%s, name:%s], because %s", vr.getUuid(), vr.getManagementNic().getIp(), JSONObjectUtil.toJsonString(info), spec.getVmInventory().getUuid(), spec.getVmInventory().getName(), struct.getL3Network().getUuid(), struct.getL3Network().getName(), ret.getError()); logger.warn(err); //TODO GC } else { String msg = String.format( "virtual router[uuid:%s, ip:%s] released snat[%s] for vm[uuid:%s, name:%s] on L3Network[uuid:%s, name:%s], because %s", vr.getUuid(), vr.getManagementNic().getIp(), JSONObjectUtil.toJsonString(info), spec.getVmInventory().getUuid(), spec.getVmInventory().getName(), struct.getL3Network().getUuid(), struct.getL3Network().getName(), ret.getError()); logger.warn(msg); } } releaseSnat(it, spec, completion); } }); } @Override public void releaseSnatService(List<SnatStruct> snatStructList, VmInstanceSpec spec, NoErrorCompletion completion) { if (snatStructList.isEmpty()) { completion.done(); return; } releaseSnat(snatStructList.iterator(), spec, completion); } }