Provide codec Serializer via RibSupport
[bgpcep.git] / bgp / rib-impl / src / main / java / org / opendaylight / protocol / bgp / rib / impl / EffectiveRibInWriter.java
1 /*
2  * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.protocol.bgp.rib.impl;
9
10 import static java.util.Objects.requireNonNull;
11
12 import com.google.common.collect.ImmutableMap;
13 import com.google.common.collect.ImmutableSet;
14 import java.util.Collection;
15 import java.util.Map;
16 import java.util.Optional;
17 import java.util.Set;
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.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.rib.impl.spi.RIB;
30 import org.opendaylight.protocol.bgp.rib.impl.spi.RIBSupportContextRegistry;
31 import org.opendaylight.protocol.bgp.rib.impl.state.peer.PrefixesInstalledCounters;
32 import org.opendaylight.protocol.bgp.rib.impl.state.peer.PrefixesReceivedCounters;
33 import org.opendaylight.protocol.bgp.rib.spi.RIBSupport;
34 import org.opendaylight.protocol.bgp.rib.spi.policy.BGPRibRoutingPolicy;
35 import org.opendaylight.protocol.bgp.rib.spi.policy.BGPRouteEntryImportParameters;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.path.attributes.Attributes;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.Route;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.rib.Peer;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.rib.PeerKey;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.rib.peer.AdjRibIn;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.rib.peer.EffectiveRibIn;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.Tables;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.TablesBuilder;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.TablesKey;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.tables.Routes;
46 import org.opendaylight.yangtools.concepts.ListenerRegistration;
47 import org.opendaylight.yangtools.yang.binding.DataObject;
48 import org.opendaylight.yangtools.yang.binding.Identifier;
49 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
50 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
51 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
52 import org.slf4j.Logger;
53 import org.slf4j.LoggerFactory;
54
55 /**
56  * Implementation of the BGP import policy. Listens on peer's Adj-RIB-In, inspects all inbound
57  * routes in the context of the advertising peer's role and applies the inbound policy.
58  * <p>
59  * Inbound policy is applied as follows:
60  * <p>
61  * 1) if the peer is an eBGP peer, perform attribute replacement and filtering
62  * 2) check if a route is admissible based on attributes attached to it, as well as the
63  * advertising peer's role
64  * 3) output admitting routes with edited attributes into /bgp-rib/rib/peer/effective-rib-in/tables/routes
65  */
66 @NotThreadSafe
67 final class EffectiveRibInWriter implements PrefixesReceivedCounters, PrefixesInstalledCounters,
68         AutoCloseable, ClusteredDataTreeChangeListener<Tables> {
69
70     private static final Logger LOG = LoggerFactory.getLogger(EffectiveRibInWriter.class);
71     static final NodeIdentifier TABLE_ROUTES = new NodeIdentifier(Routes.QNAME);
72
73     private final RIBSupportContextRegistry registry;
74     private final KeyedInstanceIdentifier<Peer, PeerKey> peerIId;
75     private final InstanceIdentifier<EffectiveRibIn> effRibTables;
76     private final DataBroker databroker;
77     private ListenerRegistration<?> reg;
78     private BindingTransactionChain chain;
79     private final Map<TablesKey, LongAdder> prefixesReceived;
80     private final Map<TablesKey, LongAdder> prefixesInstalled;
81     private final BGPRibRoutingPolicy ribPolicies;
82     private final BGPRouteEntryImportParameters peerImportParameters;
83
84     EffectiveRibInWriter(final BGPRouteEntryImportParameters peer, final RIB rib,
85             final BindingTransactionChain chain,
86             final KeyedInstanceIdentifier<Peer, PeerKey> peerIId,
87             @Nonnull final Set<TablesKey> tables) {
88         this.registry = requireNonNull(rib.getRibSupportContext());
89         this.chain = requireNonNull(chain);
90         this.peerIId = requireNonNull(peerIId);
91         this.effRibTables = this.peerIId.child(EffectiveRibIn.class);
92         this.prefixesInstalled = buildPrefixesTables(tables);
93         this.prefixesReceived = buildPrefixesTables(tables);
94         this.ribPolicies = requireNonNull(rib.getRibPolicies());
95         this.databroker = requireNonNull(rib.getDataBroker());
96         this.peerImportParameters = peer;
97     }
98
99     @SuppressWarnings("unchecked")
100     public void init() {
101         final DataTreeIdentifier treeId = new DataTreeIdentifier(LogicalDatastoreType.OPERATIONAL,
102                 this.peerIId.child(AdjRibIn.class).child(Tables.class));
103         LOG.debug("Registered Effective RIB on {}", this.peerIId);
104         this.reg = requireNonNull(this.databroker).registerDataTreeChangeListener(treeId, this);
105     }
106
107     private Map<TablesKey, LongAdder> buildPrefixesTables(final Set<TablesKey> tables) {
108         final ImmutableMap.Builder<TablesKey, LongAdder> b = ImmutableMap.builder();
109         tables.forEach(table -> b.put(table, new LongAdder()));
110         return b.build();
111     }
112
113     @Override
114     @SuppressWarnings("unchecked")
115     public synchronized void onDataTreeChanged(@Nonnull final Collection<DataTreeModification<Tables>> changes) {
116         if (this.chain == null) {
117             LOG.trace("Chain closed. Ignoring Changes : {}", changes);
118             return;
119         }
120         LOG.trace("Data changed called to effective RIB. Change : {}", changes);
121         WriteTransaction tx = null;
122         for (final DataTreeModification<Tables> tc : changes) {
123             final DataObjectModification<Tables> table = tc.getRootNode();
124             if (tx == null) {
125                 tx = this.chain.newWriteOnlyTransaction();
126             }
127             final DataObjectModification.ModificationType modificationType = table.getModificationType();
128             switch (modificationType) {
129                 case DELETE:
130                     final Tables removeTable = table.getDataBefore();
131                     final TablesKey tableKey = removeTable.getKey();
132                     final KeyedInstanceIdentifier<Tables, TablesKey> effectiveTablePath
133                             = this.effRibTables.child(Tables.class, tableKey);
134                     LOG.debug("Delete Effective Table {} modification type {}, "
135                             , effectiveTablePath, modificationType);
136                     tx.delete(LogicalDatastoreType.OPERATIONAL, effectiveTablePath);
137                     CountersUtil.decrement(this.prefixesInstalled.get(tableKey), tableKey);
138                     break;
139                 case SUBTREE_MODIFIED:
140                     final Tables before = table.getDataBefore();
141                     final Tables after = table.getDataAfter();
142                     final TablesKey tk = after.getKey();
143                     LOG.debug("Process table {} type {}, dataAfter {}, dataBefore {}",
144                             tk, modificationType, after, before);
145
146                     final KeyedInstanceIdentifier<Tables, TablesKey> tablePath
147                             = this.effRibTables.child(Tables.class, tk);
148                     final RIBSupport ribSupport = this.registry.getRIBSupport(tk);
149                     if (ribSupport == null) {
150                         break;
151                     }
152                     tx.put(LogicalDatastoreType.OPERATIONAL,
153                             tablePath.child(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp
154                                     .rib.rev180329.rib.tables.Attributes.class), after.getAttributes());
155
156                     final DataObjectModification routesChangesContainer =
157                             table.getModifiedChildContainer(ribSupport.routesContainerClass());
158
159                     if (routesChangesContainer == null) {
160                         break;
161                     }
162                     updateRoutes(tx, tk, ribSupport, tablePath, routesChangesContainer.getModifiedChildren());
163                     break;
164                 case WRITE:
165                     writeTable(tx, table);
166                     break;
167                 default:
168                     LOG.warn("Ignoring unhandled root {}", table);
169                     break;
170             }
171         }
172         if (tx != null) {
173             tx.submit();
174         }
175     }
176
177     @SuppressWarnings("unchecked")
178     private void updateRoutes(
179             final WriteTransaction tx,
180             final TablesKey tableKey, final RIBSupport ribSupport,
181             final KeyedInstanceIdentifier<Tables, TablesKey> tablePath,
182             final Collection<DataObjectModification<? extends DataObject>> routeChanges) {
183         for (final DataObjectModification<? extends DataObject> routeChanged : routeChanges) {
184             final Identifier routeKey
185                     = ((InstanceIdentifier.IdentifiableItem) routeChanged.getIdentifier()).getKey();
186             switch (routeChanged.getModificationType()) {
187                 case SUBTREE_MODIFIED:
188                 case WRITE:
189                     writeRoutes(tx, tableKey, ribSupport, tablePath, routeKey, (Route) routeChanged.getDataAfter());
190                     break;
191                 case DELETE:
192                     final InstanceIdentifier routeIID = ribSupport.createRouteIdentifier(tablePath, routeKey);
193                     tx.delete(LogicalDatastoreType.OPERATIONAL, routeIID);
194                     break;
195             }
196         }
197     }
198
199     @SuppressWarnings("unchecked")
200     private void writeRoutes(final WriteTransaction tx, final TablesKey tk, final RIBSupport ribSupport,
201             final KeyedInstanceIdentifier<Tables, TablesKey> tablePath, final Identifier routeKey,
202             final Route route) {
203         final InstanceIdentifier routeIID = ribSupport.createRouteIdentifier(tablePath, routeKey);
204         CountersUtil.increment(this.prefixesReceived.get(tk), tk);
205         final Optional<Attributes> effAtt = this.ribPolicies
206                 .applyImportPolicies(this.peerImportParameters, route.getAttributes());
207         if (effAtt.isPresent()) {
208             CountersUtil.increment(this.prefixesInstalled.get(tk), tk);
209             tx.put(LogicalDatastoreType.OPERATIONAL, routeIID, route);
210             tx.put(LogicalDatastoreType.OPERATIONAL, routeIID.child(Attributes.class), effAtt.get());
211         } else {
212             tx.delete(LogicalDatastoreType.OPERATIONAL, routeIID);
213         }
214     }
215
216     @SuppressWarnings("unchecked")
217     private void writeTable(final WriteTransaction tx, final DataObjectModification<Tables> table) {
218         final Tables newTable = table.getDataAfter();
219         if (newTable == null) {
220             return;
221         }
222         final TablesKey tableKey = newTable.getKey();
223         final KeyedInstanceIdentifier<Tables, TablesKey> tablePath
224                 = this.effRibTables.child(Tables.class, tableKey);
225
226         // Create an empty table
227         LOG.trace("Create Empty table", tablePath);
228         if (table.getDataBefore() == null) {
229             tx.put(LogicalDatastoreType.OPERATIONAL, tablePath, new TablesBuilder()
230                     .setAfi(tableKey.getAfi()).setSafi(tableKey.getSafi())
231                     .setRoutes(this.registry.getRIBSupport(tableKey).emptyRoutesCase())
232                     .setAttributes(newTable.getAttributes()).build());
233         }
234
235         final RIBSupport ribSupport = this.registry.getRIBSupport(tableKey);
236         final Routes routes = newTable.getRoutes();
237         if (ribSupport == null || routes == null) {
238             return;
239         }
240
241         final DataObjectModification routesChangesContainer =
242                 table.getModifiedChildContainer(ribSupport.routesContainerClass());
243
244         if (routesChangesContainer == null) {
245             return;
246         }
247         updateRoutes(tx, tableKey, ribSupport, tablePath, routesChangesContainer.getModifiedChildren());
248     }
249
250     @Override
251     public synchronized void close() {
252         if (this.reg != null) {
253             this.reg.close();
254             this.reg = null;
255         }
256         if (this.chain != null) {
257             this.chain.close();
258             this.chain = null;
259         }
260         this.prefixesReceived.values().forEach(LongAdder::reset);
261         this.prefixesInstalled.values().forEach(LongAdder::reset);
262     }
263
264     @Override
265     public long getPrefixedReceivedCount(final TablesKey tablesKey) {
266         final LongAdder counter = this.prefixesReceived.get(tablesKey);
267         if (counter == null) {
268             return 0;
269         }
270         return counter.longValue();
271     }
272
273     @Override
274     public Set<TablesKey> getTableKeys() {
275         return ImmutableSet.copyOf(this.prefixesReceived.keySet());
276     }
277
278     @Override
279     public boolean isSupported(final TablesKey tablesKey) {
280         return this.prefixesReceived.containsKey(tablesKey);
281     }
282
283     @Override
284     public long getPrefixedInstalledCount(final TablesKey tablesKey) {
285         final LongAdder counter = this.prefixesInstalled.get(tablesKey);
286         if (counter == null) {
287             return 0;
288         }
289         return counter.longValue();
290     }
291
292     @Override
293     public long getTotalPrefixesInstalled() {
294         return this.prefixesInstalled.values().stream().mapToLong(LongAdder::longValue).sum();
295     }
296 }