a58583416c3e912b257c67789a5736257a929db6
[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      * @param registry
120      * @return
121      */
122     private ImmutableMap.Builder<TablesKey, TableContext> createTableInstance(final Set<TablesKey> tableTypes,
123             final YangInstanceIdentifier yangTableRootIId, final DOMDataWriteTransaction tx, final RIBExtensionConsumerContext ribExtensions,
124             final BindingCodecTree tree) {
125
126         final ImmutableMap.Builder<TablesKey, TableContext> tb = ImmutableMap.builder();
127         for (final TablesKey k : tableTypes) {
128             final RIBSupport rs = ribExtensions.getRIBSupport(k);
129             if (rs == null) {
130                 LOG.warn("No support for table type {}, skipping it", k);
131                 continue;
132             }
133             final InstanceIdentifierBuilder idb = YangInstanceIdentifier.builder(yangTableRootIId);
134             final NodeIdentifierWithPredicates key = TablesUtil.toYangTablesKey(k);
135             idb.nodeWithKey(key.getNodeType(), key.getKeyValues());
136             final TableContext ctx = new TableContext(rs, idb.build(), tree);
137             ctx.createTable(tx);
138
139             tx.put(LogicalDatastoreType.OPERATIONAL, ctx.getTableId().node(BMP_ATTRIBUTES_QNAME).node(ATTRIBUTES_UPTODATE_FALSE.getNodeType()), ATTRIBUTES_UPTODATE_FALSE);
140             LOG.debug("Created table instance {}", ctx.getTableId());
141             tb.put(k, ctx);
142         }
143         return tb;
144     }
145
146     private void addRoutes(final MpReachNlri nlri, final org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang
147         .bgp.message.rev130919.path.attributes.Attributes attributes) {
148         final TablesKey key = new TablesKey(nlri.getAfi(), nlri.getSafi());
149         final TableContext ctx = this.tables.get(key);
150
151         if (ctx == null) {
152             LOG.debug("No table for {}, not accepting NLRI {}", key, nlri);
153             return;
154         }
155
156         final DOMDataWriteTransaction tx = this.chain.newWriteOnlyTransaction();
157         ctx.writeRoutes(tx, nlri, attributes);
158         LOG.trace("Write routes {}", nlri);
159         tx.submit();
160     }
161
162     private void removeRoutes(final MpUnreachNlri nlri) {
163         final TablesKey key = new TablesKey(nlri.getAfi(), nlri.getSafi());
164         final TableContext ctx = this.tables.get(key);
165
166         if (ctx == null) {
167             LOG.debug("No table for {}, not accepting NLRI {}", key, nlri);
168             return;
169         }
170         LOG.trace("Removing routes {}", nlri);
171         final DOMDataWriteTransaction tx = this.chain.newWriteOnlyTransaction();
172         ctx.removeRoutes(tx, nlri);
173         tx.submit();
174     }
175
176     /**
177      * Creates MPReach for the prefixes to be handled in the same way as linkstate routes
178      *
179      * @param message Update message containing prefixes in NLRI
180      * @return MpReachNlri with prefixes from the nlri field
181      */
182     private MpReachNlri prefixesToMpReach(final UpdateMessage message) {
183         final List<Ipv4Prefixes> prefixes = new ArrayList<>();
184         for (final Ipv4Prefix p : message.getNlri().getNlri()) {
185             prefixes.add(new Ipv4PrefixesBuilder().setPrefix(p).build());
186         }
187         final MpReachNlriBuilder b = new MpReachNlriBuilder().setAfi(Ipv4AddressFamily.class).setSafi(
188             UnicastSubsequentAddressFamily.class).setAdvertizedRoutes(
189             new AdvertizedRoutesBuilder().setDestinationType(
190                 new DestinationIpv4CaseBuilder().setDestinationIpv4(
191                     new DestinationIpv4Builder().setIpv4Prefixes(prefixes).build()).build()).build());
192         if (message.getAttributes() != null) {
193             b.setCNextHop(message.getAttributes().getCNextHop());
194         }
195         return b.build();
196     }
197
198     /**
199      * Create MPUnreach for the prefixes to be handled in the same way as linkstate routes
200      *
201      * @param message Update message containing withdrawn routes
202      * @return MpUnreachNlri with prefixes from the withdrawn routes field
203      */
204     private MpUnreachNlri prefixesToMpUnreach(final UpdateMessage message) {
205         final List<Ipv4Prefixes> prefixes = new ArrayList<>();
206         for (final Ipv4Prefix p : message.getWithdrawnRoutes().getWithdrawnRoutes()) {
207             prefixes.add(new Ipv4PrefixesBuilder().setPrefix(p).build());
208         }
209         return new MpUnreachNlriBuilder().setAfi(Ipv4AddressFamily.class).setSafi(UnicastSubsequentAddressFamily.class).setWithdrawnRoutes(
210             new WithdrawnRoutesBuilder().setDestinationType(
211                 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(
212                     new DestinationIpv4Builder().setIpv4Prefixes(prefixes).build()).build()).build()).build();
213     }
214
215     /**
216      * For each received Update message, the upd sync variable needs to be updated to true, for particular AFI/SAFI
217      * combination. Currently we only assume Unicast SAFI. From the Update message we have to extract the AFI. Each
218      * Update message can contain BGP Object with one type of AFI. If the object is BGP Link, BGP Node or a BGPPrefix
219      * the AFI is Linkstate. In case of BGPRoute, the AFI depends on the IP Address of the prefix.
220      *
221      * @param msg received Update message
222      */
223     private boolean checkEndOfRib(final UpdateMessage msg) {
224         TablesKey type = new TablesKey(Ipv4AddressFamily.class, UnicastSubsequentAddressFamily.class);
225         boolean isEOR = false;
226         if (msg.getNlri() == null && msg.getWithdrawnRoutes() == null) {
227             if (msg.getAttributes() != null) {
228                 if (msg.getAttributes().getAugmentation(Attributes1.class) != null) {
229                     final Attributes1 pa = msg.getAttributes().getAugmentation(Attributes1.class);
230                     if (pa.getMpReachNlri() != null) {
231                         type = new TablesKey(pa.getMpReachNlri().getAfi(), pa.getMpReachNlri().getSafi());
232                     }
233                 } else if (msg.getAttributes().getAugmentation(Attributes2.class) != null) {
234                     final Attributes2 pa = msg.getAttributes().getAugmentation(Attributes2.class);
235                     if (pa.getMpUnreachNlri() != null) {
236                         type = new TablesKey(pa.getMpUnreachNlri().getAfi(), pa.getMpUnreachNlri().getSafi());
237                     }
238                     if (pa.getMpUnreachNlri().getWithdrawnRoutes() == null) {
239                         // EOR message contains only MPUnreach attribute and no NLRI
240                         isEOR = true;
241                     }
242                 }
243             } else {
244                 // true for empty Update Message
245                 isEOR = true;
246             }
247         }
248
249         if (isEOR) {
250             markTableUptodated(type);
251             LOG.debug("BMP Synchronization finished for table {} ", type);
252         }
253
254         return isEOR;
255     }
256
257     private void markTableUptodated(final TablesKey tableTypes) {
258         final DOMDataWriteTransaction tx = this.chain.newWriteOnlyTransaction();
259         final TableContext ctxPre = this.tables.get(tableTypes);
260         tx.merge(LogicalDatastoreType.OPERATIONAL, ctxPre.getTableId().node(BMP_ATTRIBUTES_QNAME).node(ATTRIBUTES_UPTODATE_TRUE.getNodeType()),
261                 ATTRIBUTES_UPTODATE_TRUE);
262         tx.submit();
263     }
264 }