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