All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opendaylight.netvirt.natservice.internal.NatSouthboundEventHandlers Maven / Gradle / Ivy

There is a newer version: 0.11.4
Show newest version
/*
 * Copyright (c) 2016 - 2018 Ericsson India Global Services Pvt Ltd. and others.  All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/epl-v10.html
 */
package org.opendaylight.netvirt.natservice.internal;

import static org.opendaylight.genius.infra.Datastore.CONFIGURATION;
import static org.opendaylight.genius.infra.Datastore.OPERATIONAL;

import com.google.common.base.Optional;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Table;
import com.google.common.util.concurrent.FluentFuture;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.locks.ReentrantLock;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.opendaylight.controller.md.sal.binding.api.DataBroker;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
import org.opendaylight.genius.infra.Datastore.Operational;
import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
import org.opendaylight.genius.infra.TypedReadWriteTransaction;
import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface;
import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.router.interfaces.RouterInterface;
import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.RouterPorts;
import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.router.ports.Ports;
import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.router.ports.ports.InternalToExternalPortMap;
import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.neutron.vip.states.VipState;
import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.NeutronvpnService;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Singleton
public class NatSouthboundEventHandlers {

    private static final Logger LOG = LoggerFactory.getLogger(NatSouthboundEventHandlers.class);
    private static final String NAT_DS = "NATDS";
    private final DataBroker dataBroker;
    private final ManagedNewTransactionRunner txRunner;
    private final OdlInterfaceRpcService odlInterfaceRpcService;
    private final JobCoordinator coordinator;
    private final FloatingIPListener floatingIPListener;
    private final NeutronvpnService neutronVpnService;
    private final IMdsalApiManager mdsalManager;
    private final NaptManager naptManager;
    private final VipStateTracker vipStateTracker;
    Table stateTable = HashBasedTable.create();

    enum IntfTransitionState {
        STATE_UP,
        STATE_DOWN,
        STATE_IGNORE
    }

    private void initialize() {
        stateTable.put(Interface.OperStatus.Up, Interface.OperStatus.Down, IntfTransitionState.STATE_DOWN);
        stateTable.put(Interface.OperStatus.Down, Interface.OperStatus.Up, IntfTransitionState.STATE_UP);
        stateTable.put(Interface.OperStatus.Unknown, Interface.OperStatus.Up, IntfTransitionState.STATE_UP);
        stateTable.put(Interface.OperStatus.Unknown, Interface.OperStatus.Down, IntfTransitionState.STATE_DOWN);
        stateTable.put(Interface.OperStatus.Up, Interface.OperStatus.Unknown, IntfTransitionState.STATE_DOWN);
    }

    @Inject
    public NatSouthboundEventHandlers(final DataBroker dataBroker,
            final OdlInterfaceRpcService odlInterfaceRpcService, final JobCoordinator coordinator,
            final FloatingIPListener floatingIPListener,final NeutronvpnService neutronvpnService,
            final IMdsalApiManager mdsalManager, final NaptManager naptManager, final VipStateTracker vipStateTracker) {
        this.dataBroker = dataBroker;
        this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
        this.odlInterfaceRpcService = odlInterfaceRpcService;
        this.coordinator = coordinator;
        this.floatingIPListener = floatingIPListener;
        this.neutronVpnService = neutronvpnService;
        this.mdsalManager = mdsalManager;
        this.naptManager = naptManager;
        this.vipStateTracker = vipStateTracker;
        initialize();
    }

    public void handleAdd(String interfaceName, BigInteger intfDpnId, RouterInterface routerInterface) {
        handleAdd(interfaceName, intfDpnId, routerInterface, null);
    }

    public void handleAdd(String interfaceName, BigInteger intfDpnId,
                          RouterInterface routerInterface, @Nullable VipState vipState) {
        String routerName = routerInterface.getRouterName();
        NatInterfaceStateAddWorker natIfaceStateAddWorker = new NatInterfaceStateAddWorker(interfaceName,
                intfDpnId, routerName);
        coordinator.enqueueJob(NAT_DS + "-" + interfaceName, natIfaceStateAddWorker);

        NatFlowAddWorker natFlowAddWorker = new NatFlowAddWorker(interfaceName, routerName, intfDpnId, vipState);
        coordinator.enqueueJob(NAT_DS + "-" + interfaceName, natFlowAddWorker, NatConstants.NAT_DJC_MAX_RETRIES);
    }

    public void handleRemove(String interfaceName, BigInteger intfDpnId, RouterInterface routerInterface) {
        String routerName = routerInterface.getRouterName();
        NatInterfaceStateRemoveWorker natIfaceStateRemoveWorker = new NatInterfaceStateRemoveWorker(interfaceName,
                intfDpnId, routerName);
        coordinator.enqueueJob(NAT_DS + "-" + interfaceName, natIfaceStateRemoveWorker);

        NatFlowRemoveWorker natFlowRemoveWorker = new NatFlowRemoveWorker(interfaceName, intfDpnId, routerName);
        coordinator.enqueueJob(NAT_DS + "-" + interfaceName, natFlowRemoveWorker,
                NatConstants.NAT_DJC_MAX_RETRIES);
    }

    public void handleUpdate(Interface original, Interface update,
                             BigInteger intfDpnId, RouterInterface routerInterface) {
        String routerName = routerInterface.getRouterName();
        NatInterfaceStateUpdateWorker natIfaceStateupdateWorker = new NatInterfaceStateUpdateWorker(original,
                update, intfDpnId, routerName);
        coordinator.enqueueJob(NAT_DS + "-" + update.getName(), natIfaceStateupdateWorker);
        NatFlowUpdateWorker natFlowUpdateWorker = new NatFlowUpdateWorker(original, update, routerName);
        coordinator.enqueueJob(NAT_DS + "-" + update.getName(), natFlowUpdateWorker,
                NatConstants.NAT_DJC_MAX_RETRIES);
    }

    void handleRouterInterfacesUpEvent(String routerName, String interfaceName, BigInteger dpId,
            TypedReadWriteTransaction operTx) throws ExecutionException, InterruptedException {
        LOG.debug("handleRouterInterfacesUpEvent : Handling UP event for router interface {} in Router {} on Dpn {}",
                interfaceName, routerName, dpId);
        NatUtil.addToNeutronRouterDpnsMap(routerName, interfaceName, dpId, operTx);
        NatUtil.addToDpnRoutersMap(routerName, interfaceName, dpId, operTx);
    }

    void handleRouterInterfacesDownEvent(String routerName, String interfaceName, BigInteger dpnId,
                                         TypedReadWriteTransaction operTx)
        throws ExecutionException, InterruptedException {
        LOG.debug("handleRouterInterfacesDownEvent : Handling DOWN event for router Interface {} in Router {}",
                interfaceName, routerName);
        NatUtil.removeFromNeutronRouterDpnsMap(routerName, dpnId, operTx);
        NatUtil.removeFromDpnRoutersMap(dataBroker, routerName, interfaceName, dpnId, odlInterfaceRpcService,
                operTx);
    }

    private IntfTransitionState getTransitionState(Interface.OperStatus original , Interface.OperStatus updated) {
        IntfTransitionState transitionState = stateTable.get(original, updated);

        if (transitionState == null) {
            return IntfTransitionState.STATE_IGNORE;
        }
        return transitionState;
    }

    private class NatInterfaceStateAddWorker implements Callable>> {
        private final String interfaceName;
        private final String routerName;
        private final BigInteger intfDpnId;

        NatInterfaceStateAddWorker(String interfaceName, BigInteger intfDpnId, String routerName) {
            this.interfaceName = interfaceName;
            this.routerName = routerName;
            this.intfDpnId = intfDpnId;
        }

        @Override
        @SuppressWarnings("checkstyle:IllegalCatch")
        public List> call() {
            LOG.trace("call : Received interface {} PORT UP OR ADD event ", interfaceName);
            List> futures = new ArrayList<>();
            final ReentrantLock lock = NatUtil.lockForNat(intfDpnId);
            lock.lock();
            try {
                futures.add(txRunner.callWithNewReadWriteTransactionAndSubmit(OPERATIONAL, tx ->
                    handleRouterInterfacesUpEvent(routerName, interfaceName, intfDpnId, tx)));
            } catch (Exception e) {
                LOG.error("call : Exception caught in Interface {} Operational State Up event",
                    interfaceName, e);
            } finally {
                lock.unlock();
            }
            return futures;
        }
    }

    private class NatInterfaceStateRemoveWorker implements Callable>> {
        private final String interfaceName;
        private final String routerName;
        private final BigInteger intfDpnId;

        NatInterfaceStateRemoveWorker(String interfaceName, BigInteger intfDpnId, String routerName) {
            this.interfaceName = interfaceName;
            this.routerName = routerName;
            this.intfDpnId = intfDpnId;
        }

        @Override
        @SuppressWarnings("checkstyle:IllegalCatch")
        public List> call() {
            LOG.trace("call : Received interface {} PORT DOWN or REMOVE event", interfaceName);
            List> futures = new ArrayList<>();
            final ReentrantLock lock = NatUtil.lockForNat(intfDpnId);
            lock.lock();
            try {
                futures.add(txRunner.callWithNewReadWriteTransactionAndSubmit(OPERATIONAL, tx ->
                    handleRouterInterfacesDownEvent(routerName, interfaceName, intfDpnId, tx)));
            } catch (Exception e) {
                LOG.error("call : Exception observed in handling deletion of VPN Interface {}.", interfaceName, e);
            } finally {
                lock.unlock();
            }
            return futures;
        }
    }

    private class NatInterfaceStateUpdateWorker implements Callable>> {
        private final Interface original;
        private final Interface update;
        private final BigInteger intfDpnId;
        private final String routerName;

        NatInterfaceStateUpdateWorker(Interface original, Interface update, BigInteger intfDpnId, String routerName) {
            this.original = original;
            this.update = update;
            this.intfDpnId = intfDpnId;
            this.routerName = routerName;
        }

        @Override
        @SuppressWarnings("checkstyle:IllegalCatch")
        public List> call() {
            final String interfaceName = update.getName();
            LOG.trace("call : Received interface {} state change event", interfaceName);
            LOG.debug("call : DPN ID {} for the interface {} ", intfDpnId, interfaceName);

            List> futures = new ArrayList<>();
            final ReentrantLock lock = NatUtil.lockForNat(intfDpnId);
            lock.lock();
            try {
                IntfTransitionState state = getTransitionState(original.getOperStatus(), update.getOperStatus());
                if (state.equals(IntfTransitionState.STATE_IGNORE)) {
                    LOG.info("NAT Service: Interface {} state original {} updated {} not handled",
                        interfaceName, original.getOperStatus(), update.getOperStatus());
                    return futures;
                }
                futures.add(txRunner.callWithNewReadWriteTransactionAndSubmit(OPERATIONAL, tx -> {
                    if (state.equals(IntfTransitionState.STATE_DOWN)) {
                        LOG.debug("call : DPN {} connnected to the interface {} has gone down."
                                + "Hence clearing the dpn-vpninterfaces-list entry from the"
                                + " neutron-router-dpns model in the ODL:L3VPN", intfDpnId, interfaceName);
                        // If the interface state is unknown, it means that the corresponding DPN has gone down.
                        // So remove the dpn-vpninterfaces-list from the neutron-router-dpns model.
                        NatUtil.removeFromNeutronRouterDpnsMap(routerName, interfaceName,
                            intfDpnId, tx);
                    } else if (state.equals(IntfTransitionState.STATE_UP)) {
                        LOG.debug("call : DPN {} connnected to the interface {} has come up. Hence adding"
                                + " the dpn-vpninterfaces-list entry from the neutron-router-dpns model"
                                + " in the ODL:L3VPN", intfDpnId, interfaceName);
                        handleRouterInterfacesUpEvent(routerName, interfaceName, intfDpnId, tx);
                    }
                }));
            } catch (Exception e) {
                LOG.error("call : Exception observed in handling updation of VPN Interface {}.", update.getName(), e);
            } finally {
                lock.unlock();
            }
            return futures;
        }
    }

    private void processInterfaceAdded(String portName, String routerId, BigInteger dpnId, VipState vipState) {
        LOG.trace("processInterfaceAdded : Processing Interface Add Event for interface {}", portName);
        List intExtPortMapList = getIntExtPortMapListForPortName(portName, routerId);
        if (intExtPortMapList.isEmpty()) {
            LOG.debug("processInterfaceAdded : Ip Mapping list is empty/null for portname {}", portName);
            return;
        }
        InstanceIdentifier portIid = NatUtil.buildRouterPortsIdentifier(routerId);
        FluentFuture future = txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, tx -> {
            for (InternalToExternalPortMap intExtPortMap : intExtPortMapList) {
                floatingIPListener.createNATFlowEntries(portName, intExtPortMap, portIid, routerId, dpnId, tx);
            }
        });
        future.transform((ignored) -> {
            if (vipState != null) {
                return this.vipStateTracker.writeVipState(vipState);
            }
            return null;
        }, MoreExecutors.directExecutor());
    }

    @NonNull
    private List getIntExtPortMapListForPortName(String portName, String routerId) {
        InstanceIdentifier portToIpMapIdentifier = NatUtil.buildPortToIpMapIdentifier(routerId, portName);
        Optional port =
                SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(dataBroker,
                        LogicalDatastoreType.CONFIGURATION, portToIpMapIdentifier);
        if (!port.isPresent()) {
            LOG.info("getIntExtPortMapListForPortName : Unable to read router port entry for router ID {} "
                    + "and port name {}", routerId, portName);
            return Collections.emptyList();
        }
        return port.get().nonnullInternalToExternalPortMap();
    }

    private void processInterfaceRemoved(String portName, BigInteger dpnId, String routerId,
            List> futures) {
        LOG.trace("processInterfaceRemoved : Processing Interface Removed Event for interface {} on DPN ID {}",
                portName, dpnId);
        List intExtPortMapList = getIntExtPortMapListForPortName(portName, routerId);
        if (intExtPortMapList.isEmpty()) {
            LOG.debug("processInterfaceRemoved : Ip Mapping list is empty/null for portName {}", portName);
            return;
        }
        InstanceIdentifier portIid = NatUtil.buildRouterPortsIdentifier(routerId);
        ListenableFuture future = txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, tx -> {
            for (InternalToExternalPortMap intExtPortMap : intExtPortMapList) {
                LOG.trace("processInterfaceRemoved : Removing DNAT Flow entries for dpnId {} ", dpnId);
                floatingIPListener.removeNATFlowEntries(portName, intExtPortMap, portIid, routerId, dpnId, tx);
            }
        });
        futures.add(future);
        try {
            future.get();
        } catch (InterruptedException | ExecutionException e) {
            LOG.error("Error processing interface removal", e);
        }
    }

    private class NatFlowAddWorker implements Callable>> {
        private final String interfaceName;
        private final String routerName;
        private final BigInteger dpnId;
        private final VipState vipState;

        NatFlowAddWorker(String interfaceName,String routerName, BigInteger dpnId, VipState vipState) {
            this.interfaceName = interfaceName;
            this.routerName = routerName;
            this.dpnId = dpnId;
            this.vipState = vipState;
        }

        @Override
        @SuppressWarnings("checkstyle:IllegalCatch")
        public List> call() {
            final List> futures = new ArrayList<>();
            LOG.trace("call : Interface {} up event received", interfaceName);
            try {
                LOG.trace("call : Port added event received for interface {} ", interfaceName);
                processInterfaceAdded(interfaceName, routerName, dpnId, vipState);
            } catch (Exception ex) {
                LOG.error("call : Exception caught in Interface {} Operational State Up event",
                        interfaceName, ex);
            }
            return futures;
        }
    }

    private class NatFlowUpdateWorker implements Callable>> {
        private final Interface original;
        private final Interface update;
        private final String routerName;

        NatFlowUpdateWorker(Interface original, Interface update, String routerName) {
            this.original = original;
            this.update = update;
            this.routerName = routerName;
        }

        @Override
        @SuppressWarnings("checkstyle:IllegalCatch")
        public List> call() {
            final List> futures = new ArrayList<>();
            String interfaceName = update.getName();
            IntfTransitionState state = getTransitionState(original.getOperStatus(), update.getOperStatus());
            if (state.equals(IntfTransitionState.STATE_IGNORE)) {
                LOG.info("NAT Service: Interface {} state original {} updated {} not handled",
                        interfaceName, original.getOperStatus(), update.getOperStatus());
                return futures;
            }
            if (state.equals(IntfTransitionState.STATE_UP)) {
                LOG.debug("call : Port UP event received for interface {} ", interfaceName);
            } else if (state.equals(IntfTransitionState.STATE_DOWN)) {
                LOG.debug("call : Port DOWN event received for interface {} ", interfaceName);
                try {
                    if (NatUtil.isSnatEnabledForRouterId(dataBroker, routerName)) {
                        NatUtil.removeSnatEntriesForPort(dataBroker, naptManager, mdsalManager, neutronVpnService,
                            interfaceName, routerName);
                    }
                } catch (Exception ex) {
                    LOG.error("call : Exception caught in Interface {} OperationalStateDown", interfaceName, ex);
                }
            }
            return futures;
        }
    }

    private class NatFlowRemoveWorker implements Callable>> {
        private final String interfaceName;
        private final String routerName;
        private final BigInteger intfDpnId;

        NatFlowRemoveWorker(String interfaceName, BigInteger intfDpnId, String routerName) {
            this.interfaceName = interfaceName;
            this.routerName = routerName;
            this.intfDpnId = intfDpnId;
        }

        @Override
        @SuppressWarnings("checkstyle:IllegalCatch")
        public List> call() {
            final List> futures = new ArrayList<>();
            LOG.trace("call : Interface {} removed event received", interfaceName);
            try {
                LOG.trace("call : Port removed event received for interface {} ", interfaceName);
                processInterfaceRemoved(interfaceName, intfDpnId, routerName, futures);
                if (NatUtil.isSnatEnabledForRouterId(dataBroker, routerName)) {
                    NatUtil.removeSnatEntriesForPort(dataBroker, naptManager, mdsalManager, neutronVpnService,
                        interfaceName, routerName);
                }
            } catch (Exception e) {
                LOG.error("call : Exception caught in Interface {} OperationalStateRemove", interfaceName, e);
            }
            return futures;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy