Bug 6788: peer singleton service closed just after initialization
[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.Futures;
17 import com.google.common.util.concurrent.ListenableFuture;
18 import java.util.ArrayList;
19 import java.util.HashMap;
20 import java.util.HashSet;
21 import java.util.List;
22 import java.util.Map;
23 import java.util.Map.Entry;
24 import java.util.Set;
25 import javax.annotation.Nonnull;
26 import javax.annotation.Nullable;
27 import javax.annotation.concurrent.ThreadSafe;
28 import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
29 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
30 import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
31 import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
32 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
33 import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
34 import org.opendaylight.controller.md.sal.dom.api.DOMDataBrokerExtension;
35 import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeService;
36 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
37 import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
38 import org.opendaylight.mdsal.binding.dom.codec.api.BindingCodecTreeFactory;
39 import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonService;
40 import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceProvider;
41 import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceRegistration;
42 import org.opendaylight.mdsal.singleton.common.api.ServiceGroupIdentifier;
43 import org.opendaylight.protocol.bgp.mode.api.PathSelectionMode;
44 import org.opendaylight.protocol.bgp.mode.impl.base.BasePathSelectionModeFactory;
45 import org.opendaylight.protocol.bgp.openconfig.spi.BGPConfigModuleTracker;
46 import org.opendaylight.protocol.bgp.openconfig.spi.BGPOpenConfigProvider;
47 import org.opendaylight.protocol.bgp.rib.DefaultRibReference;
48 import org.opendaylight.protocol.bgp.rib.impl.spi.BGPDispatcher;
49 import org.opendaylight.protocol.bgp.rib.impl.spi.BgpDeployer;
50 import org.opendaylight.protocol.bgp.rib.impl.spi.CodecsRegistry;
51 import org.opendaylight.protocol.bgp.rib.impl.spi.ImportPolicyPeerTracker;
52 import org.opendaylight.protocol.bgp.rib.impl.spi.RIB;
53 import org.opendaylight.protocol.bgp.rib.impl.spi.RIBSupportContextRegistry;
54 import org.opendaylight.protocol.bgp.rib.impl.stats.rib.impl.BGPRenderStats;
55 import org.opendaylight.protocol.bgp.rib.impl.stats.rib.impl.RIBImplRuntimeMXBeanImpl;
56 import org.opendaylight.protocol.bgp.rib.spi.ExportPolicyPeerTracker;
57 import org.opendaylight.protocol.bgp.rib.spi.RIBExtensionConsumerContext;
58 import org.opendaylight.protocol.bgp.rib.spi.RibSupportUtils;
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
95     private final BGPDispatcher dispatcher;
96     private final DOMTransactionChain domChain;
97     private final AsNumber localAs;
98     private final BgpId bgpIdentifier;
99     private final Set<BgpTableType> localTables;
100     private final Set<TablesKey> localTablesKeys;
101     private final DOMDataBroker domDataBroker;
102     private final RIBExtensionConsumerContext extensions;
103     private final YangInstanceIdentifier yangRibId;
104     private final RIBSupportContextRegistryImpl ribContextRegistry;
105     private final CodecsRegistryImpl codecsRegistry;
106     private final ServiceGroupIdentifier serviceGroupIdentifier;
107     private final ClusterSingletonServiceProvider provider;
108     private final BgpDeployer.WriteConfiguration configurationWriter;
109     private ClusterSingletonServiceRegistration registration;
110     private final DOMDataBrokerExtension service;
111     private final List<LocRibWriter> locRibs = new ArrayList<>();
112     private final BGPConfigModuleTracker configModuleTracker;
113     private final BGPOpenConfigProvider openConfigProvider;
114     private final Map<TablesKey, PathSelectionMode> bestPathSelectionStrategies;
115     private final ImportPolicyPeerTracker importPolicyPeerTracker;
116     private final RIBImplRuntimeMXBeanImpl renderStats;
117     private final RibId ribId;
118     private final Map<TablesKey, ExportPolicyPeerTracker> exportPolicyPeerTrackerMap;
119
120     public RIBImpl(final ClusterSingletonServiceProvider provider, final RibId ribId, final AsNumber localAs, final BgpId localBgpId,
121         final ClusterIdentifier clusterId, final RIBExtensionConsumerContext extensions, final BGPDispatcher dispatcher,
122         final BindingCodecTreeFactory codecFactory, final DOMDataBroker domDataBroker, final List<BgpTableType> localTables,
123         @Nonnull final Map<TablesKey, PathSelectionMode> bestPathSelectionStrategies, final GeneratedClassLoadingStrategy classStrategy,
124         final BGPConfigModuleTracker moduleTracker, final BGPOpenConfigProvider openConfigProvider,
125         final BgpDeployer.WriteConfiguration configurationWriter) {
126
127         super(InstanceIdentifier.create(BgpRib.class).child(Rib.class, new RibKey(Preconditions.checkNotNull(ribId))));
128         this.domChain = domDataBroker.createTransactionChain(this);
129         this.localAs = Preconditions.checkNotNull(localAs);
130         this.bgpIdentifier = Preconditions.checkNotNull(localBgpId);
131         this.dispatcher = Preconditions.checkNotNull(dispatcher);
132         this.localTables = ImmutableSet.copyOf(localTables);
133         this.localTablesKeys = new HashSet<>();
134         this.domDataBroker = Preconditions.checkNotNull(domDataBroker);
135         this.service = this.domDataBroker.getSupportedExtensions().get(DOMDataTreeChangeService.class);
136         this.extensions = Preconditions.checkNotNull(extensions);
137         this.codecsRegistry = CodecsRegistryImpl.create(codecFactory, classStrategy);
138         this.ribContextRegistry = RIBSupportContextRegistryImpl.create(extensions, this.codecsRegistry);
139         final InstanceIdentifierBuilder yangRibIdBuilder = YangInstanceIdentifier.builder().node(BgpRib.QNAME).node(Rib.QNAME);
140         this.yangRibId = yangRibIdBuilder.nodeWithKey(Rib.QNAME, RIB_ID_QNAME, ribId.getValue()).build();
141         this.configModuleTracker = moduleTracker;
142         this.openConfigProvider = openConfigProvider;
143         this.bestPathSelectionStrategies = Preconditions.checkNotNull(bestPathSelectionStrategies);
144         final ClusterIdentifier cId = (clusterId == null) ? new ClusterIdentifier(localBgpId) : clusterId;
145         this.renderStats = new RIBImplRuntimeMXBeanImpl(localBgpId, ribId, localAs, cId);
146         this.ribId = ribId;
147         final PolicyDatabase policyDatabase = new PolicyDatabase(this.localAs.getValue(), localBgpId, cId);
148         this.importPolicyPeerTracker = new ImportPolicyPeerTrackerImpl(policyDatabase);
149         this.serviceGroupIdentifier = ServiceGroupIdentifier.create(this.ribId.getValue() + "-service-group");
150         Preconditions.checkNotNull(provider, "ClusterSingletonServiceProvider is null");
151         this.provider = provider;
152         this.configurationWriter = configurationWriter;
153
154         final ImmutableMap.Builder<TablesKey, ExportPolicyPeerTracker> exportPolicies = new ImmutableMap.Builder<>();
155         for (final BgpTableType t : this.localTables) {
156             final TablesKey key = new TablesKey(t.getAfi(), t.getSafi());
157             this.localTablesKeys.add(key);
158             exportPolicies.put(key, new ExportPolicyPeerTrackerImpl(policyDatabase, key));
159         }
160         this.exportPolicyPeerTrackerMap = exportPolicies.build();
161
162         LOG.info("RIB Singleton Service {} registered", getIdentifier());
163         //this need to be always the last step
164         this.registration = registerClusterSingletonService(this);
165     }
166
167     public RIBImpl(final ClusterSingletonServiceProvider provider, final RibId ribId, final AsNumber localAs, final BgpId localBgpId, @Nullable final ClusterIdentifier clusterId,
168         final RIBExtensionConsumerContext extensions, final BGPDispatcher dispatcher, final BindingCodecTreeFactory codecFactory,
169         final DOMDataBroker domDataBroker, final List<BgpTableType> localTables, final Map<TablesKey, PathSelectionMode> bestPathSelectionstrategies,
170         final GeneratedClassLoadingStrategy classStrategy, final BgpDeployer.WriteConfiguration configurationWriter) {
171         this(provider, ribId, localAs, localBgpId, clusterId, extensions, dispatcher, codecFactory,
172             domDataBroker, localTables, bestPathSelectionstrategies, classStrategy, null, null, configurationWriter);
173     }
174
175     private void startLocRib(final TablesKey key) {
176         LOG.debug("Creating LocRib table for {}", key);
177         // create locRibWriter for each table
178         final DOMDataWriteTransaction tx = this.domChain.newWriteOnlyTransaction();
179
180         final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> table = ImmutableNodes.mapEntryBuilder();
181         table.withNodeIdentifier(RibSupportUtils.toYangTablesKey(key));
182         table.withChild(EMPTY_TABLE_ATTRIBUTES);
183
184         final NodeIdentifierWithPredicates tableKey = RibSupportUtils.toYangTablesKey(key);
185         final InstanceIdentifierBuilder tableId = YangInstanceIdentifier.builder(this.yangRibId.node(LocRib.QNAME).node(Tables.QNAME));
186         tableId.nodeWithKey(tableKey.getNodeType(), tableKey.getKeyValues());
187         for (final Entry<QName, Object> e : tableKey.getKeyValues().entrySet()) {
188             table.withChild(ImmutableNodes.leafNode(e.getKey(), e.getValue()));
189         }
190
191         final ChoiceNode routes = this.ribContextRegistry.getRIBSupportContext(key).getRibSupport().emptyRoutes();
192         table.withChild(routes);
193
194         tx.put(LogicalDatastoreType.OPERATIONAL, tableId.build(), table.build());
195         try {
196             tx.submit().checkedGet();
197         } catch (final TransactionCommitFailedException e1) {
198             LOG.error("Failed to initiate LocRIB for key {}", key, e1);
199         }
200
201         PathSelectionMode pathSelectionStrategy = this.bestPathSelectionStrategies.get(key);
202         if (pathSelectionStrategy == null) {
203             pathSelectionStrategy = BasePathSelectionModeFactory.createBestPathSelectionStrategy();
204         }
205
206         this.locRibs.add(LocRibWriter.create(this.ribContextRegistry, key, createPeerChain(this), getYangRibId(), this.localAs, getService(),
207             this.exportPolicyPeerTrackerMap.get(key), pathSelectionStrategy, this.renderStats.getLocRibRouteCounter().init(key)));
208     }
209
210     @Override
211     public String toString() {
212         return addToStringAttributes(MoreObjects.toStringHelper(this)).toString();
213     }
214
215     protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
216         return toStringHelper;
217     }
218
219     @Override
220     public synchronized void close() throws Exception {
221         if (this.registration != null) {
222             this.registration.close();
223             this.registration = null;
224         }
225         if(this.domChain != null) {
226             this.domChain.close();
227         }
228     }
229
230     @Override
231     public AsNumber getLocalAs() {
232         return this.localAs;
233     }
234
235     @Override
236     public BgpId getBgpIdentifier() {
237         return this.bgpIdentifier;
238     }
239
240     @Nonnull
241     @Override
242     public Set<? extends BgpTableType> getLocalTables() {
243         return this.localTables;
244     }
245
246     @Override
247     public BGPDispatcher getDispatcher() {
248         return this.dispatcher;
249     }
250
251     @Override
252     public void onTransactionChainFailed(final TransactionChain<?, ?> chain, final AsyncTransaction<?, ?> transaction, final Throwable cause) {
253         LOG.error("Broken chain in RIB {} transaction {}", getInstanceIdentifier(), transaction != null ? transaction.getIdentifier() : null, cause);
254     }
255
256     @Override
257     public void onTransactionChainSuccessful(final TransactionChain<?, ?> chain) {
258         LOG.info("RIB {} closed successfully", getInstanceIdentifier());
259     }
260
261     @Override
262     public Set<TablesKey> getLocalTablesKeys() {
263         return this.localTablesKeys;
264     }
265
266     @Override
267     public DOMDataTreeChangeService getService() {
268         return (DOMDataTreeChangeService) this.service;
269     }
270
271     @Override
272     public BGPRenderStats getRenderStats() {
273         return this.renderStats;
274     }
275
276     @Override
277     public YangInstanceIdentifier getYangRibId() {
278         return this.yangRibId;
279     }
280
281     @Override
282     public DOMTransactionChain createPeerChain(final TransactionChainListener listener) {
283         return this.domDataBroker.createTransactionChain(listener);
284     }
285
286     @Override
287     public RIBExtensionConsumerContext getRibExtensions() {
288         return this.extensions;
289     }
290
291     @Override
292     public RIBSupportContextRegistry getRibSupportContext() {
293         return this.ribContextRegistry;
294     }
295
296     @Override
297     public void onGlobalContextUpdated(final SchemaContext context) {
298         this.codecsRegistry.onSchemaContextUpdated(context);
299     }
300
301     @Override
302     public CodecsRegistry getCodecsRegistry() {
303         return this.codecsRegistry;
304     }
305
306     @Override
307     public Optional<BGPOpenConfigProvider> getOpenConfigProvider() {
308         return Optional.fromNullable(this.openConfigProvider);
309     }
310
311     @Override
312     public ImportPolicyPeerTracker getImportPolicyPeerTracker() {
313         return this.importPolicyPeerTracker;
314     }
315
316     @Override
317     public ExportPolicyPeerTracker getExportPolicyPeerTracker(final TablesKey tablesKey) {
318         return this.exportPolicyPeerTrackerMap.get(tablesKey);
319     }
320
321     @Override
322     public void instantiateServiceInstance() {
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         for (final LocRibWriter locRib : this.locRibs) {
365             try {
366                 locRib.close();
367             } catch (final Exception e) {
368                 LOG.warn("Could not close LocalRib reference: {}", locRib, e);
369             }
370         }
371         try {
372             final DOMDataWriteTransaction t = this.domChain.newWriteOnlyTransaction();
373             t.delete(LogicalDatastoreType.OPERATIONAL, getYangRibId());
374             t.submit().checkedGet();
375         } catch (final TransactionCommitFailedException e) {
376             LOG.warn("Failed to remove RIB instance {} from DS.", getYangRibId(), e);
377         }
378         this.renderStats.getLocRibRouteCounter().resetAll();
379
380         if (this.configModuleTracker != null) {
381             this.configModuleTracker.onInstanceClose();
382         }
383         return Futures.immediateFuture(null);
384     }
385
386     @Override
387     public ServiceGroupIdentifier getIdentifier() {
388         return this.serviceGroupIdentifier;
389     }
390
391     @Override
392     public ClusterSingletonServiceRegistration registerClusterSingletonService(final ClusterSingletonService clusterSingletonService) {
393         return this.provider.registerClusterSingletonService(clusterSingletonService);
394     }
395
396     @Override
397     public ServiceGroupIdentifier getRibIServiceGroupIdentifier() {
398         return getIdentifier();
399     }
400 }