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

org.opendaylight.protocol.bgp.rib.impl.RIBImpl Maven / Gradle / Ivy

There is a newer version: 0.22.4
Show newest version
/*
 * Copyright (c) 2013 Cisco Systems, Inc. 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.protocol.bgp.rib.impl;

import static com.google.common.base.Verify.verifyNotNull;
import static java.util.Objects.requireNonNull;
import static org.opendaylight.protocol.bgp.rib.spi.RIBNodeIdentifiers.BGPRIB_NID;
import static org.opendaylight.protocol.bgp.rib.spi.RIBNodeIdentifiers.LOCRIB_NID;
import static org.opendaylight.protocol.bgp.rib.spi.RIBNodeIdentifiers.PEER_NID;
import static org.opendaylight.protocol.bgp.rib.spi.RIBNodeIdentifiers.RIB_NID;
import static org.opendaylight.protocol.bgp.rib.spi.RIBNodeIdentifiers.TABLES_NID;

import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableSet;
import com.google.common.util.concurrent.FluentFuture;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.MoreExecutors;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;
import org.checkerframework.checker.lock.qual.GuardedBy;
import org.opendaylight.mdsal.common.api.CommitInfo;
import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
import org.opendaylight.mdsal.dom.api.DOMDataBroker;
import org.opendaylight.mdsal.dom.api.DOMDataBroker.DataTreeChangeExtension;
import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
import org.opendaylight.protocol.bgp.mode.api.PathSelectionMode;
import org.opendaylight.protocol.bgp.mode.impl.base.BasePathSelectionModeFactory;
import org.opendaylight.protocol.bgp.openconfig.spi.BGPTableTypeRegistryConsumer;
import org.opendaylight.protocol.bgp.rib.impl.spi.BGPDispatcher;
import org.opendaylight.protocol.bgp.rib.impl.spi.CodecsRegistry;
import org.opendaylight.protocol.bgp.rib.impl.spi.RIB;
import org.opendaylight.protocol.bgp.rib.impl.spi.RIBSupportContextRegistry;
import org.opendaylight.protocol.bgp.rib.impl.spi.RibOutRefresh;
import org.opendaylight.protocol.bgp.rib.impl.state.BGPRibStateImpl;
import org.opendaylight.protocol.bgp.rib.spi.BGPPeerTracker;
import org.opendaylight.protocol.bgp.rib.spi.RIBExtensionConsumerContext;
import org.opendaylight.protocol.bgp.rib.spi.RIBSupport;
import org.opendaylight.protocol.bgp.rib.spi.policy.BGPRibRoutingPolicy;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.AsNumber;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.BgpTableType;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.BgpRib;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.PeerId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.RibId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.Rib;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.RibKey;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.Tables;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.TablesKey;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.tables.Routes;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev200120.BgpId;
import org.opendaylight.yangtools.binding.ChildOf;
import org.opendaylight.yangtools.binding.ChoiceIn;
import org.opendaylight.yangtools.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.common.Empty;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.InstanceIdentifierBuilder;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
import org.opendaylight.yangtools.yang.data.spi.node.ImmutableNodes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

// This class is thread-safe
public final class RIBImpl extends BGPRibStateImpl implements RIB {
    private static final Logger LOG = LoggerFactory.getLogger(RIBImpl.class);
    private static final QName RIB_ID_QNAME = QName.create(Rib.QNAME, "id").intern();

    private final BGPDispatcher dispatcher;
    private final AsNumber localAs;
    private final BgpId bgpIdentifier;
    private final Set localTables;
    private final Set localTablesKeys;
    private final DOMDataBroker domDataBroker;
    private final RIBExtensionConsumerContext extensions;
    private final YangInstanceIdentifier yangRibId;
    private final RIBSupportContextRegistryImpl ribContextRegistry;
    private final CodecsRegistry codecsRegistry;
    private final BGPTableTypeRegistryConsumer tableTypeRegistry;
    private final DataTreeChangeExtension domService;
    private final Map> txChainToLocRibWriter = new HashMap<>();
    private final Map vpnTableRefresher = new HashMap<>();
    private final Map bestPathSelectionStrategies;
    private final RibId ribId;
    private final BGPPeerTracker peerTracker = new BGPPeerTrackerImpl();
    private final BGPRibRoutingPolicy ribPolicies;
    @GuardedBy("this")
    private DOMTransactionChain domChain;
    @GuardedBy("this")
    private boolean isServiceInstantiated;

    public RIBImpl(
            final BGPTableTypeRegistryConsumer tableTypeRegistry,
            final RibId ribId,
            final AsNumber localAs,
            final BgpId localBgpId,
            final RIBExtensionConsumerContext extensions,
            final BGPDispatcher dispatcher,
            final CodecsRegistry codecsRegistry,
            final DOMDataBroker domDataBroker,
            final BGPRibRoutingPolicy ribPolicies,
            final List localTables,
            final Map bestPathSelectionStrategies
    ) {
        super(InstanceIdentifier.create(BgpRib.class).child(Rib.class, new RibKey(requireNonNull(ribId))),
                localBgpId, localAs);
        this.tableTypeRegistry = requireNonNull(tableTypeRegistry);
        this.localAs = requireNonNull(localAs);
        bgpIdentifier = requireNonNull(localBgpId);
        this.dispatcher = requireNonNull(dispatcher);

        this.localTables = ImmutableSet.copyOf(localTables);
        // FIXME: can this be immutable?
        localTablesKeys = localTables.stream()
            .map(t -> new TablesKey(t.getAfi(), t.getSafi()))
            .collect(Collectors.toCollection(HashSet::new));

        this.domDataBroker = requireNonNull(domDataBroker);
        domService = domDataBroker.extension(DataTreeChangeExtension.class);
        this.extensions = requireNonNull(extensions);
        this.ribPolicies = requireNonNull(ribPolicies);
        this.codecsRegistry = codecsRegistry;
        ribContextRegistry = RIBSupportContextRegistryImpl.create(extensions, codecsRegistry);
        yangRibId = YangInstanceIdentifier.builder().node(BGPRIB_NID).node(RIB_NID)
                .nodeWithKey(Rib.QNAME, RIB_ID_QNAME, ribId.getValue()).build();
        this.bestPathSelectionStrategies = requireNonNull(bestPathSelectionStrategies);
        this.ribId = ribId;
    }

    // FIXME: make this asynchronous?
    private synchronized void startLocRib(final TablesKey key) {
        LOG.debug("Creating LocRib table for {}", key);
        // create locRibWriter for each table
        final DOMDataTreeWriteTransaction tx = domChain.newWriteOnlyTransaction();

        final RIBSupport ribSupport = ribContextRegistry.getRIBSupport(key);
        if (ribSupport != null) {
            final MapEntryNode emptyTable = ribSupport.emptyTable();
            final InstanceIdentifierBuilder tableId = YangInstanceIdentifier
                    .builder(yangRibId.node(LOCRIB_NID).node(TABLES_NID)).node(emptyTable.name());

            tx.put(LogicalDatastoreType.OPERATIONAL, tableId.build(), emptyTable);
            try {
                tx.commit().get();
            } catch (final InterruptedException | ExecutionException e) {
                LOG.error("Failed to initiate LocRIB for key {}", key, e);
            }
        } else {
            LOG.warn("There's no registered RIB Context for {}", key.getAfi());
        }
    }

    private synchronized , S extends ChildOf>
            void createLocRibWriter(final TablesKey key) {
        final RIBSupport ribSupport = ribContextRegistry.getRIBSupport(key);
        if (ribSupport == null) {
            return;
        }
        LOG.debug("Creating LocRIB writer for key {}", key);
        final DOMTransactionChain txChain = createPeerDOMChain();
        addCallback(txChain);
        PathSelectionMode pathSelectionStrategy = bestPathSelectionStrategies.get(key);
        if (pathSelectionStrategy == null) {
            pathSelectionStrategy = BasePathSelectionModeFactory.createBestPathSelectionStrategy();
        }

        final LocRibWriter locRibWriter = LocRibWriter.create(
                ribSupport,
                verifyNotNull(tableTypeRegistry.getAfiSafiType(key)),
                txChain,
                yangRibId,
                localAs,
                getService(),
                ribPolicies,
                peerTracker,
                pathSelectionStrategy);
        vpnTableRefresher.put(key, locRibWriter);
        registerTotalPathCounter(key, locRibWriter);
        registerTotalPrefixesCounter(key, locRibWriter);
        txChainToLocRibWriter.put(txChain, locRibWriter);
    }

    @Override
    public String toString() {
        return MoreObjects.toStringHelper(this).add("bgpId", bgpIdentifier).add("localTables", localTables).toString();
    }

    @Override
    public AsNumber getLocalAs() {
        return localAs;
    }

    @Override
    public BgpId getBgpIdentifier() {
        return bgpIdentifier;
    }

    @Override
    public Set getLocalTables() {
        return localTables;
    }

    @Override
    public BGPDispatcher getDispatcher() {
        return dispatcher;
    }

    private void addCallback(final DOMTransactionChain txChain) {
        txChain.addCallback(new FutureCallback() {
            @Override
            public void onSuccess(final Empty result) {
                LOG.info("RIB {} closed successfully", getInstanceIdentifier());
            }

            @Override
            public void onFailure(final Throwable cause) {
                RIBImpl.this.onFailure(txChain, cause);
            }
        });
    }

    private synchronized void onFailure(final DOMTransactionChain chain, final Throwable cause) {
        LOG.error("Broken chain in RIB {}", getInstanceIdentifier(), cause);
        final LocRibWriter locRibWriter = txChainToLocRibWriter.remove(chain);
        if (locRibWriter != null) {
            final DOMTransactionChain newChain = createPeerDOMChain();
            addCallback(newChain);
            startLocRib(locRibWriter.getTableKey());
            locRibWriter.restart(newChain);
            txChainToLocRibWriter.put(newChain, locRibWriter);
        }
    }

    @Override
    public Set getLocalTablesKeys() {
        return localTablesKeys;
    }

    @Override
    public boolean supportsTable(final TablesKey tableKey) {
        return localTablesKeys.contains(tableKey);
    }

    @Override
    public BGPRibRoutingPolicy getRibPolicies() {
        return ribPolicies;
    }

    @Override
    public BGPPeerTracker getPeerTracker() {
        return peerTracker;
    }

    @Override
    public void refreshTable(final TablesKey tk, final PeerId peerId) {
        final RibOutRefresh table = vpnTableRefresher.get(tk);
        if (table != null) {
            table.refreshTable(tk, peerId);
        }
    }

    @Override
    public DataTreeChangeExtension getService() {
        return domService;
    }

    @Override
    public YangInstanceIdentifier getYangRibId() {
        return yangRibId;
    }

    @Override
    public DOMTransactionChain createPeerDOMChain() {
        return domDataBroker.createMergingTransactionChain();
    }

    @Override
    public RIBExtensionConsumerContext getRibExtensions() {
        return extensions;
    }

    @Override
    public RIBSupportContextRegistry getRibSupportContext() {
        return ribContextRegistry;
    }

    @Override
    public CodecsRegistry getCodecsRegistry() {
        return codecsRegistry;
    }

    public synchronized void instantiateServiceInstance() {
        LOG.debug("Instantiating RIB table {} at {}", ribId, yangRibId);

        isServiceInstantiated = true;
        setActive(true);
        domChain = domDataBroker.createMergingTransactionChain();
        addCallback(domChain);

        final ContainerNode bgpRib = ImmutableNodes.newContainerBuilder().withNodeIdentifier(BGPRIB_NID)
                .addChild(ImmutableNodes.newSystemMapBuilder().withNodeIdentifier(RIB_NID).build()).build();

        final MapEntryNode ribInstance = ImmutableNodes.newMapEntryBuilder()
            .withNodeIdentifier(NodeIdentifierWithPredicates.of(Rib.QNAME, RIB_ID_QNAME, ribId.getValue()))
            .addChild(ImmutableNodes.leafNode(RIB_ID_QNAME, ribId.getValue()))
            .addChild(ImmutableNodes.newSystemMapBuilder().withNodeIdentifier(PEER_NID).build())
            .addChild(ImmutableNodes.newContainerBuilder().withNodeIdentifier(LOCRIB_NID)
                .addChild(ImmutableNodes.newSystemMapBuilder().withNodeIdentifier(TABLES_NID).build())
                .build())
            .build();

        final DOMDataTreeWriteTransaction trans = domChain.newWriteOnlyTransaction();

        // merge empty BgpRib + Rib, to make sure the top-level parent structure is present
        trans.merge(LogicalDatastoreType.OPERATIONAL, YangInstanceIdentifier.of(BGPRIB_NID), bgpRib);
        trans.put(LogicalDatastoreType.OPERATIONAL, yangRibId, ribInstance);

        try {
            trans.commit().get();
        } catch (final InterruptedException | ExecutionException e) {
            LOG.error("Failed to initiate RIB {}", yangRibId, e);
        }

        LOG.debug("Effective RIB created.");

        localTablesKeys.forEach(this::startLocRib);
        localTablesKeys.forEach(this::createLocRibWriter);
    }

    public synchronized FluentFuture closeServiceInstance() {
        if (!isServiceInstantiated) {
            LOG.trace("RIB {} already closed", ribId.getValue());
            return CommitInfo.emptyFluentFuture();
        }
        LOG.info("Close RIB {}", ribId.getValue());
        isServiceInstantiated = false;
        setActive(false);

        txChainToLocRibWriter.values().forEach(LocRibWriter::close);
        txChainToLocRibWriter.clear();

        final DOMDataTreeWriteTransaction t = domChain.newWriteOnlyTransaction();
        t.delete(LogicalDatastoreType.OPERATIONAL, getYangRibId());
        final FluentFuture cleanFuture = t.commit();
        cleanFuture.addCallback(new FutureCallback() {
            @Override
            public void onSuccess(final CommitInfo result) {
                LOG.info("RIB cleaned {}", ribId.getValue());
            }

            @Override
            public void onFailure(final Throwable throwable) {
                LOG.error("Failed to clean RIB {}",
                        ribId.getValue(), throwable);
            }
        }, MoreExecutors.directExecutor());
        domChain.close();
        return cleanFuture;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy