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