Bug 4488: Metadata not available for modification NodeModification
[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.annotations.VisibleForTesting;
11 import com.google.common.base.MoreObjects;
12 import com.google.common.base.MoreObjects.ToStringHelper;
13 import com.google.common.base.Optional;
14 import com.google.common.base.Preconditions;
15 import com.google.common.collect.ImmutableSet;
16 import java.util.ArrayList;
17 import java.util.HashSet;
18 import java.util.List;
19 import java.util.Map;
20 import java.util.Map.Entry;
21 import java.util.Set;
22 import java.util.concurrent.ExecutionException;
23 import javax.annotation.Nonnull;
24 import javax.annotation.concurrent.ThreadSafe;
25 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
26 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
27 import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
28 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
29 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
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.protocol.bgp.mode.api.PathSelectionMode;
39 import org.opendaylight.protocol.bgp.mode.impl.base.BasePathSelectionModeFactory;
40 import org.opendaylight.protocol.bgp.openconfig.spi.BGPConfigModuleTracker;
41 import org.opendaylight.protocol.bgp.openconfig.spi.BGPOpenConfigProvider;
42 import org.opendaylight.protocol.bgp.rib.DefaultRibReference;
43 import org.opendaylight.protocol.bgp.rib.impl.spi.BGPDispatcher;
44 import org.opendaylight.protocol.bgp.rib.impl.spi.CodecsRegistry;
45 import org.opendaylight.protocol.bgp.rib.impl.spi.RIB;
46 import org.opendaylight.protocol.bgp.rib.impl.spi.RIBSupportContextRegistry;
47 import org.opendaylight.protocol.bgp.rib.spi.CacheDisconnectedPeers;
48 import org.opendaylight.protocol.bgp.rib.spi.RIBExtensionConsumerContext;
49 import org.opendaylight.protocol.bgp.rib.spi.RibSupportUtils;
50 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.AsNumber;
51 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev150305.bgp.rib.rib.loc.rib.tables.routes.Ipv4RoutesCase;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev150305.bgp.rib.rib.loc.rib.tables.routes.Ipv6RoutesCase;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.BgpTableType;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.BgpRib;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.RibId;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.Rib;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.RibKey;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.LocRib;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.Peer;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.Tables;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.TablesKey;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.tables.Routes;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.ClusterIdentifier;
65 import org.opendaylight.yangtools.binding.data.codec.api.BindingCodecTreeFactory;
66 import org.opendaylight.yangtools.sal.binding.generator.impl.GeneratedClassLoadingStrategy;
67 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
68 import org.opendaylight.yangtools.yang.common.QName;
69 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
70 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.InstanceIdentifierBuilder;
71 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
72 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
73 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
74 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
75 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
76 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
77 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
78 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
79 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
80 import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
81 import org.slf4j.Logger;
82 import org.slf4j.LoggerFactory;
83
84 @ThreadSafe
85 public final class RIBImpl extends DefaultRibReference implements AutoCloseable, RIB, TransactionChainListener, SchemaContextListener {
86     private static final Logger LOG = LoggerFactory.getLogger(RIBImpl.class);
87     @VisibleForTesting
88     public static final QName RIB_ID_QNAME = QName.create(Rib.QNAME, "id").intern();
89     @VisibleForTesting
90     public 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);
91
92     private final BGPDispatcher dispatcher;
93     private final DOMTransactionChain domChain;
94     private final AsNumber localAs;
95     private final Ipv4Address bgpIdentifier;
96     private final Set<BgpTableType> localTables;
97     private final Set<TablesKey> localTablesKeys;
98     private final DataBroker dataBroker;
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 DOMDataBrokerExtension service;
105     private final List<LocRibWriter> locRibs = new ArrayList<>();
106     private final BGPConfigModuleTracker configModuleTracker;
107     private final BGPOpenConfigProvider openConfigProvider;
108     private final CacheDisconnectedPeers cacheDisconnectedPeers;
109     private final Map<TablesKey, PathSelectionMode> bestPathSelectionStrategies;
110     private final ImportPolicyPeerTracker importPolicyPeerTracker;
111
112     public RIBImpl(final RibId ribId, final AsNumber localAs, final Ipv4Address localBgpId, final Ipv4Address clusterId, final RIBExtensionConsumerContext extensions,
113         final BGPDispatcher dispatcher, final BindingCodecTreeFactory codecFactory,
114         final DataBroker dps, final DOMDataBroker domDataBroker, final List<BgpTableType> localTables,
115         @Nonnull final Map<TablesKey, PathSelectionMode> bestPathSelectionStrategies, final GeneratedClassLoadingStrategy classStrategy,
116         final BGPConfigModuleTracker moduleTracker, final BGPOpenConfigProvider openConfigProvider) {
117         super(InstanceIdentifier.create(BgpRib.class).child(Rib.class, new RibKey(Preconditions.checkNotNull(ribId))));
118         this.domChain = domDataBroker.createTransactionChain(this);
119         this.localAs = Preconditions.checkNotNull(localAs);
120         this.bgpIdentifier = Preconditions.checkNotNull(localBgpId);
121         this.dispatcher = Preconditions.checkNotNull(dispatcher);
122         this.localTables = ImmutableSet.copyOf(localTables);
123         this.localTablesKeys = new HashSet<>();
124         this.dataBroker = dps;
125         this.domDataBroker = Preconditions.checkNotNull(domDataBroker);
126         this.extensions = Preconditions.checkNotNull(extensions);
127         this.codecsRegistry = CodecsRegistryImpl.create(codecFactory, classStrategy);
128         this.ribContextRegistry = RIBSupportContextRegistryImpl.create(extensions, this.codecsRegistry);
129         final InstanceIdentifierBuilder yangRibIdBuilder = YangInstanceIdentifier.builder().node(BgpRib.QNAME).node(Rib.QNAME);
130         this.yangRibId = yangRibIdBuilder.nodeWithKey(Rib.QNAME, RIB_ID_QNAME, ribId.getValue()).build();
131         this.configModuleTracker = moduleTracker;
132         this.openConfigProvider = openConfigProvider;
133         this.cacheDisconnectedPeers = new CacheDisconnectedPeersImpl();
134         this.bestPathSelectionStrategies = Preconditions.checkNotNull(bestPathSelectionStrategies);
135
136         LOG.debug("Instantiating RIB table {} at {}", ribId, this.yangRibId);
137
138         final ContainerNode bgpRib = Builders.containerBuilder()
139             .withNodeIdentifier(new NodeIdentifier(BgpRib.QNAME))
140                 .addChild(ImmutableNodes.mapNodeBuilder(Rib.QNAME).build()).build();
141
142         final MapEntryNode ribInstance = Builders.mapEntryBuilder().withNodeIdentifier(
143                 new NodeIdentifierWithPredicates(Rib.QNAME, RIB_ID_QNAME, ribId.getValue()))
144                     .addChild(ImmutableNodes.leafNode(RIB_ID_QNAME, ribId.getValue()))
145                     .addChild(ImmutableNodes.mapNodeBuilder(Peer.QNAME).build())
146                     .addChild(Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(LocRib.QNAME))
147                     .addChild(ImmutableNodes.mapNodeBuilder(Tables.QNAME).build())
148                     .build()).build();
149
150
151         final DOMDataWriteTransaction trans = this.domChain.newWriteOnlyTransaction();
152
153         // merge empty BgpRib + Rib, to make sure the top-level parent structure is present
154         trans.merge(LogicalDatastoreType.OPERATIONAL, YangInstanceIdentifier.builder().node(BgpRib.QNAME).build(), bgpRib);
155         trans.put(LogicalDatastoreType.OPERATIONAL, yangRibIdBuilder.build(), ribInstance);
156
157         try {
158             trans.submit().checkedGet();
159         } catch (final TransactionCommitFailedException e) {
160             LOG.error("Failed to initiate RIB {}", this.yangRibId, e);
161         }
162         final ClusterIdentifier cId = (clusterId == null) ? new ClusterIdentifier(localBgpId) : new ClusterIdentifier(clusterId);
163         final PolicyDatabase policyDatabase  = new PolicyDatabase(localAs.getValue(), localBgpId, cId);
164         this.importPolicyPeerTracker = new ImportPolicyPeerTracker(policyDatabase);
165
166         final DOMDataBrokerExtension domDatatreeChangeService = this.domDataBroker.getSupportedExtensions().get(DOMDataTreeChangeService.class);
167         this.service = domDatatreeChangeService;
168         LOG.debug("Effective RIB created.");
169
170         for (final BgpTableType t : this.localTables) {
171             final TablesKey key = new TablesKey(t.getAfi(), t.getSafi());
172             this.localTablesKeys.add(key);
173             startLocRib(key, policyDatabase);
174         }
175
176         if (this.configModuleTracker != null) {
177             this.configModuleTracker.onInstanceCreate();
178         }
179     }
180
181     public RIBImpl(final RibId ribId, final AsNumber localAs, final Ipv4Address localBgpId, final Ipv4Address clusterId, final RIBExtensionConsumerContext extensions,
182             final BGPDispatcher dispatcher, final BindingCodecTreeFactory codecFactory,
183             final DataBroker dps, final DOMDataBroker domDataBroker, final List<BgpTableType> localTables,
184             final Map<TablesKey, PathSelectionMode> bestPathSelectionstrategies, final GeneratedClassLoadingStrategy classStrategy) {
185         this(ribId, localAs, localBgpId, clusterId, extensions, dispatcher, codecFactory,
186                 dps, domDataBroker, localTables, bestPathSelectionstrategies, classStrategy, null, null);
187     }
188
189     private void startLocRib(final TablesKey key, final PolicyDatabase pd) {
190         LOG.debug("Creating LocRib table for {}", key);
191         // create locRibWriter for each table
192         final DOMDataWriteTransaction tx = this.domChain.newWriteOnlyTransaction();
193
194         final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> table = ImmutableNodes.mapEntryBuilder();
195         table.withNodeIdentifier(RibSupportUtils.toYangTablesKey(key));
196         table.withChild(EMPTY_TABLE_ATTRIBUTES);
197
198         final NodeIdentifierWithPredicates tableKey = RibSupportUtils.toYangTablesKey(key);
199         final InstanceIdentifierBuilder tableId = YangInstanceIdentifier.builder(this.yangRibId.node(LocRib.QNAME).node(Tables.QNAME));
200         tableId.nodeWithKey(tableKey.getNodeType(), tableKey.getKeyValues());
201         for (final Entry<QName, Object> e : tableKey.getKeyValues().entrySet()) {
202             table.withChild(ImmutableNodes.leafNode(e.getKey(), e.getValue()));
203         }
204
205         final ChoiceNode routes = this.ribContextRegistry.getRIBSupportContext(key).getRibSupport().emptyRoutes();
206         table.withChild(routes);
207
208         tx.put(LogicalDatastoreType.OPERATIONAL, tableId.build(), table.build());
209         try {
210             tx.submit().checkedGet();
211         } catch (final TransactionCommitFailedException e1) {
212             LOG.error("Failed to initiate LocRIB for key {}", key, e1);
213         }
214
215         PathSelectionMode pathSelectionStrategy = this.bestPathSelectionStrategies.get(key);
216         if (pathSelectionStrategy == null) {
217             pathSelectionStrategy = BasePathSelectionModeFactory.createBestPathSelectionStrategy();
218         }
219         this.locRibs.add(LocRibWriter.create(this.ribContextRegistry, key, createPeerChain(this), getYangRibId(), this.localAs, getService(), pd,
220             this.cacheDisconnectedPeers, pathSelectionStrategy));
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 InterruptedException, ExecutionException {
234         final DOMDataWriteTransaction t = this.domChain.newWriteOnlyTransaction();
235         t.delete(LogicalDatastoreType.OPERATIONAL, getYangRibId());
236         t.submit().get();
237         this.domChain.close();
238         for (final LocRibWriter locRib : this.locRibs) {
239             try {
240                 locRib.close();
241             } catch (final Exception e) {
242                 LOG.warn("Could not close LocalRib reference: {}", locRib, e);
243             }
244         }
245         if (this.configModuleTracker != null) {
246             this.configModuleTracker.onInstanceClose();
247         }
248     }
249
250     @Override
251     public AsNumber getLocalAs() {
252         return this.localAs;
253     }
254
255     @Override
256     public Ipv4Address getBgpIdentifier() {
257         return this.bgpIdentifier;
258     }
259
260     @Override
261     public Set<? extends BgpTableType> getLocalTables() {
262         return this.localTables;
263     }
264
265     @Override
266     public BGPDispatcher getDispatcher() {
267         return this.dispatcher;
268     }
269
270     @Override
271     public void onTransactionChainFailed(final TransactionChain<?, ?> chain, final AsyncTransaction<?, ?> transaction, final Throwable cause) {
272         LOG.error("Broken chain in RIB {} transaction {}", getInstanceIdentifier(), transaction != null ? transaction.getIdentifier() : null, cause);
273     }
274
275     @Override
276     public void onTransactionChainSuccessful(final TransactionChain<?, ?> chain) {
277         LOG.info("RIB {} closed successfully", getInstanceIdentifier());
278     }
279
280     @Override
281     public long getRoutesCount(final TablesKey key) {
282         try (final ReadOnlyTransaction tx = this.dataBroker.newReadOnlyTransaction()) {
283             final Optional<Tables> tableMaybe = tx.read(LogicalDatastoreType.OPERATIONAL,
284                     getInstanceIdentifier().child(LocRib.class).child(Tables.class, key)).checkedGet();
285             if (tableMaybe.isPresent()) {
286                 final Tables table = tableMaybe.get();
287                 return countIpRoutes(table.getRoutes());
288             }
289         } catch (final ReadFailedException e) {
290             LOG.debug("Failed to read tables", e);
291         }
292         return 0;
293     }
294
295     private int countIpRoutes(final Routes routes) {
296         if (routes instanceof Ipv4RoutesCase) {
297             final Ipv4RoutesCase routesCase = (Ipv4RoutesCase) routes;
298             if (routesCase.getIpv4Routes() != null && routesCase.getIpv4Routes().getIpv4Route() != null) {
299                 return routesCase.getIpv4Routes().getIpv4Route().size();
300             }
301         } else if (routes instanceof Ipv6RoutesCase) {
302             final Ipv6RoutesCase routesCase = (Ipv6RoutesCase) routes;
303             if (routesCase.getIpv6Routes() != null && routesCase.getIpv6Routes().getIpv6Route() != null) {
304                 return routesCase.getIpv6Routes().getIpv6Route().size();
305             }
306         }
307         return 0;
308     }
309
310     public Set<TablesKey> getLocalTablesKeys() {
311         return this.localTablesKeys;
312     }
313
314     @Override
315     public DOMDataTreeChangeService getService() {
316         return (DOMDataTreeChangeService) this.service;
317     }
318
319     @Override
320     public YangInstanceIdentifier getYangRibId() {
321         return this.yangRibId;
322     }
323
324     @Override
325     public DOMTransactionChain createPeerChain(final TransactionChainListener listener) {
326         return this.domDataBroker.createTransactionChain(listener);
327     }
328
329     @Override
330     public RIBExtensionConsumerContext getRibExtensions() {
331         return this.extensions;
332     }
333
334     @Override
335     public RIBSupportContextRegistry getRibSupportContext() {
336         return this.ribContextRegistry;
337     }
338
339     @Override
340     public void onGlobalContextUpdated(final SchemaContext context) {
341         this.codecsRegistry.onSchemaContextUpdated(context);
342     }
343
344     @Override
345     public CodecsRegistry getCodecsRegistry() {
346         return this.codecsRegistry;
347     }
348
349     @Override
350     public Optional<BGPOpenConfigProvider> getOpenConfigProvider() {
351         return Optional.fromNullable(this.openConfigProvider);
352     }
353
354     @Override
355     public CacheDisconnectedPeers getCacheDisconnectedPeers() {
356         return this.cacheDisconnectedPeers;
357     }
358
359     public ImportPolicyPeerTracker getImportPolicyPeerTracker() {
360         return this.importPolicyPeerTracker;
361     }
362 }