Bug 5048 - Unable to deploy multiple RIB instances
[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.openconfig.spi.BGPConfigModuleTracker;
37 import org.opendaylight.protocol.bgp.openconfig.spi.BGPOpenConfigProvider;
38 import org.opendaylight.protocol.bgp.rib.DefaultRibReference;
39 import org.opendaylight.protocol.bgp.rib.impl.spi.BGPDispatcher;
40 import org.opendaylight.protocol.bgp.rib.impl.spi.CodecsRegistry;
41 import org.opendaylight.protocol.bgp.rib.impl.spi.RIB;
42 import org.opendaylight.protocol.bgp.rib.impl.spi.RIBSupportContextRegistry;
43 import org.opendaylight.protocol.bgp.rib.spi.RIBExtensionConsumerContext;
44 import org.opendaylight.protocol.bgp.rib.spi.RibSupportUtils;
45 import org.opendaylight.protocol.framework.ReconnectStrategyFactory;
46 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.AsNumber;
47 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev150305.bgp.rib.rib.loc.rib.tables.routes.Ipv4RoutesCase;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev150305.bgp.rib.rib.loc.rib.tables.routes.Ipv6RoutesCase;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.BgpTableType;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.BgpRib;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.RibId;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.Rib;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.RibKey;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.LocRib;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.Peer;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.Tables;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.TablesKey;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.tables.Routes;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.ClusterIdentifier;
61 import org.opendaylight.yangtools.binding.data.codec.api.BindingCodecTreeFactory;
62 import org.opendaylight.yangtools.sal.binding.generator.impl.GeneratedClassLoadingStrategy;
63 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
64 import org.opendaylight.yangtools.yang.common.QName;
65 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
66 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.InstanceIdentifierBuilder;
67 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
68 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
69 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
70 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
71 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
72 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
73 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
74 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
75 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
76 import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
77 import org.slf4j.Logger;
78 import org.slf4j.LoggerFactory;
79
80 @ThreadSafe
81 public final class RIBImpl extends DefaultRibReference implements AutoCloseable, RIB, TransactionChainListener, SchemaContextListener {
82     private static final Logger LOG = LoggerFactory.getLogger(RIBImpl.class);
83     @VisibleForTesting
84     public static final QName RIB_ID_QNAME = QName.create(Rib.QNAME, "id").intern();
85     @VisibleForTesting
86     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);
87
88     private final ReconnectStrategyFactory tcpStrategyFactory;
89     private final ReconnectStrategyFactory sessionStrategyFactory;
90
91     private final BGPDispatcher dispatcher;
92     private final DOMTransactionChain domChain;
93     private final AsNumber localAs;
94     private final Ipv4Address bgpIdentifier;
95     private final Set<BgpTableType> localTables;
96     private final Set<TablesKey> localTablesKeys;
97     private final DataBroker dataBroker;
98     private final DOMDataBroker domDataBroker;
99     private final RIBExtensionConsumerContext extensions;
100     private final YangInstanceIdentifier yangRibId;
101     private final RIBSupportContextRegistryImpl ribContextRegistry;
102     private final CodecsRegistryImpl codecsRegistry;
103     private final EffectiveRibInWriter efWriter;
104     private final DOMDataBrokerExtension service;
105     private final List<LocRibWriter> locRibs = new ArrayList<>();
106     private final BGPConfigModuleTracker configModuleTracker;
107     private final BGPOpenConfigProvider openConfigProvider;
108
109     public RIBImpl(final RibId ribId, final AsNumber localAs, final Ipv4Address localBgpId, final Ipv4Address clusterId, final RIBExtensionConsumerContext extensions,
110         final BGPDispatcher dispatcher, final ReconnectStrategyFactory tcpStrategyFactory, final BindingCodecTreeFactory codecFactory,
111         final ReconnectStrategyFactory sessionStrategyFactory, final DataBroker dps, final DOMDataBroker domDataBroker, final List<BgpTableType> localTables,
112         final GeneratedClassLoadingStrategy classStrategy, final BGPConfigModuleTracker moduleTracker, final BGPOpenConfigProvider openConfigProvider) {
113         super(InstanceIdentifier.create(BgpRib.class).child(Rib.class, new RibKey(Preconditions.checkNotNull(ribId))));
114         this.domChain = domDataBroker.createTransactionChain(this);
115         this.localAs = Preconditions.checkNotNull(localAs);
116         this.bgpIdentifier = Preconditions.checkNotNull(localBgpId);
117         this.dispatcher = Preconditions.checkNotNull(dispatcher);
118         this.sessionStrategyFactory = Preconditions.checkNotNull(sessionStrategyFactory);
119         this.tcpStrategyFactory = Preconditions.checkNotNull(tcpStrategyFactory);
120         this.localTables = ImmutableSet.copyOf(localTables);
121         this.localTablesKeys = new HashSet<TablesKey>();
122         this.dataBroker = dps;
123         this.domDataBroker = Preconditions.checkNotNull(domDataBroker);
124         this.extensions = Preconditions.checkNotNull(extensions);
125         this.codecsRegistry = CodecsRegistryImpl.create(codecFactory, classStrategy);
126         this.ribContextRegistry = RIBSupportContextRegistryImpl.create(extensions, this.codecsRegistry);
127         final InstanceIdentifierBuilder yangRibIdBuilder = YangInstanceIdentifier.builder().node(BgpRib.QNAME).node(Rib.QNAME);
128         this.yangRibId = yangRibIdBuilder.nodeWithKey(Rib.QNAME, RIB_ID_QNAME, ribId.getValue()).build();
129         this.configModuleTracker = moduleTracker;
130         this.openConfigProvider = openConfigProvider;
131
132         LOG.debug("Instantiating RIB table {} at {}", ribId, this.yangRibId);
133
134         final ContainerNode bgpRib = Builders.containerBuilder()
135             .withNodeIdentifier(new NodeIdentifier(BgpRib.QNAME))
136                 .addChild(ImmutableNodes.mapNodeBuilder(Rib.QNAME).build()).build();
137
138         final MapEntryNode ribInstance = Builders.mapEntryBuilder().withNodeIdentifier(
139                 new NodeIdentifierWithPredicates(Rib.QNAME, RIB_ID_QNAME, ribId.getValue()))
140                     .addChild(ImmutableNodes.leafNode(RIB_ID_QNAME, ribId.getValue()))
141                     .addChild(ImmutableNodes.mapNodeBuilder(Peer.QNAME).build())
142                     .addChild(Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(LocRib.QNAME))
143                     .addChild(ImmutableNodes.mapNodeBuilder(Tables.QNAME).build())
144                     .build()).build();
145
146
147         final DOMDataWriteTransaction trans = this.domChain.newWriteOnlyTransaction();
148
149         // merge empty BgpRib + Rib, to make sure the top-level parent structure is present
150         trans.merge(LogicalDatastoreType.OPERATIONAL, YangInstanceIdentifier.builder().node(BgpRib.QNAME).build(), bgpRib);
151         trans.put(LogicalDatastoreType.OPERATIONAL, yangRibIdBuilder.build(), ribInstance);
152
153         try {
154             trans.submit().checkedGet();
155         } catch (final TransactionCommitFailedException e) {
156             LOG.error("Failed to initiate RIB {}", this.yangRibId, e);
157         }
158         final ClusterIdentifier cId = (clusterId == null) ? new ClusterIdentifier(localBgpId) : new ClusterIdentifier(clusterId);
159         final PolicyDatabase pd  = new PolicyDatabase(localAs.getValue(), localBgpId, cId);
160
161         final DOMDataBrokerExtension domDatatreeChangeService = this.domDataBroker.getSupportedExtensions().get(DOMDataTreeChangeService.class);
162         this.service = domDatatreeChangeService;
163         this.efWriter = EffectiveRibInWriter.create(getService(), createPeerChain(this), getYangRibId(), pd, this.ribContextRegistry);
164         LOG.debug("Effective RIB created.");
165
166         for (final BgpTableType t : this.localTables) {
167             final TablesKey key = new TablesKey(t.getAfi(), t.getSafi());
168             this.localTablesKeys.add(key);
169             startLocRib(key, pd);
170         }
171
172         if (this.configModuleTracker != null) {
173             this.configModuleTracker.onInstanceCreate();
174         }
175     }
176
177     public RIBImpl(final RibId ribId, final AsNumber localAs, final Ipv4Address localBgpId, final Ipv4Address clusterId, final RIBExtensionConsumerContext extensions,
178             final BGPDispatcher dispatcher, final ReconnectStrategyFactory tcpStrategyFactory, final BindingCodecTreeFactory codecFactory,
179             final ReconnectStrategyFactory sessionStrategyFactory, final DataBroker dps, final DOMDataBroker domDataBroker, final List<BgpTableType> localTables,
180             final GeneratedClassLoadingStrategy classStrategy) {
181         this(ribId, localAs, localBgpId, clusterId, extensions, dispatcher, tcpStrategyFactory, codecFactory, sessionStrategyFactory,
182                 dps, domDataBroker, localTables, classStrategy, null, null);
183     }
184
185     private void startLocRib(final TablesKey key, final PolicyDatabase pd) {
186         LOG.debug("Creating LocRib table for {}", key);
187         // create locRibWriter for each table
188         final DOMDataWriteTransaction tx = this.domChain.newWriteOnlyTransaction();
189
190         final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> table = ImmutableNodes.mapEntryBuilder();
191         table.withNodeIdentifier(RibSupportUtils.toYangTablesKey(key));
192         table.withChild(EMPTY_TABLE_ATTRIBUTES);
193
194         final NodeIdentifierWithPredicates tableKey = RibSupportUtils.toYangTablesKey(key);
195         final InstanceIdentifierBuilder tableId = YangInstanceIdentifier.builder(this.yangRibId.node(LocRib.QNAME).node(Tables.QNAME));
196         tableId.nodeWithKey(tableKey.getNodeType(), tableKey.getKeyValues());
197         for (final Entry<QName, Object> e : tableKey.getKeyValues().entrySet()) {
198             table.withChild(ImmutableNodes.leafNode(e.getKey(), e.getValue()));
199         }
200
201         final ChoiceNode routes = this.ribContextRegistry.getRIBSupportContext(key).getRibSupport().emptyRoutes();
202         table.withChild(routes);
203
204         tx.put(LogicalDatastoreType.OPERATIONAL, tableId.build(), table.build());
205         try {
206             tx.submit().checkedGet();
207         } catch (final TransactionCommitFailedException e1) {
208             LOG.error("Failed to initiate LocRIB for key {}", key, e1);
209         }
210         this.locRibs.add(LocRibWriter.create(this.ribContextRegistry, key, createPeerChain(this), getYangRibId(), this.localAs, getService(), pd));
211     }
212
213     @Override
214     public String toString() {
215         return addToStringAttributes(MoreObjects.toStringHelper(this)).toString();
216     }
217
218     protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
219         return toStringHelper;
220     }
221
222     @Override
223     public synchronized void close() throws InterruptedException, ExecutionException {
224         final DOMDataWriteTransaction t = this.domChain.newWriteOnlyTransaction();
225         t.delete(LogicalDatastoreType.OPERATIONAL, getYangRibId());
226         t.submit().get();
227         this.domChain.close();
228         this.efWriter.close();
229         for (final LocRibWriter locRib : this.locRibs) {
230             try {
231                 locRib.close();
232             } catch (final Exception e) {
233                 LOG.warn("Could not close LocalRib reference: {}", locRib, e);
234             }
235         }
236         if (this.configModuleTracker != null) {
237             this.configModuleTracker.onInstanceClose();
238         }
239     }
240
241     @Override
242     public AsNumber getLocalAs() {
243         return this.localAs;
244     }
245
246     @Override
247     public Ipv4Address getBgpIdentifier() {
248         return this.bgpIdentifier;
249     }
250
251     @Override
252     public Set<? extends BgpTableType> getLocalTables() {
253         return this.localTables;
254     }
255
256     @Override
257     public ReconnectStrategyFactory getTcpStrategyFactory() {
258         return this.tcpStrategyFactory;
259     }
260
261     @Override
262     public ReconnectStrategyFactory getSessionStrategyFactory() {
263         return this.sessionStrategyFactory;
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 }