2 * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.protocol.bgp.rib.impl;
10 import static java.util.Objects.requireNonNull;
12 import com.google.common.primitives.UnsignedInteger;
13 import java.util.Collection;
14 import java.util.HashMap;
16 import java.util.Optional;
18 import java.util.concurrent.atomic.LongAdder;
19 import javax.annotation.Nonnull;
20 import javax.annotation.concurrent.GuardedBy;
21 import javax.annotation.concurrent.NotThreadSafe;
22 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
23 import org.opendaylight.controller.md.sal.dom.api.ClusteredDOMDataTreeChangeListener;
24 import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeService;
25 import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeIdentifier;
26 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
27 import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
28 import org.opendaylight.protocol.bgp.mode.api.PathSelectionMode;
29 import org.opendaylight.protocol.bgp.mode.api.RouteEntry;
30 import org.opendaylight.protocol.bgp.rib.impl.spi.RIBSupportContextRegistry;
31 import org.opendaylight.protocol.bgp.rib.impl.state.rib.TotalPathsCounter;
32 import org.opendaylight.protocol.bgp.rib.impl.state.rib.TotalPrefixesCounter;
33 import org.opendaylight.protocol.bgp.rib.spi.ExportPolicyPeerTracker;
34 import org.opendaylight.protocol.bgp.rib.spi.IdentifierUtils;
35 import org.opendaylight.protocol.bgp.rib.spi.PeerExportGroup;
36 import org.opendaylight.protocol.bgp.rib.spi.RIBSupport;
37 import org.opendaylight.protocol.bgp.rib.spi.RibSupportUtils;
38 import org.opendaylight.protocol.bgp.rib.spi.RouterIds;
39 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.AsNumber;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.PeerId;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.PeerRole;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.bgp.rib.rib.LocRib;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.bgp.rib.rib.Peer;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.bgp.rib.rib.peer.EffectiveRibIn;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.rib.Tables;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.rib.TablesKey;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.rib.tables.Attributes;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.rib.tables.Routes;
49 import org.opendaylight.yangtools.concepts.ListenerRegistration;
50 import org.opendaylight.yangtools.yang.common.QName;
51 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
52 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
53 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
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;
63 final class LocRibWriter implements AutoCloseable, TotalPrefixesCounter, TotalPathsCounter,
64 ClusteredDOMDataTreeChangeListener {
66 private static final Logger LOG = LoggerFactory.getLogger(LocRibWriter.class);
68 private static final LeafNode<Boolean> ATTRIBUTES_UPTODATE_TRUE = ImmutableNodes
69 .leafNode(QName.create(Attributes.QNAME, "uptodate"), Boolean.TRUE);
71 private final Map<NodeIdentifierWithPredicates, RouteEntry> routeEntries = new HashMap<>();
72 private final YangInstanceIdentifier locRibTarget;
73 private final NodeIdentifierWithPredicates tableKey;
74 private final ExportPolicyPeerTracker exportPolicyPeerTracker;
75 private final NodeIdentifier attributesIdentifier;
76 private final Long ourAs;
77 private final RIBSupport ribSupport;
78 private final YangInstanceIdentifier target;
79 private final DOMDataTreeChangeService service;
80 private final PathSelectionMode pathSelectionMode;
81 private final LongAdder totalPathsCounter = new LongAdder();
82 private final LongAdder totalPrefixesCounter = new LongAdder();
83 private final RouteEntryDependenciesContainerImpl entryDep;
84 private DOMTransactionChain chain;
86 private ListenerRegistration<LocRibWriter> reg;
88 private LocRibWriter(final RIBSupportContextRegistry registry, final DOMTransactionChain chain,
89 final YangInstanceIdentifier target, final Long ourAs, final DOMDataTreeChangeService service,
90 final ExportPolicyPeerTracker exportPolicyPeerTracker, final TablesKey tablesKey,
91 final PathSelectionMode pathSelectionMode) {
92 this.chain = requireNonNull(chain);
93 this.target = requireNonNull(target);
94 this.tableKey = RibSupportUtils.toYangTablesKey(requireNonNull(tablesKey));
95 this.locRibTarget = YangInstanceIdentifier.create(target.node(LocRib.QNAME).node(Tables.QNAME)
96 .node(this.tableKey).getPathArguments());
97 this.ourAs = requireNonNull(ourAs);
98 this.service = requireNonNull(service);
99 this.ribSupport = registry.getRIBSupportContext(tablesKey).getRibSupport();
100 this.attributesIdentifier = this.ribSupport.routeAttributesIdentifier();
101 this.exportPolicyPeerTracker = exportPolicyPeerTracker;
102 this.pathSelectionMode = pathSelectionMode;
104 this.entryDep = new RouteEntryDependenciesContainerImpl(this.ribSupport,
105 tablesKey, this.locRibTarget, this.exportPolicyPeerTracker);
109 private synchronized void init() {
110 final DOMDataWriteTransaction tx = this.chain.newWriteOnlyTransaction();
111 tx.merge(LogicalDatastoreType.OPERATIONAL, this.locRibTarget.node(Routes.QNAME), this.ribSupport.emptyRoutes());
112 tx.merge(LogicalDatastoreType.OPERATIONAL, this.locRibTarget.node(Attributes.QNAME)
113 .node(ATTRIBUTES_UPTODATE_TRUE.getNodeType()), ATTRIBUTES_UPTODATE_TRUE);
116 final YangInstanceIdentifier tableId = this.target.node(Peer.QNAME).node(Peer.QNAME).node(EffectiveRibIn.QNAME)
117 .node(Tables.QNAME).node(this.tableKey);
118 final DOMDataTreeIdentifier wildcard = new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, tableId);
119 this.reg = this.service.registerDataTreeChangeListener(wildcard, this);
122 public static LocRibWriter create(@Nonnull final RIBSupportContextRegistry registry,
123 @Nonnull final TablesKey tablesKey,
124 @Nonnull final DOMTransactionChain chain,
125 @Nonnull final YangInstanceIdentifier target,
126 @Nonnull final AsNumber ourAs,
127 @Nonnull final DOMDataTreeChangeService service,
128 @Nonnull final ExportPolicyPeerTracker ep,
129 @Nonnull final PathSelectionMode pathSelectionStrategy) {
130 return new LocRibWriter(registry, chain, target, ourAs.getValue(), service, ep, tablesKey,
131 pathSelectionStrategy);
135 * Re-initialize this LocRibWriter with new transaction chain.
137 * @param newChain new transaction chain
139 synchronized void restart(@Nonnull final DOMTransactionChain newChain) {
140 requireNonNull(newChain);
142 this.chain = newChain;
147 public synchronized void close() {
148 if (this.reg != null) {
156 private RouteEntry createEntry(final NodeIdentifierWithPredicates routeId) {
157 final RouteEntry ret = this.pathSelectionMode.createRouteEntry(this.ribSupport.isComplexRoute());
158 this.routeEntries.put(routeId, ret);
159 this.totalPrefixesCounter.increment();
160 LOG.trace("Created new entry for {}", routeId);
165 * We use two-stage processing here in hopes that we avoid duplicate
166 * calculations when multiple peers have changed a particular entry.
168 * @param changes on supported table
171 public void onDataTreeChanged(final Collection<DataTreeCandidate> changes) {
172 LOG.trace("Received data change {} to LocRib {}", changes, this);
174 final DOMDataWriteTransaction tx = this.chain.newWriteOnlyTransaction();
176 final Map<RouteUpdateKey, RouteEntry> toUpdate = update(tx, changes);
178 if (!toUpdate.isEmpty()) {
179 walkThrough(tx, toUpdate.entrySet());
181 } catch (final Exception e) {
182 LOG.error("Failed to completely propagate updates {}, state is undefined", changes, e);
188 private Map<RouteUpdateKey, RouteEntry> update(final DOMDataWriteTransaction tx,
189 final Collection<DataTreeCandidate> changes) {
190 final Map<RouteUpdateKey, RouteEntry> ret = new HashMap<>();
191 changes.forEach(tc -> {
192 final DataTreeCandidateNode table = tc.getRootNode();
193 final YangInstanceIdentifier rootPath = tc.getRootPath();
194 final PeerId peerId = IdentifierUtils.peerKeyToPeerId(rootPath);
195 initializeTableWithExistentRoutes(table, peerId, rootPath, tx);
196 updateNodes(table, peerId, tx, ret);
201 private void initializeTableWithExistentRoutes(final DataTreeCandidateNode table, final PeerId peerIdOfNewPeer,
202 final YangInstanceIdentifier rootPath, final DOMDataWriteTransaction tx) {
203 if (!table.getDataBefore().isPresent() && this.exportPolicyPeerTracker.isTableSupported(peerIdOfNewPeer)) {
204 this.exportPolicyPeerTracker.registerPeerAsInitialized(peerIdOfNewPeer);
205 LOG.debug("Peer {} table has been created, inserting existent routes", peerIdOfNewPeer);
206 if (this.routeEntries.isEmpty()) {
209 final PeerRole newPeerRole = this.exportPolicyPeerTracker.getRole(IdentifierUtils.peerPath(rootPath));
210 final PeerExportGroup peerGroup = this.exportPolicyPeerTracker.getPeerGroup(newPeerRole);
211 this.routeEntries.forEach((key, value) -> value.initializeBestPaths(this.entryDep,
212 new RouteEntryInfoImpl(peerIdOfNewPeer, key, rootPath.getParent().getParent().getParent()),
217 private void updateNodes(final DataTreeCandidateNode table, final PeerId peerId, final DOMDataWriteTransaction tx,
218 final Map<RouteUpdateKey, RouteEntry> routes) {
219 for (final DataTreeCandidateNode child : table.getChildNodes()) {
220 LOG.debug("Modification type {}", child.getModificationType());
221 if (Attributes.QNAME.equals(child.getIdentifier().getNodeType())) {
222 if (child.getDataAfter().isPresent()) {
223 // putting uptodate attribute in
224 LOG.trace("Uptodate found for {}", child.getDataAfter());
225 tx.put(LogicalDatastoreType.OPERATIONAL, this.locRibTarget.node(child.getIdentifier()),
226 child.getDataAfter().get());
230 updateRoutesEntries(child, peerId, routes);
234 private void updateRoutesEntries(final DataTreeCandidateNode child, final PeerId peerId,
235 final Map<RouteUpdateKey, RouteEntry> routes) {
236 final UnsignedInteger routerId = RouterIds.routerIdForPeerId(peerId);
237 final Collection<DataTreeCandidateNode> modifiedRoutes = this.ribSupport.changedRoutes(child);
238 for (final DataTreeCandidateNode route : modifiedRoutes) {
239 final NodeIdentifierWithPredicates routeId = this.ribSupport
240 .createRouteKeyPathArgument((NodeIdentifierWithPredicates) route.getIdentifier());
241 RouteEntry entry = this.routeEntries.get(routeId);
242 final Optional<NormalizedNode<?, ?>> maybeData = route.getDataAfter();
243 final Optional<NormalizedNode<?, ?>> maybeDataBefore = route.getDataBefore();
244 if (maybeData.isPresent()) {
246 entry = createEntry(routeId);
248 entry.addRoute(routerId, this.ribSupport.extractPathId(maybeData.get()),
249 this.attributesIdentifier, maybeData.get());
250 this.totalPathsCounter.increment();
251 } else if (entry != null) {
252 this.totalPathsCounter.decrement();
253 if (entry.removeRoute(routerId, this.ribSupport.extractPathId(maybeDataBefore.get()))) {
254 this.routeEntries.remove(routeId);
255 this.totalPrefixesCounter.decrement();
256 LOG.trace("Removed route from {}", routerId);
259 final RouteUpdateKey routeUpdateKey = new RouteUpdateKey(peerId, routeId);
260 LOG.debug("Updated route {} entry {}", routeId, entry);
261 routes.put(routeUpdateKey, entry);
265 private void walkThrough(final DOMDataWriteTransaction tx,
266 final Set<Map.Entry<RouteUpdateKey, RouteEntry>> toUpdate) {
267 for (final Map.Entry<RouteUpdateKey, RouteEntry> e : toUpdate) {
268 LOG.trace("Walking through {}", e);
269 final RouteEntry entry = e.getValue();
271 if (!entry.selectBest(this.ourAs)) {
272 LOG.trace("Best path has not changed, continuing");
275 entry.updateBestPaths(entryDep, e.getKey().getRouteId(), tx);
280 public long getPrefixesCount() {
281 return this.totalPrefixesCounter.longValue();
285 public long getPathsCount() {
286 return this.totalPathsCounter.longValue();