blob: 5c879214a41017ca201322ca77aefecef82d74ed [file] [log] [blame]
tomea961ff2014-10-01 12:45:15 -07001package org.onlab.onos.store.trivial.impl;
tom4c6606f2014-09-07 11:11:21 -07002
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -07003import com.google.common.base.Function;
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -07004import com.google.common.collect.FluentIterable;
tomeadbb462014-09-07 16:10:19 -07005import com.google.common.collect.HashMultimap;
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -07006import com.google.common.collect.SetMultimap;
Yuta HIGUCHI06dc6b92014-09-25 16:06:16 -07007
tom5bcc9462014-09-19 10:11:31 -07008import org.apache.felix.scr.annotations.Activate;
tom35c0dc32014-09-19 10:00:58 -07009import org.apache.felix.scr.annotations.Component;
tom5bcc9462014-09-19 10:11:31 -070010import org.apache.felix.scr.annotations.Deactivate;
tom35c0dc32014-09-19 10:00:58 -070011import org.apache.felix.scr.annotations.Service;
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070012import org.onlab.onos.net.AnnotationsUtil;
tomeadbb462014-09-07 16:10:19 -070013import org.onlab.onos.net.ConnectPoint;
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070014import org.onlab.onos.net.DefaultAnnotations;
tomeadbb462014-09-07 16:10:19 -070015import org.onlab.onos.net.DefaultLink;
16import org.onlab.onos.net.DeviceId;
17import org.onlab.onos.net.Link;
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070018import org.onlab.onos.net.SparseAnnotations;
19import org.onlab.onos.net.Link.Type;
Yuta HIGUCHI06dc6b92014-09-25 16:06:16 -070020import org.onlab.onos.net.LinkKey;
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070021import org.onlab.onos.net.link.DefaultLinkDescription;
tomeadbb462014-09-07 16:10:19 -070022import org.onlab.onos.net.link.LinkDescription;
23import org.onlab.onos.net.link.LinkEvent;
tom35c0dc32014-09-19 10:00:58 -070024import org.onlab.onos.net.link.LinkStore;
tomf80c9722014-09-24 14:49:18 -070025import org.onlab.onos.net.link.LinkStoreDelegate;
tomeadbb462014-09-07 16:10:19 -070026import org.onlab.onos.net.provider.ProviderId;
tomf80c9722014-09-24 14:49:18 -070027import org.onlab.onos.store.AbstractStore;
tom5bcc9462014-09-19 10:11:31 -070028import org.slf4j.Logger;
tomeadbb462014-09-07 16:10:19 -070029
30import java.util.Collections;
Yuta HIGUCHIdd841b72014-10-16 13:46:09 -070031import java.util.HashMap;
tomeadbb462014-09-07 16:10:19 -070032import java.util.HashSet;
Yuta HIGUCHIdd841b72014-10-16 13:46:09 -070033import java.util.Map;
tomeadbb462014-09-07 16:10:19 -070034import java.util.Set;
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070035import java.util.Map.Entry;
tomeadbb462014-09-07 16:10:19 -070036import java.util.concurrent.ConcurrentHashMap;
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070037import java.util.concurrent.ConcurrentMap;
tomeadbb462014-09-07 16:10:19 -070038
Yuta HIGUCHI885be1d2014-10-04 21:47:26 -070039import static org.onlab.onos.net.DefaultAnnotations.union;
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070040import static org.onlab.onos.net.DefaultAnnotations.merge;
tomd176fc42014-09-08 00:12:30 -070041import static org.onlab.onos.net.Link.Type.DIRECT;
42import static org.onlab.onos.net.Link.Type.INDIRECT;
Yuta HIGUCHI18ab8a92014-10-13 11:16:19 -070043import static org.onlab.onos.net.LinkKey.linkKey;
tom35c0dc32014-09-19 10:00:58 -070044import static org.onlab.onos.net.link.LinkEvent.Type.*;
tom5bcc9462014-09-19 10:11:31 -070045import static org.slf4j.LoggerFactory.getLogger;
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070046import static com.google.common.collect.Multimaps.synchronizedSetMultimap;
47import static com.google.common.base.Predicates.notNull;
Yuta HIGUCHIdd841b72014-10-16 13:46:09 -070048import static com.google.common.base.Verify.verifyNotNull;
tomd176fc42014-09-08 00:12:30 -070049
tom4c6606f2014-09-07 11:11:21 -070050/**
tomcbff9392014-09-10 00:45:23 -070051 * Manages inventory of infrastructure links using trivial in-memory structures
tom4c6606f2014-09-07 11:11:21 -070052 * implementation.
53 */
tom35c0dc32014-09-19 10:00:58 -070054@Component(immediate = true)
55@Service
tomf80c9722014-09-24 14:49:18 -070056public class SimpleLinkStore
57 extends AbstractStore<LinkEvent, LinkStoreDelegate>
58 implements LinkStore {
tomeadbb462014-09-07 16:10:19 -070059
tom5bcc9462014-09-19 10:11:31 -070060 private final Logger log = getLogger(getClass());
61
tomeadbb462014-09-07 16:10:19 -070062 // Link inventory
Yuta HIGUCHIdd841b72014-10-16 13:46:09 -070063 private final ConcurrentMap<LinkKey, Map<ProviderId, LinkDescription>>
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070064 linkDescs = new ConcurrentHashMap<>();
65
66 // Link instance cache
67 private final ConcurrentMap<LinkKey, Link> links = new ConcurrentHashMap<>();
tomeadbb462014-09-07 16:10:19 -070068
69 // Egress and ingress link sets
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070070 private final SetMultimap<DeviceId, LinkKey> srcLinks = createSynchronizedHashMultiMap();
71 private final SetMultimap<DeviceId, LinkKey> dstLinks = createSynchronizedHashMultiMap();
72
tomeadbb462014-09-07 16:10:19 -070073
tom5bcc9462014-09-19 10:11:31 -070074 @Activate
75 public void activate() {
76 log.info("Started");
77 }
78
79 @Deactivate
80 public void deactivate() {
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070081 linkDescs.clear();
82 links.clear();
83 srcLinks.clear();
84 dstLinks.clear();
tom5bcc9462014-09-19 10:11:31 -070085 log.info("Stopped");
86 }
tomeadbb462014-09-07 16:10:19 -070087
tom35c0dc32014-09-19 10:00:58 -070088 @Override
89 public int getLinkCount() {
tomeadbb462014-09-07 16:10:19 -070090 return links.size();
91 }
92
tom35c0dc32014-09-19 10:00:58 -070093 @Override
94 public Iterable<Link> getLinks() {
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070095 return Collections.unmodifiableCollection(links.values());
tomeadbb462014-09-07 16:10:19 -070096 }
97
tom35c0dc32014-09-19 10:00:58 -070098 @Override
99 public Set<Link> getDeviceEgressLinks(DeviceId deviceId) {
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700100 // lock for iteration
101 synchronized (srcLinks) {
102 return FluentIterable.from(srcLinks.get(deviceId))
103 .transform(lookupLink())
104 .filter(notNull())
105 .toSet();
106 }
tomeadbb462014-09-07 16:10:19 -0700107 }
108
tom35c0dc32014-09-19 10:00:58 -0700109 @Override
110 public Set<Link> getDeviceIngressLinks(DeviceId deviceId) {
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700111 // lock for iteration
112 synchronized (dstLinks) {
113 return FluentIterable.from(dstLinks.get(deviceId))
114 .transform(lookupLink())
115 .filter(notNull())
116 .toSet();
117 }
tomeadbb462014-09-07 16:10:19 -0700118 }
119
tom35c0dc32014-09-19 10:00:58 -0700120 @Override
121 public Link getLink(ConnectPoint src, ConnectPoint dst) {
Yuta HIGUCHI18ab8a92014-10-13 11:16:19 -0700122 return links.get(linkKey(src, dst));
tomd176fc42014-09-08 00:12:30 -0700123 }
124
tom35c0dc32014-09-19 10:00:58 -0700125 @Override
126 public Set<Link> getEgressLinks(ConnectPoint src) {
tomeadbb462014-09-07 16:10:19 -0700127 Set<Link> egress = new HashSet<>();
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700128 for (LinkKey linkKey : srcLinks.get(src.deviceId())) {
129 if (linkKey.src().equals(src)) {
130 egress.add(links.get(linkKey));
tomeadbb462014-09-07 16:10:19 -0700131 }
132 }
133 return egress;
134 }
135
tom35c0dc32014-09-19 10:00:58 -0700136 @Override
137 public Set<Link> getIngressLinks(ConnectPoint dst) {
tomeadbb462014-09-07 16:10:19 -0700138 Set<Link> ingress = new HashSet<>();
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700139 for (LinkKey linkKey : dstLinks.get(dst.deviceId())) {
140 if (linkKey.dst().equals(dst)) {
141 ingress.add(links.get(linkKey));
tomeadbb462014-09-07 16:10:19 -0700142 }
143 }
144 return ingress;
145 }
146
tom35c0dc32014-09-19 10:00:58 -0700147 @Override
tomeadbb462014-09-07 16:10:19 -0700148 public LinkEvent createOrUpdateLink(ProviderId providerId,
149 LinkDescription linkDescription) {
Yuta HIGUCHI990aecc2014-10-13 22:21:42 -0700150 LinkKey key = linkKey(linkDescription.src(), linkDescription.dst());
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700151
Yuta HIGUCHIdd841b72014-10-16 13:46:09 -0700152 Map<ProviderId, LinkDescription> descs = getOrCreateLinkDescriptions(key);
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700153 synchronized (descs) {
154 final Link oldLink = links.get(key);
155 // update description
156 createOrUpdateLinkDescription(descs, providerId, linkDescription);
157 final Link newLink = composeLink(descs);
158 if (oldLink == null) {
159 return createLink(key, newLink);
160 }
161 return updateLink(key, oldLink, newLink);
tomeadbb462014-09-07 16:10:19 -0700162 }
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700163 }
164
165 // Guarded by linkDescs value (=locking each Link)
166 private LinkDescription createOrUpdateLinkDescription(
Yuta HIGUCHIdd841b72014-10-16 13:46:09 -0700167 Map<ProviderId, LinkDescription> descs,
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700168 ProviderId providerId,
169 LinkDescription linkDescription) {
170
171 // merge existing attributes and merge
172 LinkDescription oldDesc = descs.get(providerId);
173 LinkDescription newDesc = linkDescription;
174 if (oldDesc != null) {
Yuta HIGUCHIa85542b2014-10-21 19:29:49 -0700175 // we only allow transition from INDIRECT -> DIRECT
176 final Type newType;
177 if (oldDesc.type() == DIRECT) {
178 newType = DIRECT;
179 } else {
180 newType = linkDescription.type();
181 }
Yuta HIGUCHI885be1d2014-10-04 21:47:26 -0700182 SparseAnnotations merged = union(oldDesc.annotations(),
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700183 linkDescription.annotations());
184 newDesc = new DefaultLinkDescription(
185 linkDescription.src(),
186 linkDescription.dst(),
Yuta HIGUCHIa85542b2014-10-21 19:29:49 -0700187 newType, merged);
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700188 }
189 return descs.put(providerId, newDesc);
tomeadbb462014-09-07 16:10:19 -0700190 }
191
192 // Creates and stores the link and returns the appropriate event.
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700193 // Guarded by linkDescs value (=locking each Link)
194 private LinkEvent createLink(LinkKey key, Link newLink) {
195
196 if (newLink.providerId().isAncillary()) {
197 // TODO: revisit ancillary only Link handling
198
199 // currently treating ancillary only as down (not visible outside)
200 return null;
tomeadbb462014-09-07 16:10:19 -0700201 }
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700202
203 links.put(key, newLink);
204 srcLinks.put(newLink.src().deviceId(), key);
205 dstLinks.put(newLink.dst().deviceId(), key);
206 return new LinkEvent(LINK_ADDED, newLink);
tomeadbb462014-09-07 16:10:19 -0700207 }
208
209 // Updates, if necessary the specified link and returns the appropriate event.
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700210 // Guarded by linkDescs value (=locking each Link)
211 private LinkEvent updateLink(LinkKey key, Link oldLink, Link newLink) {
tomd176fc42014-09-08 00:12:30 -0700212
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700213 if (newLink.providerId().isAncillary()) {
214 // TODO: revisit ancillary only Link handling
215
216 // currently treating ancillary only as down (not visible outside)
217 return null;
218 }
219
220 if ((oldLink.type() == INDIRECT && newLink.type() == DIRECT) ||
221 !AnnotationsUtil.isEqual(oldLink.annotations(), newLink.annotations())) {
222
223 links.put(key, newLink);
224 // strictly speaking following can be ommitted
225 srcLinks.put(oldLink.src().deviceId(), key);
226 dstLinks.put(oldLink.dst().deviceId(), key);
227 return new LinkEvent(LINK_UPDATED, newLink);
tomd176fc42014-09-08 00:12:30 -0700228 }
tomeadbb462014-09-07 16:10:19 -0700229 return null;
230 }
231
tom35c0dc32014-09-19 10:00:58 -0700232 @Override
233 public LinkEvent removeLink(ConnectPoint src, ConnectPoint dst) {
Yuta HIGUCHI18ab8a92014-10-13 11:16:19 -0700234 final LinkKey key = linkKey(src, dst);
Yuta HIGUCHIdd841b72014-10-16 13:46:09 -0700235 Map<ProviderId, LinkDescription> descs = getOrCreateLinkDescriptions(key);
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700236 synchronized (descs) {
237 Link link = links.remove(key);
Yuta HIGUCHI0d6a5e62014-10-03 15:54:09 -0700238 descs.clear();
tomd176fc42014-09-08 00:12:30 -0700239 if (link != null) {
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700240 srcLinks.remove(link.src().deviceId(), key);
241 dstLinks.remove(link.dst().deviceId(), key);
tomd176fc42014-09-08 00:12:30 -0700242 return new LinkEvent(LINK_REMOVED, link);
243 }
244 return null;
tomeadbb462014-09-07 16:10:19 -0700245 }
246 }
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700247
248 private static <K, V> SetMultimap<K, V> createSynchronizedHashMultiMap() {
249 return synchronizedSetMultimap(HashMultimap.<K, V>create());
250 }
251
252 /**
253 * @return primary ProviderID, or randomly chosen one if none exists
254 */
Yuta HIGUCHIdd841b72014-10-16 13:46:09 -0700255 // Guarded by linkDescs value (=locking each Link)
256 private ProviderId getBaseProviderId(Map<ProviderId, LinkDescription> providerDescs) {
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700257
258 ProviderId fallBackPrimary = null;
259 for (Entry<ProviderId, LinkDescription> e : providerDescs.entrySet()) {
260 if (!e.getKey().isAncillary()) {
261 return e.getKey();
262 } else if (fallBackPrimary == null) {
263 // pick randomly as a fallback in case there is no primary
264 fallBackPrimary = e.getKey();
265 }
266 }
267 return fallBackPrimary;
268 }
269
Yuta HIGUCHIdd841b72014-10-16 13:46:09 -0700270 // Guarded by linkDescs value (=locking each Link)
271 private Link composeLink(Map<ProviderId, LinkDescription> descs) {
272 ProviderId primary = getBaseProviderId(descs);
273 LinkDescription base = descs.get(verifyNotNull(primary));
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700274
275 ConnectPoint src = base.src();
276 ConnectPoint dst = base.dst();
277 Type type = base.type();
Yuta HIGUCHI885be1d2014-10-04 21:47:26 -0700278 DefaultAnnotations annotations = DefaultAnnotations.builder().build();
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700279 annotations = merge(annotations, base.annotations());
280
281 for (Entry<ProviderId, LinkDescription> e : descs.entrySet()) {
282 if (primary.equals(e.getKey())) {
283 continue;
284 }
285
286 // TODO: should keep track of Description timestamp
287 // and only merge conflicting keys when timestamp is newer
288 // Currently assuming there will never be a key conflict between
289 // providers
290
291 // annotation merging. not so efficient, should revisit later
292 annotations = merge(annotations, e.getValue().annotations());
293 }
294
295 return new DefaultLink(primary , src, dst, type, annotations);
296 }
297
Yuta HIGUCHIdd841b72014-10-16 13:46:09 -0700298 private Map<ProviderId, LinkDescription> getOrCreateLinkDescriptions(LinkKey key) {
299 Map<ProviderId, LinkDescription> r;
300 r = linkDescs.get(key);
301 if (r != null) {
302 return r;
303 }
304 r = new HashMap<>();
305 final Map<ProviderId, LinkDescription> concurrentlyAdded;
306 concurrentlyAdded = linkDescs.putIfAbsent(key, r);
307 if (concurrentlyAdded == null) {
308 return r;
309 } else {
310 return concurrentlyAdded;
311 }
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700312 }
313
314 private final Function<LinkKey, Link> lookupLink = new LookupLink();
315 private Function<LinkKey, Link> lookupLink() {
316 return lookupLink;
317 }
318
319 private final class LookupLink implements Function<LinkKey, Link> {
320 @Override
321 public Link apply(LinkKey input) {
Yuta HIGUCHIdd841b72014-10-16 13:46:09 -0700322 if (input == null) {
323 return null;
324 } else {
325 return links.get(input);
326 }
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700327 }
328 }
tom4c6606f2014-09-07 11:11:21 -0700329}