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