Bug-7614: LocRibWriter does not recover well from transaction chain failure
[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.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.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 Map<TransactionChain, LocRibWriter> txChainToLocRibWriter = new HashMap<>();
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         createLocRibWriter(key);
203     }
204
205     private synchronized void createLocRibWriter(final TablesKey key) {
206         LOG.debug("Creating LocRIB writer for key {}", key);
207         final DOMTransactionChain txChain = createPeerChain(this);
208         PathSelectionMode pathSelectionStrategy = this.bestPathSelectionStrategies.get(key);
209         if (pathSelectionStrategy == null) {
210             pathSelectionStrategy = BasePathSelectionModeFactory.createBestPathSelectionStrategy();
211         }
212
213         final LocRibWriter locRibWriter = LocRibWriter.create(this.ribContextRegistry, key, txChain,
214             getYangRibId(), this.localAs, getService(), this.exportPolicyPeerTrackerMap.get(key),
215             pathSelectionStrategy, this.renderStats.getLocRibRouteCounter().init(key));
216         this.txChainToLocRibWriter.put(txChain, locRibWriter);
217     }
218
219     @Override
220     public String toString() {
221         return addToStringAttributes(MoreObjects.toStringHelper(this)).toString();
222     }
223
224     protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
225         return toStringHelper;
226     }
227
228     @Override
229     public synchronized void close() throws Exception {
230         if (this.registration != null) {
231             this.registration.close();
232             this.registration = null;
233         }
234     }
235
236     @Override
237     public AsNumber getLocalAs() {
238         return this.localAs;
239     }
240
241     @Override
242     public BgpId getBgpIdentifier() {
243         return this.bgpIdentifier;
244     }
245
246     @Nonnull
247     @Override
248     public Set<? extends BgpTableType> getLocalTables() {
249         return this.localTables;
250     }
251
252     @Override
253     public BGPDispatcher getDispatcher() {
254         return this.dispatcher;
255     }
256
257     @Override
258     public synchronized void onTransactionChainFailed(final TransactionChain<?, ?> chain, final AsyncTransaction<?, ?> transaction, final Throwable cause) {
259         LOG.error("Broken chain in RIB {} transaction {}", getInstanceIdentifier(), transaction != null ? transaction.getIdentifier() : null, cause);
260         if (this.txChainToLocRibWriter.containsKey(chain)) {
261             final LocRibWriter locRibWriter = this.txChainToLocRibWriter.remove(chain);
262             final DOMTransactionChain newChain = createPeerChain(this);
263             locRibWriter.restart(newChain);
264             this.txChainToLocRibWriter.put(newChain, locRibWriter);
265         }
266     }
267
268     @Override
269     public void onTransactionChainSuccessful(final TransactionChain<?, ?> chain) {
270         LOG.info("RIB {} closed successfully", getInstanceIdentifier());
271     }
272
273     @Override
274     public Set<TablesKey> getLocalTablesKeys() {
275         return this.localTablesKeys;
276     }
277
278     @Override
279     public DOMDataTreeChangeService getService() {
280         return (DOMDataTreeChangeService) this.service;
281     }
282
283     @Override
284     public BGPRenderStats getRenderStats() {
285         return this.renderStats;
286     }
287
288     @Override
289     public YangInstanceIdentifier getYangRibId() {
290         return this.yangRibId;
291     }
292
293     @Override
294     public DOMTransactionChain createPeerChain(final TransactionChainListener listener) {
295         return this.domDataBroker.createTransactionChain(listener);
296     }
297
298     @Override
299     public RIBExtensionConsumerContext getRibExtensions() {
300         return this.extensions;
301     }
302
303     @Override
304     public RIBSupportContextRegistry getRibSupportContext() {
305         return this.ribContextRegistry;
306     }
307
308     @Override
309     public void onGlobalContextUpdated(final SchemaContext context) {
310         this.codecsRegistry.onSchemaContextUpdated(context);
311     }
312
313     @Override
314     public CodecsRegistry getCodecsRegistry() {
315         return this.codecsRegistry;
316     }
317
318     @Override
319     public Optional<BGPOpenConfigProvider> getOpenConfigProvider() {
320         return Optional.fromNullable(this.openConfigProvider);
321     }
322
323     @Override
324     public ImportPolicyPeerTracker getImportPolicyPeerTracker() {
325         return this.importPolicyPeerTracker;
326     }
327
328     @Override
329     public ExportPolicyPeerTracker getExportPolicyPeerTracker(final TablesKey tablesKey) {
330         return this.exportPolicyPeerTrackerMap.get(tablesKey);
331     }
332
333     @Override
334     public void instantiateServiceInstance() {
335         this.domChain = this.domDataBroker.createTransactionChain(this);
336         if(this.configurationWriter != null) {
337             this.configurationWriter.apply();
338         }
339         LOG.info("RIB Singleton Service {} instantiated", getIdentifier());
340         LOG.debug("Instantiating RIB table {} at {}", this.ribId , this.yangRibId);
341
342         final ContainerNode bgpRib = Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(BgpRib.QNAME))
343             .addChild(ImmutableNodes.mapNodeBuilder(Rib.QNAME).build()).build();
344
345         final MapEntryNode ribInstance = Builders.mapEntryBuilder().withNodeIdentifier(
346             new NodeIdentifierWithPredicates(Rib.QNAME, RIB_ID_QNAME, this.ribId .getValue()))
347             .addChild(ImmutableNodes.leafNode(RIB_ID_QNAME, this.ribId .getValue()))
348             .addChild(ImmutableNodes.mapNodeBuilder(Peer.QNAME).build())
349             .addChild(Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(LocRib.QNAME))
350                 .addChild(ImmutableNodes.mapNodeBuilder(Tables.QNAME).build())
351                 .build()).build();
352
353
354         final DOMDataWriteTransaction trans = this.domChain.newWriteOnlyTransaction();
355
356         // merge empty BgpRib + Rib, to make sure the top-level parent structure is present
357         trans.merge(LogicalDatastoreType.OPERATIONAL, YangInstanceIdentifier.builder().node(BgpRib.QNAME).build(), bgpRib);
358         trans.put(LogicalDatastoreType.OPERATIONAL, this.yangRibId, ribInstance);
359
360         try {
361             trans.submit().checkedGet();
362         } catch (final TransactionCommitFailedException e) {
363             LOG.error("Failed to initiate RIB {}", this.yangRibId, e);
364         }
365
366         LOG.debug("Effective RIB created.");
367
368         this.localTablesKeys.forEach(this::startLocRib);
369         if (this.configModuleTracker != null) {
370             this.configModuleTracker.onInstanceCreate();
371         }
372     }
373
374     @Override
375     public synchronized ListenableFuture<Void> closeServiceInstance() {
376         LOG.info("RIB {} closing instance", this.ribId.getValue());
377         this.txChainToLocRibWriter.values().forEach(LocRibWriter::close);
378         this.txChainToLocRibWriter.clear();
379
380         this.renderStats.getLocRibRouteCounter().resetAll();
381
382         if (this.configModuleTracker != null) {
383             this.configModuleTracker.onInstanceClose();
384         }
385
386         final DOMDataWriteTransaction t = this.domChain.newWriteOnlyTransaction();
387         t.delete(LogicalDatastoreType.OPERATIONAL, getYangRibId());
388         final CheckedFuture<Void, TransactionCommitFailedException> cleanFuture = t.submit();
389
390         this.domChain.close();
391
392         return cleanFuture;
393     }
394
395     @Override
396     public ServiceGroupIdentifier getIdentifier() {
397         return this.serviceGroupIdentifier;
398     }
399
400     @Override
401     public ClusterSingletonServiceRegistration registerClusterSingletonService(final ClusterSingletonService clusterSingletonService) {
402         return ClusterSingletonServiceRegistrationHelper.registerSingletonService(this.provider, clusterSingletonService, MAX_REGISTRATION_ATTEMPTS,
403                 SLEEP_TIME);
404     }
405
406     @Override
407     public ServiceGroupIdentifier getRibIServiceGroupIdentifier() {
408         return getIdentifier();
409     }
410 }