d376bf242635fe1359ddde5ccdaba732c06a1deb
[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.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.PathAttributes;
20 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.Update;
21 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.UpdateBuilder;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.update.PathAttributesBuilder;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.PathAttributes1;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.PathAttributes1Builder;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.path.attributes.MpReachNlriBuilder;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.LocRib;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.Tables;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.TablesBuilder;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.TablesKey;
30 import org.opendaylight.yangtools.yang.binding.DataObject;
31 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
34
35 import com.google.common.base.Preconditions;
36
37 @ThreadSafe
38 public abstract class AbstractAdjRIBsIn<I, D extends DataObject> implements AdjRIBsIn {
39         protected abstract static class RIBEntryData<I, D extends DataObject> {
40                 private final PathAttributes attributes;
41
42                 protected RIBEntryData(final PathAttributes attributes) {
43                         this.attributes = Preconditions.checkNotNull(attributes);
44                 }
45
46                 public PathAttributes getPathAttributes() {
47                         return this.attributes;
48                 }
49
50                 protected abstract D getDataObject(I key);
51         }
52
53         /**
54          * A single RIB table entry, which holds multiple versions of the entry's state and elects the authoritative based
55          * on ordering specified by the supplied comparator.
56          * 
57          */
58         private final class RIBEntry {
59                 /*
60                  * TODO: we could dramatically optimize performance by using the comparator
61                  *       to retain the candidate states ordered -- thus selection would occur
62                  *       automatically through insertion, without the need of a second walk.
63                  */
64                 private final Map<Peer, RIBEntryData<I, D>> candidates = new HashMap<>();
65                 private final I key;
66
67                 @GuardedBy("this")
68                 private InstanceIdentifier<?> name;
69                 @GuardedBy("this")
70                 private RIBEntryData<I, D> currentState;
71
72                 RIBEntry(final I key) {
73                         this.key = Preconditions.checkNotNull(key);
74                 }
75
76                 private InstanceIdentifier<?> getName() {
77                         if (this.name == null) {
78                                 this.name = identifierForKey(AbstractAdjRIBsIn.this.basePath, this.key);
79                         }
80                         return this.name;
81                 }
82
83                 private RIBEntryData<I, D> findCandidate(final RIBEntryData<I, D> initial) {
84                         RIBEntryData<I, D> newState = initial;
85                         for (final RIBEntryData<I, D> s : this.candidates.values()) {
86                                 if (newState == null || AbstractAdjRIBsIn.this.comparator.compare(newState.attributes, s.attributes) > 0) {
87                                         newState = s;
88                                 }
89                         }
90
91                         return newState;
92                 }
93
94                 private void electCandidate(final DataModificationTransaction transaction, final RIBEntryData<I, D> candidate) {
95                         LOG.trace("Electing state {} to supersede {}", candidate, this.currentState);
96
97                         if (this.currentState == null || !this.currentState.equals(candidate)) {
98                                 transaction.putOperationalData(getName(), candidate.getDataObject(this.key));
99                                 this.currentState = candidate;
100                         }
101                 }
102
103                 synchronized boolean removeState(final DataModificationTransaction transaction, final Peer peer) {
104                         final RIBEntryData<I, D> data = this.candidates.remove(peer);
105                         LOG.trace("Removed data {}", data);
106
107                         final RIBEntryData<I, D> candidate = findCandidate(null);
108                         if (candidate != null) {
109                                 electCandidate(transaction, candidate);
110                         } else {
111                                 LOG.trace("Final candidate disappeared, removing entry {}", this.name);
112                                 transaction.removeOperationalData(this.name);
113                         }
114
115                         return this.candidates.isEmpty();
116                 }
117
118                 synchronized void setState(final DataModificationTransaction transaction, final Peer peer, final RIBEntryData<I, D> state) {
119                         this.candidates.put(Preconditions.checkNotNull(peer), Preconditions.checkNotNull(state));
120                         electCandidate(transaction, findCandidate(state));
121                 }
122         }
123
124         private static final Logger LOG = LoggerFactory.getLogger(AbstractAdjRIBsIn.class);
125         private final Comparator<PathAttributes> comparator;
126         private final InstanceIdentifier<Tables> basePath;
127         private final Update eor;
128
129         @GuardedBy("this")
130         private final Map<I, RIBEntry> entries = new HashMap<>();
131
132         @GuardedBy("this")
133         private final Map<Peer, Boolean> peers = new HashMap<>();
134
135         protected AbstractAdjRIBsIn(final DataModificationTransaction trans, final Comparator<PathAttributes> comparator, final TablesKey key) {
136                 this.comparator = Preconditions.checkNotNull(comparator);
137                 this.basePath = InstanceIdentifier.builder(LocRib.class).child(Tables.class, key).toInstance();
138
139                 this.eor = new UpdateBuilder().setPathAttributes(
140                                 new PathAttributesBuilder().addAugmentation(
141                                                 PathAttributes1.class,
142                                                 new PathAttributes1Builder().setMpReachNlri(
143                                                                 new MpReachNlriBuilder().setAfi(key.getAfi()).setSafi(key.getSafi()).build()).build()).build()).build();
144
145                 trans.putOperationalData(this.basePath,
146                                 new TablesBuilder().setAfi(key.getAfi()).setSafi(key.getSafi()).setUptodate(Boolean.FALSE).build());
147         }
148
149         private void setUptodate(final DataModificationTransaction trans, final Boolean uptodate) {
150                 final Tables t = (Tables) trans.readOperationalData(this.basePath);
151                 if (t == null || !uptodate.equals(t.isUptodate())) {
152                         LOG.debug("Table {} switching uptodate to {}", uptodate);
153                         trans.putOperationalData(this.basePath, new TablesBuilder(t).setUptodate(uptodate).build());
154                 }
155         }
156
157         @Override
158         public synchronized void clear(final DataModificationTransaction trans, final Peer peer) {
159                 final Iterator<Map.Entry<I, RIBEntry>> i = this.entries.entrySet().iterator();
160                 while (i.hasNext()) {
161                         final Map.Entry<I, RIBEntry> e = i.next();
162
163                         if (e.getValue().removeState(trans, peer)) {
164                                 i.remove();
165                         }
166                 }
167
168                 this.peers.remove(peer);
169                 setUptodate(trans, !this.peers.values().contains(Boolean.FALSE));
170         }
171
172         protected abstract InstanceIdentifier<?> identifierForKey(final InstanceIdentifier<Tables> basePath, final I id);
173
174         protected synchronized void add(final DataModificationTransaction trans, final Peer peer, final I id, final RIBEntryData<I, D> data) {
175                 RIBEntry e = this.entries.get(Preconditions.checkNotNull(id));
176                 if (e == null) {
177                         e = new RIBEntry(id);
178                         this.entries.put(id, e);
179                 }
180
181                 e.setState(trans, peer, data);
182                 if (!this.peers.containsKey(peer)) {
183                         this.peers.put(peer, Boolean.FALSE);
184                         setUptodate(trans, Boolean.FALSE);
185                 }
186         }
187
188         protected synchronized void remove(final DataModificationTransaction trans, final Peer peer, final I id) {
189                 final RIBEntry e = this.entries.get(id);
190                 if (e != null && e.removeState(trans, peer)) {
191                         LOG.debug("Removed last state, removing entry for {}", id);
192                         this.entries.remove(id);
193                 }
194         }
195
196         @Override
197         public final void markUptodate(final DataModificationTransaction trans, final Peer peer) {
198                 this.peers.put(peer, Boolean.TRUE);
199                 setUptodate(trans, !this.peers.values().contains(Boolean.FALSE));
200         }
201
202         @Override
203         public final Update endOfRib() {
204                 return this.eor;
205         }
206 }