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