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