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