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 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;
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;
62 final class LocRibWriter implements AutoCloseable, TotalPrefixesCounter, TotalPathsCounter,
63 ClusteredDOMDataTreeChangeListener {
65 private static final Logger LOG = LoggerFactory.getLogger(LocRibWriter.class);
67 private static final LeafNode<Boolean> ATTRIBUTES_UPTODATE_TRUE = ImmutableNodes.leafNode(QName.create(Attributes.QNAME, "uptodate"), Boolean.TRUE);
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();
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 final NodeIdentifierWithPredicates tableKey = RibSupportUtils.toYangTablesKey(tablesKey);
92 this.localTablesKey = tablesKey;
93 this.tableKey = tableKey;
94 this.locRibTarget = YangInstanceIdentifier.create(target.node(LocRib.QNAME).node(Tables.QNAME).node(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;
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);
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);
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);
127 * Re-initialize this LocRibWriter with new transaction chain.
129 * @param newChain new transaction chain
131 synchronized void restart(@Nonnull final DOMTransactionChain newChain) {
132 Preconditions.checkNotNull(newChain);
134 this.chain = newChain;
139 public void close() {
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);
155 * We use two-stage processing here in hopes that we avoid duplicate
156 * calculations when multiple peers have changed a particular entry.
158 * @param changes on supported table
161 public void onDataTreeChanged(final Collection<DataTreeCandidate> changes) {
162 LOG.trace("Received data change {} to LocRib {}", changes, this);
164 final DOMDataWriteTransaction tx = this.chain.newWriteOnlyTransaction();
166 final Map<RouteUpdateKey, RouteEntry> toUpdate = update(tx, changes);
168 if (!toUpdate.isEmpty()) {
169 walkThrough(tx, toUpdate.entrySet());
171 } catch (final Exception e) {
172 LOG.error("Failed to completely propagate updates {}, state is undefined", changes, e);
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);
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));
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());
213 updateRoutesEntries(child, peerId, routes);
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()) {
227 entry = createEntry(routeId);
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);
239 final RouteUpdateKey routeUpdateKey = new RouteUpdateKey(peerId, routeId);
240 LOG.debug("Updated route {} entry {}", routeId, entry);
241 routes.put(routeUpdateKey, entry);
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();
250 if (!entry.selectBest(this.ourAs)) {
251 LOG.trace("Best path has not changed, continuing");
254 entry.updateRoute(this.localTablesKey, this.exportPolicyPeerTracker, this.locRibTarget, this.ribSupport, tx, e.getKey().getRouteId());
259 public long getPrefixesCount() {
260 return this.totalPrefixesCounter.longValue();
264 public long getPathsCount() {
265 return this.totalPathsCounter.longValue();