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