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 com.google.common.util.concurrent.FutureCallback;
15 import com.google.common.util.concurrent.MoreExecutors;
16 import java.util.Collection;
18 import java.util.Optional;
20 import java.util.concurrent.atomic.LongAdder;
21 import javax.annotation.Nonnull;
22 import javax.annotation.concurrent.NotThreadSafe;
23 import org.opendaylight.controller.md.sal.binding.api.BindingTransactionChain;
24 import org.opendaylight.controller.md.sal.binding.api.ClusteredDataTreeChangeListener;
25 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
26 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
27 import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
28 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
29 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
30 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
31 import org.opendaylight.mdsal.common.api.CommitInfo;
32 import org.opendaylight.protocol.bgp.openconfig.spi.BGPTableTypeRegistryConsumer;
33 import org.opendaylight.protocol.bgp.rib.impl.spi.RIB;
34 import org.opendaylight.protocol.bgp.rib.impl.spi.RIBSupportContextRegistry;
35 import org.opendaylight.protocol.bgp.rib.impl.state.peer.PrefixesInstalledCounters;
36 import org.opendaylight.protocol.bgp.rib.impl.state.peer.PrefixesReceivedCounters;
37 import org.opendaylight.protocol.bgp.rib.spi.RIBSupport;
38 import org.opendaylight.protocol.bgp.rib.spi.policy.BGPRibRoutingPolicy;
39 import org.opendaylight.protocol.bgp.rib.spi.policy.BGPRouteEntryImportParameters;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.path.attributes.Attributes;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.Route;
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.AdjRibIn;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.rib.peer.EffectiveRibIn;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.Tables;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.TablesBuilder;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.TablesKey;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.tables.Routes;
50 import org.opendaylight.yangtools.concepts.ListenerRegistration;
51 import org.opendaylight.yangtools.yang.binding.DataObject;
52 import org.opendaylight.yangtools.yang.binding.Identifier;
53 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
54 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
55 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
56 import org.slf4j.Logger;
57 import org.slf4j.LoggerFactory;
60 * Implementation of the BGP import policy. Listens on peer's Adj-RIB-In, inspects all inbound
61 * routes in the context of the advertising peer's role and applies the inbound policy.
63 * Inbound policy is applied as follows:
65 * 1) if the peer is an eBGP peer, perform attribute replacement and filtering
66 * 2) check if a route is admissible based on attributes attached to it, as well as the
67 * advertising peer's role
68 * 3) output admitting routes with edited attributes into /bgp-rib/rib/peer/effective-rib-in/tables/routes
71 final class EffectiveRibInWriter implements PrefixesReceivedCounters, PrefixesInstalledCounters,
72 AutoCloseable, ClusteredDataTreeChangeListener<Tables> {
74 private static final Logger LOG = LoggerFactory.getLogger(EffectiveRibInWriter.class);
75 static final NodeIdentifier TABLE_ROUTES = new NodeIdentifier(Routes.QNAME);
77 private final RIBSupportContextRegistry registry;
78 private final KeyedInstanceIdentifier<Peer, PeerKey> peerIId;
79 private final InstanceIdentifier<EffectiveRibIn> effRibTables;
80 private final DataBroker databroker;
81 private ListenerRegistration<?> reg;
82 private BindingTransactionChain chain;
83 private final Map<TablesKey, LongAdder> prefixesReceived;
84 private final Map<TablesKey, LongAdder> prefixesInstalled;
85 private final BGPRibRoutingPolicy ribPolicies;
86 private final BGPRouteEntryImportParameters peerImportParameters;
87 private final BGPTableTypeRegistryConsumer tableTypeRegistry;
90 final BGPRouteEntryImportParameters peer,
92 final BindingTransactionChain chain,
93 final KeyedInstanceIdentifier<Peer, PeerKey> peerIId,
94 final Set<TablesKey> tables,
95 final BGPTableTypeRegistryConsumer tableTypeRegistry
97 this.registry = requireNonNull(rib.getRibSupportContext());
98 this.chain = requireNonNull(chain);
99 this.peerIId = requireNonNull(peerIId);
100 this.effRibTables = this.peerIId.child(EffectiveRibIn.class);
101 this.prefixesInstalled = buildPrefixesTables(tables);
102 this.prefixesReceived = buildPrefixesTables(tables);
103 this.ribPolicies = requireNonNull(rib.getRibPolicies());
104 this.databroker = requireNonNull(rib.getDataBroker());
105 this.tableTypeRegistry = requireNonNull(tableTypeRegistry);
106 this.peerImportParameters = peer;
109 @SuppressWarnings("unchecked")
111 final DataTreeIdentifier treeId = new DataTreeIdentifier(LogicalDatastoreType.OPERATIONAL,
112 this.peerIId.child(AdjRibIn.class).child(Tables.class));
113 LOG.debug("Registered Effective RIB on {}", this.peerIId);
114 this.reg = requireNonNull(this.databroker).registerDataTreeChangeListener(treeId, this);
117 private Map<TablesKey, LongAdder> buildPrefixesTables(final Set<TablesKey> tables) {
118 final ImmutableMap.Builder<TablesKey, LongAdder> b = ImmutableMap.builder();
119 tables.forEach(table -> b.put(table, new LongAdder()));
124 @SuppressWarnings("unchecked")
125 public synchronized void onDataTreeChanged(@Nonnull final Collection<DataTreeModification<Tables>> changes) {
126 if (this.chain == null) {
127 LOG.trace("Chain closed. Ignoring Changes : {}", changes);
130 LOG.trace("Data changed called to effective RIB. Change : {}", changes);
131 WriteTransaction tx = null;
132 for (final DataTreeModification<Tables> tc : changes) {
133 final DataObjectModification<Tables> table = tc.getRootNode();
135 tx = this.chain.newWriteOnlyTransaction();
137 final DataObjectModification.ModificationType modificationType = table.getModificationType();
138 switch (modificationType) {
140 final Tables removeTable = table.getDataBefore();
141 final TablesKey tableKey = removeTable.getKey();
142 final KeyedInstanceIdentifier<Tables, TablesKey> effectiveTablePath
143 = this.effRibTables.child(Tables.class, tableKey);
144 LOG.debug("Delete Effective Table {} modification type {}, "
145 , effectiveTablePath, modificationType);
146 tx.delete(LogicalDatastoreType.OPERATIONAL, effectiveTablePath);
147 CountersUtil.decrement(this.prefixesInstalled.get(tableKey), tableKey);
149 case SUBTREE_MODIFIED:
150 final Tables before = table.getDataBefore();
151 final Tables after = table.getDataAfter();
152 final TablesKey tk = after.getKey();
153 LOG.debug("Process table {} type {}, dataAfter {}, dataBefore {}",
154 tk, modificationType, after, before);
156 final KeyedInstanceIdentifier<Tables, TablesKey> tablePath
157 = this.effRibTables.child(Tables.class, tk);
158 final RIBSupport ribSupport = this.registry.getRIBSupport(tk);
159 if (ribSupport == null) {
162 tx.put(LogicalDatastoreType.OPERATIONAL,
163 tablePath.child(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp
164 .rib.rev180329.rib.tables.Attributes.class), after.getAttributes());
166 final DataObjectModification routesChangesContainer =
167 table.getModifiedChildContainer(ribSupport.routesContainerClass());
169 if (routesChangesContainer == null) {
172 updateRoutes(tx, tk, ribSupport, tablePath, routesChangesContainer.getModifiedChildren());
175 writeTable(tx, table);
178 LOG.warn("Ignoring unhandled root {}", table);
183 tx.commit().addCallback(new FutureCallback<CommitInfo>() {
185 public void onSuccess(final CommitInfo result) {
186 LOG.trace("Successful commit");
190 public void onFailure(final Throwable trw) {
191 LOG.error("Failed commit", trw);
193 }, MoreExecutors.directExecutor());
197 @SuppressWarnings("unchecked")
198 private void updateRoutes(
199 final WriteTransaction tx,
200 final TablesKey tableKey, final RIBSupport ribSupport,
201 final KeyedInstanceIdentifier<Tables, TablesKey> tablePath,
202 final Collection<DataObjectModification<? extends DataObject>> routeChanges) {
203 for (final DataObjectModification<? extends DataObject> routeChanged : routeChanges) {
204 final Identifier routeKey
205 = ((InstanceIdentifier.IdentifiableItem) routeChanged.getIdentifier()).getKey();
206 switch (routeChanged.getModificationType()) {
207 case SUBTREE_MODIFIED:
209 writeRoutes(tx, tableKey, ribSupport, tablePath, routeKey, (Route) routeChanged.getDataAfter());
212 final InstanceIdentifier routeIID = ribSupport.createRouteIdentifier(tablePath, routeKey);
213 tx.delete(LogicalDatastoreType.OPERATIONAL, routeIID);
219 @SuppressWarnings("unchecked")
220 private void writeRoutes(final WriteTransaction tx, final TablesKey tk, final RIBSupport ribSupport,
221 final KeyedInstanceIdentifier<Tables, TablesKey> tablePath, final Identifier routeKey,
223 final InstanceIdentifier routeIID = ribSupport.createRouteIdentifier(tablePath, routeKey);
224 CountersUtil.increment(this.prefixesReceived.get(tk), tk);
225 final Optional<Attributes> effAtt = this.ribPolicies
226 .applyImportPolicies(this.peerImportParameters, route.getAttributes(),
227 tableTypeRegistry.getAfiSafiType(ribSupport.getTablesKey()).get());
228 if (effAtt.isPresent()) {
229 CountersUtil.increment(this.prefixesInstalled.get(tk), tk);
230 tx.put(LogicalDatastoreType.OPERATIONAL, routeIID, route);
231 tx.put(LogicalDatastoreType.OPERATIONAL, routeIID.child(Attributes.class), effAtt.get());
233 tx.delete(LogicalDatastoreType.OPERATIONAL, routeIID);
237 @SuppressWarnings("unchecked")
238 private void writeTable(final WriteTransaction tx, final DataObjectModification<Tables> table) {
239 final Tables newTable = table.getDataAfter();
240 if (newTable == null) {
243 final TablesKey tableKey = newTable.getKey();
244 final KeyedInstanceIdentifier<Tables, TablesKey> tablePath
245 = this.effRibTables.child(Tables.class, tableKey);
247 // Create an empty table
248 LOG.trace("Create Empty table", tablePath);
249 if (table.getDataBefore() == null) {
250 tx.put(LogicalDatastoreType.OPERATIONAL, tablePath, new TablesBuilder()
251 .setAfi(tableKey.getAfi()).setSafi(tableKey.getSafi())
252 .setRoutes(this.registry.getRIBSupport(tableKey).emptyRoutesCase())
253 .setAttributes(newTable.getAttributes()).build());
256 final RIBSupport ribSupport = this.registry.getRIBSupport(tableKey);
257 final Routes routes = newTable.getRoutes();
258 if (ribSupport == null || routes == null) {
262 final DataObjectModification routesChangesContainer =
263 table.getModifiedChildContainer(ribSupport.routesContainerClass());
265 if (routesChangesContainer == null) {
268 updateRoutes(tx, tableKey, ribSupport, tablePath, routesChangesContainer.getModifiedChildren());
272 public synchronized void close() {
273 if (this.reg != null) {
277 if (this.chain != null) {
281 this.prefixesReceived.values().forEach(LongAdder::reset);
282 this.prefixesInstalled.values().forEach(LongAdder::reset);
286 public long getPrefixedReceivedCount(final TablesKey tablesKey) {
287 final LongAdder counter = this.prefixesReceived.get(tablesKey);
288 if (counter == null) {
291 return counter.longValue();
295 public Set<TablesKey> getTableKeys() {
296 return ImmutableSet.copyOf(this.prefixesReceived.keySet());
300 public boolean isSupported(final TablesKey tablesKey) {
301 return this.prefixesReceived.containsKey(tablesKey);
305 public long getPrefixedInstalledCount(final TablesKey tablesKey) {
306 final LongAdder counter = this.prefixesInstalled.get(tablesKey);
307 if (counter == null) {
310 return counter.longValue();
314 public long getTotalPrefixesInstalled() {
315 return this.prefixesInstalled.values().stream().mapToLong(LongAdder::longValue).sum();