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