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;
17 import java.util.concurrent.atomic.LongAdder;
18 import javax.annotation.Nonnull;
19 import javax.annotation.concurrent.GuardedBy;
20 import javax.annotation.concurrent.NotThreadSafe;
21 import org.opendaylight.controller.md.sal.binding.api.BindingTransactionChain;
22 import org.opendaylight.controller.md.sal.binding.api.ClusteredDataTreeChangeListener;
23 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
24 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
25 import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
26 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
27 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
28 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
29 import org.opendaylight.protocol.bgp.mode.api.PathSelectionMode;
30 import org.opendaylight.protocol.bgp.mode.api.RouteEntry;
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.BGPPeerTracker;
34 import org.opendaylight.protocol.bgp.rib.spi.RIBSupport;
35 import org.opendaylight.protocol.bgp.rib.spi.RouterIds;
36 import org.opendaylight.protocol.bgp.rib.spi.policy.BGPRibRoutingPolicy;
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.rev180329.Route;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.Rib;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.RibKey;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.rib.LocRib;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.rib.Peer;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.rib.PeerKey;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.rib.peer.EffectiveRibIn;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.Tables;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.TablesKey;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.tables.Attributes;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.tables.AttributesBuilder;
49 import org.opendaylight.yangtools.concepts.ListenerRegistration;
50 import org.opendaylight.yangtools.yang.binding.DataObject;
51 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
52 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
53 import org.slf4j.Logger;
54 import org.slf4j.LoggerFactory;
57 final class LocRibWriter implements AutoCloseable, TotalPrefixesCounter, TotalPathsCounter,
58 ClusteredDataTreeChangeListener<Tables> {
60 private static final Logger LOG = LoggerFactory.getLogger(LocRibWriter.class);
62 private final Map<String, RouteEntry> routeEntries = new HashMap<>();
63 private final Long ourAs;
64 private final RIBSupport ribSupport;
65 private final DataBroker dataBroker;
66 private final PathSelectionMode pathSelectionMode;
67 private final LongAdder totalPathsCounter = new LongAdder();
68 private final LongAdder totalPrefixesCounter = new LongAdder();
69 private final RouteEntryDependenciesContainerImpl entryDep;
70 private final BGPPeerTracker peerTracker;
71 private final KeyedInstanceIdentifier<Rib, RibKey> ribIId;
72 private final TablesKey tk;
73 private final KeyedInstanceIdentifier<Tables, TablesKey> locRibTableIID;
74 private BindingTransactionChain chain;
76 private ListenerRegistration<LocRibWriter> reg;
78 private LocRibWriter(final RIBSupport ribSupport,
79 final BindingTransactionChain chain,
80 final KeyedInstanceIdentifier<Rib, RibKey> ribIId,
82 final DataBroker dataBroker,
83 final BGPRibRoutingPolicy ribPolicies,
84 final BGPPeerTracker peerTracker,
85 final TablesKey tablesKey,
86 final PathSelectionMode pathSelectionMode) {
87 this.chain = requireNonNull(chain);
88 this.ribIId = requireNonNull(ribIId);
89 this.tk = requireNonNull(tablesKey);
90 this.locRibTableIID = ribIId.child(LocRib.class).child(Tables.class, this.tk);
91 this.ourAs = requireNonNull(ourAs);
92 this.dataBroker = requireNonNull(dataBroker);
93 this.ribSupport = requireNonNull(ribSupport);
94 this.peerTracker = peerTracker;
95 this.pathSelectionMode = pathSelectionMode;
97 this.entryDep = new RouteEntryDependenciesContainerImpl(this.ribSupport, ribPolicies,
98 tablesKey, this.locRibTableIID);
102 public static LocRibWriter create(@Nonnull final RIBSupport ribSupport,
103 @Nonnull final TablesKey tablesKey,
104 @Nonnull final BindingTransactionChain chain,
105 @Nonnull final KeyedInstanceIdentifier<Rib, RibKey> ribIId,
106 @Nonnull final AsNumber ourAs,
107 @Nonnull final DataBroker dataBroker,
108 final BGPRibRoutingPolicy ribPolicies,
109 @Nonnull final BGPPeerTracker peerTracker,
110 @Nonnull final PathSelectionMode pathSelectionStrategy) {
111 return new LocRibWriter(ribSupport, chain, ribIId, ourAs.getValue(), dataBroker, ribPolicies,
112 peerTracker, tablesKey, pathSelectionStrategy);
115 @SuppressWarnings("unchecked")
116 private synchronized void init() {
117 final WriteTransaction tx = this.chain.newWriteOnlyTransaction();
118 tx.merge(LogicalDatastoreType.OPERATIONAL,
119 this.locRibTableIID.builder().child(Attributes.class).build(),
120 new AttributesBuilder().setUptodate(true).build());
123 final InstanceIdentifier<Tables> tableId = this.ribIId.builder().child(Peer.class)
124 .child(EffectiveRibIn.class).child(Tables.class, this.tk).build();
125 this.reg = this.dataBroker.registerDataTreeChangeListener(
126 new DataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, tableId), this);
130 * Re-initialize this LocRibWriter with new transaction chain.
132 * @param newChain new transaction chain
134 synchronized void restart(@Nonnull final BindingTransactionChain newChain) {
135 requireNonNull(newChain);
137 this.chain = newChain;
142 public synchronized void close() {
143 if (this.reg != null) {
151 private RouteEntry createEntry(final String routeId) {
152 final RouteEntry ret = this.pathSelectionMode.createRouteEntry();
153 this.routeEntries.put(routeId, ret);
154 this.totalPrefixesCounter.increment();
155 LOG.trace("Created new entry for {}", routeId);
160 * We use two-stage processing here in hopes that we avoid duplicate
161 * calculations when multiple peers have changed a particular entry.
163 * @param changes on supported table
166 public void onDataTreeChanged(final Collection<DataTreeModification<Tables>> changes) {
167 LOG.trace("Received data change {} to LocRib {}", changes, this);
169 final WriteTransaction tx = this.chain.newWriteOnlyTransaction();
171 final Map<RouteUpdateKey, RouteEntry> toUpdate = update(tx, changes);
173 if (!toUpdate.isEmpty()) {
174 walkThrough(tx, toUpdate.entrySet());
176 } catch (final Exception e) {
177 LOG.error("Failed to completely propagate updates {}, state is undefined", changes, e);
183 @SuppressWarnings("unchecked")
184 private Map<RouteUpdateKey, RouteEntry> update(final WriteTransaction tx,
185 final Collection<DataTreeModification<Tables>> changes) {
186 final Map<RouteUpdateKey, RouteEntry> ret = new HashMap<>();
187 for (final DataTreeModification<Tables> tc : changes) {
188 final DataObjectModification<Tables> table = tc.getRootNode();
189 final DataTreeIdentifier<Tables> rootPath = tc.getRootPath();
190 final KeyedInstanceIdentifier<Peer, PeerKey> peerKIid = (KeyedInstanceIdentifier<Peer, PeerKey>)
191 rootPath.getRootIdentifier().firstIdentifierOf(Peer.class);
192 final UnsignedInteger peerUuid = RouterIds.routerIdForPeerId(peerKIid.getKey().getPeerId());
194 Initialize Peer with routes under loc rib
196 if (!this.routeEntries.isEmpty() && table.getDataBefore() == null) {
197 final org.opendaylight.protocol.bgp.rib.spi.Peer peer
198 = this.peerTracker.getPeer(peerKIid.getKey().getPeerId());
199 if (peer != null && peer.supportsTable(this.entryDep.getLocalTablesKey())) {
200 LOG.debug("Peer {} table has been created, inserting existent routes", peer.getPeerId());
201 this.routeEntries.forEach((key, value) -> value.initializeBestPaths(this.entryDep,
202 new RouteEntryInfoImpl(peer, key), tx));
206 Process new routes from Peer
208 updateNodes(table, peerUuid, tx, ret);
213 @SuppressWarnings("unchecked")
214 private void updateNodes(
215 final DataObjectModification<Tables> table,
216 final UnsignedInteger peerUuid,
217 final WriteTransaction tx,
218 final Map<RouteUpdateKey, RouteEntry> routes
221 final DataObjectModification<Attributes> attUpdate = table.getModifiedChildContainer(Attributes.class);
223 if (attUpdate != null && attUpdate.getDataAfter() != null) {
224 final Attributes newAttValue = attUpdate.getDataAfter();
225 LOG.trace("Uptodate found for {}", newAttValue);
226 tx.put(LogicalDatastoreType.OPERATIONAL, this.locRibTableIID.child(Attributes.class), newAttValue);
229 final DataObjectModification routesChangesContainer =
230 table.getModifiedChildContainer(this.ribSupport.routesContainerClass());
231 if (routesChangesContainer == null) {
234 updateRoutesEntries(routesChangesContainer.getModifiedChildren(), peerUuid, routes);
237 @SuppressWarnings("unchecked")
238 private void updateRoutesEntries(
239 final Collection<DataObjectModification<? extends DataObject>> routeChanges,
240 final UnsignedInteger routerId,
241 final Map<RouteUpdateKey, RouteEntry> routes
243 for (final DataObjectModification<? extends DataObject> route : routeChanges) {
244 final Route newRoute = (Route) route.getDataAfter();
245 final Route oldRoute = (Route) route.getDataBefore();
248 if (newRoute != null) {
249 routeKey = newRoute.getRouteKey();
250 entry = this.routeEntries.get(routeKey);
253 entry = createEntry(routeKey);
256 final long pathId = newRoute.getPathId().getValue();
257 entry.addRoute(routerId, pathId, newRoute);
258 this.totalPathsCounter.increment();
260 routeKey = oldRoute.getRouteKey();
261 entry = this.routeEntries.get(routeKey);
263 this.totalPathsCounter.decrement();
264 final long pathId = oldRoute.getPathId().getValue();
265 if (entry.removeRoute(routerId, pathId)) {
266 this.routeEntries.remove(routeKey);
267 this.totalPrefixesCounter.decrement();
268 LOG.trace("Removed route from {}", routerId);
272 final RouteUpdateKey routeUpdateKey = new RouteUpdateKey(routerId, routeKey);
273 LOG.debug("Updated route {} entry {}", routeKey, entry);
274 routes.put(routeUpdateKey, entry);
278 private void walkThrough(final WriteTransaction tx,
279 final Set<Map.Entry<RouteUpdateKey, RouteEntry>> toUpdate) {
280 for (final Map.Entry<RouteUpdateKey, RouteEntry> e : toUpdate) {
281 LOG.trace("Walking through {}", e);
282 final RouteEntry entry = e.getValue();
284 if (!entry.selectBest(this.ourAs)) {
285 LOG.trace("Best path has not changed, continuing");
288 entry.updateBestPaths(entryDep, e.getKey().getRouteId(), tx);
293 public long getPrefixesCount() {
294 return this.totalPrefixesCounter.longValue();
298 public long getPathsCount() {
299 return this.totalPathsCounter.longValue();
302 public TablesKey getTableKey() {