4c6e5faf8a5bb7c740f7ff215c3979d591ba2bbe
[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
12 import com.google.common.base.MoreObjects;
13 import com.google.common.collect.ImmutableSet;
14 import com.google.common.util.concurrent.FluentFuture;
15 import com.google.common.util.concurrent.FutureCallback;
16 import com.google.common.util.concurrent.MoreExecutors;
17 import java.util.HashMap;
18 import java.util.HashSet;
19 import java.util.List;
20 import java.util.Map;
21 import java.util.Set;
22 import java.util.concurrent.ExecutionException;
23 import javax.annotation.Nonnull;
24 import javax.annotation.concurrent.GuardedBy;
25 import javax.annotation.concurrent.ThreadSafe;
26 import org.opendaylight.controller.md.sal.binding.api.BindingTransactionChain;
27 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
28 import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
29 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
30 import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
31 import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
32 import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
33 import org.opendaylight.controller.md.sal.dom.api.DOMDataBrokerExtension;
34 import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeService;
35 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
36 import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
37 import org.opendaylight.mdsal.common.api.CommitInfo;
38 import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceRegistration;
39 import org.opendaylight.protocol.bgp.mode.api.PathSelectionMode;
40 import org.opendaylight.protocol.bgp.mode.impl.base.BasePathSelectionModeFactory;
41 import org.opendaylight.protocol.bgp.openconfig.spi.BGPTableTypeRegistryConsumer;
42 import org.opendaylight.protocol.bgp.rib.impl.spi.BGPDispatcher;
43 import org.opendaylight.protocol.bgp.rib.impl.spi.CodecsRegistry;
44 import org.opendaylight.protocol.bgp.rib.impl.spi.RIB;
45 import org.opendaylight.protocol.bgp.rib.impl.spi.RIBSupportContextRegistry;
46 import org.opendaylight.protocol.bgp.rib.impl.spi.RibOutRefresh;
47 import org.opendaylight.protocol.bgp.rib.impl.state.BGPRIBStateImpl;
48 import org.opendaylight.protocol.bgp.rib.spi.BGPPeerTracker;
49 import org.opendaylight.protocol.bgp.rib.spi.RIBExtensionConsumerContext;
50 import org.opendaylight.protocol.bgp.rib.spi.RIBSupport;
51 import org.opendaylight.protocol.bgp.rib.spi.policy.BGPRibRoutingPolicy;
52 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.AsNumber;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.BgpTableType;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.BgpRib;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.PeerId;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.RibId;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.Route;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.Rib;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.RibKey;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.rib.LocRib;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.rib.Peer;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.Tables;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.TablesKey;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.tables.Routes;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.BgpId;
66 import org.opendaylight.yangtools.yang.binding.ChildOf;
67 import org.opendaylight.yangtools.yang.binding.ChoiceIn;
68 import org.opendaylight.yangtools.yang.binding.DataObject;
69 import org.opendaylight.yangtools.yang.binding.Identifiable;
70 import org.opendaylight.yangtools.yang.binding.Identifier;
71 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
72 import org.opendaylight.yangtools.yang.common.QName;
73 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
74 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.InstanceIdentifierBuilder;
75 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
76 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
77 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
78 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
79 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
80 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
81 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
82 import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
83 import org.slf4j.Logger;
84 import org.slf4j.LoggerFactory;
85
86 @ThreadSafe
87 public final class RIBImpl extends BGPRIBStateImpl implements RIB, TransactionChainListener,
88         SchemaContextListener, AutoCloseable {
89     private static final Logger LOG = LoggerFactory.getLogger(RIBImpl.class);
90     private static final QName RIB_ID_QNAME = QName.create(Rib.QNAME, "id").intern();
91
92     private final BGPDispatcher dispatcher;
93     private final AsNumber localAs;
94     private final BgpId bgpIdentifier;
95     private final Set<BgpTableType> localTables;
96     private final Set<TablesKey> localTablesKeys;
97     private final DOMDataBroker domDataBroker;
98     private final DataBroker dataBroker;
99     private final RIBExtensionConsumerContext extensions;
100     private final YangInstanceIdentifier yangRibId;
101     private final RIBSupportContextRegistryImpl ribContextRegistry;
102     private final CodecsRegistryImpl codecsRegistry;
103     private final BGPTableTypeRegistryConsumer tableTypeRegistry;
104     private final DOMDataBrokerExtension domService;
105     private final Map<TransactionChain<?, ?>, LocRibWriter> txChainToLocRibWriter = new HashMap<>();
106     private final Map<TablesKey, PathSelectionMode> bestPathSelectionStrategies;
107     private final RibId ribId;
108     private final BGPPeerTracker peerTracker = new BGPPeerTrackerImpl();
109     private final BGPRibRoutingPolicy ribPolicies;
110     @GuardedBy("this")
111     private ClusterSingletonServiceRegistration registration;
112     @GuardedBy("this")
113     private DOMTransactionChain domChain;
114     @GuardedBy("this")
115     private boolean isServiceInstantiated;
116     private final Map<TablesKey, RibOutRefresh> vpnTableRefresher = new HashMap<>();
117
118     public RIBImpl(
119             final BGPTableTypeRegistryConsumer tableTypeRegistry,
120             final RibId ribId,
121             final AsNumber localAs,
122             final BgpId localBgpId,
123             final RIBExtensionConsumerContext extensions,
124             final BGPDispatcher dispatcher,
125             final CodecsRegistryImpl codecsRegistry,
126             final DOMDataBroker domDataBroker,
127             final DataBroker dataBroker,
128             final BGPRibRoutingPolicy ribPolicies,
129             final List<BgpTableType> localTables,
130             final Map<TablesKey, PathSelectionMode> bestPathSelectionStrategies
131     ) {
132         super(InstanceIdentifier.create(BgpRib.class).child(Rib.class, new RibKey(requireNonNull(ribId))),
133                 localBgpId, localAs);
134         this.tableTypeRegistry = requireNonNull(tableTypeRegistry);
135         this.localAs = requireNonNull(localAs);
136         this.bgpIdentifier = requireNonNull(localBgpId);
137         this.dispatcher = requireNonNull(dispatcher);
138         this.localTables = ImmutableSet.copyOf(localTables);
139         this.localTablesKeys = new HashSet<>();
140         this.domDataBroker = requireNonNull(domDataBroker);
141         this.dataBroker = requireNonNull(dataBroker);
142         this.domService = this.domDataBroker.getSupportedExtensions().get(DOMDataTreeChangeService.class);
143         this.extensions = requireNonNull(extensions);
144         this.ribPolicies = requireNonNull(ribPolicies);
145         this.codecsRegistry = codecsRegistry;
146         this.ribContextRegistry = RIBSupportContextRegistryImpl.create(extensions, this.codecsRegistry);
147         final InstanceIdentifierBuilder yangRibIdBuilder = YangInstanceIdentifier.builder().node(BgpRib.QNAME).node(Rib.QNAME);
148         this.yangRibId = yangRibIdBuilder.nodeWithKey(Rib.QNAME, RIB_ID_QNAME, ribId.getValue()).build();
149         this.bestPathSelectionStrategies = requireNonNull(bestPathSelectionStrategies);
150         this.ribId = ribId;
151
152         for (final BgpTableType t : this.localTables) {
153             final TablesKey key = new TablesKey(t.getAfi(), t.getSafi());
154             this.localTablesKeys.add(key);
155         }
156     }
157
158     private synchronized void startLocRib(final TablesKey key) {
159         LOG.debug("Creating LocRib table for {}", key);
160         // create locRibWriter for each table
161         final DOMDataWriteTransaction tx = this.domChain.newWriteOnlyTransaction();
162
163         final RIBSupport<? extends Routes, ?, ?, ?> ribSupport = this.ribContextRegistry.getRIBSupport(key);
164         if (ribSupport != null) {
165             final MapEntryNode emptyTable = ribSupport.emptyTable();
166             final InstanceIdentifierBuilder tableId = YangInstanceIdentifier
167                     .builder(this.yangRibId.node(LocRib.QNAME).node(Tables.QNAME)).node(emptyTable.getIdentifier());
168
169             tx.put(LogicalDatastoreType.OPERATIONAL, tableId.build(), emptyTable);
170             try {
171                 tx.commit().get();
172             } catch (final InterruptedException | ExecutionException e1) {
173                 LOG.error("Failed to initiate LocRIB for key {}", key, e1);
174             }
175         } else {
176             LOG.warn("There's no registered RIB Context for {}", key.getAfi());
177         }
178     }
179
180     private synchronized <C extends Routes & DataObject & ChoiceIn<Tables>, S extends ChildOf<? super C>,
181             R extends Route & ChildOf<? super S> & Identifiable<I>, I extends Identifier<R>>
182             void createLocRibWriter(final TablesKey key) {
183         final RIBSupport<C, S, R, I> ribSupport = this.ribContextRegistry.getRIBSupport(key);
184         if (ribSupport == null) {
185             return;
186         }
187         LOG.debug("Creating LocRIB writer for key {}", key);
188         final BindingTransactionChain txChain = createPeerChain(this);
189         PathSelectionMode pathSelectionStrategy = this.bestPathSelectionStrategies.get(key);
190         if (pathSelectionStrategy == null) {
191             pathSelectionStrategy = BasePathSelectionModeFactory.createBestPathSelectionStrategy();
192         }
193
194         final LocRibWriter locRibWriter = LocRibWriter.create(
195                 ribSupport,
196                 key,
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() throws Exception {
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     @Nonnull
235     @Override
236     public Set<? extends BgpTableType> getLocalTables() {
237         return this.localTables;
238     }
239
240     @Override
241     public BGPDispatcher getDispatcher() {
242         return this.dispatcher;
243     }
244
245     @Override
246     public synchronized void onTransactionChainFailed(final TransactionChain<?, ?> chain,
247             final AsyncTransaction<?, ?> transaction, final Throwable cause) {
248         LOG.error("Broken chain in RIB {} transaction {}",
249                 getInstanceIdentifier(), transaction != null ? transaction.getIdentifier() : null, cause);
250         if (this.txChainToLocRibWriter.containsKey(chain)) {
251             final LocRibWriter locRibWriter = this.txChainToLocRibWriter.remove(chain);
252             final BindingTransactionChain newChain = createPeerChain(this);
253             startLocRib(locRibWriter.getTableKey());
254             locRibWriter.restart(newChain);
255             this.txChainToLocRibWriter.put(newChain, locRibWriter);
256         }
257     }
258
259     @Override
260     public void onTransactionChainSuccessful(final TransactionChain<?, ?> chain) {
261         LOG.info("RIB {} closed successfully", getInstanceIdentifier());
262     }
263
264     @Override
265     public Set<TablesKey> getLocalTablesKeys() {
266         return this.localTablesKeys;
267     }
268
269     @Override
270     public boolean supportsTable(final TablesKey tableKey) {
271         return this.localTablesKeys.contains(tableKey);
272     }
273
274     @Override
275     public BGPRibRoutingPolicy getRibPolicies() {
276         return this.ribPolicies;
277     }
278
279     @Override
280     public BGPPeerTracker getPeerTracker() {
281         return this.peerTracker;
282     }
283
284     @Override
285     public void refreshTable(final TablesKey tk, final PeerId peerId) {
286         final RibOutRefresh table = this.vpnTableRefresher.get(tk);
287         if (table != null) {
288             table.refreshTable(tk, peerId);
289         }
290     }
291
292     @Override
293     public DOMDataTreeChangeService getService() {
294         return (DOMDataTreeChangeService) this.domService;
295     }
296
297     @Override
298     public DataBroker getDataBroker() {
299         return this.dataBroker;
300     }
301
302     @Override
303     public YangInstanceIdentifier getYangRibId() {
304         return this.yangRibId;
305     }
306
307     @Override
308     public BindingTransactionChain createPeerChain(final TransactionChainListener listener) {
309         return this.dataBroker.createTransactionChain(listener);
310     }
311
312     @Override
313     public DOMTransactionChain createPeerDOMChain(final TransactionChainListener listener) {
314         return this.domDataBroker.createTransactionChain(listener);
315     }
316
317     @Override
318     public RIBExtensionConsumerContext getRibExtensions() {
319         return this.extensions;
320     }
321
322     @Override
323     public RIBSupportContextRegistry getRibSupportContext() {
324         return this.ribContextRegistry;
325     }
326
327     @Override
328     public void onGlobalContextUpdated(final SchemaContext context) {
329         this.codecsRegistry.onSchemaContextUpdated(context);
330     }
331
332     @Override
333     public CodecsRegistry getCodecsRegistry() {
334         return this.codecsRegistry;
335     }
336
337     public synchronized void instantiateServiceInstance() {
338         this.isServiceInstantiated = true;
339         setActive(true);
340         this.domChain = this.domDataBroker.createTransactionChain(this);
341         LOG.debug("Instantiating RIB table {} at {}", this.ribId, this.yangRibId);
342
343         final ContainerNode bgpRib = Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(BgpRib.QNAME))
344                 .addChild(ImmutableNodes.mapNodeBuilder(Rib.QNAME).build()).build();
345
346         final MapEntryNode ribInstance = Builders.mapEntryBuilder().withNodeIdentifier(
347                 new NodeIdentifierWithPredicates(Rib.QNAME, RIB_ID_QNAME, this.ribId.getValue()))
348                 .addChild(ImmutableNodes.leafNode(RIB_ID_QNAME, this.ribId.getValue()))
349                 .addChild(ImmutableNodes.mapNodeBuilder(Peer.QNAME).build())
350                 .addChild(Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(LocRib.QNAME))
351                         .addChild(ImmutableNodes.mapNodeBuilder(Tables.QNAME).build())
352                         .build()).build();
353
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.builder().node(BgpRib.QNAME).build(), 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 }