BUG-6647 Increase code coverage and clean up IV
[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.md.sal.common.api.data.AsyncTransaction;
27 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
28 import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
29 import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
30 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
31 import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
32 import org.opendaylight.controller.md.sal.dom.api.DOMDataBrokerExtension;
33 import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeService;
34 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
35 import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
36 import org.opendaylight.mdsal.binding.dom.codec.api.BindingCodecTreeFactory;
37 import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonService;
38 import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceProvider;
39 import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceRegistration;
40 import org.opendaylight.mdsal.singleton.common.api.ServiceGroupIdentifier;
41 import org.opendaylight.protocol.bgp.mode.api.PathSelectionMode;
42 import org.opendaylight.protocol.bgp.mode.impl.base.BasePathSelectionModeFactory;
43 import org.opendaylight.protocol.bgp.openconfig.spi.BGPConfigModuleTracker;
44 import org.opendaylight.protocol.bgp.openconfig.spi.BGPOpenConfigProvider;
45 import org.opendaylight.protocol.bgp.rib.DefaultRibReference;
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.RIBSupportContextRegistry;
52 import org.opendaylight.protocol.bgp.rib.impl.stats.rib.impl.BGPRenderStats;
53 import org.opendaylight.protocol.bgp.rib.impl.stats.rib.impl.RIBImplRuntimeMXBeanImpl;
54 import org.opendaylight.protocol.bgp.rib.spi.CacheDisconnectedPeers;
55 import org.opendaylight.protocol.bgp.rib.spi.RIBExtensionConsumerContext;
56 import org.opendaylight.protocol.bgp.rib.spi.RibSupportUtils;
57 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.AsNumber;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.BgpTableType;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.BgpRib;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.RibId;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.Rib;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.RibKey;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.LocRib;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.Peer;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.Tables;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.TablesKey;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.BgpId;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.ClusterIdentifier;
69 import org.opendaylight.yangtools.sal.binding.generator.impl.GeneratedClassLoadingStrategy;
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 DefaultRibReference implements ClusterSingletonService, AutoCloseable, RIB, TransactionChainListener, SchemaContextListener {
89     private static final Logger LOG = LoggerFactory.getLogger(RIBImpl.class);
90     private static final QName RIB_ID_QNAME = QName.create(Rib.QNAME, "id").intern();
91     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);
92
93     private final BGPDispatcher dispatcher;
94     private final DOMTransactionChain domChain;
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 PolicyDatabase policyDatabase;
107     private final BgpDeployer.WriteConfiguration configurationWriter;
108     private ClusterSingletonServiceRegistration registration;
109     private final DOMDataBrokerExtension service;
110     private final List<LocRibWriter> locRibs = new ArrayList<>();
111     private final BGPConfigModuleTracker configModuleTracker;
112     private final BGPOpenConfigProvider openConfigProvider;
113     private final CacheDisconnectedPeers cacheDisconnectedPeers;
114     private final Map<TablesKey, PathSelectionMode> bestPathSelectionStrategies;
115     private final ImportPolicyPeerTracker importPolicyPeerTracker;
116     private final RIBImplRuntimeMXBeanImpl renderStats;
117     private final RibId ribId;
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 BGPConfigModuleTracker moduleTracker, final BGPOpenConfigProvider openConfigProvider,
124         final BgpDeployer.WriteConfiguration configurationWriter) {
125
126         super(InstanceIdentifier.create(BgpRib.class).child(Rib.class, new RibKey(Preconditions.checkNotNull(ribId))));
127         this.domChain = domDataBroker.createTransactionChain(this);
128         this.localAs = Preconditions.checkNotNull(localAs);
129         this.bgpIdentifier = Preconditions.checkNotNull(localBgpId);
130         this.dispatcher = Preconditions.checkNotNull(dispatcher);
131         this.localTables = ImmutableSet.copyOf(localTables);
132         this.localTablesKeys = new HashSet<>();
133         this.domDataBroker = Preconditions.checkNotNull(domDataBroker);
134         this.service = this.domDataBroker.getSupportedExtensions().get(DOMDataTreeChangeService.class);
135         this.extensions = Preconditions.checkNotNull(extensions);
136         this.codecsRegistry = CodecsRegistryImpl.create(codecFactory, classStrategy);
137         this.ribContextRegistry = RIBSupportContextRegistryImpl.create(extensions, this.codecsRegistry);
138         final InstanceIdentifierBuilder yangRibIdBuilder = YangInstanceIdentifier.builder().node(BgpRib.QNAME).node(Rib.QNAME);
139         this.yangRibId = yangRibIdBuilder.nodeWithKey(Rib.QNAME, RIB_ID_QNAME, ribId.getValue()).build();
140         this.configModuleTracker = moduleTracker;
141         this.openConfigProvider = openConfigProvider;
142         this.cacheDisconnectedPeers = new CacheDisconnectedPeersImpl();
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         this.policyDatabase  = new PolicyDatabase(this.localAs.getValue(), localBgpId, cId);
148         this.importPolicyPeerTracker = new ImportPolicyPeerTrackerImpl( this.policyDatabase);
149         this.serviceGroupIdentifier = ServiceGroupIdentifier.create(this.ribId + "-service-group");
150         Preconditions.checkNotNull(provider, "ClusterSingletonServiceProvider is null");
151         this.provider = provider;
152         this.configurationWriter = configurationWriter;
153         LOG.info("RIB Singleton Service {} registered", getIdentifier());
154         //this need to be always the last step
155         this.registration = registerClusterSingletonService(this);
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         if (this.registration != null) {
213             this.registration.close();
214             this.registration = null;
215         }
216         if(this.domChain != null) {
217             this.domChain.close();
218         }
219     }
220
221     @Override
222     public AsNumber getLocalAs() {
223         return this.localAs;
224     }
225
226     @Override
227     public BgpId getBgpIdentifier() {
228         return this.bgpIdentifier;
229     }
230
231     @Nonnull
232     @Override
233     public Set<? extends BgpTableType> getLocalTables() {
234         return this.localTables;
235     }
236
237     @Override
238     public BGPDispatcher getDispatcher() {
239         return this.dispatcher;
240     }
241
242     @Override
243     public void onTransactionChainFailed(final TransactionChain<?, ?> chain, final AsyncTransaction<?, ?> transaction, final Throwable cause) {
244         LOG.error("Broken chain in RIB {} transaction {}", getInstanceIdentifier(), transaction != null ? transaction.getIdentifier() : null, cause);
245     }
246
247     @Override
248     public void onTransactionChainSuccessful(final TransactionChain<?, ?> chain) {
249         LOG.info("RIB {} closed successfully", getInstanceIdentifier());
250     }
251
252     @Override
253     public Set<TablesKey> getLocalTablesKeys() {
254         return this.localTablesKeys;
255     }
256
257     @Override
258     public DOMDataTreeChangeService getService() {
259         return (DOMDataTreeChangeService) this.service;
260     }
261
262     @Override
263     public BGPRenderStats getRenderStats() {
264         return this.renderStats;
265     }
266
267     @Override
268     public YangInstanceIdentifier getYangRibId() {
269         return this.yangRibId;
270     }
271
272     @Override
273     public DOMTransactionChain createPeerChain(final TransactionChainListener listener) {
274         return this.domDataBroker.createTransactionChain(listener);
275     }
276
277     @Override
278     public RIBExtensionConsumerContext getRibExtensions() {
279         return this.extensions;
280     }
281
282     @Override
283     public RIBSupportContextRegistry getRibSupportContext() {
284         return this.ribContextRegistry;
285     }
286
287     @Override
288     public void onGlobalContextUpdated(final SchemaContext context) {
289         this.codecsRegistry.onSchemaContextUpdated(context);
290     }
291
292     @Override
293     public CodecsRegistry getCodecsRegistry() {
294         return this.codecsRegistry;
295     }
296
297     @Override
298     public Optional<BGPOpenConfigProvider> getOpenConfigProvider() {
299         return Optional.fromNullable(this.openConfigProvider);
300     }
301
302     @Override
303     public CacheDisconnectedPeers getCacheDisconnectedPeers() {
304         return this.cacheDisconnectedPeers;
305     }
306
307     @Override
308     public ImportPolicyPeerTracker getImportPolicyPeerTracker() {
309         return this.importPolicyPeerTracker;
310     }
311
312     @Override
313     public void instantiateServiceInstance() {
314         if(this.configurationWriter != null) {
315             this.configurationWriter.apply();
316         }
317         LOG.info("RIB Singleton Service {} instantiated", getIdentifier());
318         LOG.debug("Instantiating RIB table {} at {}", this.ribId , this.yangRibId);
319
320         final ContainerNode bgpRib = Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(BgpRib.QNAME))
321             .addChild(ImmutableNodes.mapNodeBuilder(Rib.QNAME).build()).build();
322
323         final MapEntryNode ribInstance = Builders.mapEntryBuilder().withNodeIdentifier(
324             new NodeIdentifierWithPredicates(Rib.QNAME, RIB_ID_QNAME, this.ribId .getValue()))
325             .addChild(ImmutableNodes.leafNode(RIB_ID_QNAME, this.ribId .getValue()))
326             .addChild(ImmutableNodes.mapNodeBuilder(Peer.QNAME).build())
327             .addChild(Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(LocRib.QNAME))
328                 .addChild(ImmutableNodes.mapNodeBuilder(Tables.QNAME).build())
329                 .build()).build();
330
331
332         final DOMDataWriteTransaction trans = this.domChain.newWriteOnlyTransaction();
333
334         // merge empty BgpRib + Rib, to make sure the top-level parent structure is present
335         trans.merge(LogicalDatastoreType.OPERATIONAL, YangInstanceIdentifier.builder().node(BgpRib.QNAME).build(), bgpRib);
336         trans.put(LogicalDatastoreType.OPERATIONAL, this.yangRibId, ribInstance);
337
338         try {
339             trans.submit().checkedGet();
340         } catch (final TransactionCommitFailedException e) {
341             LOG.error("Failed to initiate RIB {}", this.yangRibId, e);
342         }
343
344         LOG.debug("Effective RIB created.");
345
346         for (final BgpTableType t : this.localTables) {
347             final TablesKey key = new TablesKey(t.getAfi(), t.getSafi());
348             this.localTablesKeys.add(key);
349             startLocRib(key, this.policyDatabase);
350         }
351
352         if (this.configModuleTracker != null) {
353             this.configModuleTracker.onInstanceCreate();
354         }
355     }
356
357     @Override
358     public ListenableFuture<Void> closeServiceInstance() {
359         LOG.info("Close RIB Singleton Service {}", getIdentifier());
360         for (final LocRibWriter locRib : this.locRibs) {
361             try {
362                 locRib.close();
363             } catch (final Exception e) {
364                 LOG.warn("Could not close LocalRib reference: {}", locRib, e);
365             }
366         }
367         try {
368             final DOMDataWriteTransaction t = this.domChain.newWriteOnlyTransaction();
369             t.delete(LogicalDatastoreType.OPERATIONAL, getYangRibId());
370             t.submit().checkedGet();
371         } catch (final TransactionCommitFailedException e) {
372             LOG.warn("Failed to remove RIB instance {} from DS.", getYangRibId(), e);
373         }
374         this.renderStats.getLocRibRouteCounter().resetAll();
375
376         if (this.configModuleTracker != null) {
377             this.configModuleTracker.onInstanceClose();
378         }
379         return Futures.immediateFuture(null);
380     }
381
382     @Override
383     public ServiceGroupIdentifier getIdentifier() {
384         return this.serviceGroupIdentifier;
385     }
386
387     @Override
388     public ClusterSingletonServiceRegistration registerClusterSingletonService(final ClusterSingletonService clusterSingletonService) {
389         return this.provider.registerClusterSingletonService(clusterSingletonService);
390     }
391
392     @Override
393     public ServiceGroupIdentifier getRibIServiceGroupIdentifier() {
394         return getIdentifier();
395     }
396 }