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.YangInstanceIdentifier.PathArgument;
55 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
56 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
57 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
58 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
59 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
60 import org.slf4j.Logger;
61 import org.slf4j.LoggerFactory;
64 final class LocRibWriter implements AutoCloseable, TotalPrefixesCounter, TotalPathsCounter,
65 ClusteredDOMDataTreeChangeListener {
67 private static final Logger LOG = LoggerFactory.getLogger(LocRibWriter.class);
69 private static final LeafNode<Boolean> ATTRIBUTES_UPTODATE_TRUE = ImmutableNodes
70 .leafNode(QName.create(Attributes.QNAME, "uptodate"), Boolean.TRUE);
72 private final Map<PathArgument, RouteEntry> routeEntries = new HashMap<>();
73 private final YangInstanceIdentifier locRibTarget;
74 private final NodeIdentifierWithPredicates tableKey;
75 private final ExportPolicyPeerTracker exportPolicyPeerTracker;
76 private final NodeIdentifier attributesIdentifier;
77 private final Long ourAs;
78 private final RIBSupport ribSupport;
79 private final TablesKey localTablesKey;
80 private final YangInstanceIdentifier target;
81 private final DOMDataTreeChangeService service;
82 private final PathSelectionMode pathSelectionMode;
83 private final LongAdder totalPathsCounter = new LongAdder();
84 private final LongAdder totalPrefixesCounter = new LongAdder();
85 private DOMTransactionChain chain;
87 private ListenerRegistration<LocRibWriter> reg;
89 private LocRibWriter(final RIBSupportContextRegistry registry, final DOMTransactionChain chain,
90 final YangInstanceIdentifier target, final Long ourAs, final DOMDataTreeChangeService service,
91 final ExportPolicyPeerTracker exportPolicyPeerTracker, final TablesKey tablesKey,
92 final PathSelectionMode pathSelectionMode) {
93 this.chain = requireNonNull(chain);
94 this.target = requireNonNull(target);
95 this.tableKey = RibSupportUtils.toYangTablesKey(tablesKey);
96 this.localTablesKey = tablesKey;
97 this.locRibTarget = YangInstanceIdentifier.create(target.node(LocRib.QNAME).node(Tables.QNAME)
98 .node(this.tableKey).getPathArguments());
99 this.ourAs = requireNonNull(ourAs);
100 this.service = requireNonNull(service);
101 this.ribSupport = registry.getRIBSupportContext(tablesKey).getRibSupport();
102 this.attributesIdentifier = this.ribSupport.routeAttributesIdentifier();
103 this.exportPolicyPeerTracker = exportPolicyPeerTracker;
104 this.pathSelectionMode = pathSelectionMode;
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 PathArgument 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,
203 final DOMDataWriteTransaction tx) {
204 if (!table.getDataBefore().isPresent() && this.exportPolicyPeerTracker.isTableSupported(peerIdOfNewPeer)) {
205 this.exportPolicyPeerTracker.registerPeerAsInitialized(peerIdOfNewPeer);
206 LOG.debug("Peer {} table has been created, inserting existent routes", peerIdOfNewPeer);
207 final PeerRole newPeerRole = this.exportPolicyPeerTracker.getRole(IdentifierUtils.peerPath(rootPath));
208 final PeerExportGroup peerGroup = this.exportPolicyPeerTracker.getPeerGroup(newPeerRole);
209 this.routeEntries.forEach((key, value) -> value.writeRoute(peerIdOfNewPeer, key,
210 rootPath.getParent().getParent().getParent(), peerGroup, this.localTablesKey,
211 this.exportPolicyPeerTracker, this.ribSupport, tx));
215 private void updateNodes(final DataTreeCandidateNode table, final PeerId peerId, final DOMDataWriteTransaction tx,
216 final Map<RouteUpdateKey, RouteEntry> routes) {
217 for (final DataTreeCandidateNode child : table.getChildNodes()) {
218 LOG.debug("Modification type {}", child.getModificationType());
219 if (Attributes.QNAME.equals(child.getIdentifier().getNodeType())) {
220 if (child.getDataAfter().isPresent()) {
221 // putting uptodate attribute in
222 LOG.trace("Uptodate found for {}", child.getDataAfter());
223 tx.put(LogicalDatastoreType.OPERATIONAL, this.locRibTarget.node(child.getIdentifier()),
224 child.getDataAfter().get());
228 updateRoutesEntries(child, peerId, routes);
232 private void updateRoutesEntries(final DataTreeCandidateNode child, final PeerId peerId,
233 final Map<RouteUpdateKey, RouteEntry> routes) {
234 final UnsignedInteger routerId = RouterIds.routerIdForPeerId(peerId);
235 final Collection<DataTreeCandidateNode> modifiedRoutes = this.ribSupport.changedRoutes(child);
236 for (final DataTreeCandidateNode route : modifiedRoutes) {
237 final NodeIdentifierWithPredicates routeId = (NodeIdentifierWithPredicates)
238 this.ribSupport.createRouteKeyPathArgument(route.getIdentifier());
239 RouteEntry entry = this.routeEntries.get(routeId);
240 final Optional<NormalizedNode<?, ?>> maybeData = route.getDataAfter();
241 final Optional<NormalizedNode<?, ?>> maybeDataBefore = route.getDataBefore();
242 if (maybeData.isPresent()) {
244 entry = createEntry(routeId);
246 entry.addRoute(routerId, this.ribSupport.extractPathId(maybeData.get()),
247 this.attributesIdentifier, maybeData.get());
248 this.totalPathsCounter.increment();
249 } else if (entry != null) {
250 this.totalPathsCounter.decrement();
251 if(entry.removeRoute(routerId, this.ribSupport.extractPathId(maybeDataBefore.get()))) {
252 this.routeEntries.remove(routeId);
253 this.totalPrefixesCounter.decrement();
254 LOG.trace("Removed route from {}", routerId);
257 final RouteUpdateKey routeUpdateKey = new RouteUpdateKey(peerId, routeId);
258 LOG.debug("Updated route {} entry {}", routeId, entry);
259 routes.put(routeUpdateKey, entry);
263 private void walkThrough(final DOMDataWriteTransaction tx,
264 final Set<Map.Entry<RouteUpdateKey, RouteEntry>> toUpdate) {
265 for (final Map.Entry<RouteUpdateKey, RouteEntry> e : toUpdate) {
266 LOG.trace("Walking through {}", e);
267 final RouteEntry entry = e.getValue();
269 if (!entry.selectBest(this.ourAs)) {
270 LOG.trace("Best path has not changed, continuing");
273 entry.updateRoute(this.localTablesKey, this.exportPolicyPeerTracker, this.locRibTarget,
274 this.ribSupport, tx, e.getKey().getRouteId());
279 public long getPrefixesCount() {
280 return this.totalPrefixesCounter.longValue();
284 public long getPathsCount() {
285 return this.totalPathsCounter.longValue();