Bug 6622 - ClusterSingletonService registration race condition
[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.ImmutableMap;
15 import com.google.common.collect.ImmutableSet;
16 import com.google.common.util.concurrent.CheckedFuture;
17 import com.google.common.util.concurrent.ListenableFuture;
18 import java.util.ArrayList;
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.Nullable;
26 import javax.annotation.concurrent.ThreadSafe;
27 import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
28 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
29 import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
30 import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
31 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
32 import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
33 import org.opendaylight.controller.md.sal.dom.api.DOMDataBrokerExtension;
34 import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeService;
35 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
36 import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
37 import org.opendaylight.mdsal.binding.dom.codec.api.BindingCodecTreeFactory;
38 import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonService;
39 import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceProvider;
40 import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceRegistration;
41 import org.opendaylight.mdsal.singleton.common.api.ServiceGroupIdentifier;
42 import org.opendaylight.protocol.bgp.mode.api.PathSelectionMode;
43 import org.opendaylight.protocol.bgp.mode.impl.base.BasePathSelectionModeFactory;
44 import org.opendaylight.protocol.bgp.openconfig.spi.BGPConfigModuleTracker;
45 import org.opendaylight.protocol.bgp.openconfig.spi.BGPOpenConfigProvider;
46 import org.opendaylight.protocol.bgp.rib.DefaultRibReference;
47 import org.opendaylight.protocol.bgp.rib.impl.spi.BGPDispatcher;
48 import org.opendaylight.protocol.bgp.rib.impl.spi.BgpDeployer;
49 import org.opendaylight.protocol.bgp.rib.impl.spi.CodecsRegistry;
50 import org.opendaylight.protocol.bgp.rib.impl.spi.ImportPolicyPeerTracker;
51 import org.opendaylight.protocol.bgp.rib.impl.spi.RIB;
52 import org.opendaylight.protocol.bgp.rib.impl.spi.RIBSupportContextRegistry;
53 import org.opendaylight.protocol.bgp.rib.impl.stats.rib.impl.BGPRenderStats;
54 import org.opendaylight.protocol.bgp.rib.impl.stats.rib.impl.RIBImplRuntimeMXBeanImpl;
55 import org.opendaylight.protocol.bgp.rib.spi.ExportPolicyPeerTracker;
56 import org.opendaylight.protocol.bgp.rib.spi.RIBExtensionConsumerContext;
57 import org.opendaylight.protocol.bgp.rib.spi.RibSupportUtils;
58 import org.opendaylight.protocol.bgp.rib.spi.util.ClusterSingletonServiceRegistrationHelper;
59 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.AsNumber;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.BgpTableType;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.BgpRib;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.RibId;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.Rib;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.RibKey;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.LocRib;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.Peer;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.Tables;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.TablesKey;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.BgpId;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.ClusterIdentifier;
71 import org.opendaylight.yangtools.sal.binding.generator.impl.GeneratedClassLoadingStrategy;
72 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
73 import org.opendaylight.yangtools.yang.common.QName;
74 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
75 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.InstanceIdentifierBuilder;
76 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
77 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
78 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
79 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
80 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
81 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
82 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
83 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
84 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
85 import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
86 import org.slf4j.Logger;
87 import org.slf4j.LoggerFactory;
88
89 @ThreadSafe
90 public final class RIBImpl extends DefaultRibReference implements ClusterSingletonService, AutoCloseable, RIB, TransactionChainListener, SchemaContextListener {
91     private static final Logger LOG = LoggerFactory.getLogger(RIBImpl.class);
92     private static final QName RIB_ID_QNAME = QName.create(Rib.QNAME, "id").intern();
93     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);
94     private static final int MAX_REGISTRATION_ATTEMPTS = 10;
95     private static final int SLEEP_TIME = MAX_REGISTRATION_ATTEMPTS;
96
97     private final BGPDispatcher dispatcher;
98     private final AsNumber localAs;
99     private final BgpId bgpIdentifier;
100     private final Set<BgpTableType> localTables;
101     private final Set<TablesKey> localTablesKeys;
102     private final DOMDataBroker domDataBroker;
103     private final RIBExtensionConsumerContext extensions;
104     private final YangInstanceIdentifier yangRibId;
105     private final RIBSupportContextRegistryImpl ribContextRegistry;
106     private final CodecsRegistryImpl codecsRegistry;
107     private final ServiceGroupIdentifier serviceGroupIdentifier;
108     private final ClusterSingletonServiceProvider provider;
109     private final BgpDeployer.WriteConfiguration configurationWriter;
110     private ClusterSingletonServiceRegistration registration;
111     private final DOMDataBrokerExtension service;
112     private final List<LocRibWriter> locRibs = new ArrayList<>();
113     private final BGPConfigModuleTracker configModuleTracker;
114     private final BGPOpenConfigProvider openConfigProvider;
115     private final Map<TablesKey, PathSelectionMode> bestPathSelectionStrategies;
116     private final ImportPolicyPeerTracker importPolicyPeerTracker;
117     private final RIBImplRuntimeMXBeanImpl renderStats;
118     private final RibId ribId;
119     private final Map<TablesKey, ExportPolicyPeerTracker> exportPolicyPeerTrackerMap;
120
121     private DOMTransactionChain domChain;
122
123     public RIBImpl(final ClusterSingletonServiceProvider provider, final RibId ribId, final AsNumber localAs, final BgpId localBgpId,
124         final ClusterIdentifier clusterId, final RIBExtensionConsumerContext extensions, final BGPDispatcher dispatcher,
125         final BindingCodecTreeFactory codecFactory, final DOMDataBroker domDataBroker, final List<BgpTableType> localTables,
126         @Nonnull final Map<TablesKey, PathSelectionMode> bestPathSelectionStrategies, final GeneratedClassLoadingStrategy classStrategy,
127         final BGPConfigModuleTracker moduleTracker, final BGPOpenConfigProvider openConfigProvider,
128         final BgpDeployer.WriteConfiguration configurationWriter) {
129
130         super(InstanceIdentifier.create(BgpRib.class).child(Rib.class, new RibKey(Preconditions.checkNotNull(ribId))));
131         this.localAs = Preconditions.checkNotNull(localAs);
132         this.bgpIdentifier = Preconditions.checkNotNull(localBgpId);
133         this.dispatcher = Preconditions.checkNotNull(dispatcher);
134         this.localTables = ImmutableSet.copyOf(localTables);
135         this.localTablesKeys = new HashSet<>();
136         this.domDataBroker = Preconditions.checkNotNull(domDataBroker);
137         this.service = this.domDataBroker.getSupportedExtensions().get(DOMDataTreeChangeService.class);
138         this.extensions = Preconditions.checkNotNull(extensions);
139         this.codecsRegistry = CodecsRegistryImpl.create(codecFactory, classStrategy);
140         this.ribContextRegistry = RIBSupportContextRegistryImpl.create(extensions, this.codecsRegistry);
141         final InstanceIdentifierBuilder yangRibIdBuilder = YangInstanceIdentifier.builder().node(BgpRib.QNAME).node(Rib.QNAME);
142         this.yangRibId = yangRibIdBuilder.nodeWithKey(Rib.QNAME, RIB_ID_QNAME, ribId.getValue()).build();
143         this.configModuleTracker = moduleTracker;
144         this.openConfigProvider = openConfigProvider;
145         this.bestPathSelectionStrategies = Preconditions.checkNotNull(bestPathSelectionStrategies);
146         final ClusterIdentifier cId = (clusterId == null) ? new ClusterIdentifier(localBgpId) : clusterId;
147         this.renderStats = new RIBImplRuntimeMXBeanImpl(localBgpId, ribId, localAs, cId);
148         this.ribId = ribId;
149         final PolicyDatabase policyDatabase = new PolicyDatabase(this.localAs.getValue(), localBgpId, cId);
150         this.importPolicyPeerTracker = new ImportPolicyPeerTrackerImpl(policyDatabase);
151         this.serviceGroupIdentifier = ServiceGroupIdentifier.create(this.ribId.getValue() + "-service-group");
152         Preconditions.checkNotNull(provider, "ClusterSingletonServiceProvider is null");
153         this.provider = provider;
154         this.configurationWriter = configurationWriter;
155
156         final ImmutableMap.Builder<TablesKey, ExportPolicyPeerTracker> exportPolicies = new ImmutableMap.Builder<>();
157         for (final BgpTableType t : this.localTables) {
158             final TablesKey key = new TablesKey(t.getAfi(), t.getSafi());
159             this.localTablesKeys.add(key);
160             exportPolicies.put(key, new ExportPolicyPeerTrackerImpl(policyDatabase, key));
161         }
162         this.exportPolicyPeerTrackerMap = exportPolicies.build();
163
164         LOG.info("RIB Singleton Service {} registered", getIdentifier());
165         //this need to be always the last step
166         this.registration = registerClusterSingletonService(this);
167     }
168
169     public RIBImpl(final ClusterSingletonServiceProvider provider, final RibId ribId, final AsNumber localAs, final BgpId localBgpId, @Nullable final ClusterIdentifier clusterId,
170         final RIBExtensionConsumerContext extensions, final BGPDispatcher dispatcher, final BindingCodecTreeFactory codecFactory,
171         final DOMDataBroker domDataBroker, final List<BgpTableType> localTables, final Map<TablesKey, PathSelectionMode> bestPathSelectionstrategies,
172         final GeneratedClassLoadingStrategy classStrategy, final BgpDeployer.WriteConfiguration configurationWriter) {
173         this(provider, ribId, localAs, localBgpId, clusterId, extensions, dispatcher, codecFactory,
174             domDataBroker, localTables, bestPathSelectionstrategies, classStrategy, null, null, configurationWriter);
175     }
176
177     private void startLocRib(final TablesKey key) {
178         LOG.debug("Creating LocRib table for {}", key);
179         // create locRibWriter for each table
180         final DOMDataWriteTransaction tx = this.domChain.newWriteOnlyTransaction();
181
182         final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> table = ImmutableNodes.mapEntryBuilder();
183         table.withNodeIdentifier(RibSupportUtils.toYangTablesKey(key));
184         table.withChild(EMPTY_TABLE_ATTRIBUTES);
185
186         final NodeIdentifierWithPredicates tableKey = RibSupportUtils.toYangTablesKey(key);
187         final InstanceIdentifierBuilder tableId = YangInstanceIdentifier.builder(this.yangRibId.node(LocRib.QNAME).node(Tables.QNAME));
188         tableId.nodeWithKey(tableKey.getNodeType(), tableKey.getKeyValues());
189         for (final Entry<QName, Object> e : tableKey.getKeyValues().entrySet()) {
190             table.withChild(ImmutableNodes.leafNode(e.getKey(), e.getValue()));
191         }
192
193         final ChoiceNode routes = this.ribContextRegistry.getRIBSupportContext(key).getRibSupport().emptyRoutes();
194         table.withChild(routes);
195
196         tx.put(LogicalDatastoreType.OPERATIONAL, tableId.build(), table.build());
197         try {
198             tx.submit().checkedGet();
199         } catch (final TransactionCommitFailedException e1) {
200             LOG.error("Failed to initiate LocRIB for key {}", key, e1);
201         }
202
203         PathSelectionMode pathSelectionStrategy = this.bestPathSelectionStrategies.get(key);
204         if (pathSelectionStrategy == null) {
205             pathSelectionStrategy = BasePathSelectionModeFactory.createBestPathSelectionStrategy();
206         }
207
208         this.locRibs.add(LocRibWriter.create(this.ribContextRegistry, key, createPeerChain(this), getYangRibId(), this.localAs, getService(),
209             this.exportPolicyPeerTrackerMap.get(key), pathSelectionStrategy, this.renderStats.getLocRibRouteCounter().init(key)));
210     }
211
212     @Override
213     public String toString() {
214         return addToStringAttributes(MoreObjects.toStringHelper(this)).toString();
215     }
216
217     protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
218         return toStringHelper;
219     }
220
221     @Override
222     public synchronized void close() throws Exception {
223         if (this.registration != null) {
224             this.registration.close();
225             this.registration = null;
226         }
227     }
228
229     @Override
230     public AsNumber getLocalAs() {
231         return this.localAs;
232     }
233
234     @Override
235     public BgpId getBgpIdentifier() {
236         return this.bgpIdentifier;
237     }
238
239     @Nonnull
240     @Override
241     public Set<? extends BgpTableType> getLocalTables() {
242         return this.localTables;
243     }
244
245     @Override
246     public BGPDispatcher getDispatcher() {
247         return this.dispatcher;
248     }
249
250     @Override
251     public void onTransactionChainFailed(final TransactionChain<?, ?> chain, final AsyncTransaction<?, ?> transaction, final Throwable cause) {
252         LOG.error("Broken chain in RIB {} transaction {}", getInstanceIdentifier(), transaction != null ? transaction.getIdentifier() : null, cause);
253     }
254
255     @Override
256     public void onTransactionChainSuccessful(final TransactionChain<?, ?> chain) {
257         LOG.info("RIB {} closed successfully", getInstanceIdentifier());
258     }
259
260     @Override
261     public Set<TablesKey> getLocalTablesKeys() {
262         return this.localTablesKeys;
263     }
264
265     @Override
266     public DOMDataTreeChangeService getService() {
267         return (DOMDataTreeChangeService) this.service;
268     }
269
270     @Override
271     public BGPRenderStats getRenderStats() {
272         return this.renderStats;
273     }
274
275     @Override
276     public YangInstanceIdentifier getYangRibId() {
277         return this.yangRibId;
278     }
279
280     @Override
281     public DOMTransactionChain createPeerChain(final TransactionChainListener listener) {
282         return this.domDataBroker.createTransactionChain(listener);
283     }
284
285     @Override
286     public RIBExtensionConsumerContext getRibExtensions() {
287         return this.extensions;
288     }
289
290     @Override
291     public RIBSupportContextRegistry getRibSupportContext() {
292         return this.ribContextRegistry;
293     }
294
295     @Override
296     public void onGlobalContextUpdated(final SchemaContext context) {
297         this.codecsRegistry.onSchemaContextUpdated(context);
298     }
299
300     @Override
301     public CodecsRegistry getCodecsRegistry() {
302         return this.codecsRegistry;
303     }
304
305     @Override
306     public Optional<BGPOpenConfigProvider> getOpenConfigProvider() {
307         return Optional.fromNullable(this.openConfigProvider);
308     }
309
310     @Override
311     public ImportPolicyPeerTracker getImportPolicyPeerTracker() {
312         return this.importPolicyPeerTracker;
313     }
314
315     @Override
316     public ExportPolicyPeerTracker getExportPolicyPeerTracker(final TablesKey tablesKey) {
317         return this.exportPolicyPeerTrackerMap.get(tablesKey);
318     }
319
320     @Override
321     public void instantiateServiceInstance() {
322         this.domChain = this.domDataBroker.createTransactionChain(this);
323         if(this.configurationWriter != null) {
324             this.configurationWriter.apply();
325         }
326         LOG.info("RIB Singleton Service {} instantiated", getIdentifier());
327         LOG.debug("Instantiating RIB table {} at {}", this.ribId , this.yangRibId);
328
329         final ContainerNode bgpRib = Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(BgpRib.QNAME))
330             .addChild(ImmutableNodes.mapNodeBuilder(Rib.QNAME).build()).build();
331
332         final MapEntryNode ribInstance = Builders.mapEntryBuilder().withNodeIdentifier(
333             new NodeIdentifierWithPredicates(Rib.QNAME, RIB_ID_QNAME, this.ribId .getValue()))
334             .addChild(ImmutableNodes.leafNode(RIB_ID_QNAME, this.ribId .getValue()))
335             .addChild(ImmutableNodes.mapNodeBuilder(Peer.QNAME).build())
336             .addChild(Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(LocRib.QNAME))
337                 .addChild(ImmutableNodes.mapNodeBuilder(Tables.QNAME).build())
338                 .build()).build();
339
340
341         final DOMDataWriteTransaction trans = this.domChain.newWriteOnlyTransaction();
342
343         // merge empty BgpRib + Rib, to make sure the top-level parent structure is present
344         trans.merge(LogicalDatastoreType.OPERATIONAL, YangInstanceIdentifier.builder().node(BgpRib.QNAME).build(), bgpRib);
345         trans.put(LogicalDatastoreType.OPERATIONAL, this.yangRibId, ribInstance);
346
347         try {
348             trans.submit().checkedGet();
349         } catch (final TransactionCommitFailedException e) {
350             LOG.error("Failed to initiate RIB {}", this.yangRibId, e);
351         }
352
353         LOG.debug("Effective RIB created.");
354
355         this.localTablesKeys.forEach(this::startLocRib);
356         if (this.configModuleTracker != null) {
357             this.configModuleTracker.onInstanceCreate();
358         }
359     }
360
361     @Override
362     public ListenableFuture<Void> closeServiceInstance() {
363         LOG.info("Close RIB Singleton Service {}", getIdentifier());
364         this.locRibs.forEach(LocRibWriter::close);
365         this.locRibs.clear();
366
367         this.renderStats.getLocRibRouteCounter().resetAll();
368
369         if (this.configModuleTracker != null) {
370             this.configModuleTracker.onInstanceClose();
371         }
372
373         final DOMDataWriteTransaction t = this.domChain.newWriteOnlyTransaction();
374         t.delete(LogicalDatastoreType.OPERATIONAL, getYangRibId());
375         final CheckedFuture<Void, TransactionCommitFailedException> cleanFuture = t.submit();
376
377         this.domChain.close();
378
379         return cleanFuture;
380     }
381
382     @Override
383     public ServiceGroupIdentifier getIdentifier() {
384         return this.serviceGroupIdentifier;
385     }
386
387     @Override
388     public ClusterSingletonServiceRegistration registerClusterSingletonService(final ClusterSingletonService clusterSingletonService) {
389         return ClusterSingletonServiceRegistrationHelper.registerSingletonService(this.provider, clusterSingletonService, MAX_REGISTRATION_ATTEMPTS,
390                 SLEEP_TIME);
391     }
392
393     @Override
394     public ServiceGroupIdentifier getRibIServiceGroupIdentifier() {
395         return getIdentifier();
396     }
397 }