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