BUG-6747: Race condition on peer connection
[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.Preconditions;
13 import com.google.common.collect.ImmutableMap;
14 import com.google.common.collect.ImmutableSet;
15 import com.google.common.util.concurrent.Futures;
16 import com.google.common.util.concurrent.ListenableFuture;
17 import java.util.ArrayList;
18 import java.util.HashMap;
19 import java.util.HashSet;
20 import java.util.List;
21 import java.util.Map;
22 import java.util.Map.Entry;
23 import java.util.Set;
24 import javax.annotation.Nonnull;
25 import javax.annotation.concurrent.ThreadSafe;
26 import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
27 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
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.mdsal.binding.dom.codec.api.BindingCodecTreeFactory;
37 import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonService;
38 import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceProvider;
39 import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceRegistration;
40 import org.opendaylight.mdsal.singleton.common.api.ServiceGroupIdentifier;
41 import org.opendaylight.protocol.bgp.mode.api.PathSelectionMode;
42 import org.opendaylight.protocol.bgp.mode.impl.base.BasePathSelectionModeFactory;
43 import org.opendaylight.protocol.bgp.rib.DefaultRibReference;
44 import org.opendaylight.protocol.bgp.rib.impl.spi.BGPDispatcher;
45 import org.opendaylight.protocol.bgp.rib.impl.spi.BgpDeployer;
46 import org.opendaylight.protocol.bgp.rib.impl.spi.CodecsRegistry;
47 import org.opendaylight.protocol.bgp.rib.impl.spi.ImportPolicyPeerTracker;
48 import org.opendaylight.protocol.bgp.rib.impl.spi.RIB;
49 import org.opendaylight.protocol.bgp.rib.impl.spi.RIBSupportContextRegistry;
50 import org.opendaylight.protocol.bgp.rib.impl.stats.rib.impl.BGPRenderStats;
51 import org.opendaylight.protocol.bgp.rib.impl.stats.rib.impl.RIBImplRuntimeMXBeanImpl;
52 import org.opendaylight.protocol.bgp.rib.spi.ExportPolicyPeerTracker;
53 import org.opendaylight.protocol.bgp.rib.spi.RIBExtensionConsumerContext;
54 import org.opendaylight.protocol.bgp.rib.spi.RibSupportUtils;
55 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.AsNumber;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.BgpTableType;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.BgpRib;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.RibId;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.Rib;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.RibKey;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.LocRib;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.Peer;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.Tables;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.TablesKey;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.BgpId;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.ClusterIdentifier;
67 import org.opendaylight.yangtools.sal.binding.generator.impl.GeneratedClassLoadingStrategy;
68 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
69 import org.opendaylight.yangtools.yang.common.QName;
70 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
71 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.InstanceIdentifierBuilder;
72 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
73 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
74 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
75 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
76 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
77 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
78 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
79 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
80 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
81 import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
82 import org.slf4j.Logger;
83 import org.slf4j.LoggerFactory;
84
85 @ThreadSafe
86 public final class RIBImpl extends DefaultRibReference implements ClusterSingletonService, AutoCloseable, RIB, TransactionChainListener, SchemaContextListener {
87     private static final Logger LOG = LoggerFactory.getLogger(RIBImpl.class);
88     private static final QName RIB_ID_QNAME = QName.create(Rib.QNAME, "id").intern();
89     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);
90
91     private final BGPDispatcher dispatcher;
92     private final DOMTransactionChain domChain;
93     private final AsNumber localAs;
94     private final BgpId bgpIdentifier;
95     private final Set<BgpTableType> localTables;
96     private final Set<TablesKey> localTablesKeys;
97     private final DOMDataBroker domDataBroker;
98     private final RIBExtensionConsumerContext extensions;
99     private final YangInstanceIdentifier yangRibId;
100     private final RIBSupportContextRegistryImpl ribContextRegistry;
101     private final CodecsRegistryImpl codecsRegistry;
102     private final ServiceGroupIdentifier serviceGroupIdentifier;
103     private final ClusterSingletonServiceProvider provider;
104     private final BgpDeployer.WriteConfiguration configurationWriter;
105     private ClusterSingletonServiceRegistration registration;
106     private final DOMDataBrokerExtension service;
107     private final List<LocRibWriter> locRibs = new ArrayList<>();
108     private final Map<TablesKey, PathSelectionMode> bestPathSelectionStrategies;
109     private final ImportPolicyPeerTracker importPolicyPeerTracker;
110     private final RIBImplRuntimeMXBeanImpl renderStats;
111     private final RibId ribId;
112     private final Map<TablesKey, ExportPolicyPeerTracker> exportPolicyPeerTrackerMap;
113
114     public RIBImpl(final ClusterSingletonServiceProvider provider, final RibId ribId, final AsNumber localAs, final BgpId localBgpId,
115         final ClusterIdentifier clusterId, final RIBExtensionConsumerContext extensions, final BGPDispatcher dispatcher,
116         final BindingCodecTreeFactory codecFactory, final DOMDataBroker domDataBroker, final List<BgpTableType> localTables,
117         @Nonnull final Map<TablesKey, PathSelectionMode> bestPathSelectionStrategies, final GeneratedClassLoadingStrategy classStrategy,
118         final BgpDeployer.WriteConfiguration configurationWriter) {
119
120         super(InstanceIdentifier.create(BgpRib.class).child(Rib.class, new RibKey(Preconditions.checkNotNull(ribId))));
121         this.domChain = domDataBroker.createTransactionChain(this);
122         this.localAs = Preconditions.checkNotNull(localAs);
123         this.bgpIdentifier = Preconditions.checkNotNull(localBgpId);
124         this.dispatcher = Preconditions.checkNotNull(dispatcher);
125         this.localTables = ImmutableSet.copyOf(localTables);
126         this.localTablesKeys = new HashSet<>();
127         this.domDataBroker = Preconditions.checkNotNull(domDataBroker);
128         this.service = this.domDataBroker.getSupportedExtensions().get(DOMDataTreeChangeService.class);
129         this.extensions = Preconditions.checkNotNull(extensions);
130         this.codecsRegistry = CodecsRegistryImpl.create(codecFactory, classStrategy);
131         this.ribContextRegistry = RIBSupportContextRegistryImpl.create(extensions, this.codecsRegistry);
132         final InstanceIdentifierBuilder yangRibIdBuilder = YangInstanceIdentifier.builder().node(BgpRib.QNAME).node(Rib.QNAME);
133         this.yangRibId = yangRibIdBuilder.nodeWithKey(Rib.QNAME, RIB_ID_QNAME, ribId.getValue()).build();
134         this.bestPathSelectionStrategies = Preconditions.checkNotNull(bestPathSelectionStrategies);
135         final ClusterIdentifier cId = (clusterId == null) ? new ClusterIdentifier(localBgpId) : clusterId;
136         this.renderStats = new RIBImplRuntimeMXBeanImpl(localBgpId, ribId, localAs, cId);
137         this.ribId = ribId;
138         final PolicyDatabase policyDatabase = new PolicyDatabase(this.localAs.getValue(), localBgpId, cId);
139         this.importPolicyPeerTracker = new ImportPolicyPeerTrackerImpl(policyDatabase);
140         this.serviceGroupIdentifier = ServiceGroupIdentifier.create(this.ribId.getValue() + "-service-group");
141         Preconditions.checkNotNull(provider, "ClusterSingletonServiceProvider is null");
142         this.provider = provider;
143         this.configurationWriter = configurationWriter;
144
145         final ImmutableMap.Builder<TablesKey, ExportPolicyPeerTracker> exportPolicies = new ImmutableMap.Builder<>();
146         for (final BgpTableType t : this.localTables) {
147             final TablesKey key = new TablesKey(t.getAfi(), t.getSafi());
148             this.localTablesKeys.add(key);
149             exportPolicies.put(key, new ExportPolicyPeerTrackerImpl(policyDatabase, key));
150         }
151         this.exportPolicyPeerTrackerMap = exportPolicies.build();
152
153         LOG.info("RIB Singleton Service {} registered", getIdentifier());
154         //this need to be always the last step
155         this.registration = registerClusterSingletonService(this);
156     }
157
158     private void startLocRib(final TablesKey key) {
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
184         PathSelectionMode pathSelectionStrategy = this.bestPathSelectionStrategies.get(key);
185         if (pathSelectionStrategy == null) {
186             pathSelectionStrategy = BasePathSelectionModeFactory.createBestPathSelectionStrategy();
187         }
188
189         this.locRibs.add(LocRibWriter.create(this.ribContextRegistry, key, createPeerChain(this), getYangRibId(), this.localAs, getService(),
190             this.exportPolicyPeerTrackerMap.get(key), pathSelectionStrategy, this.renderStats.getLocRibRouteCounter().init(key)));
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 Exception {
204         if (this.registration != null) {
205             this.registration.close();
206             this.registration = null;
207         }
208         if(this.domChain != null) {
209             this.domChain.close();
210         }
211     }
212
213     @Override
214     public AsNumber getLocalAs() {
215         return this.localAs;
216     }
217
218     @Override
219     public BgpId getBgpIdentifier() {
220         return this.bgpIdentifier;
221     }
222
223     @Nonnull
224     @Override
225     public Set<? extends BgpTableType> getLocalTables() {
226         return this.localTables;
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 != null ? transaction.getIdentifier() : null, 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 Set<TablesKey> getLocalTablesKeys() {
246         return this.localTablesKeys;
247     }
248
249     @Override
250     public DOMDataTreeChangeService getService() {
251         return (DOMDataTreeChangeService) this.service;
252     }
253
254     @Override
255     public BGPRenderStats getRenderStats() {
256         return this.renderStats;
257     }
258
259     @Override
260     public YangInstanceIdentifier getYangRibId() {
261         return this.yangRibId;
262     }
263
264     @Override
265     public DOMTransactionChain createPeerChain(final TransactionChainListener listener) {
266         return this.domDataBroker.createTransactionChain(listener);
267     }
268
269     @Override
270     public RIBExtensionConsumerContext getRibExtensions() {
271         return this.extensions;
272     }
273
274     @Override
275     public RIBSupportContextRegistry getRibSupportContext() {
276         return this.ribContextRegistry;
277     }
278
279     @Override
280     public void onGlobalContextUpdated(final SchemaContext context) {
281         this.codecsRegistry.onSchemaContextUpdated(context);
282     }
283
284     @Override
285     public CodecsRegistry getCodecsRegistry() {
286         return this.codecsRegistry;
287     }
288
289     @Override
290     public ImportPolicyPeerTracker getImportPolicyPeerTracker() {
291         return this.importPolicyPeerTracker;
292     }
293
294     @Override
295     public ExportPolicyPeerTracker getExportPolicyPeerTracker(final TablesKey tablesKey) {
296         return this.exportPolicyPeerTrackerMap.get(tablesKey);
297     }
298
299     @Override
300     public void instantiateServiceInstance() {
301         if(this.configurationWriter != null) {
302             this.configurationWriter.apply();
303         }
304         LOG.info("RIB Singleton Service {} instantiated", getIdentifier());
305         LOG.debug("Instantiating RIB table {} at {}", this.ribId , this.yangRibId);
306
307         final ContainerNode bgpRib = Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(BgpRib.QNAME))
308             .addChild(ImmutableNodes.mapNodeBuilder(Rib.QNAME).build()).build();
309
310         final MapEntryNode ribInstance = Builders.mapEntryBuilder().withNodeIdentifier(
311             new NodeIdentifierWithPredicates(Rib.QNAME, RIB_ID_QNAME, this.ribId .getValue()))
312             .addChild(ImmutableNodes.leafNode(RIB_ID_QNAME, this.ribId .getValue()))
313             .addChild(ImmutableNodes.mapNodeBuilder(Peer.QNAME).build())
314             .addChild(Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(LocRib.QNAME))
315                 .addChild(ImmutableNodes.mapNodeBuilder(Tables.QNAME).build())
316                 .build()).build();
317
318
319         final DOMDataWriteTransaction trans = this.domChain.newWriteOnlyTransaction();
320
321         // merge empty BgpRib + Rib, to make sure the top-level parent structure is present
322         trans.merge(LogicalDatastoreType.OPERATIONAL, YangInstanceIdentifier.builder().node(BgpRib.QNAME).build(), bgpRib);
323         trans.put(LogicalDatastoreType.OPERATIONAL, this.yangRibId, ribInstance);
324
325         try {
326             trans.submit().checkedGet();
327         } catch (final TransactionCommitFailedException e) {
328             LOG.error("Failed to initiate RIB {}", this.yangRibId, e);
329         }
330
331         LOG.debug("Effective RIB created.");
332
333         this.localTablesKeys.forEach(this::startLocRib);
334     }
335
336     @Override
337     public ListenableFuture<Void> closeServiceInstance() {
338         LOG.info("Close RIB Singleton Service {}", getIdentifier());
339         for (final LocRibWriter locRib : this.locRibs) {
340             try {
341                 locRib.close();
342             } catch (final Exception e) {
343                 LOG.warn("Could not close LocalRib reference: {}", locRib, e);
344             }
345         }
346         try {
347             final DOMDataWriteTransaction t = this.domChain.newWriteOnlyTransaction();
348             t.delete(LogicalDatastoreType.OPERATIONAL, getYangRibId());
349             t.submit().checkedGet();
350         } catch (final TransactionCommitFailedException e) {
351             LOG.warn("Failed to remove RIB instance {} from DS.", getYangRibId(), e);
352         }
353         this.renderStats.getLocRibRouteCounter().resetAll();
354
355         return Futures.immediateFuture(null);
356     }
357
358     @Override
359     public ServiceGroupIdentifier getIdentifier() {
360         return this.serviceGroupIdentifier;
361     }
362
363     @Override
364     public ClusterSingletonServiceRegistration registerClusterSingletonService(final ClusterSingletonService clusterSingletonService) {
365         return this.provider.registerClusterSingletonService(clusterSingletonService);
366     }
367
368     @Override
369     public ServiceGroupIdentifier getRibIServiceGroupIdentifier() {
370         return getIdentifier();
371     }
372 }