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

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

There is a newer version: 0.22.4
Show newest version
/*
 * Copyright (c) 2014 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.verify;
import static java.util.Objects.requireNonNull;
import static org.opendaylight.protocol.bgp.rib.spi.RIBNodeIdentifiers.ADJRIBIN_NID;
import static org.opendaylight.protocol.bgp.rib.spi.RIBNodeIdentifiers.ADJRIBOUT_NID;
import static org.opendaylight.protocol.bgp.rib.spi.RIBNodeIdentifiers.PEER_NID;
import static org.opendaylight.protocol.bgp.rib.spi.RIBNodeIdentifiers.ROUTES_NID;
import static org.opendaylight.protocol.bgp.rib.spi.RIBNodeIdentifiers.TABLES_NID;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.util.concurrent.FluentFuture;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.MoreExecutors;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.opendaylight.mdsal.common.api.CommitInfo;
import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
import org.opendaylight.mdsal.dom.api.DOMDataBroker.DataTreeChangeExtension;
import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeListener;
import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
import org.opendaylight.protocol.bgp.openconfig.spi.BGPTableTypeRegistryConsumer;
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.state.BGPSessionStateImpl;
import org.opendaylight.protocol.bgp.rib.spi.IdentifierUtils;
import org.opendaylight.protocol.bgp.rib.spi.RIBNodeIdentifiers;
import org.opendaylight.protocol.bgp.rib.spi.RouterIds;
import org.opendaylight.protocol.bgp.rib.spi.state.BGPSessionState;
import org.opendaylight.protocol.bgp.rib.spi.state.BGPTimersState;
import org.opendaylight.protocol.bgp.rib.spi.state.BGPTransportState;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddressNoZone;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4AddressNoZone;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.SendReceive;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.ApplicationRibId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.PeerRole;
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.types.rev200120.RouteTarget;
import org.opendaylight.yangtools.concepts.Registration;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.data.tree.api.DataTreeCandidate;
import org.opendaylight.yangtools.yang.data.tree.api.DataTreeCandidateNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Application Peer is a special case of BGP peer. It serves as an interface
 * for user to advertise user routes to ODL and through ODL to other BGP peers.
 *
 * 

* This peer has it's own RIB, where it stores all user routes. This RIB is * located in configurational datastore. Routes are added through RESTCONF. * *

* They are then processed as routes from any other peer, through AdjRib, * EffectiveRib,LocRib and if they are advertised further, through AdjRibOut. * *

* For purposed of import policies such as Best Path Selection, application * peer needs to have a BGP-ID that is configurable. */ public class ApplicationPeer extends AbstractPeer implements DOMDataTreeChangeListener { @FunctionalInterface interface RegisterAppPeerListener { /** * Register Application Peer Change Listener once AdjRibIn has been successfully initialized. */ void register(); } private static final Logger LOG = LoggerFactory.getLogger(ApplicationPeer.class); private static final String APP_PEER_GROUP = "application-peers"; private final LoadingCache tablesIId = CacheBuilder.newBuilder().build(new CacheLoader() { @Override public YangInstanceIdentifier load(final NodeIdentifierWithPredicates key) { return peerRibOutIId.node(RIBNodeIdentifiers.TABLES_NID).node(key); } }); private final YangInstanceIdentifier adjRibsInId; private final YangInstanceIdentifier peerRibOutIId; private final BGPTableTypeRegistryConsumer tableTypeRegistry; private EffectiveRibInWriter effectiveRibInWriter; private AdjRibInWriter adjRibInWriter; private Registration registration; private final Set supportedTables = new HashSet<>(); private final BGPSessionStateImpl bgpSessionState = new BGPSessionStateImpl(); private Registration trackerRegistration; private YangInstanceIdentifier peerPath; public ApplicationPeer( final BGPTableTypeRegistryConsumer tableTypeRegistry, final ApplicationRibId applicationRibId, final Ipv4AddressNoZone ipAddress, final RIB rib) { super(rib, applicationRibId.getValue(), APP_PEER_GROUP, PeerRole.Internal, null, null, new IpAddressNoZone(ipAddress), rib.getLocalTablesKeys(), Set.of(), Map.of()); this.tableTypeRegistry = requireNonNull(tableTypeRegistry); peerId = RouterIds.createPeerId(ipAddress); final var peerRib = rib.getYangRibId().node(PEER_NID).node(IdentifierUtils.domPeerId(peerId)); adjRibsInId = peerRib.node(ADJRIBIN_NID).node(TABLES_NID).toOptimized(); peerRibOutIId = peerRib.node(ADJRIBOUT_NID).node(TABLES_NID).toOptimized(); createDomChain(); } public synchronized void instantiateServiceInstance(final DataTreeChangeExtension dataTreeChangeService, final DOMDataTreeIdentifier appPeerDOMId) { setActive(true); final var localTables = rib.getLocalTablesKeys(); for (var localTable : localTables) { final var tableSupport = rib.getRibSupportContext().getRIBSupport(localTable); if (tableSupport != null) { supportedTables.add(tableSupport.tablesKey()); } else { LOG.warn("Ignoring unsupported table {}", localTable); } } setAdvertizedGracefulRestartTableTypes(List.of()); createDomChain(); adjRibInWriter = AdjRibInWriter.create(rib.getYangRibId(), PeerRole.Internal, this); final RIBSupportContextRegistry context = rib.getRibSupportContext(); peerPath = createPeerPath(peerId); adjRibInWriter = adjRibInWriter.transform(peerId, peerPath, context, localTables, Map.of(), () -> { synchronized (this) { if (getDomChain() != null) { registration = dataTreeChangeService.registerTreeChangeListener(appPeerDOMId, this); } } }); final var chain = rib.createPeerDOMChain(); chain.addCallback(this); effectiveRibInWriter = new EffectiveRibInWriter(this, rib, chain, peerPath, localTables, tableTypeRegistry, new ArrayList<>(), rtCache); effectiveRibInWriter.init(); bgpSessionState.registerMessagesCounter(this); trackerRegistration = rib.getPeerTracker().registerPeer(this); } @Override public List getMemberships() { return List.of(); } @Override public void onInitialData() { // FIXME: we really want to (under a synchronized block) to ensure adj-rib-in is completely empty here. // Unfortunately that bit is already being done somewhere else. The entire the tables are being created // elsewhere and therefore we need to reign in lifecycle first } /** * Routes come from application RIB that is identified by (configurable) name. * Each route is pushed into AdjRibsInWriter with it's whole context. In this * method, it doesn't matter if the routes are removed or added, this will * be determined in LocRib. */ @Override public synchronized void onDataTreeChanged(final List changes) { final DOMTransactionChain chain = getDomChain(); if (chain == null) { LOG.trace("Skipping data changed called to Application Peer. Change : {}", changes); return; } final DOMDataTreeWriteTransaction tx = chain.newWriteOnlyTransaction(); LOG.debug("Received data change to ApplicationRib {}", changes); for (final DataTreeCandidate tc : changes) { LOG.debug("Modification Type {}", tc.getRootNode().modificationType()); final YangInstanceIdentifier path = tc.getRootPath(); final PathArgument lastArg = path.getLastPathArgument(); verify(lastArg instanceof NodeIdentifierWithPredicates, "Unexpected type %s in path %s", lastArg.getClass(), path); final NodeIdentifierWithPredicates tableKey = (NodeIdentifierWithPredicates) lastArg; if (!supportedTables.contains(tableKey)) { LOG.trace("Skipping received data change for non supported family {}.", tableKey); continue; } for (final DataTreeCandidateNode child : tc.getRootNode().childNodes()) { final PathArgument childIdentifier = child.name(); final YangInstanceIdentifier tableId = adjRibsInId.node(tableKey).node(childIdentifier); switch (child.modificationType()) { case DELETE: case DISAPPEARED: LOG.trace("App peer -> AdjRibsIn path delete: {}", childIdentifier); tx.delete(LogicalDatastoreType.OPERATIONAL, tableId); break; case UNMODIFIED: // No-op break; case SUBTREE_MODIFIED: if (ROUTES_NID.equals(childIdentifier)) { processRoutesTable(child, tableId, tx, tableId); } else { processWrite(child, tableId, tx); } break; case WRITE: case APPEARED: processWrite(child, tableId, tx); break; default: break; } } } tx.commit().addCallback(new FutureCallback() { @Override public void onSuccess(final CommitInfo result) { LOG.trace("Successful commit"); } @Override public void onFailure(final Throwable trw) { LOG.error("Failed commit", trw); } }, MoreExecutors.directExecutor()); } private static void processWrite(final DataTreeCandidateNode child, final YangInstanceIdentifier tableId, final DOMDataTreeWriteTransaction tx) { final var dataAfter = child.dataAfter(); if (dataAfter != null) { LOG.trace("App peer -> AdjRibsIn path : {}", tableId); LOG.trace("App peer -> AdjRibsIn data : {}", dataAfter); tx.put(LogicalDatastoreType.OPERATIONAL, tableId, dataAfter); } } private synchronized void processRoutesTable(final DataTreeCandidateNode node, final YangInstanceIdentifier identifier, final DOMDataTreeWriteTransaction tx, final YangInstanceIdentifier routeTableIdentifier) { for (var child : node.childNodes()) { final YangInstanceIdentifier childIdentifier = identifier.node(child.name()); switch (child.modificationType()) { case DELETE: LOG.trace("App peer -> AdjRibsIn path delete: {}", childIdentifier); tx.delete(LogicalDatastoreType.OPERATIONAL, childIdentifier); break; case UNMODIFIED: // No-op break; case SUBTREE_MODIFIED: // For be ables to use DELETE when we remove specific routes as we do when we remove the whole // routes, we need to go deeper three levels if (!routeTableIdentifier.equals(childIdentifier.getParent().getParent().getParent())) { processRoutesTable(child, childIdentifier, tx, routeTableIdentifier); } else { processRouteWrite(child, childIdentifier, tx); } break; case WRITE: processRouteWrite(child, childIdentifier, tx); break; default: break; } } } private static void processRouteWrite(final DataTreeCandidateNode child, final YangInstanceIdentifier childIdentifier, final DOMDataTreeWriteTransaction tx) { final var dataAfter = child.dataAfter(); if (dataAfter != null) { LOG.trace("App peer -> AdjRibsIn path : {}", childIdentifier); LOG.trace("App peer -> AdjRibsIn data : {}", dataAfter); tx.put(LogicalDatastoreType.OPERATIONAL, childIdentifier, dataAfter); } } @Override public synchronized FluentFuture close() { setActive(false); if (registration != null) { registration.close(); registration = null; } if (adjRibInWriter != null) { adjRibInWriter.releaseChain(); } if (effectiveRibInWriter != null) { effectiveRibInWriter.close(); } final FluentFuture future = removePeer(peerPath); closeDomChain(); if (trackerRegistration != null) { trackerRegistration.close(); trackerRegistration = null; } return future; } @Override public boolean supportsAddPathSupported(final TablesKey tableKey) { return false; } @Override public SendReceive getSupportedAddPathTables(final TablesKey tableKey) { return null; } @Override public boolean supportsTable(final TablesKey tableKey) { return rib.supportsTable(tableKey); } @Override public YangInstanceIdentifier getRibOutIId(final NodeIdentifierWithPredicates tablekey) { return tablesIId.getUnchecked(tablekey); } @Override public void onFailure(final Throwable cause) { LOG.error("Transaction chain failed.", cause); } @Override public BGPSessionState getBGPSessionState() { return bgpSessionState; } @Override public BGPTimersState getBGPTimersState() { return bgpSessionState; } @Override public BGPTransportState getBGPTransportState() { return bgpSessionState; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy