BUG-1116: Finish up egress path API changes
[bgpcep.git] / bgp / rib-spi / src / main / java / org / opendaylight / protocol / bgp / rib / spi / AbstractAdjRIBs.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 com.google.common.base.Objects;
11 import com.google.common.base.Objects.ToStringHelper;
12 import com.google.common.base.Preconditions;
13
14 import java.util.HashMap;
15 import java.util.Iterator;
16 import java.util.Map;
17
18 import javax.annotation.concurrent.GuardedBy;
19 import javax.annotation.concurrent.ThreadSafe;
20
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;
42
43 @ThreadSafe
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;
48
49         protected RIBEntryData(final Peer peer, final PathAttributes attributes) {
50             this.attributes = Preconditions.checkNotNull(attributes);
51             this.peer = Preconditions.checkNotNull(peer);
52         }
53
54         public PathAttributes getPathAttributes() {
55             return this.attributes;
56         }
57
58         public Peer getPeer() {
59             return this.peer;
60         }
61
62         /**
63          * Create a data object given the key and target instance identifier.
64          *
65          * @param key Route key
66          * @param id Data store target identifier
67          * @return Data object to be written to the data store.
68          */
69         protected abstract D getDataObject(I key, K id);
70
71         @Override
72         public final String toString() {
73             return addToStringAttributes(Objects.toStringHelper(this)).toString();
74         }
75
76         protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
77             return toStringHelper.add("attributes", this.attributes);
78         }
79     }
80
81     /**
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.
84      *
85      */
86     private final class RIBEntry {
87         /*
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.
91          */
92         private final Map<Peer, RIBEntryData<I, D, K>> candidates = new HashMap<>();
93         private final I key;
94
95         @GuardedBy("this")
96         private KeyedInstanceIdentifier<D, K> name;
97         @GuardedBy("this")
98         private RIBEntryData<I, D, K> currentState;
99
100         RIBEntry(final I key) {
101             this.key = Preconditions.checkNotNull(key);
102         }
103
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);
108             }
109             return this.name;
110         }
111
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) {
116                     newState = s;
117                 }
118             }
119
120             return newState;
121         }
122
123         private void electCandidate(final AdjRIBsTransaction transaction, final RIBEntryData<I, D, K> candidate) {
124             LOG.trace("Electing state {} to supersede {}", candidate, this.currentState);
125
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;
130             }
131         }
132
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);
136
137             final RIBEntryData<I, D, K> candidate = findCandidate(transaction.comparator(), null);
138             if (candidate != null) {
139                 electCandidate(transaction, candidate);
140             } else {
141                 LOG.trace("Final candidate disappeared, removing entry {}", getName());
142                 transaction.withdraw(AbstractAdjRIBs.this, this.key, getName());
143             }
144
145             return this.candidates.isEmpty();
146         }
147
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));
151         }
152     }
153
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;
158
159     @GuardedBy("this")
160     private final Map<I, RIBEntry> entries = new HashMap<>();
161
162     @GuardedBy("this")
163     private final Map<Peer, Boolean> peers = new HashMap<>();
164
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();
171
172     }
173
174     @Override
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();
179
180             if (e.getValue().removeState(trans, peer)) {
181                 i.remove();
182             }
183         }
184
185         this.peers.remove(peer);
186         trans.setUptodate(basePath, !this.peers.values().contains(Boolean.FALSE));
187     }
188
189     /**
190      * Construct a datastore identifier for an entry key.
191      *
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
195      */
196     protected abstract KeyedInstanceIdentifier<D, K> identifierForKey(InstanceIdentifier<Tables> basePath, I id);
197
198     /**
199      * Transform an advertised data object into the corresponding NLRI in MP_REACH attribute.
200      *
201      * @param data Data object
202      * @param builder MP_REACH attribute builder
203      */
204     protected abstract void addAdvertisement(MpReachNlriBuilder builder, D data);
205
206     /**
207      * Transform a withdrawn identifier into a the corresponding NLRI in MP_UNREACH attribute.
208      *
209      * @param id Route key
210      */
211     protected abstract void addWithdrawal(MpUnreachNlriBuilder builder, I id);
212
213     /**
214      * Common backend for {@link AdjRIBsIn#addRoutes()} implementations.
215      *
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
220      */
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);
223
224         RIBEntry e = this.entries.get(Preconditions.checkNotNull(id));
225         if (e == null) {
226             e = new RIBEntry(id);
227             this.entries.put(id, e);
228         }
229
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);
234         }
235     }
236
237     /**
238      * Common backend for {@link AdjRIBsIn#removeRoutes()} implementations.
239      *
240      * @param trans Transaction context
241      * @param peer Originating peer
242      * @param id Data store instance identifier
243      */
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);
249         }
250     }
251
252     @Override
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));
256     }
257
258     @Override
259     public final Update endOfRib() {
260         return this.eor;
261     }
262
263     @Override
264     public Update updateMessageFor(final Object key, final Route route) {
265         final UpdateBuilder ub = new UpdateBuilder();
266         final PathAttributesBuilder pab = new PathAttributesBuilder();
267
268         if (route != null) {
269             final MpReachNlriBuilder reach = new MpReachNlriBuilder(this.tableType);
270
271             addAdvertisement(reach, (D)route);
272             pab.fieldsFrom(route.getAttributes());
273             pab.addAugmentation(PathAttributes1.class, new PathAttributes1Builder().setMpReachNlri(reach.build()).build()).build();
274         } else {
275             final MpUnreachNlriBuilder unreach = new MpUnreachNlriBuilder(tableType);
276             addWithdrawal(unreach, (I)key);
277             pab.addAugmentation(PathAttributes2.class, new PathAttributes2Builder().setMpUnreachNlri(unreach.build()).build()).build();
278         }
279
280         ub.setPathAttributes(pab.build());
281         return ub.build();
282     }
283
284 }