Replace Preconditions.CheckNotNull per RequireNonNull
[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.RIBSupportContextRegistry;
52 import org.opendaylight.protocol.bgp.rib.impl.state.BGPRIBStateImpl;
53 import org.opendaylight.protocol.bgp.rib.impl.stats.rib.impl.BGPRenderStats;
54 import org.opendaylight.protocol.bgp.rib.impl.stats.rib.impl.RIBImplRuntimeMXBeanImpl;
55 import org.opendaylight.protocol.bgp.rib.spi.ExportPolicyPeerTracker;
56 import org.opendaylight.protocol.bgp.rib.spi.RIBExtensionConsumerContext;
57 import org.opendaylight.protocol.bgp.rib.spi.RibSupportUtils;
58 import org.opendaylight.protocol.bgp.rib.spi.util.ClusterSingletonServiceRegistrationHelper;
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.yang.binding.InstanceIdentifier;
72 import org.opendaylight.yangtools.yang.common.QName;
73 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
74 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.InstanceIdentifierBuilder;
75 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
76 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
77 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
78 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
79 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
80 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
81 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
82 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
83 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
84 import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
85 import org.slf4j.Logger;
86 import org.slf4j.LoggerFactory;
87
88 @ThreadSafe
89 public final class RIBImpl extends BGPRIBStateImpl implements ClusterSingletonService, RIB, TransactionChainListener,
90     SchemaContextListener, AutoCloseable {
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     private static final int MAX_REGISTRATION_ATTEMPTS = 10;
95     private static final int SLEEP_TIME = MAX_REGISTRATION_ATTEMPTS;
96
97     private final BGPDispatcher dispatcher;
98     private final AsNumber localAs;
99     private final BgpId bgpIdentifier;
100     private final Set<BgpTableType> localTables;
101     private final Set<TablesKey> localTablesKeys;
102     private final DOMDataBroker domDataBroker;
103     private final RIBExtensionConsumerContext extensions;
104     private final YangInstanceIdentifier yangRibId;
105     private final RIBSupportContextRegistryImpl ribContextRegistry;
106     private final CodecsRegistryImpl codecsRegistry;
107     private final ServiceGroupIdentifier serviceGroupIdentifier;
108     private final ClusterSingletonServiceProvider provider;
109     private final BgpDeployer.WriteConfiguration configurationWriter;
110     private ClusterSingletonServiceRegistration registration;
111     private final DOMDataBrokerExtension service;
112     private final Map<TransactionChain<?, ?>, LocRibWriter> txChainToLocRibWriter = new HashMap<>();
113     private final Map<TablesKey, PathSelectionMode> bestPathSelectionStrategies;
114     private final ImportPolicyPeerTracker importPolicyPeerTracker;
115     private final RIBImplRuntimeMXBeanImpl renderStats;
116     private final RibId ribId;
117     private final Map<TablesKey, ExportPolicyPeerTracker> exportPolicyPeerTrackerMap;
118
119     private DOMTransactionChain domChain;
120     @GuardedBy("this")
121     private boolean isServiceInstantiated;
122
123     public RIBImpl(final ClusterSingletonServiceProvider provider, final RibId ribId, final AsNumber localAs, final BgpId localBgpId,
124         final ClusterIdentifier clusterId, final RIBExtensionConsumerContext extensions, final BGPDispatcher dispatcher,
125         final BindingCodecTreeFactory codecFactory, final DOMDataBroker domDataBroker, final List<BgpTableType> localTables,
126         @Nonnull final Map<TablesKey, PathSelectionMode> bestPathSelectionStrategies, final GeneratedClassLoadingStrategy classStrategy,
127         final BgpDeployer.WriteConfiguration configurationWriter) {
128         super(InstanceIdentifier.create(BgpRib.class).child(Rib.class, new RibKey(requireNonNull(ribId))),
129             localBgpId, localAs);
130         this.localAs = requireNonNull(localAs);
131         this.bgpIdentifier = requireNonNull(localBgpId);
132         this.dispatcher = requireNonNull(dispatcher);
133         this.localTables = ImmutableSet.copyOf(localTables);
134         this.localTablesKeys = new HashSet<>();
135         this.domDataBroker = requireNonNull(domDataBroker);
136         this.service = this.domDataBroker.getSupportedExtensions().get(DOMDataTreeChangeService.class);
137         this.extensions = requireNonNull(extensions);
138         this.codecsRegistry = CodecsRegistryImpl.create(codecFactory, classStrategy);
139         this.ribContextRegistry = RIBSupportContextRegistryImpl.create(extensions, this.codecsRegistry);
140         final InstanceIdentifierBuilder yangRibIdBuilder = YangInstanceIdentifier.builder().node(BgpRib.QNAME).node(Rib.QNAME);
141         this.yangRibId = yangRibIdBuilder.nodeWithKey(Rib.QNAME, RIB_ID_QNAME, ribId.getValue()).build();
142         this.bestPathSelectionStrategies = requireNonNull(bestPathSelectionStrategies);
143         final ClusterIdentifier cId = clusterId == null ? new ClusterIdentifier(localBgpId) : clusterId;
144         this.ribId = ribId;
145         final PolicyDatabase policyDatabase = new PolicyDatabase(this.localAs.getValue(), localBgpId, cId);
146         this.importPolicyPeerTracker = new ImportPolicyPeerTrackerImpl(policyDatabase);
147         this.serviceGroupIdentifier = ServiceGroupIdentifier.create(this.ribId.getValue() + "-service-group");
148         requireNonNull(provider, "ClusterSingletonServiceProvider is null");
149         this.provider = provider;
150         this.configurationWriter = configurationWriter;
151
152         final ImmutableMap.Builder<TablesKey, ExportPolicyPeerTracker> exportPolicies = new ImmutableMap.Builder<>();
153         for (final BgpTableType t : this.localTables) {
154             final TablesKey key = new TablesKey(t.getAfi(), t.getSafi());
155             this.localTablesKeys.add(key);
156             exportPolicies.put(key, new ExportPolicyPeerTrackerImpl(policyDatabase, key));
157         }
158         this.exportPolicyPeerTrackerMap = exportPolicies.build();
159
160         this.renderStats = new RIBImplRuntimeMXBeanImpl(localBgpId, ribId, localAs, cId, this, this.localTablesKeys);
161         LOG.info("RIB Singleton Service {} registered, RIB {}", getIdentifier().getValue(), this.ribId.getValue());
162         //this need to be always the last step
163         this.registration = registerClusterSingletonService(this);
164     }
165
166     private void startLocRib(final TablesKey key) {
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         createLocRibWriter(key);
192     }
193
194     private synchronized void createLocRibWriter(final TablesKey key) {
195         LOG.debug("Creating LocRIB writer for key {}", key);
196         final DOMTransactionChain txChain = createPeerChain(this);
197         PathSelectionMode pathSelectionStrategy = this.bestPathSelectionStrategies.get(key);
198         if (pathSelectionStrategy == null) {
199             pathSelectionStrategy = BasePathSelectionModeFactory.createBestPathSelectionStrategy();
200         }
201
202         final LocRibWriter locRibWriter = LocRibWriter.create(this.ribContextRegistry, key, txChain,
203             getYangRibId(), this.localAs, getService(), this.exportPolicyPeerTrackerMap.get(key), pathSelectionStrategy);
204         registerTotalPathCounter(key, locRibWriter);
205         registerTotalPrefixesCounter(key, locRibWriter);
206         this.txChainToLocRibWriter.put(txChain, locRibWriter);
207     }
208
209     @Override
210     public String toString() {
211         return addToStringAttributes(MoreObjects.toStringHelper(this)).toString();
212     }
213
214     protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
215         return toStringHelper;
216     }
217
218     @Override
219     public synchronized void close() throws Exception {
220         if (this.registration != null) {
221             this.registration.close();
222             this.registration = null;
223         }
224     }
225
226     @Override
227     public AsNumber getLocalAs() {
228         return this.localAs;
229     }
230
231     @Override
232     public BgpId getBgpIdentifier() {
233         return this.bgpIdentifier;
234     }
235
236     @Nonnull
237     @Override
238     public Set<? extends BgpTableType> getLocalTables() {
239         return this.localTables;
240     }
241
242     @Override
243     public BGPDispatcher getDispatcher() {
244         return this.dispatcher;
245     }
246
247     @Override
248     public synchronized void onTransactionChainFailed(final TransactionChain<?, ?> chain, final AsyncTransaction<?, ?> transaction, final Throwable cause) {
249         LOG.error("Broken chain in RIB {} transaction {}", getInstanceIdentifier(), transaction != null ? transaction.getIdentifier() : null, cause);
250         if (this.txChainToLocRibWriter.containsKey(chain)) {
251             final LocRibWriter locRibWriter = this.txChainToLocRibWriter.remove(chain);
252             final DOMTransactionChain newChain = createPeerChain(this);
253             locRibWriter.restart(newChain);
254             this.txChainToLocRibWriter.put(newChain, locRibWriter);
255         }
256     }
257
258     @Override
259     public void onTransactionChainSuccessful(final TransactionChain<?, ?> chain) {
260         LOG.info("RIB {} closed successfully", getInstanceIdentifier());
261     }
262
263     @Override
264     public Set<TablesKey> getLocalTablesKeys() {
265         return this.localTablesKeys;
266     }
267
268     @Override
269     public DOMDataTreeChangeService getService() {
270         return (DOMDataTreeChangeService) this.service;
271     }
272
273     @Override
274     public BGPRenderStats getRenderStats() {
275         return this.renderStats;
276     }
277
278     @Override
279     public YangInstanceIdentifier getYangRibId() {
280         return this.yangRibId;
281     }
282
283     @Override
284     public DOMTransactionChain createPeerChain(final TransactionChainListener listener) {
285         return this.domDataBroker.createTransactionChain(listener);
286     }
287
288     @Override
289     public RIBExtensionConsumerContext getRibExtensions() {
290         return this.extensions;
291     }
292
293     @Override
294     public RIBSupportContextRegistry getRibSupportContext() {
295         return this.ribContextRegistry;
296     }
297
298     @Override
299     public void onGlobalContextUpdated(final SchemaContext context) {
300         this.codecsRegistry.onSchemaContextUpdated(context);
301     }
302
303     @Override
304     public CodecsRegistry getCodecsRegistry() {
305         return this.codecsRegistry;
306     }
307
308     @Override
309     public ImportPolicyPeerTracker getImportPolicyPeerTracker() {
310         return this.importPolicyPeerTracker;
311     }
312
313     @Override
314     public ExportPolicyPeerTracker getExportPolicyPeerTracker(final TablesKey tablesKey) {
315         return this.exportPolicyPeerTrackerMap.get(tablesKey);
316     }
317
318     @Override
319     public synchronized void instantiateServiceInstance() {
320         this.isServiceInstantiated = true;
321         this.domChain = this.domDataBroker.createTransactionChain(this);
322         if(this.configurationWriter != null) {
323             this.configurationWriter.apply();
324         }
325         LOG.info("RIB Singleton Service {} instantiated, RIB {}", getIdentifier().getValue(), this.ribId.getValue());
326         LOG.debug("Instantiating RIB table {} at {}", this.ribId , this.yangRibId);
327
328         final ContainerNode bgpRib = Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(BgpRib.QNAME))
329             .addChild(ImmutableNodes.mapNodeBuilder(Rib.QNAME).build()).build();
330
331         final MapEntryNode ribInstance = Builders.mapEntryBuilder().withNodeIdentifier(
332             new NodeIdentifierWithPredicates(Rib.QNAME, RIB_ID_QNAME, this.ribId .getValue()))
333             .addChild(ImmutableNodes.leafNode(RIB_ID_QNAME, this.ribId .getValue()))
334             .addChild(ImmutableNodes.mapNodeBuilder(Peer.QNAME).build())
335             .addChild(Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(LocRib.QNAME))
336                 .addChild(ImmutableNodes.mapNodeBuilder(Tables.QNAME).build())
337                 .build()).build();
338
339
340         final DOMDataWriteTransaction trans = this.domChain.newWriteOnlyTransaction();
341
342         // merge empty BgpRib + Rib, to make sure the top-level parent structure is present
343         trans.merge(LogicalDatastoreType.OPERATIONAL, YangInstanceIdentifier.builder().node(BgpRib.QNAME).build(), bgpRib);
344         trans.put(LogicalDatastoreType.OPERATIONAL, this.yangRibId, ribInstance);
345
346         try {
347             trans.submit().checkedGet();
348         } catch (final TransactionCommitFailedException e) {
349             LOG.error("Failed to initiate RIB {}", this.yangRibId, e);
350         }
351
352         LOG.debug("Effective RIB created.");
353
354         this.localTablesKeys.forEach(this::startLocRib);
355     }
356
357     @Override
358     public synchronized ListenableFuture<Void> closeServiceInstance() {
359         if(!this.isServiceInstantiated) {
360             LOG.trace("RIB Singleton Service {} already closed, RIB {}", getIdentifier().getValue(),
361                 this.ribId.getValue());
362             return Futures.immediateFuture(null);
363         }
364         LOG.info("Close RIB Singleton Service {}, RIB {}", getIdentifier().getValue(), this.ribId.getValue());
365         this.isServiceInstantiated = false;
366
367         this.txChainToLocRibWriter.values().forEach(LocRibWriter::close);
368         this.txChainToLocRibWriter.clear();
369
370         final DOMDataWriteTransaction t = this.domChain.newWriteOnlyTransaction();
371         t.delete(LogicalDatastoreType.OPERATIONAL, getYangRibId());
372         final CheckedFuture<Void, TransactionCommitFailedException> cleanFuture = t.submit();
373
374         this.domChain.close();
375         return cleanFuture;
376     }
377
378     @Override
379     public ServiceGroupIdentifier getIdentifier() {
380         return this.serviceGroupIdentifier;
381     }
382
383     @Override
384     public ClusterSingletonServiceRegistration registerClusterSingletonService(final ClusterSingletonService clusterSingletonService) {
385         return ClusterSingletonServiceRegistrationHelper.registerSingletonService(this.provider, clusterSingletonService, MAX_REGISTRATION_ATTEMPTS,
386                 SLEEP_TIME);
387     }
388
389     @Override
390     public ServiceGroupIdentifier getRibIServiceGroupIdentifier() {
391         return getIdentifier();
392     }
393 }