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