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.bgp.rib.impl;
10 import com.google.common.base.Preconditions;
11 import com.google.common.collect.ImmutableMap;
12 import com.google.common.collect.ImmutableMap.Builder;
13 import java.util.Collection;
14 import java.util.Collections;
16 import java.util.Map.Entry;
18 import javax.annotation.Nonnull;
19 import javax.annotation.concurrent.NotThreadSafe;
20 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
21 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
22 import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
23 import org.opendaylight.protocol.bgp.rib.impl.spi.RIBSupportContext;
24 import org.opendaylight.protocol.bgp.rib.impl.spi.RIBSupportContextRegistry;
25 import org.opendaylight.protocol.bgp.rib.spi.RibSupportUtils;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.path.attributes.Attributes;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.attributes.MpReachNlri;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.attributes.MpUnreachNlri;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.PeerId;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.PeerRole;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.Peer;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.peer.AdjRibIn;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.peer.AdjRibOut;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.peer.EffectiveRibIn;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.Tables;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.TablesKey;
37 import org.opendaylight.yangtools.yang.common.QName;
38 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
39 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.InstanceIdentifierBuilder;
40 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
41 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
42 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
43 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
44 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
45 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
46 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
47 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
48 import org.slf4j.Logger;
49 import org.slf4j.LoggerFactory;
52 * Writer of Adjacency-RIB-In for a single peer. An instance of this object
53 * is attached to each {@link BGPPeer} and {@link ApplicationPeer}.
56 final class AdjRibInWriter {
57 private static final Logger LOG = LoggerFactory.getLogger(AdjRibInWriter.class);
59 private static final LeafNode<Boolean> ATTRIBUTES_UPTODATE_FALSE = ImmutableNodes.leafNode(QName.create(Attributes.QNAME, "uptodate"), Boolean.FALSE);
60 private static final LeafNode<Boolean> ATTRIBUTES_UPTODATE_TRUE = ImmutableNodes.leafNode(ATTRIBUTES_UPTODATE_FALSE.getNodeType(), Boolean.TRUE);
61 private static final QName PEER_ID_QNAME = QName.cachedReference(QName.create(Peer.QNAME, "peer-id"));
62 private static final QName PEER_ROLE_QNAME = QName.cachedReference(QName.create(Peer.QNAME, "peer-role"));
63 private static final NodeIdentifier ADJRIBIN = new NodeIdentifier(AdjRibIn.QNAME);
64 private static final NodeIdentifier ADJRIBOUT = new NodeIdentifier(AdjRibOut.QNAME);
65 private static final NodeIdentifier EFFRIBIN = new NodeIdentifier(EffectiveRibIn.QNAME);
66 private static final NodeIdentifier PEER_ID = new NodeIdentifier(PEER_ID_QNAME);
67 private static final NodeIdentifier PEER_ROLE = new NodeIdentifier(PEER_ROLE_QNAME);
68 private static final NodeIdentifier TABLES = new NodeIdentifier(Tables.QNAME);
70 // FIXME: is there a utility method to construct this?
71 private static final ContainerNode EMPTY_ADJRIBIN = Builders.containerBuilder().withNodeIdentifier(ADJRIBIN).addChild(ImmutableNodes.mapNodeBuilder(Tables.QNAME).build()).build();
72 private static final ContainerNode EMPTY_EFFRIBIN = Builders.containerBuilder().withNodeIdentifier(EFFRIBIN).addChild(ImmutableNodes.mapNodeBuilder(Tables.QNAME).build()).build();
73 private static final ContainerNode EMPTY_ADJRIBOUT = Builders.containerBuilder().withNodeIdentifier(ADJRIBOUT).addChild(ImmutableNodes.mapNodeBuilder(Tables.QNAME).build()).build();
75 private final Map<TablesKey, TableContext> tables;
76 private final YangInstanceIdentifier tablesRoot;
77 private final YangInstanceIdentifier ribPath;
78 private final DOMTransactionChain chain;
79 private final PeerId peerId;
80 private final String role;
83 * FIXME: transaction chain has to be instantiated in caller, so it can terminate us when it fails.
85 private AdjRibInWriter(final YangInstanceIdentifier ribPath, final DOMTransactionChain chain, final PeerId peerId, final String role, final YangInstanceIdentifier tablesRoot, final Map<TablesKey, TableContext> tables) {
86 this.ribPath = Preconditions.checkNotNull(ribPath);
87 this.chain = Preconditions.checkNotNull(chain);
88 this.tables = Preconditions.checkNotNull(tables);
89 this.role = Preconditions.checkNotNull(role);
90 this.tablesRoot = tablesRoot;
94 // We could use a codec, but this should be fine, too
95 private static String roleString(final PeerRole role) {
104 throw new IllegalArgumentException("Unhandled role " + role);
109 * Create a new writer using a transaction chain.
111 * @param role peer's role
112 * @param chain transaction chain
113 * @return A fresh writer instance
115 static AdjRibInWriter create(@Nonnull final YangInstanceIdentifier ribId, @Nonnull final PeerRole role, @Nonnull final DOMTransactionChain chain) {
116 return new AdjRibInWriter(ribId, chain, null, roleString(role), null, Collections.<TablesKey, TableContext>emptyMap());
120 * Transform this writer to a new writer, which is in charge of specified tables.
121 * Empty tables are created for new entries and old tables are deleted. Once this
122 * method returns, the old instance must not be reasonably used.
124 * @param newPeerId new peer BGP identifier
125 * @param registry RIB extension registry
126 * @param tableTypes New tables, must not be null
129 AdjRibInWriter transform(final PeerId newPeerId, final RIBSupportContextRegistry registry, final Set<TablesKey> tableTypes, final boolean isAppPeer) {
130 final DOMDataWriteTransaction tx = this.chain.newWriteOnlyTransaction();
132 final YangInstanceIdentifier newTablesRoot;
133 if (!newPeerId.equals(this.peerId)) {
134 if (this.peerId != null) {
135 // Wipe old peer data completely
136 tx.delete(LogicalDatastoreType.OPERATIONAL, this.ribPath.node(Peer.QNAME).node(new NodeIdentifierWithPredicates(Peer.QNAME, PEER_ID_QNAME, this.peerId.getValue())));
139 // Install new empty peer structure
140 final NodeIdentifierWithPredicates peerKey = IdentifierUtils.domPeerId(newPeerId);
141 final YangInstanceIdentifier newPeerPath = this.ribPath.node(Peer.QNAME).node(peerKey);
143 final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> pb = Builders.mapEntryBuilder();
144 pb.withNodeIdentifier(peerKey);
145 pb.withChild(ImmutableNodes.leafNode(PEER_ID, newPeerId.getValue()));
146 pb.withChild(ImmutableNodes.leafNode(PEER_ROLE, this.role));
147 pb.withChild(EMPTY_ADJRIBIN);
148 pb.withChild(EMPTY_EFFRIBIN);
150 pb.withChild(EMPTY_ADJRIBOUT);
153 tx.put(LogicalDatastoreType.OPERATIONAL, newPeerPath, pb.build());
154 LOG.debug("New peer {} structure installed.", newPeerPath);
156 newTablesRoot = newPeerPath.node(EMPTY_ADJRIBIN.getIdentifier()).node(TABLES);
158 newTablesRoot = this.tablesRoot;
160 // Wipe tables which are not present in the new types
161 for (final Entry<TablesKey, TableContext> e : this.tables.entrySet()) {
162 if (!tableTypes.contains(e.getKey())) {
163 e.getValue().removeTable(tx);
168 // Now create new table instances, potentially creating their empty entries
169 final Builder<TablesKey, TableContext> tb = ImmutableMap.builder();
170 for (final TablesKey k : tableTypes) {
171 TableContext ctx = this.tables.get(k);
173 final RIBSupportContext rs = registry.getRIBSupportContext(k);
175 LOG.warn("No support for table type {}, skipping it", k);
179 // We will use table keys very often, make sure they are optimized
180 final InstanceIdentifierBuilder idb = YangInstanceIdentifier.builder(newTablesRoot);
182 // TODO: Use returned value once Instance Identifier builder allows for it.
183 final NodeIdentifierWithPredicates key = RibSupportUtils.toYangTablesKey(k);
184 idb.nodeWithKey(key.getNodeType(), key.getKeyValues());
185 ctx = new TableContext(rs, idb.build());
188 tx.merge(LogicalDatastoreType.OPERATIONAL, ctx.getTableId().node(Attributes.QNAME).node(ATTRIBUTES_UPTODATE_FALSE.getNodeType()), ATTRIBUTES_UPTODATE_FALSE);
190 LOG.debug("Created table instance {}", ctx.getTableId());
196 return new AdjRibInWriter(this.ribPath, this.chain, newPeerId, this.role, newTablesRoot, tb.build());
200 * Clean all routes in specified tables
202 * @param tableTypes Tables to clean.
204 void cleanTables(final Collection<TablesKey> tableTypes) {
205 final DOMDataWriteTransaction tx = this.chain.newWriteOnlyTransaction();
207 for (final TablesKey k : tableTypes) {
208 LOG.debug("Clearing table {}", k);
209 this.tables.get(k).clearTable(tx);
215 void markTablesUptodate(final Collection<TablesKey> tableTypes) {
216 final DOMDataWriteTransaction tx = this.chain.newWriteOnlyTransaction();
218 for (final TablesKey k : tableTypes) {
219 final TableContext ctx = this.tables.get(k);
220 tx.merge(LogicalDatastoreType.OPERATIONAL, ctx.getTableId().node(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.tables.Attributes.QNAME).node(ATTRIBUTES_UPTODATE_TRUE.getNodeType()), ATTRIBUTES_UPTODATE_TRUE);
226 void updateRoutes(final MpReachNlri nlri, final Attributes attributes) {
227 final TablesKey key = new TablesKey(nlri.getAfi(), nlri.getSafi());
228 final TableContext ctx = this.tables.get(key);
230 LOG.debug("No table for {}, not accepting NLRI {}", key, nlri);
234 final DOMDataWriteTransaction tx = this.chain.newWriteOnlyTransaction();
235 ctx.writeRoutes(tx, nlri, attributes);
236 LOG.trace("Write routes {}", nlri);
240 void removeRoutes(final MpUnreachNlri nlri) {
241 final TablesKey key = new TablesKey(nlri.getAfi(), nlri.getSafi());
242 final TableContext ctx = this.tables.get(key);
244 LOG.debug("No table for {}, not accepting NLRI {}", key, nlri);
247 LOG.trace("Removing routes {}", nlri);
248 final DOMDataWriteTransaction tx = this.chain.newWriteOnlyTransaction();
249 ctx.removeRoutes(tx, nlri);