Migrate deprecated submit() to commit() for BGP/BMP
[bgpcep.git] / bmp / bmp-impl / src / main / java / org / opendaylight / protocol / bmp / impl / app / BmpRibInWriter.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
9 package org.opendaylight.protocol.bmp.impl.app;
10
11 import static org.opendaylight.protocol.bmp.impl.app.TablesUtil.BMP_ATTRIBUTES_QNAME;
12
13 import com.google.common.collect.ImmutableMap;
14 import com.google.common.util.concurrent.FutureCallback;
15 import com.google.common.util.concurrent.MoreExecutors;
16 import java.util.ArrayList;
17 import java.util.List;
18 import java.util.Map;
19 import java.util.Set;
20 import java.util.stream.Collectors;
21 import javax.annotation.Nonnull;
22 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
23 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
24 import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
25 import org.opendaylight.mdsal.binding.dom.codec.api.BindingCodecTree;
26 import org.opendaylight.mdsal.common.api.CommitInfo;
27 import org.opendaylight.protocol.bgp.rib.spi.RIBExtensionConsumerContext;
28 import org.opendaylight.protocol.bgp.rib.spi.RIBSupport;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev180329.ipv4.prefixes.DestinationIpv4Builder;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev180329.ipv4.prefixes.destination.ipv4.Ipv4Prefixes;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev180329.ipv4.prefixes.destination.ipv4.Ipv4PrefixesBuilder;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev180329.update.attributes.mp.reach.nlri.advertized.routes.destination.type.DestinationIpv4CaseBuilder;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.UpdateMessage;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.Attributes1;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.Attributes2;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.MpReachNlri;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.MpReachNlriBuilder;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.MpUnreachNlri;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.MpUnreachNlriBuilder;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.mp.reach.nlri.AdvertizedRoutesBuilder;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.mp.unreach.nlri.WithdrawnRoutesBuilder;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.TablesKey;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.Ipv4AddressFamily;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.UnicastSubsequentAddressFamily;
45 import org.opendaylight.yangtools.yang.common.QName;
46 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
47 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.InstanceIdentifierBuilder;
48 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
49 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
50 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
51 import org.slf4j.Logger;
52 import org.slf4j.LoggerFactory;
53
54 final class BmpRibInWriter {
55
56     private static final Logger LOG = LoggerFactory.getLogger(BmpRibInWriter.class);
57
58     private static final LeafNode<Boolean> ATTRIBUTES_UPTODATE_FALSE =
59             ImmutableNodes.leafNode(QName.create(BMP_ATTRIBUTES_QNAME, "uptodate"), Boolean.FALSE);
60     private static final LeafNode<Boolean> ATTRIBUTES_UPTODATE_TRUE =
61             ImmutableNodes.leafNode(ATTRIBUTES_UPTODATE_FALSE.getNodeType(), Boolean.TRUE);
62
63     private final DOMTransactionChain chain;
64     private final Map<TablesKey, TableContext> tables;
65
66
67     private BmpRibInWriter(final YangInstanceIdentifier tablesRoot, final DOMTransactionChain chain,
68             final RIBExtensionConsumerContext ribExtensions,
69             final Set<TablesKey> tableTypes,  final BindingCodecTree tree) {
70         this.chain = chain;
71         final DOMDataWriteTransaction tx = this.chain.newWriteOnlyTransaction();
72         this.tables = createTableInstance(tableTypes, tablesRoot, tx, ribExtensions, tree).build();
73
74         LOG.debug("New RIB table {} structure installed.", tablesRoot.toString());
75         tx.commit().addCallback(new FutureCallback<CommitInfo>() {
76             @Override
77             public void onSuccess(final CommitInfo result) {
78                 LOG.trace("Successful commit");
79             }
80
81             @Override
82             public void onFailure(final Throwable trw) {
83                 LOG.error("Failed commit", trw);
84             }
85         }, MoreExecutors.directExecutor());
86     }
87
88     public static BmpRibInWriter create(@Nonnull final YangInstanceIdentifier tablesRootPath,
89             @Nonnull final DOMTransactionChain chain,
90             @Nonnull final RIBExtensionConsumerContext extensions, @Nonnull final Set<TablesKey> tableTypes,
91             @Nonnull  final BindingCodecTree tree) {
92         return new BmpRibInWriter(tablesRootPath, chain, extensions, tableTypes, tree);
93     }
94
95     /**
96      * Write on DS Adj-RIBs-In.
97      */
98     public void onMessage(final UpdateMessage message) {
99
100         if (!checkEndOfRib(message)) {
101             final org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.path
102                     .attributes.Attributes attrs = message.getAttributes();
103             MpReachNlri mpReach = null;
104             if (message.getNlri() != null) {
105                 mpReach = prefixesToMpReach(message);
106             } else if (attrs != null && attrs.getAugmentation(Attributes1.class) != null) {
107                 mpReach = attrs.getAugmentation(Attributes1.class).getMpReachNlri();
108             }
109             if (mpReach != null) {
110                 addRoutes(mpReach, attrs);
111                 return;
112             }
113
114             MpUnreachNlri mpUnreach = null;
115             if (message.getWithdrawnRoutes() != null) {
116                 mpUnreach = prefixesToMpUnreach(message);
117             } else if (attrs != null && attrs.getAugmentation(Attributes2.class) != null) {
118                 mpUnreach = attrs.getAugmentation(Attributes2.class).getMpUnreachNlri();
119             }
120             if (mpUnreach != null) {
121                 removeRoutes(mpUnreach);
122             }
123         }
124     }
125
126     /**
127      * Create new table instance.
128      */
129     private static ImmutableMap.Builder<TablesKey, TableContext> createTableInstance(final Set<TablesKey> tableTypes,
130             final YangInstanceIdentifier yangTableRootIId, final DOMDataWriteTransaction tx,
131             final RIBExtensionConsumerContext ribExtensions, final BindingCodecTree tree) {
132
133         final ImmutableMap.Builder<TablesKey, TableContext> tb = ImmutableMap.builder();
134         for (final TablesKey k : tableTypes) {
135             final RIBSupport rs = ribExtensions.getRIBSupport(k);
136             if (rs == null) {
137                 LOG.warn("No support for table type {}, skipping it", k);
138                 continue;
139             }
140             final InstanceIdentifierBuilder idb = YangInstanceIdentifier.builder(yangTableRootIId);
141             final NodeIdentifierWithPredicates key = TablesUtil.toYangTablesKey(k);
142             idb.nodeWithKey(key.getNodeType(), key.getKeyValues());
143             final TableContext ctx = new TableContext(rs, idb.build(), tree);
144             ctx.createTable(tx);
145
146             tx.put(LogicalDatastoreType.OPERATIONAL, ctx.getTableId().node(BMP_ATTRIBUTES_QNAME)
147                     .node(ATTRIBUTES_UPTODATE_FALSE.getNodeType()), ATTRIBUTES_UPTODATE_FALSE);
148             LOG.debug("Created table instance {}", ctx.getTableId());
149             tb.put(k, ctx);
150         }
151         return tb;
152     }
153
154     private synchronized void addRoutes(final MpReachNlri nlri, final org.opendaylight.yang.gen.v1.urn.opendaylight
155             .params.xml.ns.yang.bgp.message.rev180329.path.attributes.Attributes attributes) {
156         final TablesKey key = new TablesKey(nlri.getAfi(), nlri.getSafi());
157         final TableContext ctx = this.tables.get(key);
158
159         if (ctx == null) {
160             LOG.debug("No table for {}, not accepting NLRI {}", key, nlri);
161             return;
162         }
163
164         final DOMDataWriteTransaction tx = this.chain.newWriteOnlyTransaction();
165         ctx.writeRoutes(tx, nlri, attributes);
166         LOG.trace("Write routes {}", nlri);
167         tx.commit().addCallback(new FutureCallback<CommitInfo>() {
168             @Override
169             public void onSuccess(final CommitInfo result) {
170                 LOG.trace("Successful commit");
171             }
172
173             @Override
174             public void onFailure(final Throwable trw) {
175                 LOG.error("Failed commit", trw);
176             }
177         }, MoreExecutors.directExecutor());
178     }
179
180     /**
181      * Creates MPReach for the prefixes to be handled in the same way as linkstate routes.
182      *
183      * @param message Update message containing prefixes in NLRI
184      * @return MpReachNlri with prefixes from the nlri field
185      */
186     private static MpReachNlri prefixesToMpReach(final UpdateMessage message) {
187         final List<Ipv4Prefixes> prefixes = message.getNlri().stream()
188                 .map(n -> new Ipv4PrefixesBuilder().setPrefix(n.getPrefix()).setPathId(n.getPathId()).build())
189                 .collect(Collectors.toList());
190         final MpReachNlriBuilder b = new MpReachNlriBuilder().setAfi(Ipv4AddressFamily.class).setSafi(
191             UnicastSubsequentAddressFamily.class).setAdvertizedRoutes(
192             new AdvertizedRoutesBuilder().setDestinationType(
193                 new DestinationIpv4CaseBuilder().setDestinationIpv4(
194                     new DestinationIpv4Builder().setIpv4Prefixes(prefixes).build()).build()).build());
195         if (message.getAttributes() != null) {
196             b.setCNextHop(message.getAttributes().getCNextHop());
197         }
198         return b.build();
199     }
200
201     private synchronized void removeRoutes(final MpUnreachNlri nlri) {
202         final TablesKey key = new TablesKey(nlri.getAfi(), nlri.getSafi());
203         final TableContext ctx = this.tables.get(key);
204
205         if (ctx == null) {
206             LOG.debug("No table for {}, not accepting NLRI {}", key, nlri);
207             return;
208         }
209         LOG.trace("Removing routes {}", nlri);
210         final DOMDataWriteTransaction tx = this.chain.newWriteOnlyTransaction();
211         ctx.removeRoutes(tx, nlri);
212         tx.commit().addCallback(new FutureCallback<CommitInfo>() {
213             @Override
214             public void onSuccess(final CommitInfo result) {
215                 LOG.trace("Successful commit");
216             }
217
218             @Override
219             public void onFailure(final Throwable trw) {
220                 LOG.error("Failed commit", trw);
221             }
222         }, MoreExecutors.directExecutor());
223     }
224
225     /**
226      * Create MPUnreach for the prefixes to be handled in the same way as linkstate routes.
227      *
228      * @param message Update message containing withdrawn routes
229      * @return MpUnreachNlri with prefixes from the withdrawn routes field
230      */
231     private static MpUnreachNlri prefixesToMpUnreach(final UpdateMessage message) {
232         final List<Ipv4Prefixes> prefixes = new ArrayList<>();
233         message.getWithdrawnRoutes().forEach(
234             w -> prefixes.add(new Ipv4PrefixesBuilder().setPrefix(w.getPrefix()).setPathId(w.getPathId()).build()));
235         return new MpUnreachNlriBuilder().setAfi(Ipv4AddressFamily.class).setSafi(UnicastSubsequentAddressFamily.class)
236                 .setWithdrawnRoutes(new WithdrawnRoutesBuilder().setDestinationType(
237                         new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev180329.update
238                                 .attributes.mp.unreach.nlri.withdrawn.routes.destination.type
239                                 .DestinationIpv4CaseBuilder().setDestinationIpv4(new DestinationIpv4Builder()
240                                 .setIpv4Prefixes(prefixes).build()).build()).build()).build();
241     }
242
243     /**
244      * For each received Update message, the upd sync variable needs to be updated to true, for particular AFI/SAFI
245      * combination. Currently we only assume Unicast SAFI. From the Update message we have to extract the AFI. Each
246      * Update message can contain BGP Object with one type of AFI. If the object is BGP Link, BGP Node or a BGPPrefix
247      * the AFI is Linkstate. In case of BGPRoute, the AFI depends on the IP Address of the prefix.
248      *
249      * @param msg received Update message
250      */
251     private boolean checkEndOfRib(final UpdateMessage msg) {
252         TablesKey type = new TablesKey(Ipv4AddressFamily.class, UnicastSubsequentAddressFamily.class);
253         boolean isEOR = false;
254         if (msg.getNlri() == null && msg.getWithdrawnRoutes() == null) {
255             if (msg.getAttributes() != null) {
256                 if (msg.getAttributes().getAugmentation(Attributes1.class) != null) {
257                     final Attributes1 pa = msg.getAttributes().getAugmentation(Attributes1.class);
258                     if (pa.getMpReachNlri() != null) {
259                         type = new TablesKey(pa.getMpReachNlri().getAfi(), pa.getMpReachNlri().getSafi());
260                     }
261                 } else if (msg.getAttributes().getAugmentation(Attributes2.class) != null) {
262                     final Attributes2 pa = msg.getAttributes().getAugmentation(Attributes2.class);
263                     if (pa.getMpUnreachNlri() != null) {
264                         type = new TablesKey(pa.getMpUnreachNlri().getAfi(), pa.getMpUnreachNlri().getSafi());
265                     }
266                     if (pa.getMpUnreachNlri().getWithdrawnRoutes() == null) {
267                         // EOR message contains only MPUnreach attribute and no NLRI
268                         isEOR = true;
269                     }
270                 }
271             } else {
272                 // true for empty Update Message
273                 isEOR = true;
274             }
275         }
276
277         if (isEOR) {
278             markTableUptodated(type);
279             LOG.debug("BMP Synchronization finished for table {} ", type);
280         }
281
282         return isEOR;
283     }
284
285     private synchronized void markTableUptodated(final TablesKey tableTypes) {
286         final DOMDataWriteTransaction tx = this.chain.newWriteOnlyTransaction();
287         final TableContext ctxPre = this.tables.get(tableTypes);
288         tx.merge(LogicalDatastoreType.OPERATIONAL, ctxPre.getTableId().node(BMP_ATTRIBUTES_QNAME)
289                 .node(ATTRIBUTES_UPTODATE_TRUE.getNodeType()), ATTRIBUTES_UPTODATE_TRUE);
290         tx.commit().addCallback(new FutureCallback<CommitInfo>() {
291             @Override
292             public void onSuccess(final CommitInfo result) {
293                 LOG.trace("Successful commit");
294             }
295
296             @Override
297             public void onFailure(final Throwable trw) {
298                 LOG.error("Failed commit", trw);
299             }
300         }, MoreExecutors.directExecutor());
301     }
302 }