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