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