BUG-5685: Register BGP Peer Cluster Singleton Service
[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.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.HashSet;
19 import java.util.List;
20 import java.util.Map;
21 import java.util.Map.Entry;
22 import java.util.Set;
23 import javax.annotation.Nonnull;
24 import javax.annotation.Nullable;
25 import javax.annotation.concurrent.ThreadSafe;
26 import org.opendaylight.controller.config.yang.bgp.rib.impl.RIBImplRuntimeRegistration;
27 import org.opendaylight.controller.config.yang.bgp.rib.impl.RIBImplRuntimeRegistrator;
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.CacheDisconnectedPeers;
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 PolicyDatabase policyDatabase;
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 CacheDisconnectedPeers cacheDisconnectedPeers;
116     private final Map<TablesKey, PathSelectionMode> bestPathSelectionStrategies;
117     private final ImportPolicyPeerTracker importPolicyPeerTracker;
118     private final RIBImplRuntimeMXBeanImpl renderStats;
119     private final RibId ribId;
120
121     public RIBImpl(final ClusterSingletonServiceProvider provider, final RibId ribId, final AsNumber localAs, final BgpId localBgpId,
122         final ClusterIdentifier clusterId, final RIBExtensionConsumerContext extensions, final BGPDispatcher dispatcher,
123         final BindingCodecTreeFactory codecFactory, final DOMDataBroker domDataBroker, final List<BgpTableType> localTables,
124         @Nonnull final Map<TablesKey, PathSelectionMode> bestPathSelectionStrategies, final GeneratedClassLoadingStrategy classStrategy,
125         final BGPConfigModuleTracker moduleTracker, final BGPOpenConfigProvider openConfigProvider, 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.cacheDisconnectedPeers = new CacheDisconnectedPeersImpl();
144         this.bestPathSelectionStrategies = Preconditions.checkNotNull(bestPathSelectionStrategies);
145         final ClusterIdentifier cId = (clusterId == null) ? new ClusterIdentifier(localBgpId) : clusterId;
146         this.renderStats = new RIBImplRuntimeMXBeanImpl(localBgpId, ribId, localAs, cId);
147         this.ribId = ribId;
148         this.policyDatabase  = new PolicyDatabase(this.localAs.getValue(), localBgpId, cId);
149         this.importPolicyPeerTracker = new ImportPolicyPeerTrackerImpl( this.policyDatabase);
150         this.serviceGroupIdentifier = ServiceGroupIdentifier.create(this.ribId + "-service-group");
151         Preconditions.checkNotNull(provider, "ClusterSingletonServiceProvider is null");
152         this.provider = provider;
153         LOG.info("RIB Singleton Service {} registered", this.getIdentifier());
154         this.registration = registerClusterSingletonService(this);
155         this.configurationWriter = configurationWriter;
156     }
157
158     public RIBImpl(final ClusterSingletonServiceProvider provider, final RibId ribId, final AsNumber localAs, final BgpId localBgpId, @Nullable final ClusterIdentifier clusterId,
159         final RIBExtensionConsumerContext extensions, final BGPDispatcher dispatcher, final BindingCodecTreeFactory codecFactory,
160         final DOMDataBroker domDataBroker, final List<BgpTableType> localTables, final Map<TablesKey, PathSelectionMode> bestPathSelectionstrategies,
161         final GeneratedClassLoadingStrategy classStrategy, final BgpDeployer.WriteConfiguration configurationWriter) {
162         this(provider, ribId, localAs, localBgpId, clusterId, extensions, dispatcher, codecFactory,
163                 domDataBroker, localTables, bestPathSelectionstrategies, classStrategy, null, null, configurationWriter);
164     }
165
166     private void startLocRib(final TablesKey key, final PolicyDatabase pd) {
167         LOG.debug("Creating LocRib table for {}", key);
168         // create locRibWriter for each table
169         final DOMDataWriteTransaction tx = this.domChain.newWriteOnlyTransaction();
170
171         final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> table = ImmutableNodes.mapEntryBuilder();
172         table.withNodeIdentifier(RibSupportUtils.toYangTablesKey(key));
173         table.withChild(EMPTY_TABLE_ATTRIBUTES);
174
175         final NodeIdentifierWithPredicates tableKey = RibSupportUtils.toYangTablesKey(key);
176         final InstanceIdentifierBuilder tableId = YangInstanceIdentifier.builder(this.yangRibId.node(LocRib.QNAME).node(Tables.QNAME));
177         tableId.nodeWithKey(tableKey.getNodeType(), tableKey.getKeyValues());
178         for (final Entry<QName, Object> e : tableKey.getKeyValues().entrySet()) {
179             table.withChild(ImmutableNodes.leafNode(e.getKey(), e.getValue()));
180         }
181
182         final ChoiceNode routes = this.ribContextRegistry.getRIBSupportContext(key).getRibSupport().emptyRoutes();
183         table.withChild(routes);
184
185         tx.put(LogicalDatastoreType.OPERATIONAL, tableId.build(), table.build());
186         try {
187             tx.submit().checkedGet();
188         } catch (final TransactionCommitFailedException e1) {
189             LOG.error("Failed to initiate LocRIB for key {}", key, e1);
190         }
191
192         PathSelectionMode pathSelectionStrategy = this.bestPathSelectionStrategies.get(key);
193         if (pathSelectionStrategy == null) {
194             pathSelectionStrategy = BasePathSelectionModeFactory.createBestPathSelectionStrategy();
195         }
196
197         this.locRibs.add(LocRibWriter.create(this.ribContextRegistry, key, createPeerChain(this), getYangRibId(), this.localAs, getService(), pd,
198                 this.cacheDisconnectedPeers, pathSelectionStrategy, this.renderStats.getLocRibRouteCounter().init(key)));
199     }
200
201     @Override
202     public String toString() {
203         return addToStringAttributes(MoreObjects.toStringHelper(this)).toString();
204     }
205
206     protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
207         return toStringHelper;
208     }
209
210     @Override
211     public synchronized void close() throws Exception {
212         this.domChain.close();
213         if (registration != null) {
214             registration.close();
215             registration = null;
216         }
217     }
218
219     @Override
220     public AsNumber getLocalAs() {
221         return this.localAs;
222     }
223
224     @Override
225     public BgpId getBgpIdentifier() {
226         return this.bgpIdentifier;
227     }
228
229     @Override
230     public Set<? extends BgpTableType> getLocalTables() {
231         return this.localTables;
232     }
233
234     @Override
235     public BGPDispatcher getDispatcher() {
236         return this.dispatcher;
237     }
238
239     @Override
240     public void onTransactionChainFailed(final TransactionChain<?, ?> chain, final AsyncTransaction<?, ?> transaction, final Throwable cause) {
241         LOG.error("Broken chain in RIB {} transaction {}", getInstanceIdentifier(), transaction != null ? transaction.getIdentifier() : null, cause);
242     }
243
244     @Override
245     public void onTransactionChainSuccessful(final TransactionChain<?, ?> chain) {
246         LOG.info("RIB {} closed successfully", getInstanceIdentifier());
247     }
248
249     @Override
250     public Set<TablesKey> getLocalTablesKeys() {
251         return this.localTablesKeys;
252     }
253
254     @Override
255     public DOMDataTreeChangeService getService() {
256         return (DOMDataTreeChangeService) this.service;
257     }
258
259     @Override
260     public BGPRenderStats getRenderStats() {
261         return this.renderStats;
262     }
263
264     @Override
265     public YangInstanceIdentifier getYangRibId() {
266         return this.yangRibId;
267     }
268
269     @Override
270     public DOMTransactionChain createPeerChain(final TransactionChainListener listener) {
271         return this.domDataBroker.createTransactionChain(listener);
272     }
273
274     @Override
275     public RIBExtensionConsumerContext getRibExtensions() {
276         return this.extensions;
277     }
278
279     @Override
280     public RIBSupportContextRegistry getRibSupportContext() {
281         return this.ribContextRegistry;
282     }
283
284     @Override
285     public void onGlobalContextUpdated(final SchemaContext context) {
286         this.codecsRegistry.onSchemaContextUpdated(context);
287     }
288
289     @Override
290     public CodecsRegistry getCodecsRegistry() {
291         return this.codecsRegistry;
292     }
293
294     @Override
295     public Optional<BGPOpenConfigProvider> getOpenConfigProvider() {
296         return Optional.fromNullable(this.openConfigProvider);
297     }
298
299     @Override
300     public CacheDisconnectedPeers getCacheDisconnectedPeers() {
301         return this.cacheDisconnectedPeers;
302     }
303
304     @Override
305     public ImportPolicyPeerTracker getImportPolicyPeerTracker() {
306         return this.importPolicyPeerTracker;
307     }
308
309     @Override
310     public void instantiateServiceInstance() {
311         if(this.configurationWriter != null) {
312             this.configurationWriter.apply();
313         }
314         LOG.info("RIB Singleton Service {} instantiated", this.getIdentifier());
315         LOG.debug("Instantiating RIB table {} at {}", this.ribId , this.yangRibId);
316
317         final ContainerNode bgpRib = Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(BgpRib.QNAME))
318             .addChild(ImmutableNodes.mapNodeBuilder(Rib.QNAME).build()).build();
319
320         final MapEntryNode ribInstance = Builders.mapEntryBuilder().withNodeIdentifier(
321             new NodeIdentifierWithPredicates(Rib.QNAME, RIB_ID_QNAME, this.ribId .getValue()))
322             .addChild(ImmutableNodes.leafNode(RIB_ID_QNAME, this.ribId .getValue()))
323             .addChild(ImmutableNodes.mapNodeBuilder(Peer.QNAME).build())
324             .addChild(Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(LocRib.QNAME))
325                 .addChild(ImmutableNodes.mapNodeBuilder(Tables.QNAME).build())
326                 .build()).build();
327
328
329         final DOMDataWriteTransaction trans = this.domChain.newWriteOnlyTransaction();
330
331         // merge empty BgpRib + Rib, to make sure the top-level parent structure is present
332         trans.merge(LogicalDatastoreType.OPERATIONAL, YangInstanceIdentifier.builder().node(BgpRib.QNAME).build(), bgpRib);
333         trans.put(LogicalDatastoreType.OPERATIONAL, this.yangRibId, ribInstance);
334
335         try {
336             trans.submit().checkedGet();
337         } catch (final TransactionCommitFailedException e) {
338             LOG.error("Failed to initiate RIB {}", this.yangRibId, e);
339         }
340
341         LOG.debug("Effective RIB created.");
342
343         for (final BgpTableType t : this.localTables) {
344             final TablesKey key = new TablesKey(t.getAfi(), t.getSafi());
345             this.localTablesKeys.add(key);
346             startLocRib(key, policyDatabase);
347         }
348
349         if (this.configModuleTracker != null) {
350             this.configModuleTracker.onInstanceCreate();
351         }
352     }
353
354     @Override
355     public ListenableFuture<Void> closeServiceInstance() {
356         LOG.info("Close RIB Singleton Service {}", this.getIdentifier());
357         for (final LocRibWriter locRib : this.locRibs) {
358             try {
359                 locRib.close();
360             } catch (final Exception e) {
361                 LOG.warn("Could not close LocalRib reference: {}", locRib, e);
362             }
363         }
364         try {
365             final DOMDataWriteTransaction t = this.domChain.newWriteOnlyTransaction();
366             t.delete(LogicalDatastoreType.OPERATIONAL, getYangRibId());
367             t.submit().checkedGet();
368         } catch (final TransactionCommitFailedException e) {
369             LOG.warn("Failed to remove RIB instance {} from DS.", getYangRibId(), e);
370         }
371         this.renderStats.getLocRibRouteCounter().resetAll();
372
373         if (this.configModuleTracker != null) {
374             this.configModuleTracker.onInstanceClose();
375         }
376         return Futures.immediateFuture(null);
377     }
378
379     @Override
380     public ServiceGroupIdentifier getIdentifier() {
381         return this.serviceGroupIdentifier;
382     }
383
384     @Override
385     public ClusterSingletonServiceRegistration registerClusterSingletonService(final ClusterSingletonService clusterSingletonService) {
386         return this.provider.registerClusterSingletonService(clusterSingletonService);
387     }
388
389     @Override
390     public ServiceGroupIdentifier getRibIServiceGroupIdentifier() {
391         return getIdentifier();
392     }
393 }