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