org.opendaylight.protocol.bmp.impl.app.BmpRibInWriter Maven / Gradle / Ivy
/*
* Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
package org.opendaylight.protocol.bmp.impl.app;
import static org.opendaylight.protocol.bmp.impl.app.TablesUtil.BMP_ATTRIBUTES_QNAME;
import com.google.common.collect.ImmutableMap;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.MoreExecutors;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.eclipse.jdt.annotation.NonNull;
import org.opendaylight.mdsal.common.api.CommitInfo;
import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
import org.opendaylight.protocol.bgp.rib.spi.RIBExtensionConsumerContext;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev180329.ipv4.prefixes.DestinationIpv4Builder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev180329.ipv4.prefixes.destination.ipv4.Ipv4Prefixes;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev180329.ipv4.prefixes.destination.ipv4.Ipv4PrefixesBuilder;
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;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev200120.UpdateMessage;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.AttributesReach;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.AttributesUnreach;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.attributes.reach.MpReachNlri;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.attributes.reach.MpReachNlriBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.attributes.reach.mp.reach.nlri.AdvertizedRoutesBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.attributes.unreach.MpUnreachNlri;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.attributes.unreach.MpUnreachNlriBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.attributes.unreach.mp.unreach.nlri.WithdrawnRoutesBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.TablesKey;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev200120.Ipv4AddressFamily;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev200120.UnicastSubsequentAddressFamily;
import org.opendaylight.yangtools.binding.data.codec.api.BindingCodecTree;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
import org.opendaylight.yangtools.yang.data.spi.node.ImmutableNodes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
final class BmpRibInWriter {
private static final Logger LOG = LoggerFactory.getLogger(BmpRibInWriter.class);
private static final LeafNode ATTRIBUTES_UPTODATE_FALSE =
ImmutableNodes.leafNode(QName.create(BMP_ATTRIBUTES_QNAME, "uptodate").intern(), Boolean.FALSE);
private static final LeafNode ATTRIBUTES_UPTODATE_TRUE =
ImmutableNodes.leafNode(ATTRIBUTES_UPTODATE_FALSE.name(), Boolean.TRUE);
private final DOMTransactionChain chain;
private final Map tables;
private BmpRibInWriter(final YangInstanceIdentifier tablesRoot, final DOMTransactionChain chain,
final RIBExtensionConsumerContext ribExtensions,
final Set tableTypes, final BindingCodecTree tree) {
this.chain = chain;
final DOMDataTreeWriteTransaction tx = this.chain.newWriteOnlyTransaction();
tables = createTableInstance(tableTypes, tablesRoot, tx, ribExtensions, tree).build();
LOG.debug("New RIB table {} structure installed.", tablesRoot.toString());
tx.commit().addCallback(new FutureCallback() {
@Override
public void onSuccess(final CommitInfo result) {
LOG.trace("Successful commit");
}
@Override
public void onFailure(final Throwable trw) {
LOG.error("Failed commit", trw);
}
}, MoreExecutors.directExecutor());
}
public static BmpRibInWriter create(final @NonNull YangInstanceIdentifier tablesRootPath,
final @NonNull DOMTransactionChain chain,
final @NonNull RIBExtensionConsumerContext extensions, final @NonNull Set tableTypes,
final @NonNull BindingCodecTree tree) {
return new BmpRibInWriter(tablesRootPath, chain, extensions, tableTypes, tree);
}
/**
* Write on DS Adj-RIBs-In.
*/
public void onMessage(final UpdateMessage message) {
if (!checkEndOfRib(message)) {
final org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev200120.path
.attributes.Attributes attrs = message.getAttributes();
MpReachNlri mpReach = null;
if (message.getNlri() != null) {
mpReach = prefixesToMpReach(message);
} else if (attrs != null && attrs.augmentation(AttributesReach.class) != null) {
mpReach = attrs.augmentation(AttributesReach.class).getMpReachNlri();
}
if (mpReach != null) {
addRoutes(mpReach, attrs);
return;
}
MpUnreachNlri mpUnreach = null;
if (message.getWithdrawnRoutes() != null) {
mpUnreach = prefixesToMpUnreach(message);
} else if (attrs != null && attrs.augmentation(AttributesUnreach.class) != null) {
mpUnreach = attrs.augmentation(AttributesUnreach.class).getMpUnreachNlri();
}
if (mpUnreach != null) {
removeRoutes(mpUnreach);
}
}
}
/**
* Create new table instance.
*/
private static ImmutableMap.Builder createTableInstance(final Set tableTypes,
final YangInstanceIdentifier yangTableRootIId, final DOMDataTreeWriteTransaction tx,
final RIBExtensionConsumerContext ribExtensions, final BindingCodecTree tree) {
final var identityCodec = tree.getIdentityCodec();
final ImmutableMap.Builder tb = ImmutableMap.builder();
for (final TablesKey tableType : tableTypes) {
final var rs = ribExtensions.getRIBSupport(tableType);
if (rs == null) {
LOG.warn("No support for table type {}, skipping it", tableType);
continue;
}
final var domTableKey = NodeIdentifierWithPredicates.of(TablesUtil.BMP_TABLES_QNAME, ImmutableMap.of(
TablesUtil.BMP_AFI_QNAME, identityCodec.fromBinding(tableType.getAfi()),
TablesUtil.BMP_SAFI_QNAME, identityCodec.fromBinding(tableType.getSafi())));
final TableContext ctx = new TableContext(rs, yangTableRootIId.node(domTableKey).toOptimized(), tree);
ctx.createTable(tx);
tx.put(LogicalDatastoreType.OPERATIONAL, ctx.getTableId().node(BMP_ATTRIBUTES_QNAME)
.node(ATTRIBUTES_UPTODATE_FALSE.name()), ATTRIBUTES_UPTODATE_FALSE);
LOG.debug("Created table instance {}", ctx.getTableId());
tb.put(tableType, ctx);
}
return tb;
}
private synchronized void addRoutes(final MpReachNlri nlri, final org.opendaylight.yang.gen.v1.urn.opendaylight
.params.xml.ns.yang.bgp.message.rev200120.path.attributes.Attributes attributes) {
final TablesKey key = new TablesKey(nlri.getAfi(), nlri.getSafi());
final TableContext ctx = tables.get(key);
if (ctx == null) {
LOG.debug("No table for {}, not accepting NLRI {}", key, nlri);
return;
}
final DOMDataTreeWriteTransaction tx = chain.newWriteOnlyTransaction();
ctx.writeRoutes(tx, nlri, attributes);
LOG.trace("Write routes {}", nlri);
tx.commit().addCallback(new FutureCallback() {
@Override
public void onSuccess(final CommitInfo result) {
LOG.trace("Successful commit");
}
@Override
public void onFailure(final Throwable trw) {
LOG.error("Failed commit", trw);
}
}, MoreExecutors.directExecutor());
}
/**
* Creates MPReach for the prefixes to be handled in the same way as linkstate routes.
*
* @param message Update message containing prefixes in NLRI
* @return MpReachNlri with prefixes from the nlri field
*/
private static MpReachNlri prefixesToMpReach(final UpdateMessage message) {
final List prefixes = message.getNlri().stream()
.map(n -> new Ipv4PrefixesBuilder().setPrefix(n.getPrefix()).setPathId(n.getPathId()).build())
.collect(Collectors.toList());
final MpReachNlriBuilder b = new MpReachNlriBuilder().setAfi(Ipv4AddressFamily.VALUE).setSafi(
UnicastSubsequentAddressFamily.VALUE).setAdvertizedRoutes(
new AdvertizedRoutesBuilder().setDestinationType(
new DestinationIpv4CaseBuilder().setDestinationIpv4(
new DestinationIpv4Builder().setIpv4Prefixes(prefixes).build()).build()).build());
if (message.getAttributes() != null) {
b.setCNextHop(message.getAttributes().getCNextHop());
}
return b.build();
}
private synchronized void removeRoutes(final MpUnreachNlri nlri) {
final TablesKey key = new TablesKey(nlri.getAfi(), nlri.getSafi());
final TableContext ctx = tables.get(key);
if (ctx == null) {
LOG.debug("No table for {}, not accepting NLRI {}", key, nlri);
return;
}
LOG.trace("Removing routes {}", nlri);
final DOMDataTreeWriteTransaction tx = chain.newWriteOnlyTransaction();
ctx.removeRoutes(tx, nlri);
tx.commit().addCallback(new FutureCallback() {
@Override
public void onSuccess(final CommitInfo result) {
LOG.trace("Successful commit");
}
@Override
public void onFailure(final Throwable trw) {
LOG.error("Failed commit", trw);
}
}, MoreExecutors.directExecutor());
}
/**
* Create MPUnreach for the prefixes to be handled in the same way as linkstate routes.
*
* @param message Update message containing withdrawn routes
* @return MpUnreachNlri with prefixes from the withdrawn routes field
*/
private static MpUnreachNlri prefixesToMpUnreach(final UpdateMessage message) {
final List prefixes = new ArrayList<>();
message.getWithdrawnRoutes().forEach(
w -> prefixes.add(new Ipv4PrefixesBuilder().setPrefix(w.getPrefix()).setPathId(w.getPathId()).build()));
return new MpUnreachNlriBuilder().setAfi(Ipv4AddressFamily.VALUE).setSafi(UnicastSubsequentAddressFamily.VALUE)
.setWithdrawnRoutes(new WithdrawnRoutesBuilder().setDestinationType(
new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev180329.update
.attributes.mp.unreach.nlri.withdrawn.routes.destination.type
.DestinationIpv4CaseBuilder().setDestinationIpv4(new DestinationIpv4Builder()
.setIpv4Prefixes(prefixes).build()).build()).build()).build();
}
/**
* For each received Update message, the upd sync variable needs to be updated to true, for particular AFI/SAFI
* combination. Currently we only assume Unicast SAFI. From the Update message we have to extract the AFI. Each
* Update message can contain BGP Object with one type of AFI. If the object is BGP Link, BGP Node or a BGPPrefix
* the AFI is Linkstate. In case of BGPRoute, the AFI depends on the IP Address of the prefix.
*
* @param msg received Update message
*/
private boolean checkEndOfRib(final UpdateMessage msg) {
TablesKey type = new TablesKey(Ipv4AddressFamily.VALUE, UnicastSubsequentAddressFamily.VALUE);
boolean isEOR = false;
if (msg.getNlri() == null && msg.getWithdrawnRoutes() == null) {
if (msg.getAttributes() != null) {
if (msg.getAttributes().augmentation(AttributesReach.class) != null) {
final AttributesReach pa = msg.getAttributes().augmentation(AttributesReach.class);
if (pa.getMpReachNlri() != null) {
type = new TablesKey(pa.getMpReachNlri().getAfi(), pa.getMpReachNlri().getSafi());
}
} else if (msg.getAttributes().augmentation(AttributesUnreach.class) != null) {
final AttributesUnreach pa = msg.getAttributes().augmentation(AttributesUnreach.class);
if (pa.getMpUnreachNlri() != null) {
type = new TablesKey(pa.getMpUnreachNlri().getAfi(), pa.getMpUnreachNlri().getSafi());
}
if (pa.getMpUnreachNlri().getWithdrawnRoutes() == null) {
// EOR message contains only MPUnreach attribute and no NLRI
isEOR = true;
}
}
} else {
// true for empty Update Message
isEOR = true;
}
}
if (isEOR) {
markTableUptodated(type);
LOG.debug("BMP Synchronization finished for table {} ", type);
}
return isEOR;
}
private synchronized void markTableUptodated(final TablesKey tableTypes) {
final DOMDataTreeWriteTransaction tx = chain.newWriteOnlyTransaction();
final TableContext ctxPre = tables.get(tableTypes);
tx.merge(LogicalDatastoreType.OPERATIONAL, ctxPre.getTableId().node(BMP_ATTRIBUTES_QNAME)
.node(ATTRIBUTES_UPTODATE_TRUE.name()), ATTRIBUTES_UPTODATE_TRUE);
tx.commit().addCallback(new FutureCallback() {
@Override
public void onSuccess(final CommitInfo result) {
LOG.trace("Successful commit");
}
@Override
public void onFailure(final Throwable trw) {
LOG.error("Failed commit", trw);
}
}, MoreExecutors.directExecutor());
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy