Replace UnsignedInt32Counter by LongAdder
[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 com.google.common.base.Optional;
11 import com.google.common.base.Preconditions;
12 import com.google.common.base.Verify;
13 import java.util.Collection;
14 import java.util.HashSet;
15 import java.util.Map;
16 import java.util.Set;
17 import java.util.concurrent.ConcurrentHashMap;
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.common.api.data.LogicalDatastoreType;
22 import org.opendaylight.controller.md.sal.dom.api.ClusteredDOMDataTreeChangeListener;
23 import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeService;
24 import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeIdentifier;
25 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
26 import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
27 import org.opendaylight.protocol.bgp.rib.impl.spi.AbstractImportPolicy;
28 import org.opendaylight.protocol.bgp.rib.impl.spi.ImportPolicyPeerTracker;
29 import org.opendaylight.protocol.bgp.rib.impl.spi.RIBSupportContext;
30 import org.opendaylight.protocol.bgp.rib.impl.spi.RIBSupportContextRegistry;
31 import org.opendaylight.protocol.bgp.rib.impl.stats.peer.route.PerTableTypeRouteCounter;
32 import org.opendaylight.protocol.bgp.rib.spi.IdentifierUtils;
33 import org.opendaylight.protocol.bgp.rib.spi.RIBSupport;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.PeerRole;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.peer.AdjRibIn;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.peer.EffectiveRibIn;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.Tables;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.TablesKey;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.tables.Routes;
40 import org.opendaylight.yangtools.concepts.ListenerRegistration;
41 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
42 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
43 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
44 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
45 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
46 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
47 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
48 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
49 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
50 import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType;
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  *
58  * Inbound policy is applied as follows:
59  *
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  */
66 @NotThreadSafe
67 final class EffectiveRibInWriter implements AutoCloseable {
68     private static final Logger LOG = LoggerFactory.getLogger(EffectiveRibInWriter.class);
69     protected static final NodeIdentifier TABLE_ROUTES = new NodeIdentifier(Routes.QNAME);
70
71     private final class AdjInTracker implements AutoCloseable, ClusteredDOMDataTreeChangeListener {
72         private final RIBSupportContextRegistry registry;
73         private final YangInstanceIdentifier peerIId;
74         private final YangInstanceIdentifier effRibTables;
75         private final ListenerRegistration<?> reg;
76         private final DOMTransactionChain chain;
77         private final PerTableTypeRouteCounter effectiveRibInRouteCounters;
78         private final PerTableTypeRouteCounter adjRibInRouteCounters;
79         private final Map<TablesKey, Set<YangInstanceIdentifier>> effectiveRibInRouteMap = new ConcurrentHashMap<>();
80         private final Map<TablesKey, Set<YangInstanceIdentifier>> adjRibInRouteMap = new ConcurrentHashMap<>();
81
82         AdjInTracker(final DOMDataTreeChangeService service, final RIBSupportContextRegistry registry, final DOMTransactionChain chain, final YangInstanceIdentifier peerIId,
83                 @Nonnull final PerTableTypeRouteCounter effectiveRibInRouteCounters, @Nonnull final PerTableTypeRouteCounter adjRibInRouteCounters) {
84             this.registry = Preconditions.checkNotNull(registry);
85             this.chain = Preconditions.checkNotNull(chain);
86             this.peerIId = Preconditions.checkNotNull(peerIId);
87             this.effRibTables = this.peerIId.node(EffectiveRibIn.QNAME).node(Tables.QNAME);
88             this.effectiveRibInRouteCounters = Preconditions.checkNotNull(effectiveRibInRouteCounters);
89             this.adjRibInRouteCounters = Preconditions.checkNotNull(adjRibInRouteCounters);
90
91             final DOMDataTreeIdentifier treeId = new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, this.peerIId.node(AdjRibIn.QNAME).node(Tables.QNAME));
92             LOG.debug("Registered Effective RIB on {}", this.peerIId);
93             this.reg = service.registerDataTreeChangeListener(treeId, this);
94         }
95
96         /**
97          * @deprecated Should always pass in route counter
98          * @param service
99          * @param registry
100          * @param chain
101          * @param peerIId
102          */
103         @Deprecated
104         AdjInTracker(final DOMDataTreeChangeService service, final RIBSupportContextRegistry registry, final DOMTransactionChain chain, final YangInstanceIdentifier peerIId) {
105             this(service, registry, chain, peerIId, new PerTableTypeRouteCounter(), new PerTableTypeRouteCounter());
106         }
107
108         private void updateRoute(@Nonnull final PerTableTypeRouteCounter counter, @Nonnull final Map<TablesKey, Set<YangInstanceIdentifier>> routeMap,
109                 @Nonnull final TablesKey tablesKey, @Nonnull final YangInstanceIdentifier routeId) {
110             routeMap.putIfAbsent(tablesKey, new HashSet<>());
111             routeMap.get(tablesKey).add(routeId);
112
113             updateRouteCounter(counter, routeMap,tablesKey);
114         }
115
116         private void deleteRoute(@Nonnull final PerTableTypeRouteCounter counter, @Nonnull final Map<TablesKey, Set<YangInstanceIdentifier>> routeMap,
117                 @Nonnull final TablesKey tablesKey, @Nonnull final YangInstanceIdentifier routeId) {
118             if (routeMap.containsKey(tablesKey)) {
119                 routeMap.get(tablesKey).remove(routeId);
120             }
121
122             updateRouteCounter(counter, routeMap,tablesKey);
123         }
124
125         private void deleteRoute(@Nonnull final PerTableTypeRouteCounter counter, @Nonnull final Map<TablesKey, Set<YangInstanceIdentifier>> routeMap,
126                 @Nonnull final TablesKey tablesKey) {
127             routeMap.remove(tablesKey);
128
129             updateRouteCounter(counter, routeMap,tablesKey);
130         }
131
132         private void updateRouteCounter(@Nonnull final PerTableTypeRouteCounter counter, @Nonnull final Map<TablesKey, Set<YangInstanceIdentifier>> routeMap,
133                 @Nonnull final TablesKey tablesKey) {
134             final LongAdder tableCounter = counter.getCounterOrSetDefault(tablesKey);
135             tableCounter.reset();
136             tableCounter.add(routeMap.getOrDefault(tablesKey, new HashSet<>()).size());
137         }
138
139         private void processRoute(final DOMDataWriteTransaction tx, final RIBSupport ribSupport, final AbstractImportPolicy policy, final YangInstanceIdentifier routesPath, final DataTreeCandidateNode route) {
140             LOG.debug("Process route {}", route.getIdentifier());
141             final YangInstanceIdentifier routeId = ribSupport.routePath(routesPath, route.getIdentifier());
142             final TablesKey tablesKey = new TablesKey(ribSupport.getAfi(), ribSupport.getSafi());
143             switch (route.getModificationType()) {
144             case DELETE:
145             case DISAPPEARED:
146                 tx.delete(LogicalDatastoreType.OPERATIONAL, routeId);
147                 LOG.debug("Route deleted. routeId={}", routeId);
148
149                 deleteRoute(this.adjRibInRouteCounters, this.adjRibInRouteMap, tablesKey, routeId);
150                 deleteRoute(this.effectiveRibInRouteCounters, this.effectiveRibInRouteMap, tablesKey, routeId);
151                 break;
152             case UNMODIFIED:
153                 // No-op
154                 break;
155             case APPEARED:
156             case SUBTREE_MODIFIED:
157             case WRITE:
158                 tx.put(LogicalDatastoreType.OPERATIONAL, routeId, route.getDataAfter().get());
159                 // count adj-rib-in route first
160                 updateRoute(this.adjRibInRouteCounters, this.adjRibInRouteMap, tablesKey, routeId);
161                 updateRoute(this.effectiveRibInRouteCounters, this.effectiveRibInRouteMap, tablesKey, routeId);
162                 // Lookup per-table attributes from RIBSupport
163                 final ContainerNode advertisedAttrs = (ContainerNode) NormalizedNodes.findNode(route.getDataAfter(), ribSupport.routeAttributesIdentifier()).orNull();
164                 final ContainerNode effectiveAttrs;
165
166                 if (advertisedAttrs != null) {
167                     effectiveAttrs = policy.effectiveAttributes(advertisedAttrs);
168                 } else {
169                     effectiveAttrs = null;
170                 }
171
172                 LOG.debug("Route {} effective attributes {} towards {}", route.getIdentifier(), effectiveAttrs, routeId);
173
174                 if (effectiveAttrs != null) {
175                     tx.put(LogicalDatastoreType.OPERATIONAL, routeId.node(ribSupport.routeAttributesIdentifier()), effectiveAttrs);
176
177                     updateRoute(this.effectiveRibInRouteCounters, this.effectiveRibInRouteMap, tablesKey, routeId);
178                 } else {
179                     LOG.warn("Route {} advertised empty attributes", routeId);
180                     tx.delete(LogicalDatastoreType.OPERATIONAL,  routeId);
181
182                     deleteRoute(this.effectiveRibInRouteCounters, this.effectiveRibInRouteMap, tablesKey, routeId);
183                 }
184                 break;
185             default:
186                 LOG.warn("Ignoring unhandled route {}", route);
187                 break;
188             }
189         }
190
191         private void processTableChildren(final DOMDataWriteTransaction tx, final RIBSupport ribSupport, final YangInstanceIdentifier tablePath, final Collection<DataTreeCandidateNode> children) {
192             for (final DataTreeCandidateNode child : children) {
193                 final PathArgument childIdentifier = child.getIdentifier();
194                 final Optional<NormalizedNode<?, ?>> childDataAfter = child.getDataAfter();
195                 final TablesKey tablesKey = new TablesKey(ribSupport.getAfi(), ribSupport.getSafi());
196                 LOG.debug("Process table {} type {}, dataAfter {}, dataBefore {}", childIdentifier, child
197                         .getModificationType(), childDataAfter, child.getDataBefore());
198                 final YangInstanceIdentifier childPath = tablePath.node(childIdentifier);
199                 switch (child.getModificationType()) {
200                 case DELETE:
201                 case DISAPPEARED:
202                     tx.delete(LogicalDatastoreType.OPERATIONAL, childPath);
203                     LOG.debug("Route deleted. routeId={}", childPath);
204
205                     deleteRoute(this.adjRibInRouteCounters, this.adjRibInRouteMap, tablesKey, childPath);
206                     deleteRoute(this.effectiveRibInRouteCounters, this.effectiveRibInRouteMap, tablesKey, childPath);
207                     break;
208                 case UNMODIFIED:
209                     // No-op
210                     break;
211                 case SUBTREE_MODIFIED:
212                     processModifiedRouteTables(child, childIdentifier,tx, ribSupport, EffectiveRibInWriter.this.importPolicy, childPath, childDataAfter);
213                     break;
214                 case APPEARED:
215                 case WRITE:
216                     writeRouteTables(child, childIdentifier,tx, ribSupport, EffectiveRibInWriter.this.importPolicy, childPath, childDataAfter);
217
218                     break;
219                 default:
220                     LOG.warn("Ignoring unhandled child {}", child);
221                     break;
222                 }
223             }
224         }
225
226         private void processModifiedRouteTables(final DataTreeCandidateNode child, final PathArgument childIdentifier, final DOMDataWriteTransaction tx,
227                 final RIBSupport ribSupport, final AbstractImportPolicy policy, final YangInstanceIdentifier childPath, final Optional<NormalizedNode<?, ?>> childDataAfter) {
228             if (TABLE_ROUTES.equals(childIdentifier)) {
229                 for (final DataTreeCandidateNode route : ribSupport.changedRoutes(child)) {
230                     processRoute(tx, ribSupport, policy, childPath, route);
231                 }
232             } else {
233                 tx.put(LogicalDatastoreType.OPERATIONAL, childPath, childDataAfter.get());
234             }
235         }
236
237         private void writeRouteTables(final DataTreeCandidateNode child, final PathArgument childIdentifier, final DOMDataWriteTransaction tx, final RIBSupport ribSupport, final AbstractImportPolicy policy, final YangInstanceIdentifier childPath, final Optional<NormalizedNode<?, ?>> childDataAfter) {
238             if (TABLE_ROUTES.equals(childIdentifier)) {
239                 final Collection<DataTreeCandidateNode> changedRoutes = ribSupport.changedRoutes(child);
240                 if (!changedRoutes.isEmpty()) {
241                     tx.put(LogicalDatastoreType.OPERATIONAL, childPath, childDataAfter.get());
242                     // Routes are special, as they may end up being filtered. The previous put conveniently
243                     // ensured that we have them in at target, so a subsequent delete will not fail :)
244                     for (final DataTreeCandidateNode route : changedRoutes) {
245                         processRoute(tx, ribSupport, policy, childPath, route);
246                     }
247                 }
248             }
249         }
250
251         private RIBSupportContext getRibSupport(final NodeIdentifierWithPredicates tableKey) {
252             return this.registry.getRIBSupportContext(tableKey);
253         }
254
255         private YangInstanceIdentifier effectiveTablePath(final NodeIdentifierWithPredicates tableKey) {
256             return this.effRibTables.node(tableKey);
257         }
258
259         private void modifyTable(final DOMDataWriteTransaction tx, final NodeIdentifierWithPredicates tableKey, final DataTreeCandidateNode table) {
260             final RIBSupportContext ribSupport = getRibSupport(tableKey);
261             final YangInstanceIdentifier tablePath = effectiveTablePath(tableKey);
262
263             processTableChildren(tx, ribSupport.getRibSupport(), tablePath, table.getChildNodes());
264         }
265
266         private void writeTable(final DOMDataWriteTransaction tx, final NodeIdentifierWithPredicates tableKey, final DataTreeCandidateNode table) {
267             final RIBSupportContext ribSupport = getRibSupport(tableKey);
268             final YangInstanceIdentifier tablePath = effectiveTablePath(tableKey);
269
270             // Create an empty table
271             LOG.trace("Create Empty table", tablePath);
272             ribSupport.createEmptyTableStructure(tx, tablePath);
273
274             processTableChildren(tx, ribSupport.getRibSupport(), tablePath, table.getChildNodes());
275         }
276
277         @Override
278         public void onDataTreeChanged(@Nonnull final Collection<DataTreeCandidate> changes) {
279             LOG.trace("Data changed called to effective RIB. Change : {}", changes);
280
281             // we have a lot of transactions created for 'nothing' because a lot of changes
282             // are skipped, so ensure we only create one transaction when we really need it
283             DOMDataWriteTransaction tx = null;
284             for (final DataTreeCandidate tc : changes) {
285                 final YangInstanceIdentifier rootPath = tc.getRootPath();
286
287                 final DataTreeCandidateNode root = tc.getRootNode();
288                 for (final DataTreeCandidateNode table : root.getChildNodes()) {
289                     if (tx == null) {
290                         tx = this.chain.newWriteOnlyTransaction();
291                     }
292                     changeDataTree(tx, rootPath, root, table);
293                 }
294             }
295             if (tx != null) {
296                 tx.submit();
297             }
298         }
299
300         private void changeDataTree(final DOMDataWriteTransaction tx, final YangInstanceIdentifier rootPath,
301                 final DataTreeCandidateNode root, final DataTreeCandidateNode table) {
302             final PathArgument lastArg = table.getIdentifier();
303             Verify.verify(lastArg instanceof NodeIdentifierWithPredicates, "Unexpected type %s in path %s", lastArg.getClass(), rootPath);
304             final NodeIdentifierWithPredicates tableKey = (NodeIdentifierWithPredicates) lastArg;
305             final RIBSupport ribSupport = getRibSupport(tableKey).getRibSupport();
306             final ModificationType modificationType = root.getModificationType();
307             switch (modificationType) {
308             case DELETE:
309             case DISAPPEARED:
310                 final YangInstanceIdentifier effectiveTablePath = effectiveTablePath(tableKey);
311                 LOG.debug("Delete Effective Table {} modification type {}, ", effectiveTablePath, modificationType);
312
313                 // delete the corresponding effective table
314                 tx.delete(LogicalDatastoreType.OPERATIONAL, effectiveTablePath);
315
316                 deleteRoute(this.adjRibInRouteCounters, this.adjRibInRouteMap, new TablesKey(ribSupport.getAfi(), ribSupport.getSafi()));
317                 deleteRoute(this.effectiveRibInRouteCounters, this.effectiveRibInRouteMap, new TablesKey(ribSupport.getAfi(), ribSupport.getSafi()));
318                 break;
319             case SUBTREE_MODIFIED:
320                 modifyTable(tx, tableKey, table);
321                 break;
322             case UNMODIFIED:
323                 LOG.info("Ignoring spurious notification on {} data {}", rootPath, table);
324                 break;
325             case APPEARED:
326             case WRITE:
327                 writeTable(tx, tableKey, table);
328                 break;
329             default:
330                 LOG.warn("Ignoring unhandled root {}", root);
331                 break;
332             }
333         }
334
335         @Override
336         public void close() {
337             this.reg.close();
338         }
339     }
340
341     private final AdjInTracker adjInTracker;
342     private final AbstractImportPolicy importPolicy;
343
344     @Deprecated
345     static EffectiveRibInWriter create(@Nonnull final DOMDataTreeChangeService service, @Nonnull final DOMTransactionChain chain,
346             @Nonnull final YangInstanceIdentifier peerIId, @Nonnull final ImportPolicyPeerTracker importPolicyPeerTracker, @Nonnull final RIBSupportContextRegistry registry, final PeerRole peerRole) {
347         return new EffectiveRibInWriter(service, chain, peerIId, importPolicyPeerTracker, registry, peerRole);
348     }
349
350     static EffectiveRibInWriter create(@Nonnull final DOMDataTreeChangeService service, @Nonnull final DOMTransactionChain chain,
351             @Nonnull final YangInstanceIdentifier peerIId, @Nonnull final ImportPolicyPeerTracker importPolicyPeerTracker, @Nonnull final RIBSupportContextRegistry registry, final PeerRole peerRole,
352             @Nonnull final PerTableTypeRouteCounter effectiveRouteCounters, @Nonnull final PerTableTypeRouteCounter adjRibInRouteCounters) {
353         return new EffectiveRibInWriter(service, chain, peerIId, importPolicyPeerTracker, registry, peerRole, effectiveRouteCounters, adjRibInRouteCounters);
354     }
355
356     @Deprecated
357     private EffectiveRibInWriter(final DOMDataTreeChangeService service, final DOMTransactionChain chain, final YangInstanceIdentifier peerIId,
358             final ImportPolicyPeerTracker importPolicyPeerTracker, final RIBSupportContextRegistry registry, final PeerRole peerRole) {
359         importPolicyPeerTracker.peerRoleChanged(peerIId, peerRole);
360         this.importPolicy = importPolicyPeerTracker.policyFor(IdentifierUtils.peerId((NodeIdentifierWithPredicates) peerIId.getLastPathArgument()));
361         this.adjInTracker = new AdjInTracker(service, registry, chain, peerIId);
362     }
363
364     private EffectiveRibInWriter(final DOMDataTreeChangeService service, final DOMTransactionChain chain, final YangInstanceIdentifier peerIId,
365             final ImportPolicyPeerTracker importPolicyPeerTracker, final RIBSupportContextRegistry registry, final PeerRole peerRole,
366             @Nonnull final PerTableTypeRouteCounter effectiveRouteCounters, @Nonnull final PerTableTypeRouteCounter adjRibInRouteCounters) {
367         importPolicyPeerTracker.peerRoleChanged(peerIId, peerRole);
368         this.importPolicy = importPolicyPeerTracker.policyFor(IdentifierUtils.peerId((NodeIdentifierWithPredicates) peerIId.getLastPathArgument()));
369         this.adjInTracker = new AdjInTracker(service, registry, chain, peerIId, effectiveRouteCounters, adjRibInRouteCounters);
370     }
371
372     @Override
373     public void close() {
374         this.adjInTracker.close();
375     }
376 }