All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opendaylight.protocol.bmp.impl.app.BmpRibInWriter Maven / Gradle / Ivy

There is a newer version: 0.22.6
Show newest version
/*
 * 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