package org.zstack.network.service; import org.zstack.header.Component; import org.zstack.header.core.Completion; import org.zstack.header.core.NoErrorCompletion; import org.zstack.header.errorcode.ErrorCode; import org.zstack.header.exception.CloudRuntimeException; import org.zstack.header.network.l3.L3NetworkInventory; import org.zstack.header.network.service.NetworkServiceProviderType; import org.zstack.header.network.service.NetworkServiceType; import org.zstack.header.network.service.NetworkServiceSnatBackend; import org.zstack.header.network.service.SnatStruct; import org.zstack.header.vm.VmInstanceSpec; import org.zstack.header.vm.VmNicInventory; import org.zstack.utils.Utils; import org.zstack.utils.logging.CLogger; import java.util.*; /** * Created with IntelliJ IDEA. * User: frank * Time: 10:04 PM * To change this template use File | Settings | File Templates. */ public class SnatExtension extends AbstractNetworkServiceExtension implements Component { private final static CLogger logger = Utils.getLogger(SnatExtension.class); private Map<NetworkServiceProviderType, NetworkServiceSnatBackend> snatBackends = new HashMap<NetworkServiceProviderType, NetworkServiceSnatBackend>(); private final String RESULT = String.format("result.%s", SnatExtension.class.getName()); @Override public NetworkServiceType getNetworkServiceType() { return NetworkServiceType.SNAT; } private void doSnat(final Iterator<Map.Entry<NetworkServiceSnatBackend, List<SnatStruct>>> it, final VmInstanceSpec spec, final Completion complete) { if (!it.hasNext()) { complete.success(); return; } Map.Entry<NetworkServiceSnatBackend, List<SnatStruct>> e = it.next(); NetworkServiceSnatBackend bkd = e.getKey(); List<SnatStruct> structs = e.getValue(); bkd.applySnatService(structs, spec, new Completion(complete) { @Override public void success() { doSnat(it, spec, complete); } @Override public void fail(ErrorCode errorCode) { complete.fail(errorCode); } }); } @Override public void applyNetworkService(VmInstanceSpec spec, Map<String, Object> data, Completion complete) { Map<NetworkServiceSnatBackend, List<SnatStruct>> entries = workoutSnat(spec); data.put(RESULT, entries); doSnat(entries.entrySet().iterator(), spec, complete); } @Override public void releaseNetworkService(VmInstanceSpec spec, Map<String, Object> data, NoErrorCompletion completion) { // never release source nat. once SNAT service is enabled on l3Network, it stays alive all the time // during virtual router life cycle. vm stop/destroy won't effect as SNAT is shared by all vms on this // network completion.done(); } private void populateExtensions() { for (NetworkServiceSnatBackend extp : pluginRgty.getExtensionList(NetworkServiceSnatBackend.class)) { NetworkServiceSnatBackend old = snatBackends.get(extp.getProviderType()); if (old != null) { throw new CloudRuntimeException(String.format("duplicate NetworkServiceSnatBackend[%s, %s] for type[%s]", extp.getClass().getName(), old.getClass().getName(), extp.getProviderType())); } snatBackends.put(extp.getProviderType(), extp); } } private SnatStruct makeSnatStruct(VmInstanceSpec spec, L3NetworkInventory l3) { VmNicInventory nic = null; for (VmNicInventory inv : spec.getDestNics()) { if (inv.getL3NetworkUuid().equals(l3.getUuid())) { nic = inv; break; } } SnatStruct struct = new SnatStruct(); struct.setL3Network(l3); struct.setGuestGateway(nic.getGateway()); struct.setGuestIp(nic.getIp()); struct.setGuestMac(nic.getMac()); struct.setGuestNetmask(nic.getNetmask()); return struct; } private Map<NetworkServiceSnatBackend, List<SnatStruct>> workoutSnat(VmInstanceSpec spec) { Map<NetworkServiceSnatBackend, List<SnatStruct>> map = new HashMap<NetworkServiceSnatBackend, List<SnatStruct>>(); Map<NetworkServiceProviderType, List<L3NetworkInventory>> providerMap = getNetworkServiceProviderMap(NetworkServiceType.SNAT, spec.getL3Networks()); for (Map.Entry<NetworkServiceProviderType, List<L3NetworkInventory>> e : providerMap.entrySet()) { NetworkServiceProviderType ptype = e.getKey(); List<SnatStruct> lst = new ArrayList<SnatStruct>(); for (L3NetworkInventory l3 : e.getValue()) { lst.add(makeSnatStruct(spec, l3)); } NetworkServiceSnatBackend bkd = snatBackends.get(ptype); if (bkd == null) { throw new CloudRuntimeException(String.format("unable to find NetworkServiceSnatBackend[provider type: %s]", ptype)); } if (logger.isTraceEnabled()) { logger.trace(String.format("SNAT Backend[%s] is about to apply entries: \n%s", bkd.getClass().getName(), lst)); } map.put(bkd, lst); } return map; } @Override public boolean start() { populateExtensions(); return true; } @Override public boolean stop() { return true; } }