bafc4875087c95746a58e32314ef302b212c343e
[bgpcep.git] / bgp / rib-spi / src / main / java / org / opendaylight / protocol / bgp / rib / spi / AbstractAdjRIBsIn.java
1 /*
2  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.protocol.bgp.rib.spi;
9
10 import java.util.Comparator;
11 import java.util.HashMap;
12 import java.util.Iterator;
13 import java.util.Map;
14
15 import javax.annotation.concurrent.GuardedBy;
16 import javax.annotation.concurrent.ThreadSafe;
17
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;
35
36 import com.google.common.base.Objects;
37 import com.google.common.base.Objects.ToStringHelper;
38 import com.google.common.base.Preconditions;
39
40 @ThreadSafe
41 public abstract class AbstractAdjRIBsIn<I, D extends DataObject> implements AdjRIBsIn {
42         protected abstract static class RIBEntryData<I, D extends DataObject> {
43                 private final PathAttributes attributes;
44
45                 protected RIBEntryData(final PathAttributes attributes) {
46                         this.attributes = Preconditions.checkNotNull(attributes);
47                 }
48
49                 public PathAttributes getPathAttributes() {
50                         return this.attributes;
51                 }
52
53                 protected abstract D getDataObject(I key, InstanceIdentifier<D> id);
54
55                 @Override
56                 public final String toString() {
57                         return addToStringAttributes(Objects.toStringHelper(this)).toString();
58                 }
59
60                 protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
61                         return toStringHelper.add("attributes", attributes);
62                 }
63         }
64
65         /**
66          * A single RIB table entry, which holds multiple versions of the entry's state and elects the authoritative based
67          * on ordering specified by the supplied comparator.
68          *
69          */
70         private final class RIBEntry {
71                 /*
72                  * TODO: we could dramatically optimize performance by using the comparator
73                  *       to retain the candidate states ordered -- thus selection would occur
74                  *       automatically through insertion, without the need of a second walk.
75                  */
76                 private final Map<Peer, RIBEntryData<I, D>> candidates = new HashMap<>();
77                 private final I key;
78
79                 @GuardedBy("this")
80                 private InstanceIdentifier<D> name;
81                 @GuardedBy("this")
82                 private RIBEntryData<I, D> currentState;
83
84                 RIBEntry(final I key) {
85                         this.key = Preconditions.checkNotNull(key);
86                 }
87
88                 private InstanceIdentifier<D> getName() {
89                         if (this.name == null) {
90                                 this.name = identifierForKey(AbstractAdjRIBsIn.this.basePath, this.key);
91                                 LOG.trace("Entry {} grew key {}", this, this.name);
92                         }
93                         return this.name;
94                 }
95
96                 private RIBEntryData<I, D> findCandidate(final RIBEntryData<I, D> initial, final Comparator<PathAttributes> comparator) {
97                         RIBEntryData<I, D> newState = initial;
98                         for (final RIBEntryData<I, D> s : this.candidates.values()) {
99                                 if (newState == null || comparator.compare(newState.attributes, s.attributes) > 0) {
100                                         newState = s;
101                                 }
102                         }
103
104                         return newState;
105                 }
106
107                 private void electCandidate(final DataModificationTransaction transaction, final RIBEntryData<I, D> candidate) {
108                         LOG.trace("Electing state {} to supersede {}", candidate, this.currentState);
109
110                         if (this.currentState == null || !this.currentState.equals(candidate)) {
111                                 LOG.trace("Elected new state for {}: {}", getName(), candidate);
112                                 transaction.putOperationalData(getName(), candidate.getDataObject(this.key, getName()));
113                                 this.currentState = candidate;
114                         }
115                 }
116
117                 synchronized boolean removeState(final DataModificationTransaction transaction, final Peer peer) {
118                         final RIBEntryData<I, D> data = this.candidates.remove(peer);
119                         LOG.trace("Removed data {}", data);
120
121                         final RIBEntryData<I, D> candidate = findCandidate(null, peer.getComparator());
122                         if (candidate != null) {
123                                 electCandidate(transaction, candidate);
124                         } else {
125                                 LOG.trace("Final candidate disappeared, removing entry {}", getName());
126                                 transaction.removeOperationalData(getName());
127                         }
128
129                         return this.candidates.isEmpty();
130                 }
131
132                 synchronized void setState(final DataModificationTransaction transaction, final Peer peer, final RIBEntryData<I, D> state) {
133                         this.candidates.put(Preconditions.checkNotNull(peer), Preconditions.checkNotNull(state));
134                         electCandidate(transaction, findCandidate(state, peer.getComparator()));
135                 }
136         }
137
138         private static final Logger LOG = LoggerFactory.getLogger(AbstractAdjRIBsIn.class);
139         private final InstanceIdentifier<Tables> basePath;
140         private final Update eor;
141
142         @GuardedBy("this")
143         private final Map<I, RIBEntry> entries = new HashMap<>();
144
145         @GuardedBy("this")
146         private final Map<Peer, Boolean> peers = new HashMap<>();
147
148         protected AbstractAdjRIBsIn(final DataModificationTransaction trans, final RibReference rib, final TablesKey key) {
149                 this.basePath = InstanceIdentifier.builder(rib.getInstanceIdentifier()).child(LocRib.class).child(Tables.class, key).toInstance();
150
151                 this.eor = new UpdateBuilder().setPathAttributes(
152                                 new PathAttributesBuilder().addAugmentation(
153                                                 PathAttributes1.class,
154                                                 new PathAttributes1Builder().setMpReachNlri(
155                                                                 new MpReachNlriBuilder().setAfi(key.getAfi()).setSafi(key.getSafi()).build()).build()).build()).build();
156
157                 trans.putOperationalData(this.basePath,
158                                 new TablesBuilder().setAfi(key.getAfi()).setSafi(key.getSafi()).setUptodate(Boolean.FALSE).build());
159         }
160
161         private void setUptodate(final DataModificationTransaction trans, final Boolean uptodate) {
162                 final Tables t = (Tables) trans.readOperationalData(this.basePath);
163                 Preconditions.checkState(t != null);
164                 if (!uptodate.equals(t.isUptodate())) {
165                         LOG.debug("Table {} switching uptodate to {}", t, uptodate);
166                         trans.putOperationalData(this.basePath, new TablesBuilder(t).setRoutes(t.getRoutes()).setUptodate(uptodate).build());
167                 }
168         }
169
170         @Override
171         public synchronized void clear(final DataModificationTransaction trans, final Peer peer) {
172                 final Iterator<Map.Entry<I, RIBEntry>> i = this.entries.entrySet().iterator();
173                 while (i.hasNext()) {
174                         final Map.Entry<I, RIBEntry> e = i.next();
175
176                         if (e.getValue().removeState(trans, peer)) {
177                                 i.remove();
178                         }
179                 }
180
181                 this.peers.remove(peer);
182                 setUptodate(trans, !this.peers.values().contains(Boolean.FALSE));
183         }
184
185         protected abstract InstanceIdentifier<D> identifierForKey(final InstanceIdentifier<Tables> basePath, final I id);
186
187         protected synchronized void add(final DataModificationTransaction trans, final Peer peer, final I id, final RIBEntryData<I, D> data) {
188                 LOG.debug("Adding state {} for {} peer {}", data, id, peer);
189
190                 RIBEntry e = this.entries.get(Preconditions.checkNotNull(id));
191                 if (e == null) {
192                         e = new RIBEntry(id);
193                         this.entries.put(id, e);
194                 }
195
196                 e.setState(trans, peer, data);
197                 if (!this.peers.containsKey(peer)) {
198                         this.peers.put(peer, Boolean.FALSE);
199                         setUptodate(trans, Boolean.FALSE);
200                 }
201         }
202
203         protected synchronized void remove(final DataModificationTransaction trans, final Peer peer, final I id) {
204                 final RIBEntry e = this.entries.get(id);
205                 if (e != null && e.removeState(trans, peer)) {
206                         LOG.debug("Removed last state, removing entry for {}", id);
207                         this.entries.remove(id);
208                 }
209         }
210
211         @Override
212         public final void markUptodate(final DataModificationTransaction trans, final Peer peer) {
213                 this.peers.put(peer, Boolean.TRUE);
214                 setUptodate(trans, !this.peers.values().contains(Boolean.FALSE));
215         }
216
217         @Override
218         public final Update endOfRib() {
219                 return this.eor;
220         }
221 }