2 * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
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
9 package org.opendaylight.protocol.bmp.impl.app;
11 import static org.opendaylight.protocol.bmp.impl.app.TablesUtil.BMP_ATTRIBUTES_QNAME;
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;
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;
54 final class BmpRibInWriter {
56 private static final Logger LOG = LoggerFactory.getLogger(BmpRibInWriter.class);
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);
63 private final DOMTransactionChain chain;
64 private final Map<TablesKey, TableContext> tables;
67 private BmpRibInWriter(final YangInstanceIdentifier tablesRoot, final DOMTransactionChain chain,
68 final RIBExtensionConsumerContext ribExtensions,
69 final Set<TablesKey> tableTypes, final BindingCodecTree tree) {
71 final DOMDataWriteTransaction tx = this.chain.newWriteOnlyTransaction();
72 this.tables = createTableInstance(tableTypes, tablesRoot, tx, ribExtensions, tree).build();
74 LOG.debug("New RIB table {} structure installed.", tablesRoot.toString());
75 tx.commit().addCallback(new FutureCallback<CommitInfo>() {
77 public void onSuccess(final CommitInfo result) {
78 LOG.trace("Successful commit");
82 public void onFailure(final Throwable trw) {
83 LOG.error("Failed commit", trw);
85 }, MoreExecutors.directExecutor());
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);
96 * Write on DS Adj-RIBs-In.
98 public void onMessage(final UpdateMessage message) {
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();
109 if (mpReach != null) {
110 addRoutes(mpReach, attrs);
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();
120 if (mpUnreach != null) {
121 removeRoutes(mpUnreach);
127 * Create new table instance.
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) {
133 final ImmutableMap.Builder<TablesKey, TableContext> tb = ImmutableMap.builder();
134 for (final TablesKey k : tableTypes) {
135 final RIBSupport rs = ribExtensions.getRIBSupport(k);
137 LOG.warn("No support for table type {}, skipping it", k);
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);
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());
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);
160 LOG.debug("No table for {}, not accepting NLRI {}", key, nlri);
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>() {
169 public void onSuccess(final CommitInfo result) {
170 LOG.trace("Successful commit");
174 public void onFailure(final Throwable trw) {
175 LOG.error("Failed commit", trw);
177 }, MoreExecutors.directExecutor());
181 * Creates MPReach for the prefixes to be handled in the same way as linkstate routes.
183 * @param message Update message containing prefixes in NLRI
184 * @return MpReachNlri with prefixes from the nlri field
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());
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);
206 LOG.debug("No table for {}, not accepting NLRI {}", key, nlri);
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>() {
214 public void onSuccess(final CommitInfo result) {
215 LOG.trace("Successful commit");
219 public void onFailure(final Throwable trw) {
220 LOG.error("Failed commit", trw);
222 }, MoreExecutors.directExecutor());
226 * Create MPUnreach for the prefixes to be handled in the same way as linkstate routes.
228 * @param message Update message containing withdrawn routes
229 * @return MpUnreachNlri with prefixes from the withdrawn routes field
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();
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.
249 * @param msg received Update message
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());
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());
266 if (pa.getMpUnreachNlri().getWithdrawnRoutes() == null) {
267 // EOR message contains only MPUnreach attribute and no NLRI
272 // true for empty Update Message
278 markTableUptodated(type);
279 LOG.debug("BMP Synchronization finished for table {} ", type);
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>() {
292 public void onSuccess(final CommitInfo result) {
293 LOG.trace("Successful commit");
297 public void onFailure(final Throwable trw) {
298 LOG.error("Failed commit", trw);
300 }, MoreExecutors.directExecutor());