Bump upstreams
[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.impl.schema.Builders;
73 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
74 import org.slf4j.Logger;
75 import org.slf4j.LoggerFactory;
76
77 // This class is thread-safe
78 public final class RIBImpl extends BGPRibStateImpl implements RIB {
79     private static final Logger LOG = LoggerFactory.getLogger(RIBImpl.class);
80     private static final QName RIB_ID_QNAME = QName.create(Rib.QNAME, "id").intern();
81
82     private final BGPDispatcher dispatcher;
83     private final AsNumber localAs;
84     private final BgpId bgpIdentifier;
85     private final Set<BgpTableType> localTables;
86     private final Set<TablesKey> localTablesKeys;
87     private final DOMDataBroker domDataBroker;
88     private final RIBExtensionConsumerContext extensions;
89     private final YangInstanceIdentifier yangRibId;
90     private final RIBSupportContextRegistryImpl ribContextRegistry;
91     private final CodecsRegistry codecsRegistry;
92     private final BGPTableTypeRegistryConsumer tableTypeRegistry;
93     private final DataTreeChangeExtension domService;
94     private final Map<DOMTransactionChain, LocRibWriter<?, ?>> txChainToLocRibWriter = new HashMap<>();
95     private final Map<TablesKey, RibOutRefresh> vpnTableRefresher = new HashMap<>();
96     private final Map<TablesKey, PathSelectionMode> bestPathSelectionStrategies;
97     private final RibId ribId;
98     private final BGPPeerTracker peerTracker = new BGPPeerTrackerImpl();
99     private final BGPRibRoutingPolicy ribPolicies;
100     @GuardedBy("this")
101     private DOMTransactionChain domChain;
102     @GuardedBy("this")
103     private boolean isServiceInstantiated;
104
105     public RIBImpl(
106             final BGPTableTypeRegistryConsumer tableTypeRegistry,
107             final RibId ribId,
108             final AsNumber localAs,
109             final BgpId localBgpId,
110             final RIBExtensionConsumerContext extensions,
111             final BGPDispatcher dispatcher,
112             final CodecsRegistry codecsRegistry,
113             final DOMDataBroker domDataBroker,
114             final BGPRibRoutingPolicy ribPolicies,
115             final List<BgpTableType> localTables,
116             final Map<TablesKey, PathSelectionMode> bestPathSelectionStrategies
117     ) {
118         super(InstanceIdentifier.create(BgpRib.class).child(Rib.class, new RibKey(requireNonNull(ribId))),
119                 localBgpId, localAs);
120         this.tableTypeRegistry = requireNonNull(tableTypeRegistry);
121         this.localAs = requireNonNull(localAs);
122         bgpIdentifier = requireNonNull(localBgpId);
123         this.dispatcher = requireNonNull(dispatcher);
124
125         this.localTables = ImmutableSet.copyOf(localTables);
126         // FIXME: can this be immutable?
127         localTablesKeys = localTables.stream()
128             .map(t -> new TablesKey(t.getAfi(), t.getSafi()))
129             .collect(Collectors.toCollection(HashSet::new));
130
131         this.domDataBroker = requireNonNull(domDataBroker);
132         domService = domDataBroker.extension(DataTreeChangeExtension.class);
133         this.extensions = requireNonNull(extensions);
134         this.ribPolicies = requireNonNull(ribPolicies);
135         this.codecsRegistry = codecsRegistry;
136         ribContextRegistry = RIBSupportContextRegistryImpl.create(extensions, codecsRegistry);
137         yangRibId = YangInstanceIdentifier.builder().node(BGPRIB_NID).node(RIB_NID)
138                 .nodeWithKey(Rib.QNAME, RIB_ID_QNAME, ribId.getValue()).build();
139         this.bestPathSelectionStrategies = requireNonNull(bestPathSelectionStrategies);
140         this.ribId = ribId;
141     }
142
143     // FIXME: make this asynchronous?
144     private synchronized void startLocRib(final TablesKey key) {
145         LOG.debug("Creating LocRib table for {}", key);
146         // create locRibWriter for each table
147         final DOMDataTreeWriteTransaction tx = domChain.newWriteOnlyTransaction();
148
149         final RIBSupport<? extends Routes, ?> ribSupport = ribContextRegistry.getRIBSupport(key);
150         if (ribSupport != null) {
151             final MapEntryNode emptyTable = ribSupport.emptyTable();
152             final InstanceIdentifierBuilder tableId = YangInstanceIdentifier
153                     .builder(yangRibId.node(LOCRIB_NID).node(TABLES_NID)).node(emptyTable.name());
154
155             tx.put(LogicalDatastoreType.OPERATIONAL, tableId.build(), emptyTable);
156             try {
157                 tx.commit().get();
158             } catch (final InterruptedException | ExecutionException e) {
159                 LOG.error("Failed to initiate LocRIB for key {}", key, e);
160             }
161         } else {
162             LOG.warn("There's no registered RIB Context for {}", key.getAfi());
163         }
164     }
165
166     private synchronized <C extends Routes & DataObject & ChoiceIn<Tables>, S extends ChildOf<? super C>>
167             void createLocRibWriter(final TablesKey key) {
168         final RIBSupport<C, S> ribSupport = ribContextRegistry.getRIBSupport(key);
169         if (ribSupport == null) {
170             return;
171         }
172         LOG.debug("Creating LocRIB writer for key {}", key);
173         final DOMTransactionChain txChain = createPeerDOMChain();
174         addCallback(txChain);
175         PathSelectionMode pathSelectionStrategy = bestPathSelectionStrategies.get(key);
176         if (pathSelectionStrategy == null) {
177             pathSelectionStrategy = BasePathSelectionModeFactory.createBestPathSelectionStrategy();
178         }
179
180         final LocRibWriter<C, S> locRibWriter = LocRibWriter.create(
181                 ribSupport,
182                 verifyNotNull(tableTypeRegistry.getAfiSafiType(key)),
183                 txChain,
184                 yangRibId,
185                 localAs,
186                 getService(),
187                 ribPolicies,
188                 peerTracker,
189                 pathSelectionStrategy);
190         vpnTableRefresher.put(key, locRibWriter);
191         registerTotalPathCounter(key, locRibWriter);
192         registerTotalPrefixesCounter(key, locRibWriter);
193         txChainToLocRibWriter.put(txChain, locRibWriter);
194     }
195
196     @Override
197     public String toString() {
198         return MoreObjects.toStringHelper(this).add("bgpId", bgpIdentifier).add("localTables", localTables).toString();
199     }
200
201     @Override
202     public AsNumber getLocalAs() {
203         return localAs;
204     }
205
206     @Override
207     public BgpId getBgpIdentifier() {
208         return bgpIdentifier;
209     }
210
211     @Override
212     public Set<? extends BgpTableType> getLocalTables() {
213         return localTables;
214     }
215
216     @Override
217     public BGPDispatcher getDispatcher() {
218         return dispatcher;
219     }
220
221     private void addCallback(final DOMTransactionChain txChain) {
222         txChain.addCallback(new FutureCallback<Empty>() {
223             @Override
224             public void onSuccess(final Empty result) {
225                 LOG.info("RIB {} closed successfully", getInstanceIdentifier());
226             }
227
228             @Override
229             public void onFailure(final Throwable cause) {
230                 RIBImpl.this.onFailure(txChain, cause);
231             }
232         });
233     }
234
235     private synchronized void onFailure(final DOMTransactionChain chain, final Throwable cause) {
236         LOG.error("Broken chain in RIB {}", getInstanceIdentifier(), cause);
237         final LocRibWriter<?, ?> locRibWriter = txChainToLocRibWriter.remove(chain);
238         if (locRibWriter != null) {
239             final DOMTransactionChain newChain = createPeerDOMChain();
240             addCallback(newChain);
241             startLocRib(locRibWriter.getTableKey());
242             locRibWriter.restart(newChain);
243             txChainToLocRibWriter.put(newChain, locRibWriter);
244         }
245     }
246
247     @Override
248     public Set<TablesKey> getLocalTablesKeys() {
249         return localTablesKeys;
250     }
251
252     @Override
253     public boolean supportsTable(final TablesKey tableKey) {
254         return localTablesKeys.contains(tableKey);
255     }
256
257     @Override
258     public BGPRibRoutingPolicy getRibPolicies() {
259         return ribPolicies;
260     }
261
262     @Override
263     public BGPPeerTracker getPeerTracker() {
264         return peerTracker;
265     }
266
267     @Override
268     public void refreshTable(final TablesKey tk, final PeerId peerId) {
269         final RibOutRefresh table = vpnTableRefresher.get(tk);
270         if (table != null) {
271             table.refreshTable(tk, peerId);
272         }
273     }
274
275     @Override
276     public DataTreeChangeExtension getService() {
277         return domService;
278     }
279
280     @Override
281     public YangInstanceIdentifier getYangRibId() {
282         return yangRibId;
283     }
284
285     @Override
286     public DOMTransactionChain createPeerDOMChain() {
287         return domDataBroker.createMergingTransactionChain();
288     }
289
290     @Override
291     public RIBExtensionConsumerContext getRibExtensions() {
292         return extensions;
293     }
294
295     @Override
296     public RIBSupportContextRegistry getRibSupportContext() {
297         return ribContextRegistry;
298     }
299
300     @Override
301     public CodecsRegistry getCodecsRegistry() {
302         return codecsRegistry;
303     }
304
305     public synchronized void instantiateServiceInstance() {
306         LOG.debug("Instantiating RIB table {} at {}", ribId, yangRibId);
307
308         isServiceInstantiated = true;
309         setActive(true);
310         domChain = domDataBroker.createMergingTransactionChain();
311         addCallback(domChain);
312
313         final ContainerNode bgpRib = Builders.containerBuilder().withNodeIdentifier(BGPRIB_NID)
314                 .addChild(ImmutableNodes.mapNodeBuilder(RIB_NID).build()).build();
315
316         final MapEntryNode ribInstance = Builders.mapEntryBuilder().withNodeIdentifier(
317                 NodeIdentifierWithPredicates.of(Rib.QNAME, RIB_ID_QNAME, ribId.getValue()))
318                 .addChild(ImmutableNodes.leafNode(RIB_ID_QNAME, ribId.getValue()))
319                 .addChild(ImmutableNodes.mapNodeBuilder(PEER_NID).build())
320                 .addChild(Builders.containerBuilder().withNodeIdentifier(LOCRIB_NID)
321                         .addChild(ImmutableNodes.mapNodeBuilder(TABLES_NID).build())
322                         .build()).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 }