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