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.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.PathAttributes;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.path.attributes.MpReachNlri;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.path.attributes.MpUnreachNlri;
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.EffectiveRibIn;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.Tables;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.TablesKey;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.tables.Attributes;
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 EFFRIBIN = new NodeIdentifier(EffectiveRibIn.QNAME);
65 private static final NodeIdentifier PEER_ID = new NodeIdentifier(PEER_ID_QNAME);
66 private static final NodeIdentifier PEER_ROLE = new NodeIdentifier(PEER_ROLE_QNAME);
67 private static final NodeIdentifier TABLES = new NodeIdentifier(Tables.QNAME);
69 // FIXME: is there a utility method to construct this?
70 private static final ContainerNode EMPTY_ADJRIBIN = Builders.containerBuilder().withNodeIdentifier(ADJRIBIN).addChild(ImmutableNodes.mapNodeBuilder(Tables.QNAME).build()).build();
71 private static final ContainerNode EMPTY_EFFRIBIN = Builders.containerBuilder().withNodeIdentifier(EFFRIBIN).addChild(ImmutableNodes.mapNodeBuilder(Tables.QNAME).build()).build();
73 private final Map<TablesKey, TableContext> tables;
74 private final YangInstanceIdentifier tablesRoot;
75 private final YangInstanceIdentifier ribPath;
76 private final DOMTransactionChain chain;
77 private final Ipv4Address peerId = null;
78 private final String role;
81 * FIXME: transaction chain has to be instantiated in caller, so it can terminate us when it fails.
83 private AdjRibInWriter(final YangInstanceIdentifier ribPath, final DOMTransactionChain chain, final String role, final YangInstanceIdentifier tablesRoot, final Map<TablesKey, TableContext> tables) {
84 this.ribPath = Preconditions.checkNotNull(ribPath);
85 this.chain = Preconditions.checkNotNull(chain);
86 this.tables = Preconditions.checkNotNull(tables);
87 this.role = Preconditions.checkNotNull(role);
88 this.tablesRoot = tablesRoot;
91 // We could use a codec, but this should be fine, too
92 private static String roleString(final PeerRole role) {
101 throw new IllegalArgumentException("Unhandled role " + role);
106 * Create a new writer using a transaction chain.
108 * @param role peer's role
109 * @param chain transaction chain
110 * @return A fresh writer instance
112 static AdjRibInWriter create(@Nonnull final YangInstanceIdentifier ribId, @Nonnull final PeerRole role, @Nonnull final DOMTransactionChain chain) {
113 return new AdjRibInWriter(ribId, chain, roleString(role), null, Collections.<TablesKey, TableContext>emptyMap());
117 * Transform this writer to a new writer, which is in charge of specified tables.
118 * Empty tables are created for new entries and old tables are deleted. Once this
119 * method returns, the old instance must not be reasonably used.
121 * @param newPeerId new peer BGP identifier
122 * @param registry RIB extension registry
123 * @param tableTypes New tables, must not be null
126 AdjRibInWriter transform(final Ipv4Address newPeerId, final RIBSupportContextRegistry registry, final Set<TablesKey> tableTypes) {
127 final DOMDataWriteTransaction tx = this.chain.newWriteOnlyTransaction();
129 final YangInstanceIdentifier newTablesRoot;
130 if (!newPeerId.equals(this.peerId)) {
131 if (this.peerId != null) {
132 // Wipe old peer data completely
133 tx.delete(LogicalDatastoreType.OPERATIONAL, this.ribPath.node(Peer.QNAME).node(new NodeIdentifierWithPredicates(Peer.QNAME, PEER_ID_QNAME, this.peerId.getValue())));
136 // Install new empty peer structure
137 final NodeIdentifierWithPredicates peerKey = new NodeIdentifierWithPredicates(Peer.QNAME, PEER_ID_QNAME, newPeerId.getValue());
138 final YangInstanceIdentifier newPeerPath = this.ribPath.node(Peer.QNAME).node(peerKey);
140 final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> pb = Builders.mapEntryBuilder();
141 pb.withNodeIdentifier(peerKey);
142 pb.withChild(ImmutableNodes.leafNode(PEER_ID, newPeerId.getValue()));
143 pb.withChild(ImmutableNodes.leafNode(PEER_ROLE, this.role));
144 pb.withChild(EMPTY_ADJRIBIN);
145 pb.withChild(EMPTY_EFFRIBIN);
147 tx.put(LogicalDatastoreType.OPERATIONAL, newPeerPath, pb.build());
148 LOG.debug("New peer {} structure installed.", newPeerPath);
150 newTablesRoot = newPeerPath.node(EMPTY_ADJRIBIN.getIdentifier()).node(TABLES);
152 newTablesRoot = this.tablesRoot;
154 // Wipe tables which are not present in the new types
155 for (final Entry<TablesKey, TableContext> e : this.tables.entrySet()) {
156 if (!tableTypes.contains(e.getKey())) {
157 e.getValue().removeTable(tx);
162 // Now create new table instances, potentially creating their empty entries
163 final Builder<TablesKey, TableContext> tb = ImmutableMap.builder();
164 for (final TablesKey k : tableTypes) {
165 TableContext ctx = this.tables.get(k);
167 final RIBSupportContext rs = registry.getRIBSupportContext(k);
169 LOG.warn("No support for table type {}, skipping it", k);
173 // We will use table keys very often, make sure they are optimized
174 final InstanceIdentifierBuilder idb = YangInstanceIdentifier.builder(newTablesRoot);
176 // TODO: Use returned value once Instance Identifier builder allows for it.
177 final NodeIdentifierWithPredicates key = RibSupportUtils.toYangTablesKey(k);
178 idb.nodeWithKey(key.getNodeType(), key.getKeyValues());
179 ctx = new TableContext(rs, idb.build());
182 tx.merge(LogicalDatastoreType.OPERATIONAL, ctx.getTableId().node(Attributes.QNAME).node(ATTRIBUTES_UPTODATE_FALSE.getNodeType()), ATTRIBUTES_UPTODATE_FALSE);
184 LOG.debug("Created table instance {}", ctx.getTableId());
190 return new AdjRibInWriter(this.ribPath, this.chain, this.role, newTablesRoot, tb.build());
194 * Clean all routes in specified tables
196 * @param tableTypes Tables to clean.
198 void cleanTables(final Collection<TablesKey> tableTypes) {
199 final DOMDataWriteTransaction tx = this.chain.newWriteOnlyTransaction();
201 for (final TablesKey k : tableTypes) {
202 LOG.debug("Clearing table {}", k);
203 this.tables.get(k).clearTable(tx);
209 void markTablesUptodate(final Collection<TablesKey> tableTypes) {
210 final DOMDataWriteTransaction tx = this.chain.newWriteOnlyTransaction();
212 for (final TablesKey k : tableTypes) {
213 final TableContext ctx = this.tables.get(k);
214 tx.merge(LogicalDatastoreType.OPERATIONAL, ctx.getTableId().node(Attributes.QNAME).node(ATTRIBUTES_UPTODATE_TRUE.getNodeType()), ATTRIBUTES_UPTODATE_TRUE);
220 void updateRoutes(final MpReachNlri nlri, final PathAttributes attributes) {
221 final TablesKey key = new TablesKey(nlri.getAfi(), nlri.getSafi());
222 final TableContext ctx = this.tables.get(key);
224 LOG.debug("No table for {}, not accepting NLRI {}", key, nlri);
228 final DOMDataWriteTransaction tx = this.chain.newWriteOnlyTransaction();
229 ctx.writeRoutes(tx, nlri, attributes);
230 LOG.trace("Write routes {}", nlri);
234 void removeRoutes(final MpUnreachNlri nlri) {
235 final TablesKey key = new TablesKey(nlri.getAfi(), nlri.getSafi());
236 final TableContext ctx = this.tables.get(key);
238 LOG.debug("No table for {}, not accepting NLRI {}", key, nlri);
241 LOG.trace("Removing routes {}", nlri);
242 final DOMDataWriteTransaction tx = this.chain.newWriteOnlyTransaction();
243 ctx.removeRoutes(tx, nlri);