Split Peer Ribout storage from loc rib
[bgpcep.git] / bgp / rib-impl / src / main / java / org / opendaylight / protocol / bgp / rib / impl / EffectiveRibInWriter.java
1 /*
2  * Copyright (c) 2015 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.collect.ImmutableMap;
13 import com.google.common.collect.ImmutableSet;
14 import com.google.common.util.concurrent.FluentFuture;
15 import com.google.common.util.concurrent.FutureCallback;
16 import com.google.common.util.concurrent.MoreExecutors;
17 import java.util.Collection;
18 import java.util.Map;
19 import java.util.Optional;
20 import java.util.Set;
21 import java.util.concurrent.ExecutionException;
22 import java.util.concurrent.atomic.LongAdder;
23 import javax.annotation.Nonnull;
24 import javax.annotation.concurrent.GuardedBy;
25 import javax.annotation.concurrent.NotThreadSafe;
26 import org.opendaylight.controller.md.sal.binding.api.BindingTransactionChain;
27 import org.opendaylight.controller.md.sal.binding.api.ClusteredDataTreeChangeListener;
28 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
29 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
30 import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
31 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
32 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
33 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
34 import org.opendaylight.mdsal.common.api.CommitInfo;
35 import org.opendaylight.protocol.bgp.openconfig.spi.BGPTableTypeRegistryConsumer;
36 import org.opendaylight.protocol.bgp.rib.impl.spi.RIB;
37 import org.opendaylight.protocol.bgp.rib.impl.spi.RIBSupportContextRegistry;
38 import org.opendaylight.protocol.bgp.rib.impl.state.peer.PrefixesInstalledCounters;
39 import org.opendaylight.protocol.bgp.rib.impl.state.peer.PrefixesReceivedCounters;
40 import org.opendaylight.protocol.bgp.rib.spi.RIBSupport;
41 import org.opendaylight.protocol.bgp.rib.spi.policy.BGPRibRoutingPolicy;
42 import org.opendaylight.protocol.bgp.rib.spi.policy.BGPRouteEntryImportParameters;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.path.attributes.Attributes;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.Route;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.rib.Peer;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.rib.PeerKey;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.rib.peer.AdjRibIn;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.rib.peer.EffectiveRibIn;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.Tables;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.TablesBuilder;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.TablesKey;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.tables.Routes;
53 import org.opendaylight.yangtools.concepts.ListenerRegistration;
54 import org.opendaylight.yangtools.yang.binding.ChildOf;
55 import org.opendaylight.yangtools.yang.binding.ChoiceIn;
56 import org.opendaylight.yangtools.yang.binding.DataObject;
57 import org.opendaylight.yangtools.yang.binding.Identifiable;
58 import org.opendaylight.yangtools.yang.binding.Identifier;
59 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
60 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
61 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
62 import org.slf4j.Logger;
63 import org.slf4j.LoggerFactory;
64
65 /**
66  * Implementation of the BGP import policy. Listens on peer's Adj-RIB-In, inspects all inbound
67  * routes in the context of the advertising peer's role and applies the inbound policy.
68  * <p>
69  * Inbound policy is applied as follows:
70  * <p>
71  * 1) if the peer is an eBGP peer, perform attribute replacement and filtering
72  * 2) check if a route is admissible based on attributes attached to it, as well as the
73  * advertising peer's role
74  * 3) output admitting routes with edited attributes into /bgp-rib/rib/peer/effective-rib-in/tables/routes
75  */
76 @NotThreadSafe
77 final class EffectiveRibInWriter implements PrefixesReceivedCounters, PrefixesInstalledCounters,
78         AutoCloseable, ClusteredDataTreeChangeListener<Tables> {
79
80     private static final Logger LOG = LoggerFactory.getLogger(EffectiveRibInWriter.class);
81     static final NodeIdentifier TABLE_ROUTES = new NodeIdentifier(Routes.QNAME);
82
83     private final RIBSupportContextRegistry registry;
84     private final KeyedInstanceIdentifier<Peer, PeerKey> peerIId;
85     private final InstanceIdentifier<EffectiveRibIn> effRibTables;
86     private final DataBroker databroker;
87     private ListenerRegistration<?> reg;
88     private BindingTransactionChain chain;
89     private final Map<TablesKey, LongAdder> prefixesReceived;
90     private final Map<TablesKey, LongAdder> prefixesInstalled;
91     private final BGPRibRoutingPolicy ribPolicies;
92     private final BGPRouteEntryImportParameters peerImportParameters;
93     private final BGPTableTypeRegistryConsumer tableTypeRegistry;
94     @GuardedBy("this")
95     private FluentFuture<? extends CommitInfo> submitted;
96
97     EffectiveRibInWriter(
98             final BGPRouteEntryImportParameters peer,
99             final RIB rib,
100             final BindingTransactionChain chain,
101             final KeyedInstanceIdentifier<Peer, PeerKey> peerIId,
102             final Set<TablesKey> tables,
103             final BGPTableTypeRegistryConsumer tableTypeRegistry
104     ) {
105         this.registry = requireNonNull(rib.getRibSupportContext());
106         this.chain = requireNonNull(chain);
107         this.peerIId = requireNonNull(peerIId);
108         this.effRibTables = this.peerIId.child(EffectiveRibIn.class);
109         this.prefixesInstalled = buildPrefixesTables(tables);
110         this.prefixesReceived = buildPrefixesTables(tables);
111         this.ribPolicies = requireNonNull(rib.getRibPolicies());
112         this.databroker = requireNonNull(rib.getDataBroker());
113         this.tableTypeRegistry = requireNonNull(tableTypeRegistry);
114         this.peerImportParameters = peer;
115     }
116
117     public void init() {
118         final DataTreeIdentifier<Tables> treeId = new DataTreeIdentifier<>(LogicalDatastoreType.OPERATIONAL,
119                 this.peerIId.child(AdjRibIn.class).child(Tables.class));
120         LOG.debug("Registered Effective RIB on {}", this.peerIId);
121         this.reg = requireNonNull(this.databroker).registerDataTreeChangeListener(treeId, this);
122     }
123
124     private static Map<TablesKey, LongAdder> buildPrefixesTables(final Set<TablesKey> tables) {
125         final ImmutableMap.Builder<TablesKey, LongAdder> b = ImmutableMap.builder();
126         tables.forEach(table -> b.put(table, new LongAdder()));
127         return b.build();
128     }
129
130     @Override
131     @SuppressWarnings("unchecked")
132     public synchronized void onDataTreeChanged(@Nonnull final Collection<DataTreeModification<Tables>> changes) {
133         if (this.chain == null) {
134             LOG.trace("Chain closed. Ignoring Changes : {}", changes);
135             return;
136         }
137         LOG.trace("Data changed called to effective RIB. Change : {}", changes);
138         WriteTransaction tx = null;
139         for (final DataTreeModification<Tables> tc : changes) {
140             final DataObjectModification<Tables> table = tc.getRootNode();
141             if (tx == null) {
142                 tx = this.chain.newWriteOnlyTransaction();
143             }
144             final DataObjectModification.ModificationType modificationType = table.getModificationType();
145             switch (modificationType) {
146                 case DELETE:
147                     final Tables removeTable = table.getDataBefore();
148                     final TablesKey tableKey = removeTable.key();
149                     final KeyedInstanceIdentifier<Tables, TablesKey> effectiveTablePath
150                             = this.effRibTables.child(Tables.class, tableKey);
151                     LOG.debug("Delete Effective Table {} modification type {}, "
152                             , effectiveTablePath, modificationType);
153                     tx.delete(LogicalDatastoreType.OPERATIONAL, effectiveTablePath);
154                     CountersUtil.decrement(this.prefixesInstalled.get(tableKey), tableKey);
155                     break;
156                 case SUBTREE_MODIFIED:
157                     final Tables before = table.getDataBefore();
158                     final Tables after = table.getDataAfter();
159                     final TablesKey tk = after.key();
160                     LOG.debug("Process table {} type {}, dataAfter {}, dataBefore {}",
161                             tk, modificationType, after, before);
162
163                     final KeyedInstanceIdentifier<Tables, TablesKey> tablePath
164                             = this.effRibTables.child(Tables.class, tk);
165                     final RIBSupport ribSupport = this.registry.getRIBSupport(tk);
166                     if (ribSupport == null) {
167                         break;
168                     }
169                     tx.put(LogicalDatastoreType.OPERATIONAL,
170                             tablePath.child(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp
171                                     .rib.rev180329.rib.tables.Attributes.class), after.getAttributes());
172
173                     final DataObjectModification routesChangesContainer =
174                             table.getModifiedChildContainer(ribSupport.routesContainerClass());
175
176                     if (routesChangesContainer == null) {
177                         break;
178                     }
179                     updateRoutes(tx, tk, ribSupport, tablePath, routesChangesContainer.getModifiedChildren());
180                     break;
181                 case WRITE:
182                     writeTable(tx, table);
183                     break;
184                 default:
185                     LOG.warn("Ignoring unhandled root {}", table);
186                     break;
187             }
188         }
189         if (tx != null) {
190             final FluentFuture<? extends CommitInfo> future = tx.commit();
191             this.submitted = future;
192             future.addCallback(new FutureCallback<CommitInfo>() {
193                 @Override
194                 public void onSuccess(final CommitInfo result) {
195                     LOG.trace("Successful commit");
196                 }
197
198                 @Override
199                 public void onFailure(final Throwable trw) {
200                     LOG.error("Failed commit", trw);
201                 }
202             }, MoreExecutors.directExecutor());
203         }
204     }
205
206     @SuppressWarnings("unchecked")
207     private <C extends Routes & DataObject & ChoiceIn<Tables>, S extends ChildOf<? super C>,
208         R extends Route & ChildOf<? super S> & Identifiable<I>, I extends Identifier<R>> void updateRoutes(
209             final WriteTransaction tx,
210             final TablesKey tableKey, final RIBSupport<C, S, R, I> ribSupport,
211             final KeyedInstanceIdentifier<Tables, TablesKey> tablePath,
212             final Collection<DataObjectModification<? extends DataObject>> routeChanges) {
213         for (final DataObjectModification<? extends DataObject> routeChanged : routeChanges) {
214             final I routeKey
215                     = ((InstanceIdentifier.IdentifiableItem<R, I>) routeChanged.getIdentifier()).getKey();
216             switch (routeChanged.getModificationType()) {
217                 case SUBTREE_MODIFIED:
218                 case WRITE:
219                     writeRoutes(tx, tableKey, ribSupport, tablePath, routeKey, (R) routeChanged.getDataAfter());
220                     break;
221                 case DELETE:
222                     final InstanceIdentifier<R> routeIID = ribSupport.createRouteIdentifier(tablePath, routeKey);
223                     tx.delete(LogicalDatastoreType.OPERATIONAL, routeIID);
224                     break;
225             }
226         }
227     }
228
229     private <C extends Routes & DataObject & ChoiceIn<Tables>, S extends ChildOf<? super C>,
230         R extends Route & ChildOf<? super S> & Identifiable<I>, I extends Identifier<R>> void writeRoutes(
231                 final WriteTransaction tx, final TablesKey tk, final RIBSupport<C, S, R, I> ribSupport,
232             final KeyedInstanceIdentifier<Tables, TablesKey> tablePath, final I routeKey,
233             final R route) {
234         final InstanceIdentifier<R> routeIID = ribSupport.createRouteIdentifier(tablePath, routeKey);
235         CountersUtil.increment(this.prefixesReceived.get(tk), tk);
236         final Optional<Attributes> effAtt = this.ribPolicies
237                 .applyImportPolicies(this.peerImportParameters, route.getAttributes(),
238                         tableTypeRegistry.getAfiSafiType(ribSupport.getTablesKey()).get());
239         if (effAtt.isPresent()) {
240             CountersUtil.increment(this.prefixesInstalled.get(tk), tk);
241             tx.put(LogicalDatastoreType.OPERATIONAL, routeIID, route);
242             tx.put(LogicalDatastoreType.OPERATIONAL, routeIID.child(Attributes.class), effAtt.get());
243         } else {
244             tx.delete(LogicalDatastoreType.OPERATIONAL, routeIID);
245         }
246     }
247
248     @SuppressWarnings("unchecked")
249     private void writeTable(final WriteTransaction tx, final DataObjectModification<Tables> table) {
250         final Tables newTable = table.getDataAfter();
251         if (newTable == null) {
252             return;
253         }
254         final TablesKey tableKey = newTable.key();
255         final KeyedInstanceIdentifier<Tables, TablesKey> tablePath
256                 = this.effRibTables.child(Tables.class, tableKey);
257
258         // Create an empty table
259         LOG.trace("Create Empty table", tablePath);
260         if (table.getDataBefore() == null) {
261             tx.put(LogicalDatastoreType.OPERATIONAL, tablePath, new TablesBuilder()
262                     .setAfi(tableKey.getAfi()).setSafi(tableKey.getSafi())
263                     .setRoutes(this.registry.getRIBSupport(tableKey).emptyRoutesCase())
264                     .setAttributes(newTable.getAttributes()).build());
265         }
266
267         final RIBSupport ribSupport = this.registry.getRIBSupport(tableKey);
268         final Routes routes = newTable.getRoutes();
269         if (ribSupport == null || routes == null) {
270             return;
271         }
272
273         final DataObjectModification routesChangesContainer =
274                 table.getModifiedChildContainer(ribSupport.routesContainerClass());
275
276         if (routesChangesContainer == null) {
277             return;
278         }
279         updateRoutes(tx, tableKey, ribSupport, tablePath, routesChangesContainer.getModifiedChildren());
280     }
281
282     @Override
283     public synchronized void close() {
284         if (this.reg != null) {
285             this.reg.close();
286             this.reg = null;
287         }
288         if (this.submitted != null) {
289             try {
290                 this.submitted.get();
291             } catch (final InterruptedException | ExecutionException throwable) {
292                 LOG.error("Write routes failed", throwable);
293             }
294         }
295         if (this.chain != null) {
296             this.chain.close();
297             this.chain = null;
298         }
299         this.prefixesReceived.values().forEach(LongAdder::reset);
300         this.prefixesInstalled.values().forEach(LongAdder::reset);
301     }
302
303     @Override
304     public long getPrefixedReceivedCount(final TablesKey tablesKey) {
305         final LongAdder counter = this.prefixesReceived.get(tablesKey);
306         if (counter == null) {
307             return 0;
308         }
309         return counter.longValue();
310     }
311
312     @Override
313     public Set<TablesKey> getTableKeys() {
314         return ImmutableSet.copyOf(this.prefixesReceived.keySet());
315     }
316
317     @Override
318     public boolean isSupported(final TablesKey tablesKey) {
319         return this.prefixesReceived.containsKey(tablesKey);
320     }
321
322     @Override
323     public long getPrefixedInstalledCount(final TablesKey tablesKey) {
324         final LongAdder counter = this.prefixesInstalled.get(tablesKey);
325         if (counter == null) {
326             return 0;
327         }
328         return counter.longValue();
329     }
330
331     @Override
332     public long getTotalPrefixesInstalled() {
333         return this.prefixesInstalled.values().stream().mapToLong(LongAdder::longValue).sum();
334     }
335 }