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