Remove unnecesary imports
[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.base.Optional;
13 import com.google.common.base.Verify;
14 import com.google.common.collect.ImmutableMap;
15 import com.google.common.collect.ImmutableSet;
16 import java.util.Collection;
17 import java.util.Collections;
18 import java.util.HashSet;
19 import java.util.Map;
20 import java.util.Set;
21 import java.util.concurrent.ConcurrentHashMap;
22 import java.util.concurrent.atomic.LongAdder;
23 import javax.annotation.Nonnull;
24 import javax.annotation.concurrent.NotThreadSafe;
25 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
26 import org.opendaylight.controller.md.sal.dom.api.ClusteredDOMDataTreeChangeListener;
27 import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeService;
28 import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeIdentifier;
29 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
30 import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
31 import org.opendaylight.protocol.bgp.rib.impl.spi.AbstractImportPolicy;
32 import org.opendaylight.protocol.bgp.rib.impl.spi.ImportPolicyPeerTracker;
33 import org.opendaylight.protocol.bgp.rib.impl.spi.RIBSupportContext;
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.impl.stats.peer.route.PerTableTypeRouteCounter;
38 import org.opendaylight.protocol.bgp.rib.spi.IdentifierUtils;
39 import org.opendaylight.protocol.bgp.rib.spi.RIBSupport;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.PeerRole;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.peer.AdjRibIn;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.peer.EffectiveRibIn;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.Tables;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.TablesKey;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.tables.Routes;
46 import org.opendaylight.yangtools.concepts.ListenerRegistration;
47 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
48 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
49 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
50 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
51 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
52 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
53 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
54 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
55 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
56 import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType;
57 import org.slf4j.Logger;
58 import org.slf4j.LoggerFactory;
59
60 /**
61  * Implementation of the BGP import policy. Listens on peer's Adj-RIB-In, inspects all inbound
62  * routes in the context of the advertising peer's role and applies the inbound policy.
63  *
64  * Inbound policy is applied as follows:
65  *
66  * 1) if the peer is an eBGP peer, perform attribute replacement and filtering
67  * 2) check if a route is admissible based on attributes attached to it, as well as the
68  *    advertising peer's role
69  * 3) output admitting routes with edited attributes into /bgp-rib/rib/peer/effective-rib-in/tables/routes
70  *
71  */
72 @NotThreadSafe
73 final class EffectiveRibInWriter implements PrefixesReceivedCounters, PrefixesInstalledCounters, AutoCloseable {
74     private static final Logger LOG = LoggerFactory.getLogger(EffectiveRibInWriter.class);
75
76     private static final Set<YangInstanceIdentifier> EMPTY_SET = Collections.emptySet();
77     static final NodeIdentifier TABLE_ROUTES = new NodeIdentifier(Routes.QNAME);
78
79     private final class AdjInTracker implements PrefixesReceivedCounters, PrefixesInstalledCounters, AutoCloseable,
80         ClusteredDOMDataTreeChangeListener {
81         private final RIBSupportContextRegistry registry;
82         private final YangInstanceIdentifier peerIId;
83         private final YangInstanceIdentifier effRibTables;
84         private final ListenerRegistration<?> reg;
85         private final DOMTransactionChain chain;
86         private final PerTableTypeRouteCounter adjRibInRouteCounters;
87         private final Map<TablesKey, Set<YangInstanceIdentifier>> adjRibInRouteMap = new ConcurrentHashMap<>();
88         private final Map<TablesKey, LongAdder> prefixesReceived;
89         private final Map<TablesKey, LongAdder> prefixesInstalled;
90
91         AdjInTracker(final DOMDataTreeChangeService service, final RIBSupportContextRegistry registry,
92             final DOMTransactionChain chain, final YangInstanceIdentifier peerIId,
93             @Nonnull final PerTableTypeRouteCounter adjRibInRouteCounters, @Nonnull Set<TablesKey> tables) {
94             this.registry = requireNonNull(registry);
95             this.chain = requireNonNull(chain);
96             this.peerIId = requireNonNull(peerIId);
97             this.effRibTables = this.peerIId.node(EffectiveRibIn.QNAME).node(Tables.QNAME);
98             this.adjRibInRouteCounters = requireNonNull(adjRibInRouteCounters);
99             this.prefixesInstalled = buildPrefixesTables(tables);
100             this.prefixesReceived = buildPrefixesTables(tables);
101
102             final DOMDataTreeIdentifier treeId = new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL,
103                 this.peerIId.node(AdjRibIn.QNAME).node(Tables.QNAME));
104             LOG.debug("Registered Effective RIB on {}", this.peerIId);
105             this.reg = service.registerDataTreeChangeListener(treeId, this);
106         }
107
108         private Map<TablesKey, LongAdder> buildPrefixesTables(final Set<TablesKey> tables) {
109             final ImmutableMap.Builder<TablesKey, LongAdder> b = ImmutableMap.builder();
110             tables.forEach(table -> b.put(table, new LongAdder()));
111             return b.build();
112         }
113
114
115         private void updateRoute(@Nonnull final PerTableTypeRouteCounter counter, @Nonnull final Map<TablesKey, Set<YangInstanceIdentifier>> routeMap,
116                 @Nonnull final TablesKey tablesKey, @Nonnull final YangInstanceIdentifier routeId) {
117             routeMap.putIfAbsent(tablesKey, new HashSet<>());
118             routeMap.get(tablesKey).add(routeId);
119             updateRouteCounter(counter, routeMap,tablesKey);
120         }
121
122         private void deleteRoute(@Nonnull final PerTableTypeRouteCounter counter, @Nonnull final Map<TablesKey, Set<YangInstanceIdentifier>> routeMap,
123                 @Nonnull final TablesKey tablesKey, @Nonnull final YangInstanceIdentifier routeId) {
124             if (routeMap.containsKey(tablesKey)) {
125                 routeMap.get(tablesKey).remove(routeId);
126             }
127
128             updateRouteCounter(counter, routeMap,tablesKey);
129         }
130
131         private void deleteRoute(@Nonnull final PerTableTypeRouteCounter counter, @Nonnull final Map<TablesKey, Set<YangInstanceIdentifier>> routeMap,
132                 @Nonnull final TablesKey tablesKey) {
133             routeMap.remove(tablesKey);
134
135             updateRouteCounter(counter, routeMap,tablesKey);
136         }
137
138         private void updateRouteCounter(@Nonnull final PerTableTypeRouteCounter counter, @Nonnull final Map<TablesKey,
139             Set<YangInstanceIdentifier>> routeMap, @Nonnull final TablesKey tablesKey) {
140             final int size = routeMap.getOrDefault(tablesKey, EMPTY_SET).size();
141             counter.setValueToCounterOrSetDefault(tablesKey, size);
142         }
143
144         private void processRoute(final DOMDataWriteTransaction tx, final RIBSupport ribSupport, final AbstractImportPolicy policy,
145             final YangInstanceIdentifier routesPath, final DataTreeCandidateNode route) {
146             LOG.debug("Process route {}", route.getIdentifier());
147             final YangInstanceIdentifier routeId = ribSupport.routePath(routesPath, route.getIdentifier());
148             final TablesKey tablesKey = new TablesKey(ribSupport.getAfi(), ribSupport.getSafi());
149             switch (route.getModificationType()) {
150             case DELETE:
151             case DISAPPEARED:
152                 tx.delete(LogicalDatastoreType.OPERATIONAL, routeId);
153                 LOG.debug("Route deleted. routeId={}", routeId);
154
155                 deleteRoute(this.adjRibInRouteCounters, this.adjRibInRouteMap, tablesKey, routeId);
156                 CountersUtil.decrement(this.prefixesInstalled.get(tablesKey), tablesKey);
157                 break;
158             case UNMODIFIED:
159                 // No-op
160                 break;
161             case APPEARED:
162             case SUBTREE_MODIFIED:
163             case WRITE:
164                 tx.put(LogicalDatastoreType.OPERATIONAL, routeId, route.getDataAfter().get());
165                 CountersUtil.increment(this.prefixesReceived.get(tablesKey), tablesKey);
166                 // count adj-rib-in route first
167                 updateRoute(this.adjRibInRouteCounters, this.adjRibInRouteMap, tablesKey, routeId);
168                 // Lookup per-table attributes from RIBSupport
169                 final ContainerNode advertisedAttrs = (ContainerNode) NormalizedNodes.findNode(route.getDataAfter(), ribSupport.routeAttributesIdentifier()).orNull();
170                 final ContainerNode effectiveAttrs;
171
172                 if (advertisedAttrs != null) {
173                     effectiveAttrs = policy.effectiveAttributes(advertisedAttrs);
174                 } else {
175                     effectiveAttrs = null;
176                 }
177
178                 LOG.debug("Route {} effective attributes {} towards {}", route.getIdentifier(), effectiveAttrs, routeId);
179
180                 if (effectiveAttrs != null) {
181                     tx.put(LogicalDatastoreType.OPERATIONAL, routeId.node(ribSupport.routeAttributesIdentifier()), effectiveAttrs);
182                     if(route.getModificationType() == ModificationType.WRITE) {
183                         CountersUtil.increment(this.prefixesInstalled.get(tablesKey), tablesKey);
184                     }
185                 } else {
186                     LOG.warn("Route {} advertised empty attributes", routeId);
187                     tx.delete(LogicalDatastoreType.OPERATIONAL,  routeId);
188                 }
189                 break;
190             default:
191                 LOG.warn("Ignoring unhandled route {}", route);
192                 break;
193             }
194         }
195
196         private void processTableChildren(final DOMDataWriteTransaction tx, final RIBSupport ribSupport, final YangInstanceIdentifier tablePath, final Collection<DataTreeCandidateNode> children) {
197             for (final DataTreeCandidateNode child : children) {
198                 final PathArgument childIdentifier = child.getIdentifier();
199                 final Optional<NormalizedNode<?, ?>> childDataAfter = child.getDataAfter();
200                 final TablesKey tablesKey = new TablesKey(ribSupport.getAfi(), ribSupport.getSafi());
201                 LOG.debug("Process table {} type {}, dataAfter {}, dataBefore {}", childIdentifier, child
202                         .getModificationType(), childDataAfter, child.getDataBefore());
203                 final YangInstanceIdentifier childPath = tablePath.node(childIdentifier);
204                 switch (child.getModificationType()) {
205                 case DELETE:
206                 case DISAPPEARED:
207                     tx.delete(LogicalDatastoreType.OPERATIONAL, childPath);
208                     LOG.debug("Route deleted. routeId={}", childPath);
209
210                     deleteRoute(this.adjRibInRouteCounters, this.adjRibInRouteMap, tablesKey, childPath);
211                     CountersUtil.decrement(this.prefixesInstalled.get(tablesKey), tablesKey);
212                     break;
213                 case UNMODIFIED:
214                     // No-op
215                     break;
216                 case SUBTREE_MODIFIED:
217                     processModifiedRouteTables(child, childIdentifier,tx, ribSupport, EffectiveRibInWriter.this.importPolicy, childPath, childDataAfter);
218                     break;
219                 case APPEARED:
220                 case WRITE:
221                     writeRouteTables(child, childIdentifier,tx, ribSupport, EffectiveRibInWriter.this.importPolicy, childPath, childDataAfter);
222
223                     break;
224                 default:
225                     LOG.warn("Ignoring unhandled child {}", child);
226                     break;
227                 }
228             }
229         }
230
231         private void processModifiedRouteTables(final DataTreeCandidateNode child, final PathArgument childIdentifier, final DOMDataWriteTransaction tx,
232                 final RIBSupport ribSupport, final AbstractImportPolicy policy, final YangInstanceIdentifier childPath, final Optional<NormalizedNode<?, ?>> childDataAfter) {
233             if (TABLE_ROUTES.equals(childIdentifier)) {
234                 for (final DataTreeCandidateNode route : ribSupport.changedRoutes(child)) {
235                     processRoute(tx, ribSupport, policy, childPath, route);
236                 }
237             } else {
238                 tx.put(LogicalDatastoreType.OPERATIONAL, childPath, childDataAfter.get());
239             }
240         }
241
242         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) {
243             if (TABLE_ROUTES.equals(childIdentifier)) {
244                 final Collection<DataTreeCandidateNode> changedRoutes = ribSupport.changedRoutes(child);
245                 if (!changedRoutes.isEmpty()) {
246                     tx.put(LogicalDatastoreType.OPERATIONAL, childPath, childDataAfter.get());
247                     // Routes are special, as they may end up being filtered. The previous put conveniently
248                     // ensured that we have them in at target, so a subsequent delete will not fail :)
249                     for (final DataTreeCandidateNode route : changedRoutes) {
250                         processRoute(tx, ribSupport, policy, childPath, route);
251                     }
252                 }
253             }
254         }
255
256         private RIBSupportContext getRibSupport(final NodeIdentifierWithPredicates tableKey) {
257             return this.registry.getRIBSupportContext(tableKey);
258         }
259
260         private YangInstanceIdentifier effectiveTablePath(final NodeIdentifierWithPredicates tableKey) {
261             return this.effRibTables.node(tableKey);
262         }
263
264         private void modifyTable(final DOMDataWriteTransaction tx, final NodeIdentifierWithPredicates tableKey, final DataTreeCandidateNode table) {
265             final RIBSupportContext ribSupport = getRibSupport(tableKey);
266             final YangInstanceIdentifier tablePath = effectiveTablePath(tableKey);
267
268             processTableChildren(tx, ribSupport.getRibSupport(), tablePath, table.getChildNodes());
269         }
270
271         private void writeTable(final DOMDataWriteTransaction tx, final NodeIdentifierWithPredicates tableKey, final DataTreeCandidateNode table) {
272             final RIBSupportContext ribSupport = getRibSupport(tableKey);
273             final YangInstanceIdentifier tablePath = effectiveTablePath(tableKey);
274
275             // Create an empty table
276             LOG.trace("Create Empty table", tablePath);
277             ribSupport.createEmptyTableStructure(tx, tablePath);
278
279             processTableChildren(tx, ribSupport.getRibSupport(), tablePath, table.getChildNodes());
280         }
281
282         @Override
283         public void onDataTreeChanged(@Nonnull final Collection<DataTreeCandidate> changes) {
284             LOG.trace("Data changed called to effective RIB. Change : {}", changes);
285
286             // we have a lot of transactions created for 'nothing' because a lot of changes
287             // are skipped, so ensure we only create one transaction when we really need it
288             DOMDataWriteTransaction tx = null;
289             for (final DataTreeCandidate tc : changes) {
290                 final YangInstanceIdentifier rootPath = tc.getRootPath();
291
292                 final DataTreeCandidateNode root = tc.getRootNode();
293                 for (final DataTreeCandidateNode table : root.getChildNodes()) {
294                     if (tx == null) {
295                         tx = this.chain.newWriteOnlyTransaction();
296                     }
297                     changeDataTree(tx, rootPath, root, table);
298                 }
299             }
300             if (tx != null) {
301                 tx.submit();
302             }
303         }
304
305         private void changeDataTree(final DOMDataWriteTransaction tx, final YangInstanceIdentifier rootPath,
306                 final DataTreeCandidateNode root, final DataTreeCandidateNode table) {
307             final PathArgument lastArg = table.getIdentifier();
308             Verify.verify(lastArg instanceof NodeIdentifierWithPredicates, "Unexpected type %s in path %s", lastArg.getClass(), rootPath);
309             final NodeIdentifierWithPredicates tableKey = (NodeIdentifierWithPredicates) lastArg;
310             final RIBSupport ribSupport = getRibSupport(tableKey).getRibSupport();
311             final ModificationType modificationType = root.getModificationType();
312             switch (modificationType) {
313             case DELETE:
314             case DISAPPEARED:
315                 final YangInstanceIdentifier effectiveTablePath = effectiveTablePath(tableKey);
316                 LOG.debug("Delete Effective Table {} modification type {}, ", effectiveTablePath, modificationType);
317
318                 // delete the corresponding effective table
319                 tx.delete(LogicalDatastoreType.OPERATIONAL, effectiveTablePath);
320                 final TablesKey tk = new TablesKey(ribSupport.getAfi(), ribSupport.getSafi());
321                 deleteRoute(this.adjRibInRouteCounters, this.adjRibInRouteMap, tk);
322                 CountersUtil.decrement(this.prefixesInstalled.get(tk), tk);
323                 break;
324             case SUBTREE_MODIFIED:
325                 modifyTable(tx, tableKey, table);
326                 break;
327             case UNMODIFIED:
328                 LOG.info("Ignoring spurious notification on {} data {}", rootPath, table);
329                 break;
330             case APPEARED:
331             case WRITE:
332                 writeTable(tx, tableKey, table);
333                 break;
334             default:
335                 LOG.warn("Ignoring unhandled root {}", root);
336                 break;
337             }
338         }
339
340         @Override
341         public void close() {
342             this.reg.close();
343             this.prefixesReceived.values().forEach(LongAdder::reset);
344             this.prefixesInstalled.values().forEach(LongAdder::reset);
345         }
346
347         @Override
348         public long getPrefixedReceivedCount(final TablesKey tablesKey) {
349             final LongAdder counter = this.prefixesReceived.get(tablesKey);
350             if (counter == null) {
351                 return 0;
352             }
353             return counter.longValue();
354         }
355
356         @Override
357         public Set<TablesKey> getTableKeys() {
358             return ImmutableSet.copyOf(this.prefixesReceived.keySet());
359         }
360
361         @Override
362         public boolean isSupported(final TablesKey tablesKey) {
363             return this.prefixesReceived.containsKey(tablesKey);
364         }
365
366         @Override
367         public long getPrefixedInstalledCount(final TablesKey tablesKey) {
368             final LongAdder counter = this.prefixesInstalled.get(tablesKey);
369             if (counter == null) {
370                 return 0;
371             }
372             return counter.longValue();
373         }
374
375         @Override
376         public long getTotalPrefixesInstalled() {
377             return this.prefixesInstalled.values().stream().mapToLong(LongAdder::longValue).sum();
378         }
379     }
380
381     private final AdjInTracker adjInTracker;
382     private final AbstractImportPolicy importPolicy;
383
384     static EffectiveRibInWriter create(@Nonnull final DOMDataTreeChangeService service, @Nonnull final DOMTransactionChain chain,
385         @Nonnull final YangInstanceIdentifier peerIId, @Nonnull final ImportPolicyPeerTracker importPolicyPeerTracker,
386         @Nonnull final RIBSupportContextRegistry registry, final PeerRole peerRole,
387         @Nonnull final PerTableTypeRouteCounter adjRibInRouteCounters, @Nonnull Set<TablesKey> tables) {
388         return new EffectiveRibInWriter(service, chain, peerIId, importPolicyPeerTracker, registry, peerRole,
389             adjRibInRouteCounters, tables);
390     }
391
392     private EffectiveRibInWriter(final DOMDataTreeChangeService service, final DOMTransactionChain chain, final YangInstanceIdentifier peerIId,
393         final ImportPolicyPeerTracker importPolicyPeerTracker, final RIBSupportContextRegistry registry, final PeerRole peerRole,
394         @Nonnull final PerTableTypeRouteCounter adjRibInRouteCounters, @Nonnull Set<TablesKey> tables) {
395         importPolicyPeerTracker.peerRoleChanged(peerIId, peerRole);
396         this.importPolicy = importPolicyPeerTracker.policyFor(IdentifierUtils.peerId((NodeIdentifierWithPredicates) peerIId.getLastPathArgument()));
397         this.adjInTracker = new AdjInTracker(service, registry, chain, peerIId, adjRibInRouteCounters, tables);
398     }
399
400     @Override
401     public void close() {
402         this.adjInTracker.close();
403     }
404
405     @Override
406     public long getPrefixedReceivedCount(final TablesKey tablesKey) {
407         return this.adjInTracker.getPrefixedReceivedCount(tablesKey);
408     }
409
410     @Override
411     public Set<TablesKey> getTableKeys() {
412         return this.adjInTracker.getTableKeys();
413     }
414
415     @Override
416     public boolean isSupported(final TablesKey tablesKey) {
417         return this.adjInTracker.isSupported(tablesKey);
418     }
419
420     @Override
421     public long getPrefixedInstalledCount(@Nonnull final TablesKey tablesKey) {
422         return this.adjInTracker.getPrefixedInstalledCount(tablesKey);
423     }
424
425     @Override
426     public long getTotalPrefixesInstalled() {
427         return this.adjInTracker.getTotalPrefixesInstalled();
428     }
429 }