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.collect.ImmutableMap;
13 import com.google.common.collect.ImmutableSet;
14 import java.util.Collection;
16 import java.util.Optional;
18 import java.util.concurrent.atomic.LongAdder;
19 import javax.annotation.Nonnull;
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.DataObjectModification;
24 import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
25 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
26 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
27 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
28 import org.opendaylight.protocol.bgp.rib.impl.spi.RIB;
29 import org.opendaylight.protocol.bgp.rib.impl.spi.RIBSupportContextRegistry;
30 import org.opendaylight.protocol.bgp.rib.impl.state.peer.PrefixesInstalledCounters;
31 import org.opendaylight.protocol.bgp.rib.impl.state.peer.PrefixesReceivedCounters;
32 import org.opendaylight.protocol.bgp.rib.spi.RIBSupport;
33 import org.opendaylight.protocol.bgp.rib.spi.policy.BGPRibRoutingPolicy;
34 import org.opendaylight.protocol.bgp.rib.spi.policy.BGPRouteEntryImportParameters;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev171207.path.attributes.Attributes;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.Route;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.bgp.rib.rib.Peer;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.bgp.rib.rib.PeerKey;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.bgp.rib.rib.peer.AdjRibIn;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.bgp.rib.rib.peer.EffectiveRibIn;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.rib.Tables;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.rib.TablesBuilder;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.rib.TablesKey;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.rib.tables.Routes;
45 import org.opendaylight.yangtools.concepts.ListenerRegistration;
46 import org.opendaylight.yangtools.yang.binding.DataObject;
47 import org.opendaylight.yangtools.yang.binding.Identifier;
48 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
49 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
50 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
51 import org.slf4j.Logger;
52 import org.slf4j.LoggerFactory;
55 * Implementation of the BGP import policy. Listens on peer's Adj-RIB-In, inspects all inbound
56 * routes in the context of the advertising peer's role and applies the inbound policy.
58 * Inbound policy is applied as follows:
60 * 1) if the peer is an eBGP peer, perform attribute replacement and filtering
61 * 2) check if a route is admissible based on attributes attached to it, as well as the
62 * advertising peer's role
63 * 3) output admitting routes with edited attributes into /bgp-rib/rib/peer/effective-rib-in/tables/routes
66 final class EffectiveRibInWriter implements PrefixesReceivedCounters, PrefixesInstalledCounters, AutoCloseable {
67 static final NodeIdentifier TABLE_ROUTES = new NodeIdentifier(Routes.QNAME);
68 private static final Logger LOG = LoggerFactory.getLogger(EffectiveRibInWriter.class);
69 private final AdjInTracker adjInTracker;
71 private EffectiveRibInWriter(final BGPRouteEntryImportParameters peer, final RIB rib,
72 final BindingTransactionChain chain,
73 final KeyedInstanceIdentifier<Peer, PeerKey> peerIId, final Set<TablesKey> tables) {
74 this.adjInTracker = new AdjInTracker(peer, rib, chain, peerIId, tables);
77 static EffectiveRibInWriter create(final BGPRouteEntryImportParameters peer, @Nonnull final RIB rib,
78 @Nonnull final BindingTransactionChain chain,
79 @Nonnull final KeyedInstanceIdentifier<Peer, PeerKey> peerIId,
80 @Nonnull final Set<TablesKey> tables) {
81 return new EffectiveRibInWriter(peer, rib, chain, peerIId, tables);
85 public synchronized void close() {
86 this.adjInTracker.close();
90 public long getPrefixedReceivedCount(final TablesKey tablesKey) {
91 return this.adjInTracker.getPrefixedReceivedCount(tablesKey);
95 public Set<TablesKey> getTableKeys() {
96 return this.adjInTracker.getTableKeys();
100 public boolean isSupported(final TablesKey tablesKey) {
101 return this.adjInTracker.isSupported(tablesKey);
105 public long getPrefixedInstalledCount(@Nonnull final TablesKey tablesKey) {
106 return this.adjInTracker.getPrefixedInstalledCount(tablesKey);
110 public long getTotalPrefixesInstalled() {
111 return this.adjInTracker.getTotalPrefixesInstalled();
114 private static final class AdjInTracker implements PrefixesReceivedCounters, PrefixesInstalledCounters,
115 AutoCloseable, ClusteredDataTreeChangeListener<Tables> {
116 private final RIBSupportContextRegistry registry;
117 private final KeyedInstanceIdentifier<Peer, PeerKey> peerIId;
118 private final InstanceIdentifier<EffectiveRibIn> effRibTables;
119 private final ListenerRegistration<?> reg;
120 private final BindingTransactionChain chain;
121 private final Map<TablesKey, LongAdder> prefixesReceived;
122 private final Map<TablesKey, LongAdder> prefixesInstalled;
123 private final BGPRibRoutingPolicy ribPolicies;
124 private final BGPRouteEntryImportParameters peerImportParameters;
126 @SuppressWarnings("unchecked")
127 AdjInTracker(final BGPRouteEntryImportParameters peer, final RIB rib,
128 final BindingTransactionChain chain,
129 final KeyedInstanceIdentifier<Peer, PeerKey> peerIId,
130 @Nonnull final Set<TablesKey> tables) {
131 this.registry = requireNonNull(rib.getRibSupportContext());
132 this.chain = requireNonNull(chain);
133 this.peerIId = requireNonNull(peerIId);
134 this.effRibTables = this.peerIId.child(EffectiveRibIn.class);
135 this.prefixesInstalled = buildPrefixesTables(tables);
136 this.prefixesReceived = buildPrefixesTables(tables);
137 this.ribPolicies = requireNonNull(rib.getRibPolicies());
138 this.peerImportParameters = peer;
139 final DataTreeIdentifier treeId = new DataTreeIdentifier(LogicalDatastoreType.OPERATIONAL,
140 this.peerIId.child(AdjRibIn.class).child(Tables.class));
141 LOG.debug("Registered Effective RIB on {}", this.peerIId);
142 this.reg = requireNonNull(rib.getDataBroker()).registerDataTreeChangeListener(treeId, this);
145 private Map<TablesKey, LongAdder> buildPrefixesTables(final Set<TablesKey> tables) {
146 final ImmutableMap.Builder<TablesKey, LongAdder> b = ImmutableMap.builder();
147 tables.forEach(table -> b.put(table, new LongAdder()));
152 @SuppressWarnings("unchecked")
153 public synchronized void onDataTreeChanged(@Nonnull final Collection<DataTreeModification<Tables>> changes) {
154 LOG.trace("Data changed called to effective RIB. Change : {}", changes);
155 WriteTransaction tx = null;
156 for (final DataTreeModification<Tables> tc : changes) {
157 final DataObjectModification<Tables> table = tc.getRootNode();
159 tx = this.chain.newWriteOnlyTransaction();
161 final DataObjectModification.ModificationType modificationType = table.getModificationType();
162 switch (modificationType) {
164 final Tables removeTable = table.getDataBefore();
165 final TablesKey tableKey = removeTable.getKey();
166 final KeyedInstanceIdentifier<Tables, TablesKey> effectiveTablePath
167 = this.effRibTables.child(Tables.class, tableKey);
168 LOG.debug("Delete Effective Table {} modification type {}, "
169 , effectiveTablePath, modificationType);
170 tx.delete(LogicalDatastoreType.OPERATIONAL, effectiveTablePath);
171 CountersUtil.decrement(this.prefixesInstalled.get(tableKey), tableKey);
173 case SUBTREE_MODIFIED:
174 final Tables before = table.getDataBefore();
175 final Tables after = table.getDataAfter();
176 final TablesKey tk = after.getKey();
177 LOG.debug("Process table {} type {}, dataAfter {}, dataBefore {}",
178 tk, modificationType, after, before);
180 final KeyedInstanceIdentifier<Tables, TablesKey> tablePath
181 = this.effRibTables.child(Tables.class, tk);
182 final RIBSupport ribSupport = this.registry.getRIBSupport(tk);
183 if (ribSupport == null) {
186 tx.put(LogicalDatastoreType.OPERATIONAL,
187 tablePath.child(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp
188 .rib.rev171207.rib.tables.Attributes.class), after.getAttributes());
190 final DataObjectModification routesChangesContainer =
191 table.getModifiedChildContainer(ribSupport.routesContainerClass());
193 if (routesChangesContainer == null) {
196 updateRoutes(tx, tk, ribSupport, tablePath, routesChangesContainer.getModifiedChildren());
199 writeTable(tx, table);
202 LOG.warn("Ignoring unhandled root {}", table);
211 @SuppressWarnings("unchecked")
212 private void updateRoutes(
213 final WriteTransaction tx,
214 final TablesKey tableKey, final RIBSupport ribSupport,
215 final KeyedInstanceIdentifier<Tables, TablesKey> tablePath,
216 final Collection<DataObjectModification<? extends DataObject>> routeChanges) {
217 for (final DataObjectModification<? extends DataObject> routeChanged : routeChanges) {
218 final Identifier routeKey
219 = ((InstanceIdentifier.IdentifiableItem) routeChanged.getIdentifier()).getKey();
220 switch (routeChanged.getModificationType()) {
221 case SUBTREE_MODIFIED:
223 writeRoutes(tx, tableKey, ribSupport, tablePath, routeKey, (Route) routeChanged.getDataAfter());
226 final InstanceIdentifier routeIID = ribSupport.createRouteIdentifier(tablePath, routeKey);
227 tx.delete(LogicalDatastoreType.OPERATIONAL, routeIID);
233 @SuppressWarnings("unchecked")
234 private void writeRoutes(final WriteTransaction tx, final TablesKey tk, final RIBSupport ribSupport,
235 final KeyedInstanceIdentifier<Tables, TablesKey> tablePath, final Identifier routeKey,
237 final InstanceIdentifier routeIID = ribSupport.createRouteIdentifier(tablePath, routeKey);
238 CountersUtil.increment(this.prefixesReceived.get(tk), tk);
239 final Optional<Attributes> effAtt = this.ribPolicies
240 .applyImportPolicies(this.peerImportParameters, route.getAttributes());
241 if (effAtt.isPresent()) {
242 CountersUtil.increment(this.prefixesInstalled.get(tk), tk);
243 tx.put(LogicalDatastoreType.OPERATIONAL, routeIID, route, true);
244 tx.put(LogicalDatastoreType.OPERATIONAL, routeIID.child(Attributes.class), effAtt.get());
246 tx.delete(LogicalDatastoreType.OPERATIONAL, routeIID);
250 @SuppressWarnings("unchecked")
251 private void writeTable(final WriteTransaction tx, final DataObjectModification<Tables> table) {
252 final Tables newTable = table.getDataAfter();
253 if (newTable == null) {
256 final TablesKey tableKey = newTable.getKey();
257 final KeyedInstanceIdentifier<Tables, TablesKey> tablePath
258 = this.effRibTables.child(Tables.class, tableKey);
260 // Create an empty table
261 LOG.trace("Create Empty table", tablePath);
262 tx.put(LogicalDatastoreType.OPERATIONAL, tablePath, new TablesBuilder()
263 .setAfi(tableKey.getAfi()).setSafi(tableKey.getSafi())
264 .setAttributes(newTable.getAttributes()).build());
266 final RIBSupport ribSupport = this.registry.getRIBSupport(tableKey);
267 final Routes routes = newTable.getRoutes();
268 if (ribSupport == null || routes == null) {
272 final DataObjectModification routesChangesContainer =
273 table.getModifiedChildContainer(ribSupport.routesContainerClass());
275 if (routesChangesContainer == null) {
278 updateRoutes(tx, tableKey, ribSupport, tablePath, routesChangesContainer.getModifiedChildren());
282 public synchronized void close() {
284 this.prefixesReceived.values().forEach(LongAdder::reset);
285 this.prefixesInstalled.values().forEach(LongAdder::reset);
289 public long getPrefixedReceivedCount(final TablesKey tablesKey) {
290 final LongAdder counter = this.prefixesReceived.get(tablesKey);
291 if (counter == null) {
294 return counter.longValue();
298 public Set<TablesKey> getTableKeys() {
299 return ImmutableSet.copyOf(this.prefixesReceived.keySet());
303 public boolean isSupported(final TablesKey tablesKey) {
304 return this.prefixesReceived.containsKey(tablesKey);
308 public long getPrefixedInstalledCount(final TablesKey tablesKey) {
309 final LongAdder counter = this.prefixesInstalled.get(tablesKey);
310 if (counter == null) {
313 return counter.longValue();
317 public long getTotalPrefixesInstalled() {
318 return this.prefixesInstalled.values().stream().mapToLong(LongAdder::longValue).sum();