Update MRI projects for Aluminium
[bgpcep.git] / bgp / rib-impl / src / main / java / org / opendaylight / protocol / bgp / rib / impl / RIBImpl.java
1 /*
2  * Copyright (c) 2013 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 import static org.opendaylight.protocol.bgp.rib.spi.RIBNodeIdentifiers.BGPRIB_NID;
12 import static org.opendaylight.protocol.bgp.rib.spi.RIBNodeIdentifiers.LOCRIB_NID;
13 import static org.opendaylight.protocol.bgp.rib.spi.RIBNodeIdentifiers.PEER_NID;
14 import static org.opendaylight.protocol.bgp.rib.spi.RIBNodeIdentifiers.RIB_NID;
15 import static org.opendaylight.protocol.bgp.rib.spi.RIBNodeIdentifiers.TABLES_NID;
16
17 import com.google.common.base.MoreObjects;
18 import com.google.common.collect.ImmutableSet;
19 import com.google.common.util.concurrent.FluentFuture;
20 import com.google.common.util.concurrent.FutureCallback;
21 import com.google.common.util.concurrent.MoreExecutors;
22 import java.util.HashMap;
23 import java.util.HashSet;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.Set;
27 import java.util.concurrent.ExecutionException;
28 import org.checkerframework.checker.lock.qual.GuardedBy;
29 import org.opendaylight.mdsal.binding.api.DataBroker;
30 import org.opendaylight.mdsal.binding.api.Transaction;
31 import org.opendaylight.mdsal.binding.api.TransactionChain;
32 import org.opendaylight.mdsal.binding.api.TransactionChainListener;
33 import org.opendaylight.mdsal.common.api.CommitInfo;
34 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
35 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
36 import org.opendaylight.mdsal.dom.api.DOMDataBrokerExtension;
37 import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeService;
38 import org.opendaylight.mdsal.dom.api.DOMDataTreeTransaction;
39 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
40 import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
41 import org.opendaylight.mdsal.dom.api.DOMTransactionChainListener;
42 import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceRegistration;
43 import org.opendaylight.protocol.bgp.mode.api.PathSelectionMode;
44 import org.opendaylight.protocol.bgp.mode.impl.base.BasePathSelectionModeFactory;
45 import org.opendaylight.protocol.bgp.openconfig.spi.BGPTableTypeRegistryConsumer;
46 import org.opendaylight.protocol.bgp.rib.impl.spi.BGPDispatcher;
47 import org.opendaylight.protocol.bgp.rib.impl.spi.CodecsRegistry;
48 import org.opendaylight.protocol.bgp.rib.impl.spi.RIB;
49 import org.opendaylight.protocol.bgp.rib.impl.spi.RIBSupportContextRegistry;
50 import org.opendaylight.protocol.bgp.rib.impl.spi.RibOutRefresh;
51 import org.opendaylight.protocol.bgp.rib.impl.state.BGPRibStateImpl;
52 import org.opendaylight.protocol.bgp.rib.spi.BGPPeerTracker;
53 import org.opendaylight.protocol.bgp.rib.spi.RIBExtensionConsumerContext;
54 import org.opendaylight.protocol.bgp.rib.spi.RIBSupport;
55 import org.opendaylight.protocol.bgp.rib.spi.policy.BGPRibRoutingPolicy;
56 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.AsNumber;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.BgpTableType;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.BgpRib;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.PeerId;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.RibId;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.Route;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.Rib;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.RibKey;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.Tables;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.TablesKey;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.tables.Routes;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev200120.BgpId;
68 import org.opendaylight.yangtools.yang.binding.ChildOf;
69 import org.opendaylight.yangtools.yang.binding.ChoiceIn;
70 import org.opendaylight.yangtools.yang.binding.DataObject;
71 import org.opendaylight.yangtools.yang.binding.Identifiable;
72 import org.opendaylight.yangtools.yang.binding.Identifier;
73 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
74 import org.opendaylight.yangtools.yang.common.QName;
75 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
76 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.InstanceIdentifierBuilder;
77 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
78 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
79 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
80 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
81 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
82 import org.slf4j.Logger;
83 import org.slf4j.LoggerFactory;
84
85 // This class is thread-safe
86 public final class RIBImpl extends BGPRibStateImpl implements RIB, TransactionChainListener,
87         DOMTransactionChainListener, AutoCloseable {
88     private static final Logger LOG = LoggerFactory.getLogger(RIBImpl.class);
89     private static final QName RIB_ID_QNAME = QName.create(Rib.QNAME, "id").intern();
90
91     private final BGPDispatcher dispatcher;
92     private final AsNumber localAs;
93     private final BgpId bgpIdentifier;
94     private final Set<BgpTableType> localTables;
95     private final Set<TablesKey> localTablesKeys;
96     private final DOMDataBroker domDataBroker;
97     private final DataBroker dataBroker;
98     private final RIBExtensionConsumerContext extensions;
99     private final YangInstanceIdentifier yangRibId;
100     private final RIBSupportContextRegistryImpl ribContextRegistry;
101     private final CodecsRegistry codecsRegistry;
102     private final BGPTableTypeRegistryConsumer tableTypeRegistry;
103     private final DOMDataBrokerExtension domService;
104     private final Map<TransactionChain, LocRibWriter> txChainToLocRibWriter = new HashMap<>();
105     private final Map<TablesKey, PathSelectionMode> bestPathSelectionStrategies;
106     private final RibId ribId;
107     private final BGPPeerTracker peerTracker = new BGPPeerTrackerImpl();
108     private final BGPRibRoutingPolicy ribPolicies;
109     @GuardedBy("this")
110     private ClusterSingletonServiceRegistration registration;
111     @GuardedBy("this")
112     private DOMTransactionChain domChain;
113     @GuardedBy("this")
114     private boolean isServiceInstantiated;
115     private final Map<TablesKey, RibOutRefresh> vpnTableRefresher = new HashMap<>();
116
117     public RIBImpl(
118             final BGPTableTypeRegistryConsumer tableTypeRegistry,
119             final RibId ribId,
120             final AsNumber localAs,
121             final BgpId localBgpId,
122             final RIBExtensionConsumerContext extensions,
123             final BGPDispatcher dispatcher,
124             final CodecsRegistry codecsRegistry,
125             final DOMDataBroker domDataBroker,
126             final DataBroker dataBroker,
127             final BGPRibRoutingPolicy ribPolicies,
128             final List<BgpTableType> localTables,
129             final Map<TablesKey, PathSelectionMode> bestPathSelectionStrategies
130     ) {
131         super(InstanceIdentifier.create(BgpRib.class).child(Rib.class, new RibKey(requireNonNull(ribId))),
132                 localBgpId, localAs);
133         this.tableTypeRegistry = requireNonNull(tableTypeRegistry);
134         this.localAs = requireNonNull(localAs);
135         this.bgpIdentifier = requireNonNull(localBgpId);
136         this.dispatcher = requireNonNull(dispatcher);
137         this.localTables = ImmutableSet.copyOf(localTables);
138         this.localTablesKeys = new HashSet<>();
139         this.domDataBroker = requireNonNull(domDataBroker);
140         this.dataBroker = requireNonNull(dataBroker);
141         this.domService = this.domDataBroker.getExtensions().get(DOMDataTreeChangeService.class);
142         this.extensions = requireNonNull(extensions);
143         this.ribPolicies = requireNonNull(ribPolicies);
144         this.codecsRegistry = codecsRegistry;
145         this.ribContextRegistry = RIBSupportContextRegistryImpl.create(extensions, this.codecsRegistry);
146         this.yangRibId = YangInstanceIdentifier.builder().node(BGPRIB_NID).node(RIB_NID)
147                 .nodeWithKey(Rib.QNAME, RIB_ID_QNAME, ribId.getValue()).build();
148         this.bestPathSelectionStrategies = requireNonNull(bestPathSelectionStrategies);
149         this.ribId = ribId;
150
151         for (final BgpTableType t : this.localTables) {
152             final TablesKey key = new TablesKey(t.getAfi(), t.getSafi());
153             this.localTablesKeys.add(key);
154         }
155     }
156
157     private synchronized void startLocRib(final TablesKey key) {
158         LOG.debug("Creating LocRib table for {}", key);
159         // create locRibWriter for each table
160         final DOMDataTreeWriteTransaction tx = this.domChain.newWriteOnlyTransaction();
161
162         final RIBSupport<? extends Routes, ?, ?, ?> ribSupport = this.ribContextRegistry.getRIBSupport(key);
163         if (ribSupport != null) {
164             final MapEntryNode emptyTable = ribSupport.emptyTable();
165             final InstanceIdentifierBuilder tableId = YangInstanceIdentifier
166                     .builder(this.yangRibId.node(LOCRIB_NID).node(TABLES_NID)).node(emptyTable.getIdentifier());
167
168             tx.put(LogicalDatastoreType.OPERATIONAL, tableId.build(), emptyTable);
169             try {
170                 tx.commit().get();
171             } catch (final InterruptedException | ExecutionException e1) {
172                 LOG.error("Failed to initiate LocRIB for key {}", key, e1);
173             }
174         } else {
175             LOG.warn("There's no registered RIB Context for {}", key.getAfi());
176         }
177     }
178
179     private synchronized <C extends Routes & DataObject & ChoiceIn<Tables>, S extends ChildOf<? super C>,
180             R extends Route & ChildOf<? super S> & Identifiable<I>, I extends Identifier<R>>
181             void createLocRibWriter(final TablesKey key) {
182         final RIBSupport<C, S, R, I> ribSupport = this.ribContextRegistry.getRIBSupport(key);
183         if (ribSupport == null) {
184             return;
185         }
186         LOG.debug("Creating LocRIB writer for key {}", key);
187         final TransactionChain txChain = createPeerChain(this);
188         PathSelectionMode pathSelectionStrategy = this.bestPathSelectionStrategies.get(key);
189         if (pathSelectionStrategy == null) {
190             pathSelectionStrategy = BasePathSelectionModeFactory.createBestPathSelectionStrategy();
191         }
192
193         final LocRibWriter<C, S, R, I> locRibWriter = LocRibWriter.create(
194                 ribSupport,
195                 this.tableTypeRegistry.getAfiSafiType(key).get(),
196                 txChain,
197                 getInstanceIdentifier(),
198                 this.localAs,
199                 getDataBroker(),
200                 this.ribPolicies,
201                 this.peerTracker,
202                 pathSelectionStrategy);
203         this.vpnTableRefresher.put(key, locRibWriter);
204         registerTotalPathCounter(key, locRibWriter);
205         registerTotalPrefixesCounter(key, locRibWriter);
206         this.txChainToLocRibWriter.put(txChain, locRibWriter);
207     }
208
209     @Override
210     public String toString() {
211         return MoreObjects.toStringHelper(this).add("bgpId", bgpIdentifier).add("localTables", localTables).toString();
212     }
213
214     @Override
215     public synchronized void close() {
216         if (this.registration != null) {
217             this.registration.close();
218             this.registration = null;
219         }
220     }
221
222     @Override
223     public AsNumber getLocalAs() {
224         return this.localAs;
225     }
226
227     @Override
228     public BgpId getBgpIdentifier() {
229         return this.bgpIdentifier;
230     }
231
232     @Override
233     public Set<? extends BgpTableType> getLocalTables() {
234         return this.localTables;
235     }
236
237     @Override
238     public BGPDispatcher getDispatcher() {
239         return this.dispatcher;
240     }
241
242     @Override
243     public synchronized void onTransactionChainFailed(final TransactionChain chain,
244             final Transaction transaction, final Throwable cause) {
245         LOG.error("Broken chain in RIB {} transaction {}",
246                 getInstanceIdentifier(), transaction != null ? transaction.getIdentifier() : null, cause);
247         if (this.txChainToLocRibWriter.containsKey(chain)) {
248             final LocRibWriter locRibWriter = this.txChainToLocRibWriter.remove(chain);
249             final TransactionChain newChain = createPeerChain(this);
250             startLocRib(locRibWriter.getTableKey());
251             locRibWriter.restart(newChain);
252             this.txChainToLocRibWriter.put(newChain, locRibWriter);
253         }
254     }
255
256     @Override
257     public synchronized void onTransactionChainFailed(final DOMTransactionChain chain,
258             final DOMDataTreeTransaction transaction, final Throwable cause) {
259         LOG.error("Broken chain in RIB {} transaction {}",
260             getInstanceIdentifier(), transaction != null ? transaction.getIdentifier() : null, cause);
261     }
262
263     @Override
264     public void onTransactionChainSuccessful(final TransactionChain chain) {
265         LOG.info("RIB {} closed successfully", getInstanceIdentifier());
266     }
267
268     @Override
269     public void onTransactionChainSuccessful(final DOMTransactionChain chain) {
270         LOG.info("RIB {} closed successfully", getInstanceIdentifier());
271     }
272
273     @Override
274     public Set<TablesKey> getLocalTablesKeys() {
275         return this.localTablesKeys;
276     }
277
278     @Override
279     public boolean supportsTable(final TablesKey tableKey) {
280         return this.localTablesKeys.contains(tableKey);
281     }
282
283     @Override
284     public BGPRibRoutingPolicy getRibPolicies() {
285         return this.ribPolicies;
286     }
287
288     @Override
289     public BGPPeerTracker getPeerTracker() {
290         return this.peerTracker;
291     }
292
293     @Override
294     public void refreshTable(final TablesKey tk, final PeerId peerId) {
295         final RibOutRefresh table = this.vpnTableRefresher.get(tk);
296         if (table != null) {
297             table.refreshTable(tk, peerId);
298         }
299     }
300
301     @Override
302     public DOMDataTreeChangeService getService() {
303         return (DOMDataTreeChangeService) this.domService;
304     }
305
306     @Override
307     public DataBroker getDataBroker() {
308         return this.dataBroker;
309     }
310
311     @Override
312     public YangInstanceIdentifier getYangRibId() {
313         return this.yangRibId;
314     }
315
316     @Override
317     public TransactionChain createPeerChain(final TransactionChainListener listener) {
318         return this.dataBroker.createMergingTransactionChain(listener);
319     }
320
321     @Override
322     public DOMTransactionChain createPeerDOMChain(final DOMTransactionChainListener listener) {
323         return this.domDataBroker.createMergingTransactionChain(listener);
324     }
325
326     @Override
327     public RIBExtensionConsumerContext getRibExtensions() {
328         return this.extensions;
329     }
330
331     @Override
332     public RIBSupportContextRegistry getRibSupportContext() {
333         return this.ribContextRegistry;
334     }
335
336     @Override
337     public CodecsRegistry getCodecsRegistry() {
338         return this.codecsRegistry;
339     }
340
341     public synchronized void instantiateServiceInstance() {
342         this.isServiceInstantiated = true;
343         setActive(true);
344         this.domChain = this.domDataBroker.createMergingTransactionChain(this);
345         LOG.debug("Instantiating RIB table {} at {}", this.ribId, this.yangRibId);
346
347         final ContainerNode bgpRib = Builders.containerBuilder().withNodeIdentifier(BGPRIB_NID)
348                 .addChild(ImmutableNodes.mapNodeBuilder(RIB_NID).build()).build();
349
350         final MapEntryNode ribInstance = Builders.mapEntryBuilder().withNodeIdentifier(
351                 NodeIdentifierWithPredicates.of(Rib.QNAME, RIB_ID_QNAME, this.ribId.getValue()))
352                 .addChild(ImmutableNodes.leafNode(RIB_ID_QNAME, this.ribId.getValue()))
353                 .addChild(ImmutableNodes.mapNodeBuilder(PEER_NID).build())
354                 .addChild(Builders.containerBuilder().withNodeIdentifier(LOCRIB_NID)
355                         .addChild(ImmutableNodes.mapNodeBuilder(TABLES_NID).build())
356                         .build()).build();
357
358         final DOMDataTreeWriteTransaction trans = this.domChain.newWriteOnlyTransaction();
359
360         // merge empty BgpRib + Rib, to make sure the top-level parent structure is present
361         trans.merge(LogicalDatastoreType.OPERATIONAL, YangInstanceIdentifier.create(BGPRIB_NID), bgpRib);
362         trans.put(LogicalDatastoreType.OPERATIONAL, this.yangRibId, ribInstance);
363
364         try {
365             trans.commit().get();
366         } catch (final InterruptedException | ExecutionException e) {
367             LOG.error("Failed to initiate RIB {}", this.yangRibId, e);
368         }
369
370         LOG.debug("Effective RIB created.");
371
372         this.localTablesKeys.forEach(this::startLocRib);
373         this.localTablesKeys.forEach(this::createLocRibWriter);
374     }
375
376     public synchronized FluentFuture<? extends CommitInfo> closeServiceInstance() {
377         if (!this.isServiceInstantiated) {
378             LOG.trace("RIB {} already closed", this.ribId.getValue());
379             return CommitInfo.emptyFluentFuture();
380         }
381         LOG.info("Close RIB {}", this.ribId.getValue());
382         this.isServiceInstantiated = false;
383         setActive(false);
384
385         this.txChainToLocRibWriter.values().forEach(LocRibWriter::close);
386         this.txChainToLocRibWriter.clear();
387
388         final DOMDataTreeWriteTransaction t = this.domChain.newWriteOnlyTransaction();
389         t.delete(LogicalDatastoreType.OPERATIONAL, getYangRibId());
390         final FluentFuture<? extends CommitInfo> cleanFuture = t.commit();
391         cleanFuture.addCallback(new FutureCallback<CommitInfo>() {
392             @Override
393             public void onSuccess(final CommitInfo result) {
394                 LOG.info("RIB cleaned {}", RIBImpl.this.ribId.getValue());
395             }
396
397             @Override
398             public void onFailure(final Throwable throwable) {
399                 LOG.error("Failed to clean RIB {}",
400                         RIBImpl.this.ribId.getValue(), throwable);
401             }
402         }, MoreExecutors.directExecutor());
403         this.domChain.close();
404         return cleanFuture;
405     }
406 }