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