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