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