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