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
8 package org.opendaylight.protocol.bmp.impl.app;
10 import static org.opendaylight.protocol.bmp.impl.app.TablesUtil.BMP_ATTRIBUTES_QNAME;
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;
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;
52 final class BmpRibInWriter {
53 private static final Logger LOG = LoggerFactory.getLogger(BmpRibInWriter.class);
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);
60 private final DOMTransactionChain chain;
61 private final Map<TablesKey, TableContext> tables;
64 private BmpRibInWriter(final YangInstanceIdentifier tablesRoot, final DOMTransactionChain chain,
65 final RIBExtensionConsumerContext ribExtensions,
66 final Set<TablesKey> tableTypes, final BindingCodecTree tree) {
68 final DOMDataTreeWriteTransaction tx = this.chain.newWriteOnlyTransaction();
69 tables = createTableInstance(tableTypes, tablesRoot, tx, ribExtensions, tree).build();
71 LOG.debug("New RIB table {} structure installed.", tablesRoot.toString());
72 tx.commit().addCallback(new FutureCallback<CommitInfo>() {
74 public void onSuccess(final CommitInfo result) {
75 LOG.trace("Successful commit");
79 public void onFailure(final Throwable trw) {
80 LOG.error("Failed commit", trw);
82 }, MoreExecutors.directExecutor());
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);
93 * Write on DS Adj-RIBs-In.
95 public void onMessage(final UpdateMessage message) {
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();
106 if (mpReach != null) {
107 addRoutes(mpReach, attrs);
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();
117 if (mpUnreach != null) {
118 removeRoutes(mpUnreach);
124 * Create new table instance.
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();
131 final ImmutableMap.Builder<TablesKey, TableContext> tb = ImmutableMap.builder();
132 for (final TablesKey tableType : tableTypes) {
133 final RIBSupport rs = ribExtensions.getRIBSupport(tableType);
135 LOG.warn("No support for table type {}, skipping it", tableType);
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);
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);
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);
159 LOG.debug("No table for {}, not accepting NLRI {}", key, nlri);
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>() {
168 public void onSuccess(final CommitInfo result) {
169 LOG.trace("Successful commit");
173 public void onFailure(final Throwable trw) {
174 LOG.error("Failed commit", trw);
176 }, MoreExecutors.directExecutor());
180 * Creates MPReach for the prefixes to be handled in the same way as linkstate routes.
182 * @param message Update message containing prefixes in NLRI
183 * @return MpReachNlri with prefixes from the nlri field
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());
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);
205 LOG.debug("No table for {}, not accepting NLRI {}", key, nlri);
208 LOG.trace("Removing routes {}", nlri);
209 final DOMDataTreeWriteTransaction tx = chain.newWriteOnlyTransaction();
210 ctx.removeRoutes(tx, nlri);
211 tx.commit().addCallback(new FutureCallback<CommitInfo>() {
213 public void onSuccess(final CommitInfo result) {
214 LOG.trace("Successful commit");
218 public void onFailure(final Throwable trw) {
219 LOG.error("Failed commit", trw);
221 }, MoreExecutors.directExecutor());
225 * Create MPUnreach for the prefixes to be handled in the same way as linkstate routes.
227 * @param message Update message containing withdrawn routes
228 * @return MpUnreachNlri with prefixes from the withdrawn routes field
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();
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.
248 * @param msg received Update message
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());
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());
265 if (pa.getMpUnreachNlri().getWithdrawnRoutes() == null) {
266 // EOR message contains only MPUnreach attribute and no NLRI
271 // true for empty Update Message
277 markTableUptodated(type);
278 LOG.debug("BMP Synchronization finished for table {} ", type);
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>() {
291 public void onSuccess(final CommitInfo result) {
292 LOG.trace("Successful commit");
296 public void onFailure(final Throwable trw) {
297 LOG.error("Failed commit", trw);
299 }, MoreExecutors.directExecutor());