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