Split Peer Ribout storage from loc rib
[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.Map.Entry;
22 import java.util.Set;
23 import java.util.concurrent.ExecutionException;
24 import javax.annotation.Nonnull;
25 import javax.annotation.concurrent.GuardedBy;
26 import javax.annotation.concurrent.ThreadSafe;
27 import org.opendaylight.controller.md.sal.binding.api.BindingTransactionChain;
28 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
29 import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
30 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
31 import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
32 import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
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.common.api.CommitInfo;
39 import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceRegistration;
40 import org.opendaylight.protocol.bgp.mode.api.PathSelectionMode;
41 import org.opendaylight.protocol.bgp.mode.impl.base.BasePathSelectionModeFactory;
42 import org.opendaylight.protocol.bgp.openconfig.spi.BGPTableTypeRegistryConsumer;
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.RIBSupportContextRegistry;
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.RibSupportUtils;
52 import org.opendaylight.protocol.bgp.rib.spi.policy.BGPRibRoutingPolicy;
53 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.AsNumber;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.BgpTableType;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.BgpRib;
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.bgp.rib.Rib;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.RibKey;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.rib.LocRib;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.rib.Peer;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.Tables;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.TablesKey;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.tables.Routes;
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     private final BGPTableTypeRegistryConsumer tableTypeRegistry;
102     private final DOMDataBrokerExtension domService;
103     private final Map<TransactionChain<?, ?>, LocRibWriter> txChainToLocRibWriter = new HashMap<>();
104     private final Map<TablesKey, PathSelectionMode> bestPathSelectionStrategies;
105     private final RibId ribId;
106     private final BGPPeerTracker peerTracker = new BGPPeerTrackerImpl();
107     private final BGPRibRoutingPolicy ribPolicies;
108     @GuardedBy("this")
109     private ClusterSingletonServiceRegistration registration;
110     @GuardedBy("this")
111     private DOMTransactionChain domChain;
112     @GuardedBy("this")
113     private boolean isServiceInstantiated;
114
115     public RIBImpl(
116             final BGPTableTypeRegistryConsumer tableTypeRegistry,
117             final RibId ribId,
118             final AsNumber localAs,
119             final BgpId localBgpId,
120             final RIBExtensionConsumerContext extensions,
121             final BGPDispatcher dispatcher,
122             final CodecsRegistryImpl codecsRegistry,
123             final DOMDataBroker domDataBroker,
124             final DataBroker dataBroker,
125             final BGPRibRoutingPolicy ribPolicies,
126             final List<BgpTableType> localTables,
127             final Map<TablesKey, PathSelectionMode> bestPathSelectionStrategies
128     ) {
129         super(InstanceIdentifier.create(BgpRib.class).child(Rib.class, new RibKey(requireNonNull(ribId))),
130                 localBgpId, localAs);
131         this.tableTypeRegistry = requireNonNull(tableTypeRegistry);
132         this.localAs = requireNonNull(localAs);
133         this.bgpIdentifier = requireNonNull(localBgpId);
134         this.dispatcher = requireNonNull(dispatcher);
135         this.localTables = ImmutableSet.copyOf(localTables);
136         this.localTablesKeys = new HashSet<>();
137         this.domDataBroker = requireNonNull(domDataBroker);
138         this.dataBroker = requireNonNull(dataBroker);
139         this.domService = this.domDataBroker.getSupportedExtensions().get(DOMDataTreeChangeService.class);
140         this.extensions = requireNonNull(extensions);
141         this.ribPolicies = requireNonNull(ribPolicies);
142         this.codecsRegistry = codecsRegistry;
143         this.ribContextRegistry = RIBSupportContextRegistryImpl.create(extensions, this.codecsRegistry);
144         final InstanceIdentifierBuilder yangRibIdBuilder = YangInstanceIdentifier.builder().node(BgpRib.QNAME).node(Rib.QNAME);
145         this.yangRibId = yangRibIdBuilder.nodeWithKey(Rib.QNAME, RIB_ID_QNAME, ribId.getValue()).build();
146         this.bestPathSelectionStrategies = requireNonNull(bestPathSelectionStrategies);
147         this.ribId = ribId;
148
149         for (final BgpTableType t : this.localTables) {
150             final TablesKey key = new TablesKey(t.getAfi(), t.getSafi());
151             this.localTablesKeys.add(key);
152         }
153     }
154
155     private synchronized void startLocRib(final TablesKey key) {
156         LOG.debug("Creating LocRib table for {}", key);
157         // create locRibWriter for each table
158         final DOMDataWriteTransaction tx = this.domChain.newWriteOnlyTransaction();
159
160         final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> table = ImmutableNodes.mapEntryBuilder();
161         table.withNodeIdentifier(RibSupportUtils.toYangTablesKey(key));
162         table.withChild(EMPTY_TABLE_ATTRIBUTES);
163
164         final NodeIdentifierWithPredicates tableKey = RibSupportUtils.toYangTablesKey(key);
165         final InstanceIdentifierBuilder tableId = YangInstanceIdentifier
166                 .builder(this.yangRibId.node(LocRib.QNAME).node(Tables.QNAME));
167         tableId.nodeWithKey(tableKey.getNodeType(), tableKey.getKeyValues());
168         for (final Entry<QName, Object> e : tableKey.getKeyValues().entrySet()) {
169             table.withChild(ImmutableNodes.leafNode(e.getKey(), e.getValue()));
170         }
171
172         final RIBSupport<? extends Routes, ?, ?, ?> ribSupport = this.ribContextRegistry.getRIBSupport(key);
173         if (ribSupport != null) {
174             final ChoiceNode routes = ribSupport.emptyRoutes();
175             table.withChild(routes);
176
177             tx.put(LogicalDatastoreType.OPERATIONAL, tableId.build(), table.build());
178             try {
179                 tx.commit().get();
180             } catch (final InterruptedException | ExecutionException e1) {
181                 LOG.error("Failed to initiate LocRIB for key {}", key, e1);
182             }
183         } else {
184             LOG.warn("There's no registered RIB Context for {}", key.getAfi());
185         }
186     }
187
188     private synchronized void createLocRibWriter(final TablesKey key) {
189         final RIBSupport<?, ?, ?, ?> ribSupport = this.ribContextRegistry.getRIBSupport(key);
190         if (ribSupport == null) {
191             return;
192         }
193         LOG.debug("Creating LocRIB writer for key {}", key);
194         final BindingTransactionChain txChain = createPeerChain(this);
195         PathSelectionMode pathSelectionStrategy = this.bestPathSelectionStrategies.get(key);
196         if (pathSelectionStrategy == null) {
197             pathSelectionStrategy = BasePathSelectionModeFactory.createBestPathSelectionStrategy();
198         }
199
200         final LocRibWriter locRibWriter = LocRibWriter.create(
201                 ribSupport,
202                 key,
203                 this.tableTypeRegistry.getAfiSafiType(key).get(),
204                 txChain,
205                 getInstanceIdentifier(),
206                 this.localAs,
207                 getDataBroker(),
208                 this.ribPolicies,
209                 this.peerTracker,
210                 pathSelectionStrategy);
211         registerTotalPathCounter(key, locRibWriter);
212         registerTotalPrefixesCounter(key, locRibWriter);
213         this.txChainToLocRibWriter.put(txChain, locRibWriter);
214     }
215
216     @Override
217     public String toString() {
218         return MoreObjects.toStringHelper(this).add("bgpId", bgpIdentifier).add("localTables", localTables).toString();
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.commit().get();
360         } catch (final InterruptedException | ExecutionException 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 FluentFuture<? extends CommitInfo> closeServiceInstance() {
371         if (!this.isServiceInstantiated) {
372             LOG.trace("RIB {} already closed", this.ribId.getValue());
373             return CommitInfo.emptyFluentFuture();
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 FluentFuture<? extends CommitInfo> cleanFuture = t.commit();
385         cleanFuture.addCallback(new FutureCallback<CommitInfo>() {
386             @Override
387             public void onSuccess(final CommitInfo result) {
388                 LOG.info("RIB cleaned {}", RIBImpl.this.ribId.getValue());
389             }
390
391             @Override
392             public void onFailure(final Throwable throwable) {
393                 LOG.error("Failed to clean RIB {}",
394                         RIBImpl.this.ribId.getValue(), throwable);
395             }
396         }, MoreExecutors.directExecutor());
397         this.domChain.close();
398         return cleanFuture;
399     }
400 }