BUG-4827: Path Selection mode Config Subsystem integration
[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 com.google.common.annotations.VisibleForTesting;
11 import com.google.common.base.MoreObjects;
12 import com.google.common.base.MoreObjects.ToStringHelper;
13 import com.google.common.base.Optional;
14 import com.google.common.base.Preconditions;
15 import com.google.common.collect.ImmutableSet;
16 import java.util.ArrayList;
17 import java.util.HashSet;
18 import java.util.List;
19 import java.util.Map;
20 import java.util.Map.Entry;
21 import java.util.Set;
22 import java.util.concurrent.ExecutionException;
23 import javax.annotation.Nonnull;
24 import javax.annotation.concurrent.ThreadSafe;
25 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
26 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
27 import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
28 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
29 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
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.protocol.bgp.mode.api.PathSelectionMode;
39 import org.opendaylight.protocol.bgp.mode.impl.base.BasePathSelectionModeFactory;
40 import org.opendaylight.protocol.bgp.openconfig.spi.BGPConfigModuleTracker;
41 import org.opendaylight.protocol.bgp.openconfig.spi.BGPOpenConfigProvider;
42 import org.opendaylight.protocol.bgp.rib.DefaultRibReference;
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.spi.CacheDisconnectedPeers;
48 import org.opendaylight.protocol.bgp.rib.spi.RIBExtensionConsumerContext;
49 import org.opendaylight.protocol.bgp.rib.spi.RibSupportUtils;
50 import org.opendaylight.protocol.framework.ReconnectStrategyFactory;
51 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.AsNumber;
52 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev150305.bgp.rib.rib.loc.rib.tables.routes.Ipv4RoutesCase;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev150305.bgp.rib.rib.loc.rib.tables.routes.Ipv6RoutesCase;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.BgpTableType;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.BgpRib;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.RibId;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.Rib;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.RibKey;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.LocRib;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.Peer;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.Tables;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.TablesKey;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.tables.Routes;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.ClusterIdentifier;
66 import org.opendaylight.yangtools.binding.data.codec.api.BindingCodecTreeFactory;
67 import org.opendaylight.yangtools.sal.binding.generator.impl.GeneratedClassLoadingStrategy;
68 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
69 import org.opendaylight.yangtools.yang.common.QName;
70 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
71 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.InstanceIdentifierBuilder;
72 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
73 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
74 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
75 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
76 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
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 DefaultRibReference implements AutoCloseable, RIB, TransactionChainListener, SchemaContextListener {
87     private static final Logger LOG = LoggerFactory.getLogger(RIBImpl.class);
88     @VisibleForTesting
89     public static final QName RIB_ID_QNAME = QName.create(Rib.QNAME, "id").intern();
90     @VisibleForTesting
91     public static final ContainerNode EMPTY_TABLE_ATTRIBUTES = ImmutableNodes.containerNode(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.tables.Attributes.QNAME);
92
93     private final ReconnectStrategyFactory tcpStrategyFactory;
94     private final ReconnectStrategyFactory sessionStrategyFactory;
95
96     private final BGPDispatcher dispatcher;
97     private final DOMTransactionChain domChain;
98     private final AsNumber localAs;
99     private final Ipv4Address bgpIdentifier;
100     private final Set<BgpTableType> localTables;
101     private final Set<TablesKey> localTablesKeys;
102     private final DataBroker dataBroker;
103     private final DOMDataBroker domDataBroker;
104     private final RIBExtensionConsumerContext extensions;
105     private final YangInstanceIdentifier yangRibId;
106     private final RIBSupportContextRegistryImpl ribContextRegistry;
107     private final CodecsRegistryImpl codecsRegistry;
108     private final EffectiveRibInWriter efWriter;
109     private final DOMDataBrokerExtension service;
110     private final List<LocRibWriter> locRibs = new ArrayList<>();
111     private final BGPConfigModuleTracker configModuleTracker;
112     private final BGPOpenConfigProvider openConfigProvider;
113     private final CacheDisconnectedPeers cacheDisconnectedPeers;
114     private final Map<TablesKey, PathSelectionMode> bestPathSelectionStrategies;
115
116     public RIBImpl(final RibId ribId, final AsNumber localAs, final Ipv4Address localBgpId, final Ipv4Address clusterId, final RIBExtensionConsumerContext extensions,
117         final BGPDispatcher dispatcher, final ReconnectStrategyFactory tcpStrategyFactory, final BindingCodecTreeFactory codecFactory,
118         final ReconnectStrategyFactory sessionStrategyFactory, final DataBroker dps, final DOMDataBroker domDataBroker, final List<BgpTableType> localTables,
119         @Nonnull final Map<TablesKey, PathSelectionMode> bestPathSelectionStrategies, final GeneratedClassLoadingStrategy classStrategy,
120         final BGPConfigModuleTracker moduleTracker, final BGPOpenConfigProvider openConfigProvider) {
121         super(InstanceIdentifier.create(BgpRib.class).child(Rib.class, new RibKey(Preconditions.checkNotNull(ribId))));
122         this.domChain = domDataBroker.createTransactionChain(this);
123         this.localAs = Preconditions.checkNotNull(localAs);
124         this.bgpIdentifier = Preconditions.checkNotNull(localBgpId);
125         this.dispatcher = Preconditions.checkNotNull(dispatcher);
126         this.sessionStrategyFactory = Preconditions.checkNotNull(sessionStrategyFactory);
127         this.tcpStrategyFactory = Preconditions.checkNotNull(tcpStrategyFactory);
128         this.localTables = ImmutableSet.copyOf(localTables);
129         this.localTablesKeys = new HashSet<>();
130         this.dataBroker = dps;
131         this.domDataBroker = Preconditions.checkNotNull(domDataBroker);
132         this.extensions = Preconditions.checkNotNull(extensions);
133         this.codecsRegistry = CodecsRegistryImpl.create(codecFactory, classStrategy);
134         this.ribContextRegistry = RIBSupportContextRegistryImpl.create(extensions, this.codecsRegistry);
135         final InstanceIdentifierBuilder yangRibIdBuilder = YangInstanceIdentifier.builder().node(BgpRib.QNAME).node(Rib.QNAME);
136         this.yangRibId = yangRibIdBuilder.nodeWithKey(Rib.QNAME, RIB_ID_QNAME, ribId.getValue()).build();
137         this.configModuleTracker = moduleTracker;
138         this.openConfigProvider = openConfigProvider;
139         this.cacheDisconnectedPeers = new CacheDisconnectedPeersImpl();
140         this.bestPathSelectionStrategies = Preconditions.checkNotNull(bestPathSelectionStrategies);
141
142         LOG.debug("Instantiating RIB table {} at {}", ribId, this.yangRibId);
143
144         final ContainerNode bgpRib = Builders.containerBuilder()
145             .withNodeIdentifier(new NodeIdentifier(BgpRib.QNAME))
146                 .addChild(ImmutableNodes.mapNodeBuilder(Rib.QNAME).build()).build();
147
148         final MapEntryNode ribInstance = Builders.mapEntryBuilder().withNodeIdentifier(
149                 new NodeIdentifierWithPredicates(Rib.QNAME, RIB_ID_QNAME, ribId.getValue()))
150                     .addChild(ImmutableNodes.leafNode(RIB_ID_QNAME, ribId.getValue()))
151                     .addChild(ImmutableNodes.mapNodeBuilder(Peer.QNAME).build())
152                     .addChild(Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(LocRib.QNAME))
153                     .addChild(ImmutableNodes.mapNodeBuilder(Tables.QNAME).build())
154                     .build()).build();
155
156
157         final DOMDataWriteTransaction trans = this.domChain.newWriteOnlyTransaction();
158
159         // merge empty BgpRib + Rib, to make sure the top-level parent structure is present
160         trans.merge(LogicalDatastoreType.OPERATIONAL, YangInstanceIdentifier.builder().node(BgpRib.QNAME).build(), bgpRib);
161         trans.put(LogicalDatastoreType.OPERATIONAL, yangRibIdBuilder.build(), ribInstance);
162
163         try {
164             trans.submit().checkedGet();
165         } catch (final TransactionCommitFailedException e) {
166             LOG.error("Failed to initiate RIB {}", this.yangRibId, e);
167         }
168         final ClusterIdentifier cId = (clusterId == null) ? new ClusterIdentifier(localBgpId) : new ClusterIdentifier(clusterId);
169         final PolicyDatabase pd  = new PolicyDatabase(localAs.getValue(), localBgpId, cId);
170
171         final DOMDataBrokerExtension domDatatreeChangeService = this.domDataBroker.getSupportedExtensions().get(DOMDataTreeChangeService.class);
172         this.service = domDatatreeChangeService;
173         this.efWriter = EffectiveRibInWriter.create(getService(), createPeerChain(this), getYangRibId(), pd, this.ribContextRegistry);
174         LOG.debug("Effective RIB created.");
175
176         for (final BgpTableType t : this.localTables) {
177             final TablesKey key = new TablesKey(t.getAfi(), t.getSafi());
178             this.localTablesKeys.add(key);
179             startLocRib(key, pd);
180         }
181
182         if (this.configModuleTracker != null) {
183             this.configModuleTracker.onInstanceCreate();
184         }
185     }
186
187     public RIBImpl(final RibId ribId, final AsNumber localAs, final Ipv4Address localBgpId, final Ipv4Address clusterId, final RIBExtensionConsumerContext extensions,
188             final BGPDispatcher dispatcher, final ReconnectStrategyFactory tcpStrategyFactory, final BindingCodecTreeFactory codecFactory,
189             final ReconnectStrategyFactory sessionStrategyFactory, final DataBroker dps, final DOMDataBroker domDataBroker, final List<BgpTableType> localTables,
190             final Map<TablesKey, PathSelectionMode> bestPathSelectionstrategies, final GeneratedClassLoadingStrategy classStrategy) {
191         this(ribId, localAs, localBgpId, clusterId, extensions, dispatcher, tcpStrategyFactory, codecFactory, sessionStrategyFactory,
192                 dps, domDataBroker, localTables, bestPathSelectionstrategies, classStrategy, null, null);
193     }
194
195     private void startLocRib(final TablesKey key, final PolicyDatabase pd) {
196         LOG.debug("Creating LocRib table for {}", key);
197         // create locRibWriter for each table
198         final DOMDataWriteTransaction tx = this.domChain.newWriteOnlyTransaction();
199
200         final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> table = ImmutableNodes.mapEntryBuilder();
201         table.withNodeIdentifier(RibSupportUtils.toYangTablesKey(key));
202         table.withChild(EMPTY_TABLE_ATTRIBUTES);
203
204         final NodeIdentifierWithPredicates tableKey = RibSupportUtils.toYangTablesKey(key);
205         final InstanceIdentifierBuilder tableId = YangInstanceIdentifier.builder(this.yangRibId.node(LocRib.QNAME).node(Tables.QNAME));
206         tableId.nodeWithKey(tableKey.getNodeType(), tableKey.getKeyValues());
207         for (final Entry<QName, Object> e : tableKey.getKeyValues().entrySet()) {
208             table.withChild(ImmutableNodes.leafNode(e.getKey(), e.getValue()));
209         }
210
211         final ChoiceNode routes = this.ribContextRegistry.getRIBSupportContext(key).getRibSupport().emptyRoutes();
212         table.withChild(routes);
213
214         tx.put(LogicalDatastoreType.OPERATIONAL, tableId.build(), table.build());
215         try {
216             tx.submit().checkedGet();
217         } catch (final TransactionCommitFailedException e1) {
218             LOG.error("Failed to initiate LocRIB for key {}", key, e1);
219         }
220
221         PathSelectionMode pathSelectionStrategy = this.bestPathSelectionStrategies.get(key);
222         if (pathSelectionStrategy == null) {
223             pathSelectionStrategy = BasePathSelectionModeFactory.createBestPathSelectionStrategy();
224         }
225         this.locRibs.add(LocRibWriter.create(this.ribContextRegistry, key, createPeerChain(this), getYangRibId(), this.localAs, getService(), pd,
226             this.cacheDisconnectedPeers, pathSelectionStrategy));
227     }
228
229     @Override
230     public String toString() {
231         return addToStringAttributes(MoreObjects.toStringHelper(this)).toString();
232     }
233
234     protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
235         return toStringHelper;
236     }
237
238     @Override
239     public synchronized void close() throws InterruptedException, ExecutionException {
240         final DOMDataWriteTransaction t = this.domChain.newWriteOnlyTransaction();
241         t.delete(LogicalDatastoreType.OPERATIONAL, getYangRibId());
242         t.submit().get();
243         this.domChain.close();
244         this.efWriter.close();
245         for (final LocRibWriter locRib : this.locRibs) {
246             try {
247                 locRib.close();
248             } catch (final Exception e) {
249                 LOG.warn("Could not close LocalRib reference: {}", locRib, e);
250             }
251         }
252         if (this.configModuleTracker != null) {
253             this.configModuleTracker.onInstanceClose();
254         }
255     }
256
257     @Override
258     public AsNumber getLocalAs() {
259         return this.localAs;
260     }
261
262     @Override
263     public Ipv4Address getBgpIdentifier() {
264         return this.bgpIdentifier;
265     }
266
267     @Override
268     public Set<? extends BgpTableType> getLocalTables() {
269         return this.localTables;
270     }
271
272     @Override
273     public ReconnectStrategyFactory getTcpStrategyFactory() {
274         return this.tcpStrategyFactory;
275     }
276
277     @Override
278     public ReconnectStrategyFactory getSessionStrategyFactory() {
279         return this.sessionStrategyFactory;
280     }
281
282     @Override
283     public BGPDispatcher getDispatcher() {
284         return this.dispatcher;
285     }
286
287     @Override
288     public void onTransactionChainFailed(final TransactionChain<?, ?> chain, final AsyncTransaction<?, ?> transaction, final Throwable cause) {
289         LOG.error("Broken chain in RIB {} transaction {}", getInstanceIdentifier(), transaction != null ? transaction.getIdentifier() : null, cause);
290     }
291
292     @Override
293     public void onTransactionChainSuccessful(final TransactionChain<?, ?> chain) {
294         LOG.info("RIB {} closed successfully", getInstanceIdentifier());
295     }
296
297     @Override
298     public long getRoutesCount(final TablesKey key) {
299         try (final ReadOnlyTransaction tx = this.dataBroker.newReadOnlyTransaction()) {
300             final Optional<Tables> tableMaybe = tx.read(LogicalDatastoreType.OPERATIONAL,
301                     getInstanceIdentifier().child(LocRib.class).child(Tables.class, key)).checkedGet();
302             if (tableMaybe.isPresent()) {
303                 final Tables table = tableMaybe.get();
304                 return countIpRoutes(table.getRoutes());
305             }
306         } catch (final ReadFailedException e) {
307             LOG.debug("Failed to read tables", e);
308         }
309         return 0;
310     }
311
312     private int countIpRoutes(final Routes routes) {
313         if (routes instanceof Ipv4RoutesCase) {
314             final Ipv4RoutesCase routesCase = (Ipv4RoutesCase) routes;
315             if (routesCase.getIpv4Routes() != null && routesCase.getIpv4Routes().getIpv4Route() != null) {
316                 return routesCase.getIpv4Routes().getIpv4Route().size();
317             }
318         } else if (routes instanceof Ipv6RoutesCase) {
319             final Ipv6RoutesCase routesCase = (Ipv6RoutesCase) routes;
320             if (routesCase.getIpv6Routes() != null && routesCase.getIpv6Routes().getIpv6Route() != null) {
321                 return routesCase.getIpv6Routes().getIpv6Route().size();
322             }
323         }
324         return 0;
325     }
326
327     public Set<TablesKey> getLocalTablesKeys() {
328         return this.localTablesKeys;
329     }
330
331     public DOMDataTreeChangeService getService() {
332         return (DOMDataTreeChangeService) this.service;
333     }
334
335     @Override
336     public YangInstanceIdentifier getYangRibId() {
337         return this.yangRibId;
338     }
339
340     @Override
341     public DOMTransactionChain createPeerChain(final TransactionChainListener listener) {
342         return this.domDataBroker.createTransactionChain(listener);
343     }
344
345     @Override
346     public RIBExtensionConsumerContext getRibExtensions() {
347         return this.extensions;
348     }
349
350     @Override
351     public RIBSupportContextRegistry getRibSupportContext() {
352         return this.ribContextRegistry;
353     }
354
355     @Override
356     public void onGlobalContextUpdated(final SchemaContext context) {
357         this.codecsRegistry.onSchemaContextUpdated(context);
358     }
359
360     @Override
361     public CodecsRegistry getCodecsRegistry() {
362         return this.codecsRegistry;
363     }
364
365     @Override
366     public Optional<BGPOpenConfigProvider> getOpenConfigProvider() {
367         return Optional.fromNullable(this.openConfigProvider);
368     }
369
370     @Override
371     public CacheDisconnectedPeers getCacheDisconnectedPeers() {
372         return this.cacheDisconnectedPeers;
373     }
374 }