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.util.concurrent.FutureCallback;
13 import com.google.common.util.concurrent.MoreExecutors;
14 import java.util.ArrayList;
15 import java.util.Collection;
16 import java.util.HashMap;
17 import java.util.List;
19 import java.util.Map.Entry;
21 import java.util.concurrent.atomic.LongAdder;
22 import javax.annotation.Nonnull;
23 import javax.annotation.concurrent.GuardedBy;
24 import javax.annotation.concurrent.NotThreadSafe;
25 import org.opendaylight.controller.md.sal.binding.api.BindingTransactionChain;
26 import org.opendaylight.controller.md.sal.binding.api.ClusteredDataTreeChangeListener;
27 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
28 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
29 import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
30 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
31 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
32 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
33 import org.opendaylight.mdsal.common.api.CommitInfo;
34 import org.opendaylight.protocol.bgp.mode.api.PathSelectionMode;
35 import org.opendaylight.protocol.bgp.mode.api.RouteEntry;
36 import org.opendaylight.protocol.bgp.rib.impl.spi.RibOutRefresh;
37 import org.opendaylight.protocol.bgp.rib.impl.state.rib.TotalPathsCounter;
38 import org.opendaylight.protocol.bgp.rib.impl.state.rib.TotalPrefixesCounter;
39 import org.opendaylight.protocol.bgp.rib.spi.BGPPeerTracker;
40 import org.opendaylight.protocol.bgp.rib.spi.RIBSupport;
41 import org.opendaylight.protocol.bgp.rib.spi.RouterId;
42 import org.opendaylight.protocol.bgp.rib.spi.entry.ActualBestPathRoutes;
43 import org.opendaylight.protocol.bgp.rib.spi.entry.AdvertizedRoute;
44 import org.opendaylight.protocol.bgp.rib.spi.entry.StaleBestPathRoute;
45 import org.opendaylight.protocol.bgp.rib.spi.policy.BGPRibRoutingPolicy;
46 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.types.rev151009.AfiSafiType;
47 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.AsNumber;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.PathId;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.PeerId;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.Route;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.Rib;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.RibKey;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.rib.LocRib;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.rib.Peer;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.rib.PeerKey;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.rib.peer.EffectiveRibIn;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.Tables;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.TablesKey;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.tables.Attributes;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.tables.AttributesBuilder;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.tables.Routes;
62 import org.opendaylight.yangtools.concepts.ListenerRegistration;
63 import org.opendaylight.yangtools.yang.binding.ChildOf;
64 import org.opendaylight.yangtools.yang.binding.ChoiceIn;
65 import org.opendaylight.yangtools.yang.binding.DataObject;
66 import org.opendaylight.yangtools.yang.binding.Identifiable;
67 import org.opendaylight.yangtools.yang.binding.Identifier;
68 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
69 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
70 import org.slf4j.Logger;
71 import org.slf4j.LoggerFactory;
74 final class LocRibWriter<C extends Routes & DataObject & ChoiceIn<Tables>, S extends ChildOf<? super C>,
75 R extends Route & ChildOf<? super S> & Identifiable<I>, I extends Identifier<R>>
76 implements AutoCloseable, RibOutRefresh, TotalPrefixesCounter, TotalPathsCounter,
77 ClusteredDataTreeChangeListener<Tables> {
79 private static final Logger LOG = LoggerFactory.getLogger(LocRibWriter.class);
81 private final Map<String, RouteEntry<C, S, R, I>> routeEntries = new HashMap<>();
82 private final long ourAs;
83 private final RIBSupport<C, S, R, I> ribSupport;
84 private final DataBroker dataBroker;
85 private final PathSelectionMode pathSelectionMode;
86 private final LongAdder totalPathsCounter = new LongAdder();
87 private final LongAdder totalPrefixesCounter = new LongAdder();
88 private final RouteEntryDependenciesContainerImpl entryDep;
89 private final BGPPeerTracker peerTracker;
90 private final KeyedInstanceIdentifier<Rib, RibKey> ribIId;
91 private final KeyedInstanceIdentifier<Tables, TablesKey> locRibTableIID;
93 private BindingTransactionChain chain;
95 private ListenerRegistration<?> reg;
97 private LocRibWriter(final RIBSupport<C, S, R, I> ribSupport,
98 final BindingTransactionChain chain,
99 final KeyedInstanceIdentifier<Rib, RibKey> ribIId,
101 final DataBroker dataBroker,
102 final BGPRibRoutingPolicy ribPolicies,
103 final BGPPeerTracker peerTracker,
104 final Class<? extends AfiSafiType> afiSafiType,
105 final PathSelectionMode pathSelectionMode) {
106 this.chain = requireNonNull(chain);
107 this.ribIId = requireNonNull(ribIId);
108 this.ribSupport = requireNonNull(ribSupport);
109 this.locRibTableIID = ribIId.child(LocRib.class).child(Tables.class, ribSupport.getTablesKey());
111 this.dataBroker = requireNonNull(dataBroker);
112 this.peerTracker = peerTracker;
113 this.pathSelectionMode = pathSelectionMode;
115 this.entryDep = new RouteEntryDependenciesContainerImpl(this.ribSupport, this.peerTracker, ribPolicies,
116 afiSafiType, this.locRibTableIID);
120 public static <C extends Routes & DataObject & ChoiceIn<Tables>, S extends ChildOf<? super C>,
121 R extends Route & ChildOf<? super S> & Identifiable<I>, I extends Identifier<R>>
122 LocRibWriter<C, S, R, I> create(
123 @Nonnull final RIBSupport<C, S, R, I> ribSupport,
124 @Nonnull final Class<? extends AfiSafiType> afiSafiType,
125 @Nonnull final BindingTransactionChain chain,
126 @Nonnull final KeyedInstanceIdentifier<Rib, RibKey> ribIId,
127 @Nonnull final AsNumber ourAs,
128 @Nonnull final DataBroker dataBroker,
129 final BGPRibRoutingPolicy ribPolicies,
130 @Nonnull final BGPPeerTracker peerTracker,
131 @Nonnull final PathSelectionMode pathSelectionStrategy) {
132 return new LocRibWriter<>(ribSupport, chain, ribIId, ourAs.getValue(), dataBroker, ribPolicies,
133 peerTracker, afiSafiType, pathSelectionStrategy);
136 private synchronized void init() {
137 final WriteTransaction tx = this.chain.newWriteOnlyTransaction();
138 tx.merge(LogicalDatastoreType.OPERATIONAL,
139 this.locRibTableIID.builder().child(Attributes.class).build(),
140 new AttributesBuilder().setUptodate(true).build());
141 tx.commit().addCallback(new FutureCallback<CommitInfo>() {
143 public void onSuccess(final CommitInfo result) {
144 LOG.trace("Successful commit");
148 public void onFailure(final Throwable trw) {
149 LOG.error("Failed commit", trw);
151 }, MoreExecutors.directExecutor());
153 final InstanceIdentifier<Tables> tableId = this.ribIId.builder().child(Peer.class)
154 .child(EffectiveRibIn.class).child(Tables.class, getTableKey()).build();
155 this.reg = this.dataBroker.registerDataTreeChangeListener(
156 new DataTreeIdentifier<>(LogicalDatastoreType.OPERATIONAL, tableId), this);
160 * Re-initialize this LocRibWriter with new transaction chain.
162 * @param newChain new transaction chain
164 synchronized void restart(@Nonnull final BindingTransactionChain newChain) {
165 requireNonNull(newChain);
167 this.chain = newChain;
172 public synchronized void close() {
173 if (this.reg != null) {
177 if (this.chain != null) {
184 private RouteEntry<C, S, R, I> createEntry(final String routeId) {
185 final RouteEntry<C, S, R, I> ret = this.pathSelectionMode.createRouteEntry();
186 this.routeEntries.put(routeId, ret);
187 this.totalPrefixesCounter.increment();
188 LOG.trace("Created new entry for {}", routeId);
193 * We use two-stage processing here in hopes that we avoid duplicate
194 * calculations when multiple peers have changed a particular entry.
196 * @param changes on supported table
199 @SuppressWarnings("checkstyle:illegalCatch")
200 public synchronized void onDataTreeChanged(final Collection<DataTreeModification<Tables>> changes) {
201 if (this.chain == null) {
202 LOG.trace("Chain closed, ignoring received data change {} to LocRib {}", changes, this);
205 LOG.trace("Received data change {} to LocRib {}", changes, this);
206 final WriteTransaction tx = this.chain.newWriteOnlyTransaction();
208 final Map<RouteUpdateKey, RouteEntry<C, S, R, I>> toUpdate = update(tx, changes);
210 if (!toUpdate.isEmpty()) {
211 walkThrough(tx, toUpdate.entrySet());
213 } catch (final Exception e) {
214 LOG.error("Failed to completely propagate updates {}, state is undefined", changes, e);
216 tx.commit().addCallback(new FutureCallback<CommitInfo>() {
218 public void onSuccess(final CommitInfo result) {
219 LOG.trace("Successful commit");
223 public void onFailure(final Throwable trw) {
224 LOG.error("Failed commit", trw);
226 }, MoreExecutors.directExecutor());
230 @SuppressWarnings("unchecked")
231 private Map<RouteUpdateKey, RouteEntry<C, S, R, I>> update(final WriteTransaction tx,
232 final Collection<DataTreeModification<Tables>> changes) {
233 final Map<RouteUpdateKey, RouteEntry<C, S, R, I>> ret = new HashMap<>();
234 for (final DataTreeModification<Tables> tc : changes) {
235 final DataObjectModification<Tables> table = tc.getRootNode();
236 final DataTreeIdentifier<Tables> rootPath = tc.getRootPath();
237 final KeyedInstanceIdentifier<Peer, PeerKey> peerKIid = (KeyedInstanceIdentifier<Peer, PeerKey>)
238 rootPath.getRootIdentifier().firstIdentifierOf(Peer.class);
239 final RouterId peerUuid = RouterId.forPeerId(peerKIid.getKey().getPeerId());
241 Initialize Peer with routes under loc rib
243 if (!this.routeEntries.isEmpty() && table.getDataBefore() == null) {
244 final org.opendaylight.protocol.bgp.rib.spi.Peer toPeer
245 = this.peerTracker.getPeer(peerKIid.getKey().getPeerId());
246 if (toPeer != null && toPeer.supportsTable(this.entryDep.getLocalTablesKey())) {
247 LOG.debug("Peer {} table has been created, inserting existent routes", toPeer.getPeerId());
248 final List<ActualBestPathRoutes<C, S, R, I>> routesToStore = new ArrayList<>();
249 for (final Entry<String, RouteEntry<C, S, R, I>> entry : this.routeEntries.entrySet()) {
250 final List<ActualBestPathRoutes<C, S, R, I>> filteredRoute = entry.getValue()
251 .actualBestPaths(this.ribSupport, new RouteEntryInfoImpl(toPeer, entry.getKey()));
252 routesToStore.addAll(filteredRoute);
254 toPeer.initializeRibOut(this.entryDep, routesToStore);
258 Process new routes from Peer
260 updateNodes(table, peerUuid, tx, ret);
265 private void updateNodes(final DataObjectModification<Tables> table, final RouterId peerUuid,
266 final WriteTransaction tx, final Map<RouteUpdateKey, RouteEntry<C, S, R, I>> routes) {
267 final DataObjectModification<Attributes> attUpdate = table.getModifiedChildContainer(Attributes.class);
268 if (attUpdate != null) {
269 final Attributes newAttValue = attUpdate.getDataAfter();
270 if (newAttValue != null) {
271 LOG.trace("Uptodate found for {}", newAttValue);
272 tx.put(LogicalDatastoreType.OPERATIONAL, this.locRibTableIID.child(Attributes.class), newAttValue);
276 final DataObjectModification<S> routesChangesContainer
277 = table.getModifiedChildContainer(ribSupport.routesCaseClass(), ribSupport.routesContainerClass());
278 if (routesChangesContainer != null) {
279 updateRoutesEntries(routesChangesContainer.getModifiedChildren(), peerUuid, routes);
283 private void updateRoutesEntries(final Collection<? extends DataObjectModification<?>> collection,
284 final RouterId routerId, final Map<RouteUpdateKey, RouteEntry<C, S, R, I>> routes) {
285 for (final DataObjectModification<? extends DataObject> route : collection) {
286 if (!(route.getIdentifier() instanceof InstanceIdentifier.IdentifiableItem)) {
287 LOG.debug("Route {} already deleted", route.getIdentifier());
290 final I routeListKey = (I) ((InstanceIdentifier.IdentifiableItem) route.getIdentifier()).getKey();
291 final String routeKey = ribSupport.extractRouteKey(routeListKey);
292 final PathId pathId = ribSupport.extractPathId(routeListKey);
294 RouteEntry<C, S, R, I> entry;
295 switch (route.getModificationType()) {
297 entry = this.routeEntries.get(routeKey);
299 this.totalPathsCounter.decrement();
300 if (entry.removeRoute(routerId, pathId.getValue())) {
301 this.routeEntries.remove(routeKey);
302 this.totalPrefixesCounter.decrement();
303 LOG.trace("Removed route from {}", routerId);
307 case SUBTREE_MODIFIED:
309 final R newRoute = (R) route.getDataAfter();
310 entry = this.routeEntries.get(routeKey);
312 entry = createEntry(routeKey);
315 entry.addRoute(routerId, pathId.getValue(), newRoute);
316 this.totalPathsCounter.increment();
319 throw new IllegalStateException("Unhandled route modification " + route);
322 final RouteUpdateKey routeUpdateKey = new RouteUpdateKey(routerId, routeKey);
323 LOG.debug("Updated route {} entry {}", routeKey, entry);
324 routes.put(routeUpdateKey, entry);
328 private void walkThrough(final WriteTransaction tx,
329 final Set<Entry<RouteUpdateKey, RouteEntry<C, S, R, I>>> toUpdate) {
330 final List<StaleBestPathRoute<C, S, R, I>> staleRoutes = new ArrayList<>();
331 final List<AdvertizedRoute<C, S, R, I>> newRoutes = new ArrayList<>();
332 for (final Entry<RouteUpdateKey, RouteEntry<C, S, R, I>> e : toUpdate) {
333 LOG.trace("Walking through {}", e);
334 final RouteEntry<C, S, R, I> entry = e.getValue();
336 if (!entry.selectBest(this.ourAs)) {
337 LOG.trace("Best path has not changed, continuing");
341 entry.removeStalePaths(this.ribSupport, e.getKey().getRouteId()).ifPresent(staleRoutes::add);
342 newRoutes.addAll(entry.newBestPaths(this.ribSupport, e.getKey().getRouteId()));
344 updateLocRib(newRoutes, staleRoutes, tx);
345 this.peerTracker.getNonInternalPeers().parallelStream().forEach(
346 toPeer -> toPeer.refreshRibOut(this.entryDep, staleRoutes, newRoutes));
349 private void updateLocRib(final List<AdvertizedRoute<C, S, R, I>> newRoutes,
350 final List<StaleBestPathRoute<C, S, R, I>> staleRoutes,
351 final WriteTransaction tx) {
352 final KeyedInstanceIdentifier<Tables, TablesKey> locRibTarget = this.entryDep.getLocRibTableTarget();
354 for (final StaleBestPathRoute<C, S, R, I> staleContainer : staleRoutes) {
355 for (final I routeId : staleContainer.getStaleRouteKeyIdentifiers()) {
356 final InstanceIdentifier<R> routeTarget = ribSupport.createRouteIdentifier(locRibTarget, routeId);
357 LOG.debug("Delete route from LocRib {}", routeTarget);
358 tx.delete(LogicalDatastoreType.OPERATIONAL, routeTarget);
362 for (final AdvertizedRoute<C,S,R,I> advRoute : newRoutes) {
363 final R route = advRoute.getRoute();
364 final I iid = advRoute.getAddPathRouteKeyIdentifier();
365 final InstanceIdentifier<R> locRibRouteTarget
366 = this.ribSupport.createRouteIdentifier(locRibTarget, iid);
367 LOG.debug("Write route to LocRib {}", route);
368 tx.put(LogicalDatastoreType.OPERATIONAL, locRibRouteTarget, route);
373 public long getPrefixesCount() {
374 return this.totalPrefixesCounter.longValue();
378 public long getPathsCount() {
379 return this.totalPathsCounter.longValue();
382 TablesKey getTableKey() {
383 return this.ribSupport.getTablesKey();
387 public synchronized void refreshTable(final TablesKey tk, final PeerId peerId) {
388 final org.opendaylight.protocol.bgp.rib.spi.Peer toPeer = this.peerTracker.getPeer(peerId);
389 if (toPeer != null && toPeer.supportsTable(this.entryDep.getLocalTablesKey())) {
390 LOG.debug("Peer {} table has been created, inserting existent routes", toPeer.getPeerId());
391 final List<ActualBestPathRoutes<C, S, R, I>> routesToStore = new ArrayList<>();
392 for (final Entry<String, RouteEntry<C, S, R, I>> entry : this.routeEntries.entrySet()) {
393 final List<ActualBestPathRoutes<C, S, R, I>> filteredRoute = entry.getValue()
394 .actualBestPaths(this.ribSupport, new RouteEntryInfoImpl(toPeer, entry.getKey()));
395 routesToStore.addAll(filteredRoute);
397 toPeer.reEvaluateAdvertizement(this.entryDep, routesToStore);