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