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) {
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 = new NodeIdentifierWithPredicates(Peer.QNAME, PEER_ID_QNAME, newPeerId.getValue());
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);
149 pb.withChild(EMPTY_ADJRIBOUT);
151 tx.put(LogicalDatastoreType.OPERATIONAL, newPeerPath, pb.build());
152 LOG.debug("New peer {} structure installed.", newPeerPath);
154 newTablesRoot = newPeerPath.node(EMPTY_ADJRIBIN.getIdentifier()).node(TABLES);
156 newTablesRoot = this.tablesRoot;
158 // Wipe tables which are not present in the new types
159 for (final Entry<TablesKey, TableContext> e : this.tables.entrySet()) {
160 if (!tableTypes.contains(e.getKey())) {
161 e.getValue().removeTable(tx);
166 // Now create new table instances, potentially creating their empty entries
167 final Builder<TablesKey, TableContext> tb = ImmutableMap.builder();
168 for (final TablesKey k : tableTypes) {
169 TableContext ctx = this.tables.get(k);
171 final RIBSupportContext rs = registry.getRIBSupportContext(k);
173 LOG.warn("No support for table type {}, skipping it", k);
177 // We will use table keys very often, make sure they are optimized
178 final InstanceIdentifierBuilder idb = YangInstanceIdentifier.builder(newTablesRoot);
180 // TODO: Use returned value once Instance Identifier builder allows for it.
181 final NodeIdentifierWithPredicates key = RibSupportUtils.toYangTablesKey(k);
182 idb.nodeWithKey(key.getNodeType(), key.getKeyValues());
183 ctx = new TableContext(rs, idb.build());
186 tx.merge(LogicalDatastoreType.OPERATIONAL, ctx.getTableId().node(Attributes.QNAME).node(ATTRIBUTES_UPTODATE_FALSE.getNodeType()), ATTRIBUTES_UPTODATE_FALSE);
188 LOG.debug("Created table instance {}", ctx.getTableId());
194 return new AdjRibInWriter(this.ribPath, this.chain, newPeerId, this.role, newTablesRoot, tb.build());
198 * Clean all routes in specified tables
200 * @param tableTypes Tables to clean.
202 void cleanTables(final Collection<TablesKey> tableTypes) {
203 final DOMDataWriteTransaction tx = this.chain.newWriteOnlyTransaction();
205 for (final TablesKey k : tableTypes) {
206 LOG.debug("Clearing table {}", k);
207 this.tables.get(k).clearTable(tx);
213 void markTablesUptodate(final Collection<TablesKey> tableTypes) {
214 final DOMDataWriteTransaction tx = this.chain.newWriteOnlyTransaction();
216 for (final TablesKey k : tableTypes) {
217 final TableContext ctx = this.tables.get(k);
218 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);
224 void updateRoutes(final MpReachNlri nlri, final Attributes attributes) {
225 final TablesKey key = new TablesKey(nlri.getAfi(), nlri.getSafi());
226 final TableContext ctx = this.tables.get(key);
228 LOG.debug("No table for {}, not accepting NLRI {}", key, nlri);
232 final DOMDataWriteTransaction tx = this.chain.newWriteOnlyTransaction();
233 ctx.writeRoutes(tx, nlri, attributes);
234 LOG.trace("Write routes {}", nlri);
238 void removeRoutes(final MpUnreachNlri nlri) {
239 final TablesKey key = new TablesKey(nlri.getAfi(), nlri.getSafi());
240 final TableContext ctx = this.tables.get(key);
242 LOG.debug("No table for {}, not accepting NLRI {}", key, nlri);
245 LOG.trace("Removing routes {}", nlri);
246 final DOMDataWriteTransaction tx = this.chain.newWriteOnlyTransaction();
247 ctx.removeRoutes(tx, nlri);