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.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.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.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.tables.Attributes;
38 import org.opendaylight.yangtools.yang.common.QName;
39 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
40 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.InstanceIdentifierBuilder;
41 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
42 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
43 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
44 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
45 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
46 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
47 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
48 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
49 import org.slf4j.Logger;
50 import org.slf4j.LoggerFactory;
53 * Writer of Adjacency-RIB-In for a single peer. An instance of this object
54 * is attached to each {@link BGPPeer} and {@link ApplicationPeer}.
57 final class AdjRibInWriter {
58 private static final Logger LOG = LoggerFactory.getLogger(AdjRibInWriter.class);
60 private static final LeafNode<Boolean> ATTRIBUTES_UPTODATE_FALSE = ImmutableNodes.leafNode(QName.create(Attributes.QNAME, "uptodate"), Boolean.FALSE);
61 private static final LeafNode<Boolean> ATTRIBUTES_UPTODATE_TRUE = ImmutableNodes.leafNode(ATTRIBUTES_UPTODATE_FALSE.getNodeType(), Boolean.TRUE);
62 private static final QName PEER_ID_QNAME = QName.cachedReference(QName.create(Peer.QNAME, "peer-id"));
63 private static final QName PEER_ROLE_QNAME = QName.cachedReference(QName.create(Peer.QNAME, "peer-role"));
64 private static final NodeIdentifier ADJRIBIN = new NodeIdentifier(AdjRibIn.QNAME);
65 private static final NodeIdentifier ADJRIBOUT = new NodeIdentifier(AdjRibOut.QNAME);
66 private static final NodeIdentifier EFFRIBIN = new NodeIdentifier(EffectiveRibIn.QNAME);
67 private static final NodeIdentifier PEER_ID = new NodeIdentifier(PEER_ID_QNAME);
68 private static final NodeIdentifier PEER_ROLE = new NodeIdentifier(PEER_ROLE_QNAME);
69 private static final NodeIdentifier TABLES = new NodeIdentifier(Tables.QNAME);
71 // FIXME: is there a utility method to construct this?
72 private static final ContainerNode EMPTY_ADJRIBIN = Builders.containerBuilder().withNodeIdentifier(ADJRIBIN).addChild(ImmutableNodes.mapNodeBuilder(Tables.QNAME).build()).build();
73 private static final ContainerNode EMPTY_EFFRIBIN = Builders.containerBuilder().withNodeIdentifier(EFFRIBIN).addChild(ImmutableNodes.mapNodeBuilder(Tables.QNAME).build()).build();
74 private static final ContainerNode EMPTY_ADJRIBOUT = Builders.containerBuilder().withNodeIdentifier(ADJRIBOUT).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 PeerId peerId;
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 PeerId peerId, 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;
95 // We could use a codec, but this should be fine, too
96 private static String roleString(final PeerRole role) {
105 throw new IllegalArgumentException("Unhandled role " + role);
110 * Create a new writer using a transaction chain.
112 * @param role peer's role
113 * @param chain transaction chain
114 * @return A fresh writer instance
116 static AdjRibInWriter create(@Nonnull final YangInstanceIdentifier ribId, @Nonnull final PeerRole role, @Nonnull final DOMTransactionChain chain) {
117 return new AdjRibInWriter(ribId, chain, null, roleString(role), null, Collections.<TablesKey, TableContext>emptyMap());
121 * Transform this writer to a new writer, which is in charge of specified tables.
122 * Empty tables are created for new entries and old tables are deleted. Once this
123 * method returns, the old instance must not be reasonably used.
125 * @param newPeerId new peer BGP identifier
126 * @param registry RIB extension registry
127 * @param tableTypes New tables, must not be null
130 AdjRibInWriter transform(final PeerId newPeerId, final RIBSupportContextRegistry registry, final Set<TablesKey> tableTypes) {
131 final DOMDataWriteTransaction tx = this.chain.newWriteOnlyTransaction();
133 final YangInstanceIdentifier newTablesRoot;
134 if (!newPeerId.equals(this.peerId)) {
135 if (this.peerId != null) {
136 // Wipe old peer data completely
137 tx.delete(LogicalDatastoreType.OPERATIONAL, this.ribPath.node(Peer.QNAME).node(new NodeIdentifierWithPredicates(Peer.QNAME, PEER_ID_QNAME, this.peerId.getValue())));
140 // Install new empty peer structure
141 final NodeIdentifierWithPredicates peerKey = new NodeIdentifierWithPredicates(Peer.QNAME, PEER_ID_QNAME, newPeerId.getValue());
142 final YangInstanceIdentifier newPeerPath = this.ribPath.node(Peer.QNAME).node(peerKey);
144 final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> pb = Builders.mapEntryBuilder();
145 pb.withNodeIdentifier(peerKey);
146 pb.withChild(ImmutableNodes.leafNode(PEER_ID, newPeerId.getValue()));
147 pb.withChild(ImmutableNodes.leafNode(PEER_ROLE, this.role));
148 pb.withChild(EMPTY_ADJRIBIN);
149 pb.withChild(EMPTY_EFFRIBIN);
150 pb.withChild(EMPTY_ADJRIBOUT);
152 tx.put(LogicalDatastoreType.OPERATIONAL, newPeerPath, pb.build());
153 LOG.debug("New peer {} structure installed.", newPeerPath);
155 newTablesRoot = newPeerPath.node(EMPTY_ADJRIBIN.getIdentifier()).node(TABLES);
157 newTablesRoot = this.tablesRoot;
159 // Wipe tables which are not present in the new types
160 for (final Entry<TablesKey, TableContext> e : this.tables.entrySet()) {
161 if (!tableTypes.contains(e.getKey())) {
162 e.getValue().removeTable(tx);
167 // Now create new table instances, potentially creating their empty entries
168 final Builder<TablesKey, TableContext> tb = ImmutableMap.builder();
169 for (final TablesKey k : tableTypes) {
170 TableContext ctx = this.tables.get(k);
172 final RIBSupportContext rs = registry.getRIBSupportContext(k);
174 LOG.warn("No support for table type {}, skipping it", k);
178 // We will use table keys very often, make sure they are optimized
179 final InstanceIdentifierBuilder idb = YangInstanceIdentifier.builder(newTablesRoot);
181 // TODO: Use returned value once Instance Identifier builder allows for it.
182 final NodeIdentifierWithPredicates key = RibSupportUtils.toYangTablesKey(k);
183 idb.nodeWithKey(key.getNodeType(), key.getKeyValues());
184 ctx = new TableContext(rs, idb.build());
187 tx.merge(LogicalDatastoreType.OPERATIONAL, ctx.getTableId().node(Attributes.QNAME).node(ATTRIBUTES_UPTODATE_FALSE.getNodeType()), ATTRIBUTES_UPTODATE_FALSE);
189 LOG.debug("Created table instance {}", ctx.getTableId());
195 return new AdjRibInWriter(this.ribPath, this.chain, newPeerId, this.role, newTablesRoot, tb.build());
199 * Clean all routes in specified tables
201 * @param tableTypes Tables to clean.
203 void cleanTables(final Collection<TablesKey> tableTypes) {
204 final DOMDataWriteTransaction tx = this.chain.newWriteOnlyTransaction();
206 for (final TablesKey k : tableTypes) {
207 LOG.debug("Clearing table {}", k);
208 this.tables.get(k).clearTable(tx);
214 void markTablesUptodate(final Collection<TablesKey> tableTypes) {
215 final DOMDataWriteTransaction tx = this.chain.newWriteOnlyTransaction();
217 for (final TablesKey k : tableTypes) {
218 final TableContext ctx = this.tables.get(k);
219 tx.merge(LogicalDatastoreType.OPERATIONAL, ctx.getTableId().node(Attributes.QNAME).node(ATTRIBUTES_UPTODATE_TRUE.getNodeType()), ATTRIBUTES_UPTODATE_TRUE);
225 void updateRoutes(final MpReachNlri nlri, final PathAttributes attributes) {
226 final TablesKey key = new TablesKey(nlri.getAfi(), nlri.getSafi());
227 final TableContext ctx = this.tables.get(key);
229 LOG.debug("No table for {}, not accepting NLRI {}", key, nlri);
233 final DOMDataWriteTransaction tx = this.chain.newWriteOnlyTransaction();
234 ctx.writeRoutes(tx, nlri, attributes);
235 LOG.trace("Write routes {}", nlri);
239 void removeRoutes(final MpUnreachNlri nlri) {
240 final TablesKey key = new TablesKey(nlri.getAfi(), nlri.getSafi());
241 final TableContext ctx = this.tables.get(key);
243 LOG.debug("No table for {}, not accepting NLRI {}", key, nlri);
246 LOG.trace("Removing routes {}", nlri);
247 final DOMDataWriteTransaction tx = this.chain.newWriteOnlyTransaction();
248 ctx.removeRoutes(tx, nlri);