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 java.util.Comparator;
11 import java.util.HashMap;
12 import java.util.Iterator;
15 import javax.annotation.concurrent.GuardedBy;
16 import javax.annotation.concurrent.ThreadSafe;
18 import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
19 import org.opendaylight.protocol.bgp.rib.RibReference;
20 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.PathAttributes;
21 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.Update;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.UpdateBuilder;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.update.PathAttributesBuilder;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.PathAttributes1;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.PathAttributes1Builder;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.path.attributes.MpReachNlriBuilder;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.LocRib;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.Tables;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.TablesBuilder;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.TablesKey;
31 import org.opendaylight.yangtools.yang.binding.DataObject;
32 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
33 import org.slf4j.Logger;
34 import org.slf4j.LoggerFactory;
36 import com.google.common.base.Preconditions;
39 public abstract class AbstractAdjRIBsIn<I, D extends DataObject> implements AdjRIBsIn {
40 protected abstract static class RIBEntryData<I, D extends DataObject> {
41 private final PathAttributes attributes;
43 protected RIBEntryData(final PathAttributes attributes) {
44 this.attributes = Preconditions.checkNotNull(attributes);
47 public PathAttributes getPathAttributes() {
48 return this.attributes;
51 protected abstract D getDataObject(I key);
55 * A single RIB table entry, which holds multiple versions of the entry's state and elects the authoritative based
56 * on ordering specified by the supplied comparator.
59 private final class RIBEntry {
61 * TODO: we could dramatically optimize performance by using the comparator
62 * to retain the candidate states ordered -- thus selection would occur
63 * automatically through insertion, without the need of a second walk.
65 private final Map<Peer, RIBEntryData<I, D>> candidates = new HashMap<>();
69 private InstanceIdentifier<?> name;
71 private RIBEntryData<I, D> currentState;
73 RIBEntry(final I key) {
74 this.key = Preconditions.checkNotNull(key);
77 private InstanceIdentifier<?> getName() {
78 if (this.name == null) {
79 this.name = identifierForKey(AbstractAdjRIBsIn.this.basePath, this.key);
84 private RIBEntryData<I, D> findCandidate(final RIBEntryData<I, D> initial) {
85 RIBEntryData<I, D> newState = initial;
86 for (final RIBEntryData<I, D> s : this.candidates.values()) {
87 if (newState == null || AbstractAdjRIBsIn.this.comparator.compare(newState.attributes, s.attributes) > 0) {
95 private void electCandidate(final DataModificationTransaction transaction, final RIBEntryData<I, D> candidate) {
96 LOG.trace("Electing state {} to supersede {}", candidate, this.currentState);
98 if (this.currentState == null || !this.currentState.equals(candidate)) {
99 transaction.putOperationalData(getName(), candidate.getDataObject(this.key));
100 this.currentState = candidate;
104 synchronized boolean removeState(final DataModificationTransaction transaction, final Peer peer) {
105 final RIBEntryData<I, D> data = this.candidates.remove(peer);
106 LOG.trace("Removed data {}", data);
108 final RIBEntryData<I, D> candidate = findCandidate(null);
109 if (candidate != null) {
110 electCandidate(transaction, candidate);
112 LOG.trace("Final candidate disappeared, removing entry {}", this.name);
113 transaction.removeOperationalData(this.name);
116 return this.candidates.isEmpty();
119 synchronized void setState(final DataModificationTransaction transaction, final Peer peer, final RIBEntryData<I, D> state) {
120 this.candidates.put(Preconditions.checkNotNull(peer), Preconditions.checkNotNull(state));
121 electCandidate(transaction, findCandidate(state));
125 private static final Logger LOG = LoggerFactory.getLogger(AbstractAdjRIBsIn.class);
126 private final Comparator<PathAttributes> comparator;
127 private final InstanceIdentifier<Tables> basePath;
128 private final Update eor;
131 private final Map<I, RIBEntry> entries = new HashMap<>();
134 private final Map<Peer, Boolean> peers = new HashMap<>();
136 protected AbstractAdjRIBsIn(final DataModificationTransaction trans, final RibReference rib, final Comparator<PathAttributes> comparator, final TablesKey key) {
137 this.comparator = Preconditions.checkNotNull(comparator);
138 this.basePath = InstanceIdentifier.builder(rib.getInstanceIdentifier()).child(LocRib.class).child(Tables.class, key).toInstance();
140 this.eor = new UpdateBuilder().setPathAttributes(
141 new PathAttributesBuilder().addAugmentation(
142 PathAttributes1.class,
143 new PathAttributes1Builder().setMpReachNlri(
144 new MpReachNlriBuilder().setAfi(key.getAfi()).setSafi(key.getSafi()).build()).build()).build()).build();
146 trans.putOperationalData(this.basePath,
147 new TablesBuilder().setAfi(key.getAfi()).setSafi(key.getSafi()).setUptodate(Boolean.FALSE).build());
150 private void setUptodate(final DataModificationTransaction trans, final Boolean uptodate) {
151 final Tables t = (Tables) trans.readOperationalData(this.basePath);
152 if (t == null || !uptodate.equals(t.isUptodate())) {
153 LOG.debug("Table {} switching uptodate to {}", uptodate);
154 trans.putOperationalData(this.basePath, new TablesBuilder(t).setUptodate(uptodate).build());
159 public synchronized void clear(final DataModificationTransaction trans, final Peer peer) {
160 final Iterator<Map.Entry<I, RIBEntry>> i = this.entries.entrySet().iterator();
161 while (i.hasNext()) {
162 final Map.Entry<I, RIBEntry> e = i.next();
164 if (e.getValue().removeState(trans, peer)) {
169 this.peers.remove(peer);
170 setUptodate(trans, !this.peers.values().contains(Boolean.FALSE));
173 protected abstract InstanceIdentifier<?> identifierForKey(final InstanceIdentifier<Tables> basePath, final I id);
175 protected synchronized void add(final DataModificationTransaction trans, final Peer peer, final I id, final RIBEntryData<I, D> data) {
176 RIBEntry e = this.entries.get(Preconditions.checkNotNull(id));
178 e = new RIBEntry(id);
179 this.entries.put(id, e);
182 e.setState(trans, peer, data);
183 if (!this.peers.containsKey(peer)) {
184 this.peers.put(peer, Boolean.FALSE);
185 setUptodate(trans, Boolean.FALSE);
189 protected synchronized void remove(final DataModificationTransaction trans, final Peer peer, final I id) {
190 final RIBEntry e = this.entries.get(id);
191 if (e != null && e.removeState(trans, peer)) {
192 LOG.debug("Removed last state, removing entry for {}", id);
193 this.entries.remove(id);
198 public final void markUptodate(final DataModificationTransaction trans, final Peer peer) {
199 this.peers.put(peer, Boolean.TRUE);
200 setUptodate(trans, !this.peers.values().contains(Boolean.FALSE));
204 public final Update endOfRib() {