Replace Preconditions.CheckNotNull per RequireNonNull
[bgpcep.git] / bgp / rib-impl / src / main / java / org / opendaylight / protocol / bgp / rib / impl / LocRibWriter.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.primitives.UnsignedInteger;
14 import java.util.Collection;
15 import java.util.HashMap;
16 import java.util.Map;
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.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.mode.api.PathSelectionMode;
28 import org.opendaylight.protocol.bgp.mode.api.RouteEntry;
29 import org.opendaylight.protocol.bgp.rib.impl.spi.RIBSupportContextRegistry;
30 import org.opendaylight.protocol.bgp.rib.impl.state.rib.TotalPathsCounter;
31 import org.opendaylight.protocol.bgp.rib.impl.state.rib.TotalPrefixesCounter;
32 import org.opendaylight.protocol.bgp.rib.spi.ExportPolicyPeerTracker;
33 import org.opendaylight.protocol.bgp.rib.spi.IdentifierUtils;
34 import org.opendaylight.protocol.bgp.rib.spi.PeerExportGroup;
35 import org.opendaylight.protocol.bgp.rib.spi.RIBSupport;
36 import org.opendaylight.protocol.bgp.rib.spi.RibSupportUtils;
37 import org.opendaylight.protocol.bgp.rib.spi.RouterIds;
38 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.AsNumber;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.PeerId;
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.LocRib;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.Peer;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.peer.EffectiveRibIn;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.Tables;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.TablesKey;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.tables.Attributes;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.tables.Routes;
48 import org.opendaylight.yangtools.concepts.ListenerRegistration;
49 import org.opendaylight.yangtools.yang.common.QName;
50 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
51 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
52 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
53 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
54 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
55 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
56 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
57 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
58 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
59 import org.slf4j.Logger;
60 import org.slf4j.LoggerFactory;
61
62 @NotThreadSafe
63 final class LocRibWriter implements AutoCloseable, TotalPrefixesCounter, TotalPathsCounter,
64     ClusteredDOMDataTreeChangeListener {
65
66     private static final Logger LOG = LoggerFactory.getLogger(LocRibWriter.class);
67
68     private static final LeafNode<Boolean> ATTRIBUTES_UPTODATE_TRUE = ImmutableNodes.leafNode(QName.create(Attributes.QNAME, "uptodate"), Boolean.TRUE);
69
70     private final Map<PathArgument, RouteEntry> routeEntries = new HashMap<>();
71     private final YangInstanceIdentifier locRibTarget;
72     private final NodeIdentifierWithPredicates tableKey;
73     private DOMTransactionChain chain;
74     private final ExportPolicyPeerTracker exportPolicyPeerTracker;
75     private final NodeIdentifier attributesIdentifier;
76     private final Long ourAs;
77     private final RIBSupport ribSupport;
78     private final TablesKey localTablesKey;
79     private final YangInstanceIdentifier target;
80     private final DOMDataTreeChangeService service;
81     private ListenerRegistration<LocRibWriter> reg;
82     private final PathSelectionMode pathSelectionMode;
83     private final LongAdder totalPathsCounter = new LongAdder();
84     private final LongAdder totalPrefixesCounter = new LongAdder();
85
86     private LocRibWriter(final RIBSupportContextRegistry registry, final DOMTransactionChain chain,
87         final YangInstanceIdentifier target, final Long ourAs, final DOMDataTreeChangeService service,
88         final ExportPolicyPeerTracker exportPolicyPeerTracker, final TablesKey tablesKey,
89         final PathSelectionMode pathSelectionMode) {
90         this.chain = requireNonNull(chain);
91         this.target = requireNonNull(target);
92         this.tableKey  = RibSupportUtils.toYangTablesKey(tablesKey);
93         this.localTablesKey = tablesKey;
94         this.locRibTarget = YangInstanceIdentifier.create(target.node(LocRib.QNAME).node(Tables.QNAME)
95             .node(this.tableKey).getPathArguments());
96         this.ourAs = requireNonNull(ourAs);
97         this.service = requireNonNull(service);
98         this.ribSupport = registry.getRIBSupportContext(tablesKey).getRibSupport();
99         this.attributesIdentifier = this.ribSupport.routeAttributesIdentifier();
100         this.exportPolicyPeerTracker = exportPolicyPeerTracker;
101         this.pathSelectionMode = pathSelectionMode;
102
103         init();
104     }
105
106     private synchronized void init() {
107         final DOMDataWriteTransaction tx = this.chain.newWriteOnlyTransaction();
108         tx.merge(LogicalDatastoreType.OPERATIONAL, this.locRibTarget.node(Routes.QNAME), this.ribSupport.emptyRoutes());
109         tx.merge(LogicalDatastoreType.OPERATIONAL, this.locRibTarget.node(Attributes.QNAME)
110             .node(ATTRIBUTES_UPTODATE_TRUE.getNodeType()), ATTRIBUTES_UPTODATE_TRUE);
111         tx.submit();
112
113         final YangInstanceIdentifier tableId = this.target.node(Peer.QNAME).node(Peer.QNAME).node(EffectiveRibIn.QNAME)
114             .node(Tables.QNAME).node(this.tableKey);
115         final DOMDataTreeIdentifier wildcard = new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, tableId);
116         this.reg = this.service.registerDataTreeChangeListener(wildcard, this);
117     }
118
119     public static LocRibWriter create(@Nonnull final RIBSupportContextRegistry registry, @Nonnull final TablesKey tablesKey,
120         @Nonnull final DOMTransactionChain chain,
121         @Nonnull final YangInstanceIdentifier target, @Nonnull final AsNumber ourAs, @Nonnull final DOMDataTreeChangeService service,
122         @Nonnull final ExportPolicyPeerTracker ep, @Nonnull final PathSelectionMode pathSelectionStrategy) {
123         return new LocRibWriter(registry, chain, target, ourAs.getValue(), service, ep, tablesKey,
124             pathSelectionStrategy);
125     }
126
127     /**
128      * Re-initialize this LocRibWriter with new transaction chain.
129      *
130      * @param newChain new transaction chain
131      */
132     synchronized void restart(@Nonnull final DOMTransactionChain newChain) {
133         requireNonNull(newChain);
134         close();
135         this.chain = newChain;
136         init();
137     }
138
139     @Override
140     public void close() {
141         this.reg.close();
142         this.reg = null;
143         this.chain.close();
144     }
145
146     @Nonnull
147     private RouteEntry createEntry(final PathArgument routeId) {
148         final RouteEntry ret = this.pathSelectionMode.createRouteEntry(this.ribSupport.isComplexRoute());
149         this.routeEntries.put(routeId, ret);
150         this.totalPrefixesCounter.increment();
151         LOG.trace("Created new entry for {}", routeId);
152         return ret;
153     }
154
155     /**
156      * We use two-stage processing here in hopes that we avoid duplicate
157      * calculations when multiple peers have changed a particular entry.
158      *
159      * @param changes on supported table
160      */
161     @Override
162     public void onDataTreeChanged(final Collection<DataTreeCandidate> changes) {
163         LOG.trace("Received data change {} to LocRib {}", changes, this);
164
165         final DOMDataWriteTransaction tx = this.chain.newWriteOnlyTransaction();
166         try {
167             final Map<RouteUpdateKey, RouteEntry> toUpdate = update(tx, changes);
168
169             if (!toUpdate.isEmpty()) {
170                 walkThrough(tx, toUpdate.entrySet());
171             }
172         } catch (final Exception e) {
173             LOG.error("Failed to completely propagate updates {}, state is undefined", changes, e);
174         } finally {
175             tx.submit();
176         }
177     }
178
179     private Map<RouteUpdateKey, RouteEntry> update(final DOMDataWriteTransaction tx, final Collection<DataTreeCandidate> changes) {
180         final Map<RouteUpdateKey, RouteEntry> ret = new HashMap<>();
181         changes.forEach(tc -> {
182             final DataTreeCandidateNode table = tc.getRootNode();
183             final YangInstanceIdentifier rootPath = tc.getRootPath();
184             final PeerId peerId = IdentifierUtils.peerKeyToPeerId(rootPath);
185             initializeTableWithExistentRoutes(table, peerId, rootPath, tx);
186             updateNodes(table, peerId, tx, ret);
187         });
188         return ret;
189     }
190
191     private void initializeTableWithExistentRoutes(final DataTreeCandidateNode table, final PeerId peerIdOfNewPeer, final YangInstanceIdentifier rootPath,
192         final DOMDataWriteTransaction tx) {
193         if (!table.getDataBefore().isPresent() && this.exportPolicyPeerTracker.isTableSupported(peerIdOfNewPeer)) {
194             this.exportPolicyPeerTracker.registerPeerAsInitialized(peerIdOfNewPeer);
195             LOG.debug("Peer {} table has been created, inserting existent routes", peerIdOfNewPeer);
196             final PeerRole newPeerRole = this.exportPolicyPeerTracker.getRole(IdentifierUtils.peerPath(rootPath));
197             final PeerExportGroup peerGroup = this.exportPolicyPeerTracker.getPeerGroup(newPeerRole);
198             this.routeEntries.entrySet().forEach(entry -> entry.getValue().writeRoute(peerIdOfNewPeer, entry.getKey(),
199                 rootPath.getParent().getParent().getParent(), peerGroup, this.localTablesKey,
200                 this.exportPolicyPeerTracker, this.ribSupport, tx));
201         }
202     }
203
204     private void updateNodes(final DataTreeCandidateNode table, final PeerId peerId, final DOMDataWriteTransaction tx,
205         final Map<RouteUpdateKey, RouteEntry> routes) {
206         for (final DataTreeCandidateNode child : table.getChildNodes()) {
207             LOG.debug("Modification type {}", child.getModificationType());
208             if ((Attributes.QNAME).equals(child.getIdentifier().getNodeType())) {
209                 if (child.getDataAfter().isPresent()) {
210                     // putting uptodate attribute in
211                     LOG.trace("Uptodate found for {}", child.getDataAfter());
212                     tx.put(LogicalDatastoreType.OPERATIONAL, this.locRibTarget.node(child.getIdentifier()), child.getDataAfter().get());
213                 }
214                 continue;
215             }
216             updateRoutesEntries(child, peerId, routes);
217         }
218     }
219
220     private void updateRoutesEntries(final DataTreeCandidateNode child, final PeerId peerId, final Map<RouteUpdateKey, RouteEntry> routes) {
221         final UnsignedInteger routerId = RouterIds.routerIdForPeerId(peerId);
222         final Collection<DataTreeCandidateNode> modifiedRoutes = this.ribSupport.changedRoutes(child);
223         for (final DataTreeCandidateNode route : modifiedRoutes) {
224             final PathArgument routeId = this.ribSupport.createRouteKeyPathArgument(route.getIdentifier());
225             RouteEntry entry = this.routeEntries.get(routeId);
226             final Optional<NormalizedNode<?, ?>> maybeData = route.getDataAfter();
227             final Optional<NormalizedNode<?, ?>> maybeDataBefore = route.getDataBefore();
228             if (maybeData.isPresent()) {
229                 if (entry == null) {
230                     entry = createEntry(routeId);
231                 }
232                 entry.addRoute(routerId, this.ribSupport.extractPathId(maybeData.get()), this.attributesIdentifier, maybeData.get());
233                 this.totalPathsCounter.increment();
234             } else if (entry != null) {
235                 this.totalPathsCounter.decrement();
236                 if(entry.removeRoute(routerId, this.ribSupport.extractPathId(maybeDataBefore.get()))) {
237                     this.routeEntries.remove(routeId);
238                     this.totalPrefixesCounter.decrement();
239                     LOG.trace("Removed route from {}", routerId);
240                 }
241             }
242             final RouteUpdateKey routeUpdateKey = new RouteUpdateKey(peerId, routeId);
243             LOG.debug("Updated route {} entry {}", routeId, entry);
244             routes.put(routeUpdateKey, entry);
245         }
246     }
247
248     private void walkThrough(final DOMDataWriteTransaction tx, final Set<Map.Entry<RouteUpdateKey, RouteEntry>> toUpdate) {
249         for (final Map.Entry<RouteUpdateKey, RouteEntry> e : toUpdate) {
250             LOG.trace("Walking through {}", e);
251             final RouteEntry entry = e.getValue();
252
253             if (!entry.selectBest(this.ourAs)) {
254                 LOG.trace("Best path has not changed, continuing");
255                 continue;
256             }
257             entry.updateRoute(this.localTablesKey, this.exportPolicyPeerTracker, this.locRibTarget, this.ribSupport, tx, e.getKey().getRouteId());
258         }
259     }
260
261     @Override
262     public long getPrefixesCount() {
263         return this.totalPrefixesCounter.longValue();
264     }
265
266     @Override
267     public long getPathsCount() {
268         return this.totalPathsCounter.longValue();
269     }
270 }