BGPCEP-754: Start using default import policy
[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.Futures;
17 import com.google.common.util.concurrent.ListenableFuture;
18 import java.util.HashMap;
19 import java.util.HashSet;
20 import java.util.List;
21 import java.util.Map;
22 import java.util.Map.Entry;
23 import java.util.Optional;
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.BindingNormalizedNodeSerializer;
39 import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceRegistration;
40 import org.opendaylight.protocol.bgp.mode.api.PathSelectionMode;
41 import org.opendaylight.protocol.bgp.mode.impl.base.BasePathSelectionModeFactory;
42 import org.opendaylight.protocol.bgp.rib.impl.spi.BGPDispatcher;
43 import org.opendaylight.protocol.bgp.rib.impl.spi.CodecsRegistry;
44 import org.opendaylight.protocol.bgp.rib.impl.spi.ImportPolicyPeerTracker;
45 import org.opendaylight.protocol.bgp.rib.impl.spi.RIB;
46 import org.opendaylight.protocol.bgp.rib.impl.spi.RIBSupportContext;
47 import org.opendaylight.protocol.bgp.rib.impl.spi.RIBSupportContextRegistry;
48 import org.opendaylight.protocol.bgp.rib.impl.state.BGPRIBStateImpl;
49 import org.opendaylight.protocol.bgp.rib.spi.BGPPeerTracker;
50 import org.opendaylight.protocol.bgp.rib.spi.ExportPolicyPeerTracker;
51 import org.opendaylight.protocol.bgp.rib.spi.RIBExtensionConsumerContext;
52 import org.opendaylight.protocol.bgp.rib.spi.RIBSupport;
53 import org.opendaylight.protocol.bgp.rib.spi.RibSupportUtils;
54 import org.opendaylight.protocol.bgp.rib.spi.policy.BGPRibRoutingPolicy;
55 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.AsNumber;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev171207.path.attributes.Attributes;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev171207.BgpTableType;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.BgpRib;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.RibId;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.bgp.rib.Rib;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.bgp.rib.RibKey;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.bgp.rib.rib.LocRib;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.bgp.rib.rib.Peer;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.rib.Tables;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.rib.TablesKey;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.BgpId;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.ClusterIdentifier;
68 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
69 import org.opendaylight.yangtools.yang.common.QName;
70 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
71 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.InstanceIdentifierBuilder;
72 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
73 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
74 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
75 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
76 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
77 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
78 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
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 BGPRIBStateImpl implements RIB, TransactionChainListener,
89         SchemaContextListener, AutoCloseable {
90     private static final Logger LOG = LoggerFactory.getLogger(RIBImpl.class);
91     private static final QName RIB_ID_QNAME = QName.create(Rib.QNAME, "id").intern();
92     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);
93
94     private final BGPDispatcher dispatcher;
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 YangInstanceIdentifier yangTables;
103     private final RIBSupportContextRegistryImpl ribContextRegistry;
104     private final CodecsRegistryImpl codecsRegistry;
105     @GuardedBy("this")
106     private ClusterSingletonServiceRegistration registration;
107     private final DOMDataBrokerExtension service;
108     private final Map<TransactionChain<?, ?>, LocRibWriter> txChainToLocRibWriter = new HashMap<>();
109     private final Map<TablesKey, PathSelectionMode> bestPathSelectionStrategies;
110     private final ImportPolicyPeerTracker importPolicyPeerTracker;
111     private final RibId ribId;
112     private final BGPPeerTracker peerTracker;
113     private final BGPRibRoutingPolicy ribPolicies;
114     private final Map<TablesKey, ExportPolicyPeerTracker> exportPolicyPeerTrackerMap;
115     private final BindingNormalizedNodeSerializer bindingSerializer;
116
117     @GuardedBy("this")
118     private DOMTransactionChain domChain;
119     @GuardedBy("this")
120     private boolean isServiceInstantiated;
121
122     public RIBImpl(final RibId ribId,
123             final AsNumber localAs,
124             final BgpId localBgpId,
125             final ClusterIdentifier clusterId,
126             final RIBExtensionConsumerContext extensions,
127             final BGPDispatcher dispatcher,
128             final CodecsRegistryImpl codecsRegistry,
129             final DOMDataBroker domDataBroker,
130             final BGPRibRoutingPolicy ribPolicies,
131             final BGPPeerTracker bgpPeerTracker,
132             final List<BgpTableType> localTables,
133             final Map<TablesKey, PathSelectionMode> bestPathSelectionStrategies,
134             final BindingNormalizedNodeSerializer bindingSerializer
135     ) {
136         super(InstanceIdentifier.create(BgpRib.class).child(Rib.class, new RibKey(requireNonNull(ribId))),
137                 localBgpId, localAs);
138         this.localAs = requireNonNull(localAs);
139         this.bgpIdentifier = requireNonNull(localBgpId);
140         this.dispatcher = requireNonNull(dispatcher);
141         this.localTables = ImmutableSet.copyOf(localTables);
142         this.localTablesKeys = new HashSet<>();
143         this.domDataBroker = requireNonNull(domDataBroker);
144         this.service = this.domDataBroker.getSupportedExtensions().get(DOMDataTreeChangeService.class);
145         this.extensions = requireNonNull(extensions);
146         this.ribPolicies = requireNonNull(ribPolicies);
147         this.peerTracker = requireNonNull(bgpPeerTracker);
148         this.codecsRegistry = codecsRegistry;
149         this.ribContextRegistry = RIBSupportContextRegistryImpl.create(extensions, this.codecsRegistry);
150         final InstanceIdentifierBuilder yangRibIdBuilder = YangInstanceIdentifier.builder().node(BgpRib.QNAME).node(Rib.QNAME);
151         this.yangRibId = yangRibIdBuilder.nodeWithKey(Rib.QNAME, RIB_ID_QNAME, ribId.getValue()).build();
152         this.yangTables = this.yangRibId.node(LocRib.QNAME).node(Tables.QNAME);
153         this.bestPathSelectionStrategies = requireNonNull(bestPathSelectionStrategies);
154         final ClusterIdentifier cId = clusterId == null ? new ClusterIdentifier(localBgpId) : clusterId;
155         this.ribId = ribId;
156         final PolicyDatabase policyDatabase = new PolicyDatabase(this.localAs.getValue(), localBgpId, cId);
157         this.importPolicyPeerTracker = new ImportPolicyPeerTrackerImpl(policyDatabase);
158
159         final ImmutableMap.Builder<TablesKey, ExportPolicyPeerTracker> exportPolicies = new ImmutableMap.Builder<>();
160         for (final BgpTableType t : this.localTables) {
161             final TablesKey key = new TablesKey(t.getAfi(), t.getSafi());
162             this.localTablesKeys.add(key);
163             exportPolicies.put(key, new ExportPolicyPeerTrackerImpl(policyDatabase, key));
164         }
165         this.exportPolicyPeerTrackerMap = exportPolicies.build();
166         this.bindingSerializer = requireNonNull(bindingSerializer);
167     }
168
169     private synchronized void startLocRib(final TablesKey key) {
170         LOG.debug("Creating LocRib table for {}", key);
171         // create locRibWriter for each table
172         final DOMDataWriteTransaction tx = this.domChain.newWriteOnlyTransaction();
173
174         final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> table = ImmutableNodes.mapEntryBuilder();
175         table.withNodeIdentifier(RibSupportUtils.toYangTablesKey(key));
176         table.withChild(EMPTY_TABLE_ATTRIBUTES);
177
178         final NodeIdentifierWithPredicates tableKey = RibSupportUtils.toYangTablesKey(key);
179         final InstanceIdentifierBuilder tableId = YangInstanceIdentifier.builder(this.yangRibId.node(LocRib.QNAME).node(Tables.QNAME));
180         tableId.nodeWithKey(tableKey.getNodeType(), tableKey.getKeyValues());
181         for (final Entry<QName, Object> e : tableKey.getKeyValues().entrySet()) {
182             table.withChild(ImmutableNodes.leafNode(e.getKey(), e.getValue()));
183         }
184
185         final RIBSupportContext supportContext = this.ribContextRegistry.getRIBSupportContext(key);
186         if (supportContext != null) {
187             final ChoiceNode routes = supportContext.getRibSupport().emptyRoutes();
188             table.withChild(routes);
189
190             tx.put(LogicalDatastoreType.OPERATIONAL, tableId.build(), table.build());
191             try {
192                 tx.submit().checkedGet();
193             } catch (final TransactionCommitFailedException e1) {
194                 LOG.error("Failed to initiate LocRIB for key {}", key, e1);
195             }
196             createLocRibWriter(key);
197         } else {
198             LOG.warn("There's no registered RIB Context for {}", key.getAfi());
199         }
200     }
201
202     private synchronized void createLocRibWriter(final TablesKey key) {
203         LOG.debug("Creating LocRIB writer for key {}", key);
204         final DOMTransactionChain txChain = createPeerChain(this);
205         PathSelectionMode pathSelectionStrategy = this.bestPathSelectionStrategies.get(key);
206         if (pathSelectionStrategy == null) {
207             pathSelectionStrategy = BasePathSelectionModeFactory.createBestPathSelectionStrategy(this.peerTracker);
208         }
209
210         final LocRibWriter locRibWriter = LocRibWriter.create(this.ribContextRegistry, key, txChain,
211                 getYangRibId(), this.localAs, getService(), this.exportPolicyPeerTrackerMap.get(key), pathSelectionStrategy);
212         registerTotalPathCounter(key, locRibWriter);
213         registerTotalPrefixesCounter(key, locRibWriter);
214         this.txChainToLocRibWriter.put(txChain, locRibWriter);
215     }
216
217     @Override
218     public String toString() {
219         return addToStringAttributes(MoreObjects.toStringHelper(this)).toString();
220     }
221
222     protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
223         return toStringHelper;
224     }
225
226     @Override
227     public synchronized void close() throws Exception {
228         if (this.registration != null) {
229             this.registration.close();
230             this.registration = null;
231         }
232     }
233
234     @Override
235     public AsNumber getLocalAs() {
236         return this.localAs;
237     }
238
239     @Override
240     public BgpId getBgpIdentifier() {
241         return this.bgpIdentifier;
242     }
243
244     @Nonnull
245     @Override
246     public Set<? extends BgpTableType> getLocalTables() {
247         return this.localTables;
248     }
249
250     @Override
251     public BGPDispatcher getDispatcher() {
252         return this.dispatcher;
253     }
254
255     @Override
256     public synchronized void onTransactionChainFailed(final TransactionChain<?, ?> chain, final AsyncTransaction<?, ?> transaction, final Throwable cause) {
257         LOG.error("Broken chain in RIB {} transaction {}", getInstanceIdentifier(), transaction != null ? transaction.getIdentifier() : null, cause);
258         if (this.txChainToLocRibWriter.containsKey(chain)) {
259             final LocRibWriter locRibWriter = this.txChainToLocRibWriter.remove(chain);
260             final DOMTransactionChain newChain = createPeerChain(this);
261             locRibWriter.restart(newChain);
262             this.txChainToLocRibWriter.put(newChain, locRibWriter);
263         }
264     }
265
266     @Override
267     public void onTransactionChainSuccessful(final TransactionChain<?, ?> chain) {
268         LOG.info("RIB {} closed successfully", getInstanceIdentifier());
269     }
270
271     @Override
272     public Set<TablesKey> getLocalTablesKeys() {
273         return this.localTablesKeys;
274     }
275
276     @Override
277     public boolean supportsTable(final TablesKey tableKey) {
278         return this.localTablesKeys.contains(tableKey);
279     }
280
281     @Override
282     public BGPRibRoutingPolicy getRibPolicies() {
283         return this.ribPolicies;
284     }
285
286     @Override
287     public BGPPeerTracker getPeerTracker() {
288         return this.peerTracker;
289     }
290
291     @Override
292     public DOMDataTreeChangeService getService() {
293         return (DOMDataTreeChangeService) this.service;
294     }
295
296     @Override
297     public YangInstanceIdentifier getYangRibId() {
298         return this.yangRibId;
299     }
300
301     @Override
302     public DOMTransactionChain createPeerChain(final TransactionChainListener listener) {
303         return this.domDataBroker.createTransactionChain(listener);
304     }
305
306     @Override
307     public RIBExtensionConsumerContext getRibExtensions() {
308         return this.extensions;
309     }
310
311     @Override
312     public RIBSupportContextRegistry getRibSupportContext() {
313         return this.ribContextRegistry;
314     }
315
316     @Override
317     public void onGlobalContextUpdated(final SchemaContext context) {
318         this.codecsRegistry.onSchemaContextUpdated(context);
319     }
320
321     @Override
322     public CodecsRegistry getCodecsRegistry() {
323         return this.codecsRegistry;
324     }
325
326     @Override
327     public ImportPolicyPeerTracker getImportPolicyPeerTracker() {
328         return this.importPolicyPeerTracker;
329     }
330
331     @Override
332     public ExportPolicyPeerTracker getExportPolicyPeerTracker(final TablesKey tablesKey) {
333         return this.exportPolicyPeerTrackerMap.get(tablesKey);
334     }
335
336     public synchronized void instantiateServiceInstance() {
337         this.isServiceInstantiated = true;
338         setActive(true);
339         this.domChain = this.domDataBroker.createTransactionChain(this);
340         LOG.debug("Instantiating RIB table {} at {}", this.ribId, this.yangRibId);
341
342         final ContainerNode bgpRib = Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(BgpRib.QNAME))
343                 .addChild(ImmutableNodes.mapNodeBuilder(Rib.QNAME).build()).build();
344
345         final MapEntryNode ribInstance = Builders.mapEntryBuilder().withNodeIdentifier(
346                 new NodeIdentifierWithPredicates(Rib.QNAME, RIB_ID_QNAME, this.ribId.getValue()))
347                 .addChild(ImmutableNodes.leafNode(RIB_ID_QNAME, this.ribId.getValue()))
348                 .addChild(ImmutableNodes.mapNodeBuilder(Peer.QNAME).build())
349                 .addChild(Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(LocRib.QNAME))
350                         .addChild(ImmutableNodes.mapNodeBuilder(Tables.QNAME).build())
351                         .build()).build();
352
353
354         final DOMDataWriteTransaction trans = this.domChain.newWriteOnlyTransaction();
355
356         // merge empty BgpRib + Rib, to make sure the top-level parent structure is present
357         trans.merge(LogicalDatastoreType.OPERATIONAL, YangInstanceIdentifier.builder().node(BgpRib.QNAME).build(), bgpRib);
358         trans.put(LogicalDatastoreType.OPERATIONAL, this.yangRibId, ribInstance);
359
360         try {
361             trans.submit().checkedGet();
362         } catch (final TransactionCommitFailedException e) {
363             LOG.error("Failed to initiate RIB {}", this.yangRibId, e);
364         }
365
366         LOG.debug("Effective RIB created.");
367
368         this.localTablesKeys.forEach(this::startLocRib);
369     }
370
371     public synchronized ListenableFuture<Void> closeServiceInstance() {
372         if (!this.isServiceInstantiated) {
373             LOG.trace("RIB {} already closed", this.ribId.getValue());
374             return Futures.immediateFuture(null);
375         }
376         LOG.info("Close RIB {}", this.ribId.getValue());
377         this.isServiceInstantiated = false;
378         setActive(false);
379
380         this.txChainToLocRibWriter.values().forEach(LocRibWriter::close);
381         this.txChainToLocRibWriter.clear();
382
383         final DOMDataWriteTransaction t = this.domChain.newWriteOnlyTransaction();
384         t.delete(LogicalDatastoreType.OPERATIONAL, getYangRibId());
385         final ListenableFuture<Void> cleanFuture = t.submit();
386
387         this.domChain.close();
388         return cleanFuture;
389     }
390
391
392     @Override
393     @SuppressWarnings("unchecked")
394     public final Optional<ContainerNode> toNormalizedNodeAttribute(
395             final RIBSupport ribSupport,
396             final NodeIdentifierWithPredicates routeIdentifier,
397             final Optional<Attributes> attributes) {
398         if (!attributes.isPresent()) {
399             return Optional.empty();
400         }
401         final InstanceIdentifier<Attributes> yii
402                 = (InstanceIdentifier<Attributes>) this.bindingSerializer
403                 .fromYangInstanceIdentifier(ribSupport.buildRouteAttributeYii(this.yangTables, routeIdentifier));
404         return Optional.of((ContainerNode) this.bindingSerializer.toNormalizedNode(yii, attributes.get()).getValue());
405     }
406
407     @Override
408     public final Optional<Attributes> getAttributes(
409             final RIBSupport ribSupport,
410             final NodeIdentifierWithPredicates routeIdentifier,
411             final NormalizedNode<?, ?> route) {
412         final ContainerNode advertisedAttrs = (ContainerNode) NormalizedNodes
413                 .findNode(route, ribSupport.routeAttributesIdentifier()).orElse(null);
414         if (advertisedAttrs == null) {
415             return Optional.empty();
416         }
417
418         return Optional.ofNullable((Attributes) this.bindingSerializer
419                 .fromNormalizedNode(ribSupport.buildRouteAttributeYii(this.yangTables, routeIdentifier),
420                         advertisedAttrs).getValue());
421     }
422 }