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