96e55c8d92fc28c69affce072e119df2d48e7b64
[bgpcep.git] / bgp / rib-impl / src / main / java / org / opendaylight / protocol / bgp / rib / impl / ApplicationPeer.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.protocol.bgp.rib.impl;
9
10 import static java.util.Objects.requireNonNull;
11
12 import com.google.common.base.Verify;
13 import com.google.common.cache.CacheBuilder;
14 import com.google.common.cache.CacheLoader;
15 import com.google.common.cache.LoadingCache;
16 import com.google.common.net.InetAddresses;
17 import com.google.common.util.concurrent.FluentFuture;
18 import com.google.common.util.concurrent.FutureCallback;
19 import com.google.common.util.concurrent.MoreExecutors;
20 import java.util.ArrayList;
21 import java.util.Collection;
22 import java.util.Collections;
23 import java.util.HashSet;
24 import java.util.List;
25 import java.util.Set;
26 import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
27 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
28 import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
29 import org.opendaylight.controller.md.sal.dom.api.ClusteredDOMDataTreeChangeListener;
30 import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeService;
31 import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeIdentifier;
32 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
33 import org.opendaylight.mdsal.common.api.CommitInfo;
34 import org.opendaylight.protocol.bgp.openconfig.spi.BGPTableTypeRegistryConsumer;
35 import org.opendaylight.protocol.bgp.rib.impl.spi.RIB;
36 import org.opendaylight.protocol.bgp.rib.impl.spi.RIBSupportContextRegistry;
37 import org.opendaylight.protocol.bgp.rib.impl.state.BGPSessionStateImpl;
38 import org.opendaylight.protocol.bgp.rib.spi.IdentifierUtils;
39 import org.opendaylight.protocol.bgp.rib.spi.RibSupportUtils;
40 import org.opendaylight.protocol.bgp.rib.spi.RouterIds;
41 import org.opendaylight.protocol.bgp.rib.spi.state.BGPSessionState;
42 import org.opendaylight.protocol.bgp.rib.spi.state.BGPTimersState;
43 import org.opendaylight.protocol.bgp.rib.spi.state.BGPTransportState;
44 import org.opendaylight.protocol.concepts.AbstractRegistration;
45 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
46 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.SendReceive;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.ApplicationRibId;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.PeerRole;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.rib.Peer;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.rib.PeerKey;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.rib.peer.AdjRibIn;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.rib.peer.AdjRibOut;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.Tables;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.TablesKey;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.RouteTarget;
57 import org.opendaylight.yangtools.concepts.ListenerRegistration;
58 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
59 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
60 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
61 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
62 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
63 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
64 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
65 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
66 import org.slf4j.Logger;
67 import org.slf4j.LoggerFactory;
68
69 /**
70  * Application Peer is a special case of BGP peer. It serves as an interface
71  * for user to advertise user routes to ODL and through ODL to other BGP peers.
72  *
73  * This peer has it's own RIB, where it stores all user routes. This RIB is
74  * located in configurational datastore. Routes are added through RESTCONF.
75  *
76  * They are then processed as routes from any other peer, through AdjRib,
77  * EffectiveRib,LocRib and if they are advertised further, through AdjRibOut.
78  *
79  * For purposed of import policies such as Best Path Selection, application
80  * peer needs to have a BGP-ID that is configurable.
81  */
82 public class ApplicationPeer extends AbstractPeer implements ClusteredDOMDataTreeChangeListener {
83
84     private static final Logger LOG = LoggerFactory.getLogger(ApplicationPeer.class);
85
86     private static final String APP_PEER_GROUP = "application-peers";
87     private final YangInstanceIdentifier adjRibsInId;
88     private final InstanceIdentifier<AdjRibOut> peerRibOutIId;
89     private final KeyedInstanceIdentifier<Peer, PeerKey> peerIId;
90     private final BGPTableTypeRegistryConsumer tableTypeRegistry;
91     private EffectiveRibInWriter effectiveRibInWriter;
92     private AdjRibInWriter adjRibInWriter;
93     private ListenerRegistration<ApplicationPeer> registration;
94     private final Set<NodeIdentifierWithPredicates> supportedTables = new HashSet<>();
95     private final BGPSessionStateImpl bgpSessionState = new BGPSessionStateImpl();
96     private final LoadingCache<TablesKey, KeyedInstanceIdentifier<Tables, TablesKey>> tablesIId
97             = CacheBuilder.newBuilder()
98             .build(new CacheLoader<TablesKey, KeyedInstanceIdentifier<Tables, TablesKey>>() {
99                 @Override
100                 public KeyedInstanceIdentifier<Tables, TablesKey> load(final TablesKey tablesKey) {
101                     return ApplicationPeer.this.peerRibOutIId.child(Tables.class, tablesKey);
102                 }
103             });
104     private AbstractRegistration trackerRegistration;
105     private YangInstanceIdentifier peerPath;
106
107     @Override
108     public List<RouteTarget> getMemberships() {
109         return Collections.emptyList();
110     }
111
112     @FunctionalInterface
113     interface RegisterAppPeerListener {
114         /**
115          * Register Application Peer Change Listener once AdjRibIn has been successfully initialized.
116          */
117         void register();
118     }
119
120     public ApplicationPeer(
121             final BGPTableTypeRegistryConsumer tableTypeRegistry,
122             final ApplicationRibId applicationRibId, final Ipv4Address ipAddress, final RIB rib) {
123         super(rib, applicationRibId.getValue(), APP_PEER_GROUP, PeerRole.Internal,
124                 new IpAddress(ipAddress), Collections.emptySet());
125         this.tableTypeRegistry = requireNonNull(tableTypeRegistry);
126         final RIB targetRib = requireNonNull(rib);
127         this.rawIdentifier = InetAddresses.forString(ipAddress.getValue()).getAddress();
128         this.adjRibsInId = targetRib.getYangRibId().node(Peer.QNAME)
129                 .node(IdentifierUtils.domPeerId(RouterIds.createPeerId(ipAddress)))
130                 .node(AdjRibIn.QNAME).node(Tables.QNAME);
131         this.peerId = RouterIds.createPeerId(ipAddress);
132         this.peerIId = getInstanceIdentifier().child(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns
133                 .yang.bgp.rib.rev180329.bgp.rib.rib.Peer.class, new PeerKey(this.peerId));
134         this.peerRibOutIId = this.peerIId.child(AdjRibOut.class);
135     }
136
137     public synchronized void instantiateServiceInstance(final DOMDataTreeChangeService dataTreeChangeService,
138             final DOMDataTreeIdentifier appPeerDOMId) {
139         setActive(true);
140         final Set<TablesKey> localTables = this.rib.getLocalTablesKeys();
141         localTables.forEach(tablesKey -> this.supportedTables.add(RibSupportUtils.toYangTablesKey(tablesKey)));
142         setAdvertizedGracefulRestartTableTypes(Collections.emptyList());
143
144         this.adjRibInWriter = AdjRibInWriter.create(this.rib.getYangRibId(), PeerRole.Internal, this);
145         final RIBSupportContextRegistry context = this.rib.getRibSupportContext();
146         final RegisterAppPeerListener registerAppPeerListener = () -> {
147             synchronized (this) {
148                 if (getDomChain() != null) {
149                     this.registration = dataTreeChangeService.registerDataTreeChangeListener(appPeerDOMId, this);
150                 }
151             }
152         };
153         this.peerPath = createPeerPath();
154         this.adjRibInWriter = this.adjRibInWriter.transform(this.peerId, this.peerPath, context, localTables,
155                 Collections.emptyMap(), registerAppPeerListener);
156         this.effectiveRibInWriter = new EffectiveRibInWriter(this, this.rib,
157                 this.rib.createPeerChain(this), this.peerIId, localTables, this.tableTypeRegistry,
158                 new ArrayList<>(), this.rtCache);
159         this.effectiveRibInWriter.init();
160         this.bgpSessionState.registerMessagesCounter(this);
161         this.trackerRegistration = this.rib.getPeerTracker().registerPeer(this);
162     }
163
164     /**
165      * Routes come from application RIB that is identified by (configurable) name.
166      * Each route is pushed into AdjRibsInWriter with it's whole context. In this
167      * method, it doesn't matter if the routes are removed or added, this will
168      * be determined in LocRib.
169      */
170     @Override
171     public synchronized void onDataTreeChanged(final Collection<DataTreeCandidate> changes) {
172         if (getDomChain() == null) {
173             LOG.trace("Skipping data changed called to Application Peer. Change : {}", changes);
174             return;
175         }
176         final DOMDataWriteTransaction tx = getDomChain().newWriteOnlyTransaction();
177         LOG.debug("Received data change to ApplicationRib {}", changes);
178         for (final DataTreeCandidate tc : changes) {
179             LOG.debug("Modification Type {}", tc.getRootNode().getModificationType());
180             final YangInstanceIdentifier path = tc.getRootPath();
181             final PathArgument lastArg = path.getLastPathArgument();
182             Verify.verify(lastArg instanceof NodeIdentifierWithPredicates,
183                     "Unexpected type %s in path %s", lastArg.getClass(), path);
184             final NodeIdentifierWithPredicates tableKey = (NodeIdentifierWithPredicates) lastArg;
185             if (!this.supportedTables.contains(tableKey)) {
186                 LOG.trace("Skipping received data change for non supported family {}.", tableKey);
187                 continue;
188             }
189             for (final DataTreeCandidateNode child : tc.getRootNode().getChildNodes()) {
190                 final PathArgument childIdentifier = child.getIdentifier();
191                 final YangInstanceIdentifier tableId = this.adjRibsInId.node(tableKey).node(childIdentifier);
192                 switch (child.getModificationType()) {
193                     case DELETE:
194                     case DISAPPEARED:
195                         LOG.trace("App peer -> AdjRibsIn path delete: {}", childIdentifier);
196                         tx.delete(LogicalDatastoreType.OPERATIONAL, tableId);
197                         break;
198                     case UNMODIFIED:
199                         // No-op
200                         break;
201                     case SUBTREE_MODIFIED:
202                         if (EffectiveRibInWriter.TABLE_ROUTES.equals(childIdentifier)) {
203                             processRoutesTable(child, tableId, tx, tableId);
204                             break;
205                         }
206                     case WRITE:
207                     case APPEARED:
208                         if (child.getDataAfter().isPresent()) {
209                             final NormalizedNode<?, ?> dataAfter = child.getDataAfter().get();
210                             LOG.trace("App peer -> AdjRibsIn path : {}", tableId);
211                             LOG.trace("App peer -> AdjRibsIn data : {}", dataAfter);
212                             tx.put(LogicalDatastoreType.OPERATIONAL, tableId, dataAfter);
213                         }
214                         break;
215                     default:
216                         break;
217                 }
218             }
219         }
220         tx.commit().addCallback(new FutureCallback<CommitInfo>() {
221             @Override
222             public void onSuccess(final CommitInfo result) {
223                 LOG.trace("Successful commit");
224             }
225
226             @Override
227             public void onFailure(final Throwable trw) {
228                 LOG.error("Failed commit", trw);
229             }
230         }, MoreExecutors.directExecutor());
231     }
232
233     private synchronized void processRoutesTable(final DataTreeCandidateNode node,
234             final YangInstanceIdentifier identifier, final DOMDataWriteTransaction tx,
235             final YangInstanceIdentifier routeTableIdentifier) {
236         for (final DataTreeCandidateNode child : node.getChildNodes()) {
237             final YangInstanceIdentifier childIdentifier = identifier.node(child.getIdentifier());
238             switch (child.getModificationType()) {
239                 case DELETE:
240                     LOG.trace("App peer -> AdjRibsIn path delete: {}", childIdentifier);
241                     tx.delete(LogicalDatastoreType.OPERATIONAL, childIdentifier);
242                     break;
243                 case UNMODIFIED:
244                     // No-op
245                     break;
246                 case SUBTREE_MODIFIED:
247                     //For be ables to use DELETE when we remove specific routes as we do when we remove the whole routes,
248                     // we need to go deeper three levels
249                     if (!routeTableIdentifier.equals(childIdentifier.getParent().getParent().getParent())) {
250                         processRoutesTable(child, childIdentifier, tx, routeTableIdentifier);
251                         break;
252                     }
253                 case WRITE:
254                     if (child.getDataAfter().isPresent()) {
255                         final NormalizedNode<?, ?> dataAfter = child.getDataAfter().get();
256                         LOG.trace("App peer -> AdjRibsIn path : {}", childIdentifier);
257                         LOG.trace("App peer -> AdjRibsIn data : {}", dataAfter);
258                         tx.put(LogicalDatastoreType.OPERATIONAL, childIdentifier, dataAfter);
259                     }
260                     break;
261                 default:
262                     break;
263             }
264         }
265     }
266
267     @Override
268     public synchronized FluentFuture<? extends CommitInfo> close() {
269         setActive(false);
270         if (this.registration != null) {
271             this.registration.close();
272             this.registration = null;
273         }
274         if (this.adjRibInWriter != null) {
275             this.adjRibInWriter.releaseChain();
276         }
277         if (this.effectiveRibInWriter != null) {
278             this.effectiveRibInWriter.close();
279         }
280         final FluentFuture<? extends CommitInfo> future;
281         future = removePeer(this.peerPath);
282         closeDomChain();
283         if (this.trackerRegistration != null) {
284             this.trackerRegistration.close();
285             this.trackerRegistration = null;
286         }
287         return future;
288     }
289
290     @Override
291     public boolean supportsAddPathSupported(final TablesKey tableKey) {
292         return false;
293     }
294
295     @Override
296     public SendReceive getSupportedAddPathTables(final TablesKey tableKey) {
297         return null;
298     }
299
300     @Override
301     public boolean supportsTable(final TablesKey tableKey) {
302         return this.rib.supportsTable(tableKey);
303     }
304
305     @Override
306     public KeyedInstanceIdentifier<Tables, TablesKey> getRibOutIId(final TablesKey tablesKey) {
307         return this.tablesIId.getUnchecked(tablesKey);
308     }
309
310     @Override
311     public void onTransactionChainFailed(final TransactionChain<?, ?> chain,
312         final AsyncTransaction<?, ?> transaction, final Throwable cause) {
313         LOG.error("Transaction chain {} failed.", transaction != null ? transaction.getIdentifier() : null, cause);
314     }
315
316     @Override
317     public BGPSessionState getBGPSessionState() {
318         return this.bgpSessionState;
319     }
320
321     @Override
322     public BGPTimersState getBGPTimersState() {
323         return this.bgpSessionState;
324     }
325
326     @Override
327     public BGPTransportState getBGPTransportState() {
328         return this.bgpSessionState;
329     }
330 }