2 * Copyright (c) 2013 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.spi;
10 import com.google.common.base.Objects;
11 import com.google.common.base.Objects.ToStringHelper;
12 import com.google.common.base.Preconditions;
14 import java.util.HashMap;
15 import java.util.Iterator;
18 import javax.annotation.concurrent.GuardedBy;
19 import javax.annotation.concurrent.ThreadSafe;
21 import org.opendaylight.protocol.bgp.parser.BgpTableTypeImpl;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.PathAttributes;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.Update;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.UpdateBuilder;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.update.PathAttributesBuilder;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.BgpTableType;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.PathAttributes1;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.PathAttributes1Builder;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.PathAttributes2;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.PathAttributes2Builder;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.path.attributes.MpReachNlriBuilder;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.path.attributes.MpUnreachNlriBuilder;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.Route;
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.yangtools.yang.binding.Identifiable;
37 import org.opendaylight.yangtools.yang.binding.Identifier;
38 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
39 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
44 public abstract class AbstractAdjRIBs<I, D extends Identifiable<K> & Route, K extends Identifier<D>> implements AdjRIBsIn<I, D>, RouteEncoder {
45 protected abstract static class RIBEntryData<I, D extends Identifiable<K> & Route, K extends Identifier<D>> {
46 private final PathAttributes attributes;
47 private final Peer peer;
49 protected RIBEntryData(final Peer peer, final PathAttributes attributes) {
50 this.attributes = Preconditions.checkNotNull(attributes);
51 this.peer = Preconditions.checkNotNull(peer);
54 public PathAttributes getPathAttributes() {
55 return this.attributes;
58 public Peer getPeer() {
63 * Create a data object given the key and target instance identifier.
65 * @param key Route key
66 * @param id Data store target identifier
67 * @return Data object to be written to the data store.
69 protected abstract D getDataObject(I key, K id);
72 public final String toString() {
73 return addToStringAttributes(Objects.toStringHelper(this)).toString();
76 protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
77 return toStringHelper.add("attributes", this.attributes);
82 * A single RIB table entry, which holds multiple versions of the entry's state and elects the authoritative based
83 * on ordering specified by the supplied comparator.
86 private final class RIBEntry {
88 * TODO: we could dramatically optimize performance by using the comparator
89 * to retain the candidate states ordered -- thus selection would occur
90 * automatically through insertion, without the need of a second walk.
92 private final Map<Peer, RIBEntryData<I, D, K>> candidates = new HashMap<>();
96 private KeyedInstanceIdentifier<D, K> name;
98 private RIBEntryData<I, D, K> currentState;
100 RIBEntry(final I key) {
101 this.key = Preconditions.checkNotNull(key);
104 private KeyedInstanceIdentifier<D, K> getName() {
105 if (this.name == null) {
106 this.name = identifierForKey(AbstractAdjRIBs.this.basePath, this.key);
107 LOG.trace("Entry {} grew key {}", this, this.name);
112 private RIBEntryData<I, D, K> findCandidate(final BGPObjectComparator comparator, final RIBEntryData<I, D, K> initial) {
113 RIBEntryData<I, D, K> newState = initial;
114 for (final RIBEntryData<I, D, K> s : this.candidates.values()) {
115 if (newState == null || comparator.compare(newState, s) > 0) {
123 private void electCandidate(final AdjRIBsTransaction transaction, final RIBEntryData<I, D, K> candidate) {
124 LOG.trace("Electing state {} to supersede {}", candidate, this.currentState);
126 if (this.currentState == null || !this.currentState.equals(candidate)) {
127 LOG.trace("Elected new state for {}: {}", getName(), candidate);
128 transaction.advertise(AbstractAdjRIBs.this, this.key, getName(), candidate.getPeer(), candidate.getDataObject(this.key, getName().getKey()));
129 this.currentState = candidate;
133 synchronized boolean removeState(final AdjRIBsTransaction transaction, final Peer peer) {
134 final RIBEntryData<I, D, K> data = this.candidates.remove(peer);
135 LOG.trace("Removed data {}", data);
137 final RIBEntryData<I, D, K> candidate = findCandidate(transaction.comparator(), null);
138 if (candidate != null) {
139 electCandidate(transaction, candidate);
141 LOG.trace("Final candidate disappeared, removing entry {}", getName());
142 transaction.withdraw(AbstractAdjRIBs.this, this.key, getName());
145 return this.candidates.isEmpty();
148 synchronized void setState(final AdjRIBsTransaction transaction, final Peer peer, final RIBEntryData<I, D, K> state) {
149 this.candidates.put(Preconditions.checkNotNull(peer), Preconditions.checkNotNull(state));
150 electCandidate(transaction, findCandidate(transaction.comparator(), state));
154 private static final Logger LOG = LoggerFactory.getLogger(AbstractAdjRIBs.class);
155 private final KeyedInstanceIdentifier<Tables, TablesKey> basePath;
156 private final BgpTableType tableType;
157 private final Update eor;
160 private final Map<I, RIBEntry> entries = new HashMap<>();
163 private final Map<Peer, Boolean> peers = new HashMap<>();
165 protected AbstractAdjRIBs(final KeyedInstanceIdentifier<Tables, TablesKey> basePath) {
166 this.basePath = Preconditions.checkNotNull(basePath);
167 this.tableType = new BgpTableTypeImpl(basePath.getKey().getAfi(), basePath.getKey().getSafi());
168 this.eor = new UpdateBuilder().setPathAttributes(new PathAttributesBuilder().addAugmentation(
169 PathAttributes1.class, new PathAttributes1Builder().setMpReachNlri(new MpReachNlriBuilder(this.tableType)
170 .build()).build()).build()).build();
175 public final synchronized void clear(final AdjRIBsTransaction trans, final Peer peer) {
176 final Iterator<Map.Entry<I, RIBEntry>> i = this.entries.entrySet().iterator();
177 while (i.hasNext()) {
178 final Map.Entry<I, RIBEntry> e = i.next();
180 if (e.getValue().removeState(trans, peer)) {
185 this.peers.remove(peer);
186 trans.setUptodate(basePath, !this.peers.values().contains(Boolean.FALSE));
190 * Construct a datastore identifier for an entry key.
192 * @param basePath datastore base path under which the entry to be stored
193 * @param id object identifier
194 * @return Data store identifier, may not be null
196 protected abstract KeyedInstanceIdentifier<D, K> identifierForKey(InstanceIdentifier<Tables> basePath, I id);
199 * Transform an advertised data object into the corresponding NLRI in MP_REACH attribute.
201 * @param data Data object
202 * @param builder MP_REACH attribute builder
204 protected abstract void addAdvertisement(MpReachNlriBuilder builder, D data);
207 * Transform a withdrawn identifier into a the corresponding NLRI in MP_UNREACH attribute.
209 * @param id Route key
211 protected abstract void addWithdrawal(MpUnreachNlriBuilder builder, I id);
214 * Common backend for {@link AdjRIBsIn#addRoutes()} implementations.
216 * @param trans Transaction context
217 * @param peer Originating peer
218 * @param id Data store instance identifier
219 * @param data Data object to be written
221 protected final synchronized void add(final AdjRIBsTransaction trans, final Peer peer, final I id, final RIBEntryData<I, D, K> data) {
222 LOG.debug("Adding state {} for {} peer {}", data, id, peer);
224 RIBEntry e = this.entries.get(Preconditions.checkNotNull(id));
226 e = new RIBEntry(id);
227 this.entries.put(id, e);
230 e.setState(trans, peer, data);
231 if (!this.peers.containsKey(peer)) {
232 this.peers.put(peer, Boolean.FALSE);
233 trans.setUptodate(this.basePath, Boolean.FALSE);
238 * Common backend for {@link AdjRIBsIn#removeRoutes()} implementations.
240 * @param trans Transaction context
241 * @param peer Originating peer
242 * @param id Data store instance identifier
244 protected final synchronized void remove(final AdjRIBsTransaction trans, final Peer peer, final I id) {
245 final RIBEntry e = this.entries.get(id);
246 if (e != null && e.removeState(trans, peer)) {
247 LOG.debug("Removed last state, removing entry for {}", id);
248 this.entries.remove(id);
253 public final void markUptodate(final AdjRIBsTransaction trans, final Peer peer) {
254 this.peers.put(peer, Boolean.TRUE);
255 trans.setUptodate(this.basePath, !this.peers.values().contains(Boolean.FALSE));
259 public final Update endOfRib() {
264 public Update updateMessageFor(final Object key, final Route route) {
265 final UpdateBuilder ub = new UpdateBuilder();
266 final PathAttributesBuilder pab = new PathAttributesBuilder();
269 final MpReachNlriBuilder reach = new MpReachNlriBuilder(this.tableType);
271 addAdvertisement(reach, (D)route);
272 pab.fieldsFrom(route.getAttributes());
273 pab.addAugmentation(PathAttributes1.class, new PathAttributes1Builder().setMpReachNlri(reach.build()).build()).build();
275 final MpUnreachNlriBuilder unreach = new MpUnreachNlriBuilder(tableType);
276 addWithdrawal(unreach, (I)key);
277 pab.addAugmentation(PathAttributes2.class, new PathAttributes2Builder().setMpUnreachNlri(unreach.build()).build()).build();
280 ub.setPathAttributes(pab.build());