package org.zstack.network.service.portforwarding; import org.springframework.beans.factory.annotation.Autowired; import org.zstack.core.db.SimpleQuery; import org.zstack.header.core.Completion; import org.zstack.header.core.NoErrorCompletion; import org.zstack.header.errorcode.ErrorCode; import org.zstack.header.network.l3.L3NetworkInventory; import org.zstack.header.network.l3.L3NetworkVO; import org.zstack.header.network.service.NetworkServiceProviderType; import org.zstack.header.network.service.NetworkServiceType; import org.zstack.header.vm.VmInstanceConstant; import org.zstack.header.vm.VmInstanceConstant.VmOperation; import org.zstack.header.vm.VmInstanceSpec; import org.zstack.header.vm.VmNicInventory; import org.zstack.network.service.AbstractNetworkServiceExtension; import org.zstack.network.service.vip.VipInventory; import org.zstack.network.service.vip.VipVO; import org.zstack.utils.Utils; import org.zstack.utils.logging.CLogger; import java.util.*; /** * Created with IntelliJ IDEA. * User: frank * Time: 7:53 PM * To change this template use File | Settings | File Templates. */ public class PortForwardingExtension extends AbstractNetworkServiceExtension { private static final CLogger logger = Utils.getLogger(PortForwardingExtension.class); @Autowired private PortForwardingManager pfMgr; private final String SUCCESS = PortForwardingExtension.class.getName(); public NetworkServiceType getNetworkServiceType() { return NetworkServiceType.PortForwarding; } protected List<PortForwardingStruct> makePortForwardingStruct(List<VmNicInventory> nics, boolean releaseVmNicInfo,L3NetworkInventory l3) { VmNicInventory nic = null; for (VmNicInventory inv : nics) { if (inv.getL3NetworkUuid().equals(l3.getUuid())) { nic = inv; break; } } SimpleQuery<PortForwardingRuleVO> q = dbf.createQuery(PortForwardingRuleVO.class); q.add(PortForwardingRuleVO_.vmNicUuid, SimpleQuery.Op.EQ, nic.getUuid()); List<PortForwardingRuleVO> pfvos = q.list(); if (pfvos.isEmpty()) { // having port forwarding service but no rules applied yet return new ArrayList<PortForwardingStruct>(); } List<PortForwardingStruct> rules = new ArrayList<PortForwardingStruct>(); for (PortForwardingRuleVO pfvo : pfvos) { VipVO vipvo = dbf.findByUuid(pfvo.getVipUuid(), VipVO.class); L3NetworkVO l3vo = dbf.findByUuid(vipvo.getL3NetworkUuid(), L3NetworkVO.class); PortForwardingStruct struct = new PortForwardingStruct(); struct.setRule(PortForwardingRuleInventory.valueOf(pfvo)); struct.setVip(VipInventory.valueOf(vipvo)); struct.setGuestIp(nic.getIp()); struct.setGuestMac(nic.getMac()); struct.setGuestL3Network(l3); struct.setSnatInboundTraffic(PortForwardingGlobalConfig.SNAT_INBOUND_TRAFFIC.value(Boolean.class)); struct.setVipL3Network(L3NetworkInventory.valueOf(l3vo)); struct.setReleaseVmNicInfoWhenDetaching(releaseVmNicInfo); struct.setReleaseVip(false); rules.add(struct); } return rules; } @Override public void applyNetworkService(VmInstanceSpec spec, Map<String, Object> data, Completion completion) { // For new created vm, there is no port forwarding rule if (spec.getCurrentVmOperation() == VmInstanceConstant.VmOperation.NewCreate) { completion.success(); return; } Map<String, List<PortForwardingStruct>> structs = workoutPortForwarding(spec); Map<String, List<PortForwardingStruct>> applieds = new HashMap<String, List<PortForwardingStruct>>(); data.put(SUCCESS, applieds); applyNetworkService(structs.entrySet().iterator(), applieds, completion); } private void applyNetworkService(final Iterator<Map.Entry<String, List<PortForwardingStruct>>> it, final Map<String, List<PortForwardingStruct>> applieds, final Completion completion) { if (!it.hasNext()) { completion.success(); return; } Map.Entry<String, List<PortForwardingStruct>> e = it.next(); applyNetworkService(e.getValue().iterator(), e.getKey(), applieds, new Completion(completion) { @Override public void success() { applyNetworkService(it, applieds, completion); } @Override public void fail(ErrorCode errorCode) { completion.fail(errorCode); } }); } private void applyNetworkService(final Iterator<PortForwardingStruct> it, final String providerType, final Map<String, List<PortForwardingStruct>> applieds, final Completion completion) { if (!it.hasNext()) { completion.success(); return; } final PortForwardingStruct struct = it.next(); pfMgr.attachPortForwardingRule(struct, providerType, new Completion(completion) { private void addStructToApplieds() { List<PortForwardingStruct> structs = applieds.get(providerType); if (structs == null) { structs = new ArrayList<PortForwardingStruct>(); } structs.add(struct); } @Override public void success() { addStructToApplieds(); logger.debug(String.format("successfully applied %s", struct.toString())); applyNetworkService(it, providerType, applieds, completion); } @Override public void fail(ErrorCode errorCode) { completion.fail(errorCode); } }); } public void releaseNetworkService(final Iterator<Map.Entry<String, List<PortForwardingStruct>>> it, final NoErrorCompletion completion) { if (!it.hasNext()) { completion.done(); return; } Map.Entry<String, List<PortForwardingStruct>> e = it.next(); releaseNetworkService(e.getValue().iterator(), e.getKey(), new NoErrorCompletion(completion) { @Override public void done() { releaseNetworkService(it, completion); } }); } public void releaseNetworkService(final Iterator<PortForwardingStruct> it, final String providerType, final NoErrorCompletion completion) { if (!it.hasNext()) { completion.done(); return; } final PortForwardingStruct struct = it.next(); pfMgr.detachPortForwardingRule(struct, providerType, new Completion(completion) { @Override public void success() { logger.debug(String.format("successfully revoked %s on service provider[%s]", struct.toString(), providerType)); releaseNetworkService(it, providerType, completion); } @Override public void fail(ErrorCode errorCode) { logger.warn(String.format("failed to revoke %s on service provider[%s], provider should take care of cleanup", struct.toString(), providerType)); releaseNetworkService(it, providerType, completion); } }); } @Override public void releaseNetworkService(VmInstanceSpec spec, Map<String, Object> data, NoErrorCompletion completion) { Map<String, List<PortForwardingStruct>> structs; if (data.containsKey(SUCCESS)) { structs = (Map<String, List<PortForwardingStruct>>) data.get(SUCCESS); } else { structs = workoutPortForwarding(spec); } releaseNetworkService(structs.entrySet().iterator(), completion); } private Map<String, List<PortForwardingStruct>> workoutPortForwarding(VmInstanceSpec spec) { Map<String, List<PortForwardingStruct>> map = new HashMap<String, List<PortForwardingStruct>>(); Map<NetworkServiceProviderType, List<L3NetworkInventory>> providerMap = getNetworkServiceProviderMap(NetworkServiceType.PortForwarding, spec.getL3Networks()); for (Map.Entry<NetworkServiceProviderType, List<L3NetworkInventory>> e : providerMap.entrySet()) { NetworkServiceProviderType ptype = e.getKey(); List<PortForwardingStruct> lst = new ArrayList<PortForwardingStruct>(); for (L3NetworkInventory l3 : e.getValue()) { lst.addAll(makePortForwardingStruct(spec.getDestNics(), spec.getCurrentVmOperation() == VmOperation.Destroy || spec.getCurrentVmOperation() == VmOperation.DetachNic, l3)); } map.put(ptype.toString(), lst); } return map; } }