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