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.spi.RIBExtensionConsumerContext;
24 import org.opendaylight.protocol.bgp.rib.spi.RIBSupport;
25 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.PathAttributes;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.path.attributes.MpReachNlri;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.path.attributes.MpUnreachNlri;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.BgpRib;
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;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.RibKey;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.Peer;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.peer.AdjRibIn;
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.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.tables.Attributes;
38 import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
39 import org.opendaylight.yangtools.yang.common.QName;
40 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
41 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.InstanceIdentifierBuilder;
42 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
43 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
44 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
45 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
46 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
47 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
48 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
49 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
50 import org.slf4j.Logger;
51 import org.slf4j.LoggerFactory;
54 * Writer of Adjacency-RIB-In for a single peer. An instance of this object
55 * is attached to each {@link BGPPeer} and {@link ApplicationPeer}.
58 final class AdjRibInWriter {
59 private static final Logger LOG = LoggerFactory.getLogger(AdjRibInWriter.class);
61 private static final LeafNode<Boolean> ATTRIBUTES_UPTODATE_FALSE = ImmutableNodes.leafNode(QName.create(Attributes.QNAME, "uptodate"), Boolean.FALSE);
62 private static final LeafNode<Boolean> ATTRIBUTES_UPTODATE_TRUE = ImmutableNodes.leafNode(ATTRIBUTES_UPTODATE_FALSE.getNodeType(), Boolean.TRUE);
63 private static final QName RIB_ID_QNAME = QName.cachedReference(QName.create(Rib.QNAME, "id"));
64 private static final QName AFI_QNAME = QName.cachedReference(QName.create(Tables.QNAME, "afi"));
65 private static final QName SAFI_QNAME = QName.cachedReference(QName.create(Tables.QNAME, "safi"));
66 private static final QName PEER_ID_QNAME = QName.cachedReference(QName.create(Peer.QNAME, "peer-id"));
67 private static final QName PEER_ROLE_QNAME = QName.cachedReference(QName.create(Peer.QNAME, "peer-role"));
68 private static final NodeIdentifier ADJRIBIN = new NodeIdentifier(AdjRibIn.QNAME);
69 private static final NodeIdentifier PEER_ID = new NodeIdentifier(PEER_ID_QNAME);
70 private static final NodeIdentifier PEER_ROLE = new NodeIdentifier(PEER_ROLE_QNAME);
71 private static final NodeIdentifier TABLES = new NodeIdentifier(Tables.QNAME);
73 // FIXME: is there a utility method to construct this?
74 private static final ContainerNode EMPTY_ADJRIBIN = Builders.containerBuilder().withNodeIdentifier(ADJRIBIN).addChild(ImmutableNodes.mapNodeBuilder(Tables.QNAME).build()).build();
76 private final Map<TablesKey, TableContext> tables;
77 private final YangInstanceIdentifier tablesRoot;
78 private final YangInstanceIdentifier ribPath;
79 private final DOMTransactionChain chain;
80 private final Ipv4Address peerId = null;
81 private final String role;
84 * FIXME: transaction chain has to be instantiated in caller, so it can terminate us when it fails.
86 private AdjRibInWriter(final YangInstanceIdentifier ribPath, final DOMTransactionChain chain, final String role, final YangInstanceIdentifier tablesRoot, final Map<TablesKey, TableContext> tables) {
87 this.ribPath = Preconditions.checkNotNull(ribPath);
88 this.chain = Preconditions.checkNotNull(chain);
89 this.tables = Preconditions.checkNotNull(tables);
90 this.role = Preconditions.checkNotNull(role);
91 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 RibKey key, @Nonnull final PeerRole role, @Nonnull final DOMTransactionChain chain) {
116 final InstanceIdentifierBuilder b = YangInstanceIdentifier.builder();
117 b.node(BgpRib.QNAME);
119 b.nodeWithKey(Rib.QNAME, RIB_ID_QNAME, key.getId().getValue());
121 return new AdjRibInWriter(b.build(), chain, roleString(role), null, Collections.<TablesKey, TableContext>emptyMap());
125 * Transform this writer to a new writer, which is in charge of specified tables.
126 * Empty tables are created for new entries and old tables are deleted. Once this
127 * method returns, the old instance must not be reasonably used.
129 * @param newPeerId new peer BGP identifier
130 * @param registry RIB extension registry
131 * @param tableTypes New tables, must not be null
134 AdjRibInWriter transform(final Ipv4Address newPeerId, final RIBExtensionConsumerContext registry, final Set<TablesKey> tableTypes) {
135 final DOMDataWriteTransaction tx = this.chain.newWriteOnlyTransaction();
137 final YangInstanceIdentifier newTablesRoot;
138 if (!newPeerId.equals(this.peerId)) {
139 if (this.peerId != null) {
140 // Wipe old peer data completely
141 tx.delete(LogicalDatastoreType.OPERATIONAL, this.ribPath.node(Peer.QNAME).node(new NodeIdentifierWithPredicates(Peer.QNAME, PEER_ID_QNAME, this.peerId.getValue())));
144 // Install new empty peer structure
145 final NodeIdentifierWithPredicates peerKey = new NodeIdentifierWithPredicates(Peer.QNAME, PEER_ID_QNAME, newPeerId.getValue());
146 final YangInstanceIdentifier newPeerPath = this.ribPath.node(Peer.QNAME).node(peerKey);
148 final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> pb = Builders.mapEntryBuilder();
149 pb.withNodeIdentifier(peerKey);
150 pb.withChild(ImmutableNodes.leafNode(PEER_ID, newPeerId.getValue()));
151 pb.withChild(ImmutableNodes.leafNode(PEER_ROLE, this.role));
152 pb.withChild(EMPTY_ADJRIBIN);
154 tx.put(LogicalDatastoreType.OPERATIONAL, newPeerPath, pb.build());
155 LOG.debug("New peer {} structure installed.", newPeerPath);
157 newTablesRoot = newPeerPath.node(EMPTY_ADJRIBIN.getIdentifier()).node(TABLES);
159 newTablesRoot = this.tablesRoot;
161 // Wipe tables which are not present in the new types
162 for (final Entry<TablesKey, TableContext> e : this.tables.entrySet()) {
163 if (!tableTypes.contains(e.getKey())) {
164 e.getValue().removeTable(tx);
169 // Now create new table instances, potentially creating their empty entries
170 final Builder<TablesKey, TableContext> tb = ImmutableMap.builder();
171 for (final TablesKey k : tableTypes) {
172 TableContext ctx = this.tables.get(k);
174 final RIBSupport rs = registry.getRIBSupport(k);
176 LOG.warn("No support for table type {}, skipping it", k);
180 // We will use table keys very often, make sure they are optimized
181 final InstanceIdentifierBuilder idb = YangInstanceIdentifier.builder(newTablesRoot);
183 // FIXME: use codec to translate the key
184 final Map<QName, Object> keyValues = ImmutableMap.<QName, Object>of(AFI_QNAME, BindingReflections.getQName(k.getAfi()), SAFI_QNAME, BindingReflections.getQName(k.getSafi()));
185 final NodeIdentifierWithPredicates key = new NodeIdentifierWithPredicates(Tables.QNAME, keyValues);
186 idb.nodeWithKey(key.getNodeType(), keyValues);
188 ctx = new TableContext(rs, idb.build());
191 tx.merge(LogicalDatastoreType.OPERATIONAL, ctx.getTableId().node(Attributes.QNAME).node(ATTRIBUTES_UPTODATE_FALSE.getNodeType()), ATTRIBUTES_UPTODATE_FALSE);
193 LOG.debug("Created table instance {}", ctx);
199 return new AdjRibInWriter(this.ribPath, this.chain, this.role, newTablesRoot, tb.build());
203 * Clean all routes in specified tables
205 * @param tableTypes Tables to clean.
207 void cleanTables(final Collection<TablesKey> tableTypes) {
208 final DOMDataWriteTransaction tx = this.chain.newWriteOnlyTransaction();
210 for (final TablesKey k : tableTypes) {
211 LOG.debug("Clearing table {}", k);
212 this.tables.get(k).clearTable(tx);
218 void markTablesUptodate(final Collection<TablesKey> tableTypes) {
219 final DOMDataWriteTransaction tx = this.chain.newWriteOnlyTransaction();
221 for (final TablesKey k : tableTypes) {
222 final TableContext ctx = this.tables.get(k);
223 tx.merge(LogicalDatastoreType.OPERATIONAL, ctx.getTableId().node(Attributes.QNAME).node(ATTRIBUTES_UPTODATE_TRUE.getNodeType()), ATTRIBUTES_UPTODATE_TRUE);
229 void updateRoutes(final MpReachNlri nlri, final PathAttributes attributes) {
230 final TablesKey key = new TablesKey(nlri.getAfi(), nlri.getSafi());
231 final TableContext ctx = this.tables.get(key);
233 LOG.debug("No table for {}, not accepting NLRI {}", key, nlri);
237 final DOMDataWriteTransaction tx = this.chain.newWriteOnlyTransaction();
238 ctx.writeRoutes(null, tx, nlri, attributes);
242 void removeRoutes(final MpUnreachNlri nlri) {
243 final TablesKey key = new TablesKey(nlri.getAfi(), nlri.getSafi());
244 final TableContext ctx = this.tables.get(key);
246 LOG.debug("No table for {}, not accepting NLRI {}", key, nlri);
250 final DOMDataWriteTransaction tx = this.chain.newWriteOnlyTransaction();
251 ctx.removeRoutes(null, tx, nlri);