BGPCEP-577: Neighbor’s local AS configurable
[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.Futures;
18 import com.google.common.util.concurrent.ListenableFuture;
19 import java.util.Arrays;
20 import java.util.Collection;
21 import java.util.Collections;
22 import java.util.HashSet;
23 import java.util.Set;
24 import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
25 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
26 import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
27 import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
28 import org.opendaylight.controller.md.sal.dom.api.ClusteredDOMDataTreeChangeListener;
29 import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeService;
30 import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeIdentifier;
31 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
32 import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
33 import org.opendaylight.protocol.bgp.rib.impl.spi.RIB;
34 import org.opendaylight.protocol.bgp.rib.impl.spi.RIBSupportContextRegistry;
35 import org.opendaylight.protocol.bgp.rib.impl.state.BGPPeerStateImpl;
36 import org.opendaylight.protocol.bgp.rib.impl.state.BGPSessionStateImpl;
37 import org.opendaylight.protocol.bgp.rib.spi.IdentifierUtils;
38 import org.opendaylight.protocol.bgp.rib.spi.RibSupportUtils;
39 import org.opendaylight.protocol.bgp.rib.spi.RouterIds;
40 import org.opendaylight.protocol.bgp.rib.spi.policy.BGPRouteEntryImportParameters;
41 import org.opendaylight.protocol.bgp.rib.spi.state.BGPAfiSafiState;
42 import org.opendaylight.protocol.bgp.rib.spi.state.BGPErrorHandlingState;
43 import org.opendaylight.protocol.bgp.rib.spi.state.BGPSessionState;
44 import org.opendaylight.protocol.bgp.rib.spi.state.BGPTimersState;
45 import org.opendaylight.protocol.bgp.rib.spi.state.BGPTransportState;
46 import org.opendaylight.protocol.concepts.AbstractRegistration;
47 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.AsNumber;
48 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
49 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev171207.SendReceive;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.ApplicationRibId;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.PeerId;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.PeerRole;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.bgp.rib.rib.Peer;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.bgp.rib.rib.PeerKey;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.bgp.rib.rib.peer.AdjRibIn;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.bgp.rib.rib.peer.AdjRibOut;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.rib.Tables;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.rib.TablesKey;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.ClusterIdentifier;
61 import org.opendaylight.yangtools.concepts.ListenerRegistration;
62 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
63 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
64 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
65 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
66 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
67 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
68 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
69 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
70 import org.slf4j.Logger;
71 import org.slf4j.LoggerFactory;
72
73 /**
74  * Application Peer is a special case of BGP peer. It serves as an interface
75  * for user to advertise user routes to ODL and through ODL to other BGP peers.
76  *
77  * This peer has it's own RIB, where it stores all user routes. This RIB is
78  * located in configurational datastore. Routes are added through RESTCONF.
79  *
80  * They are then processed as routes from any other peer, through AdjRib,
81  * EffectiveRib,LocRib and if they are advertised further, through AdjRibOut.
82  *
83  * For purposed of import policies such as Best Path Selection, application
84  * peer needs to have a BGP-ID that is configurable.
85  */
86 public class ApplicationPeer extends BGPPeerStateImpl implements org.opendaylight.protocol.bgp.rib.spi.Peer,
87         BGPRouteEntryImportParameters, ClusteredDOMDataTreeChangeListener, TransactionChainListener {
88
89     private static final Logger LOG = LoggerFactory.getLogger(ApplicationPeer.class);
90
91     private final byte[] rawIdentifier;
92     private final String name;
93     private final YangInstanceIdentifier adjRibsInId;
94     private final RIB rib;
95     private final InstanceIdentifier<AdjRibOut> peerRibOutIId;
96     private final KeyedInstanceIdentifier<Peer, PeerKey> peerIId;
97     private DOMTransactionChain chain;
98     private DOMTransactionChain writerChain;
99     private EffectiveRibInWriter effectiveRibInWriter;
100     private AdjRibInWriter adjRibInWriter;
101     private ListenerRegistration<ApplicationPeer> registration;
102     private final Set<NodeIdentifierWithPredicates> supportedTables = new HashSet<>();
103     private final BGPSessionStateImpl bgpSessionState = new BGPSessionStateImpl();
104     private final LoadingCache<TablesKey, KeyedInstanceIdentifier<Tables, TablesKey>> tablesIId
105             = CacheBuilder.newBuilder()
106             .build(new CacheLoader<TablesKey, KeyedInstanceIdentifier<Tables, TablesKey>>() {
107                 @Override
108                 public KeyedInstanceIdentifier<Tables, TablesKey> load(final TablesKey tablesKey) {
109                     return ApplicationPeer.this.peerRibOutIId.child(Tables.class, tablesKey);
110                 }
111             });
112     private final PeerId peerId;
113     private AbstractRegistration trackerRegistration;
114
115     @FunctionalInterface
116     interface RegisterAppPeerListener {
117         /**
118          * Register Application Peer Change Listener once AdjRibIn has been successfully initialized.
119          */
120         void register();
121     }
122
123     public ApplicationPeer(final ApplicationRibId applicationRibId, final Ipv4Address ipAddress, final RIB rib) {
124         super(rib.getInstanceIdentifier(), "application-peers", new IpAddress(ipAddress),
125                 rib.getLocalTablesKeys(), Collections.emptySet());
126         this.name = applicationRibId.getValue();
127         final RIB targetRib = requireNonNull(rib);
128         this.rawIdentifier = InetAddresses.forString(ipAddress.getValue()).getAddress();
129         final NodeIdentifierWithPredicates peerIId = IdentifierUtils.domPeerId(RouterIds.createPeerId(ipAddress));
130         this.adjRibsInId = targetRib.getYangRibId().node(Peer.QNAME).node(peerIId)
131                 .node(AdjRibIn.QNAME).node(Tables.QNAME);
132         this.rib = targetRib;
133         this.peerId = RouterIds.createPeerId(ipAddress);
134         this.peerIId = getInstanceIdentifier().child(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns
135                 .yang.bgp.rib.rev171207.bgp.rib.rib.Peer.class, new PeerKey(this.peerId));
136         this.peerRibOutIId = this.peerIId.child(AdjRibOut.class);
137     }
138
139     public synchronized void instantiateServiceInstance(final DOMDataTreeChangeService dataTreeChangeService,
140             final DOMDataTreeIdentifier appPeerDOMId) {
141         setActive(true);
142         this.chain = this.rib.createPeerDOMChain(this);
143         this.writerChain = this.rib.createPeerDOMChain(this);
144
145         final Set<TablesKey> localTables = this.rib.getLocalTablesKeys();
146         localTables.forEach(tablesKey -> {
147             this.supportedTables.add(RibSupportUtils.toYangTablesKey(tablesKey));
148         });
149         setAdvertizedGracefulRestartTableTypes(Collections.emptyList());
150
151         this.adjRibInWriter = AdjRibInWriter.create(this.rib.getYangRibId(), PeerRole.Internal, this.writerChain);
152         final RIBSupportContextRegistry context = this.rib.getRibSupportContext();
153         final RegisterAppPeerListener registerAppPeerListener = () -> {
154             synchronized (this) {
155                 if (this.chain != null) {
156                     this.registration = dataTreeChangeService.registerDataTreeChangeListener(appPeerDOMId, this);
157                 }
158             }
159         };
160         this.adjRibInWriter = this.adjRibInWriter.transform(this.peerId, context, localTables, Collections.emptyMap(),
161                 registerAppPeerListener);
162         this.effectiveRibInWriter = EffectiveRibInWriter
163                 .create(this, this.rib, this.rib.createPeerChain(this), this.peerIId, localTables);
164         this.bgpSessionState.registerMessagesCounter(this);
165         this.trackerRegistration = this.rib.getPeerTracker().registerPeer(this);
166     }
167
168     /**
169      * Routes come from application RIB that is identified by (configurable) name.
170      * Each route is pushed into AdjRibsInWriter with it's whole context. In this
171      * method, it doesn't matter if the routes are removed or added, this will
172      * be determined in LocRib.
173      */
174     @Override
175     public synchronized void onDataTreeChanged(final Collection<DataTreeCandidate> changes) {
176         if (this.chain == null) {
177             LOG.trace("Skipping data changed called to Application Peer. Change : {}", changes);
178             return;
179         }
180         final DOMDataWriteTransaction tx = this.chain.newWriteOnlyTransaction();
181         LOG.debug("Received data change to ApplicationRib {}", changes);
182         for (final DataTreeCandidate tc : changes) {
183             LOG.debug("Modification Type {}", tc.getRootNode().getModificationType());
184             final YangInstanceIdentifier path = tc.getRootPath();
185             final PathArgument lastArg = path.getLastPathArgument();
186             Verify.verify(lastArg instanceof NodeIdentifierWithPredicates,
187                     "Unexpected type %s in path %s", lastArg.getClass(), path);
188             final NodeIdentifierWithPredicates tableKey = (NodeIdentifierWithPredicates) lastArg;
189             if (!this.supportedTables.contains(tableKey)) {
190                 LOG.trace("Skipping received data change for non supported family {}.", tableKey);
191                 continue;
192             }
193             for (final DataTreeCandidateNode child : tc.getRootNode().getChildNodes()) {
194                 final PathArgument childIdentifier = child.getIdentifier();
195                 final YangInstanceIdentifier tableId = this.adjRibsInId.node(tableKey).node(childIdentifier);
196                 switch (child.getModificationType()) {
197                     case DELETE:
198                         LOG.trace("App peer -> AdjRibsIn path delete: {}", childIdentifier);
199                         tx.delete(LogicalDatastoreType.OPERATIONAL, tableId);
200                         break;
201                     case UNMODIFIED:
202                         // No-op
203                         break;
204                     case SUBTREE_MODIFIED:
205                         if (EffectiveRibInWriter.TABLE_ROUTES.equals(childIdentifier)) {
206                             processRoutesTable(child, tableId, tx, tableId);
207                             break;
208                         }
209                     case WRITE:
210                         if (child.getDataAfter().isPresent()) {
211                             final NormalizedNode<?, ?> dataAfter = child.getDataAfter().get();
212                             LOG.trace("App peer -> AdjRibsIn path : {}", tableId);
213                             LOG.trace("App peer -> AdjRibsIn data : {}", dataAfter);
214                             tx.put(LogicalDatastoreType.OPERATIONAL, tableId, dataAfter);
215                         }
216                         break;
217                     default:
218                         break;
219                 }
220             }
221         }
222         tx.submit();
223     }
224
225     private synchronized void processRoutesTable(final DataTreeCandidateNode node,
226             final YangInstanceIdentifier identifier, final DOMDataWriteTransaction tx,
227             final YangInstanceIdentifier routeTableIdentifier) {
228         for (final DataTreeCandidateNode child : node.getChildNodes()) {
229             final YangInstanceIdentifier childIdentifier = identifier.node(child.getIdentifier());
230             switch (child.getModificationType()) {
231                 case DELETE:
232                     LOG.trace("App peer -> AdjRibsIn path delete: {}", childIdentifier);
233                     tx.delete(LogicalDatastoreType.OPERATIONAL, childIdentifier);
234                     break;
235                 case UNMODIFIED:
236                     // No-op
237                     break;
238                 case SUBTREE_MODIFIED:
239                     //For be ables to use DELETE when we remove specific routes as we do when we remove the whole routes,
240                     // we need to go deeper three levels
241                     if (!routeTableIdentifier.equals(childIdentifier.getParent().getParent().getParent())) {
242                         processRoutesTable(child, childIdentifier, tx, routeTableIdentifier);
243                         break;
244                     }
245                 case WRITE:
246                     if (child.getDataAfter().isPresent()) {
247                         final NormalizedNode<?, ?> dataAfter = child.getDataAfter().get();
248                         LOG.trace("App peer -> AdjRibsIn path : {}", childIdentifier);
249                         LOG.trace("App peer -> AdjRibsIn data : {}", dataAfter);
250                         tx.put(LogicalDatastoreType.OPERATIONAL, childIdentifier, dataAfter);
251                     }
252                     break;
253                 default:
254                     break;
255             }
256         }
257     }
258
259     @Override
260     public String getName() {
261         return this.name;
262     }
263
264     // FIXME ListenableFuture<?> should be used once closeServiceInstance uses wildcard too
265     @Override
266     public synchronized ListenableFuture<Void> close() {
267         setActive(false);
268         if (this.registration != null) {
269             this.registration.close();
270             this.registration = null;
271         }
272         if (this.effectiveRibInWriter != null) {
273             this.effectiveRibInWriter.close();
274         }
275         final ListenableFuture<Void> future;
276         if (this.adjRibInWriter != null) {
277             future = this.adjRibInWriter.removePeer();
278         } else {
279             future = Futures.immediateFuture(null);
280         }
281         if (this.chain != null) {
282             this.chain.close();
283             this.chain = null;
284         }
285         if (this.writerChain != null) {
286             this.writerChain.close();
287             this.writerChain = null;
288         }
289         if (this.trackerRegistration != null) {
290             this.trackerRegistration.close();
291             this.trackerRegistration = null;
292         }
293         return future;
294     }
295
296     @Override
297     public byte[] getRawIdentifier() {
298         return Arrays.copyOf(this.rawIdentifier, this.rawIdentifier.length);
299     }
300
301     @Override
302     public PeerId getPeerId() {
303         return this.peerId;
304     }
305
306     @Override
307     public boolean supportsAddPathSupported(final TablesKey tableKey) {
308         return false;
309     }
310
311     @Override
312     public SendReceive getSupportedAddPathTables(final TablesKey tableKey) {
313         return null;
314     }
315
316     @Override
317     public boolean supportsTable(final TablesKey tableKey) {
318         return this.rib.supportsTable(tableKey);
319     }
320
321     @Override
322     public PeerRole getRole() {
323         return PeerRole.Internal;
324     }
325
326     @Override
327     public ClusterIdentifier getClusterId() {
328         return null;
329     }
330
331     @Override
332     public AsNumber getLocalAs() {
333         return null;
334     }
335
336     @Override
337     public KeyedInstanceIdentifier<Tables, TablesKey> getRibOutIId(final TablesKey tablesKey) {
338         return this.tablesIId.getUnchecked(tablesKey);
339     }
340
341     @Override
342     public void onTransactionChainFailed(final TransactionChain<?, ?> chain,
343         final AsyncTransaction<?, ?> transaction, final Throwable cause) {
344         LOG.error("Transaction chain {} failed.", transaction != null ? transaction.getIdentifier() : null, cause);
345     }
346
347     @Override
348     public void onTransactionChainSuccessful(final TransactionChain<?, ?> chain) {
349         LOG.debug("Transaction chain {} successful.", chain);
350     }
351
352     @Override
353     public BGPErrorHandlingState getBGPErrorHandlingState() {
354         return this;
355     }
356
357     @Override
358     public BGPAfiSafiState getBGPAfiSafiState() {
359         return this;
360     }
361
362     @Override
363     public BGPSessionState getBGPSessionState() {
364         return this.bgpSessionState;
365     }
366
367     @Override
368     public BGPTimersState getBGPTimersState() {
369         return this.bgpSessionState;
370     }
371
372     @Override
373     public BGPTransportState getBGPTransportState() {
374         return this.bgpSessionState;
375     }
376
377
378     @Override
379     public PeerRole getFromPeerRole() {
380         return getRole();
381     }
382
383     @Override
384     public PeerId getFromPeerId() {
385         return getPeerId();
386     }
387
388     @Override
389     public ClusterIdentifier getFromClusterId() {
390         return getClusterId();
391     }
392
393     @Override
394     public AsNumber getFromPeerLocalAs() {
395         return null;
396     }
397 }