blob: bcddda38428f2a32fbe6f9dc2fc7d3979cbdeaa2 [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;
4import com.google.common.base.Predicate;
5import com.google.common.collect.FluentIterable;
tomeadbb462014-09-07 16:10:19 -07006import com.google.common.collect.HashMultimap;
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -07007import com.google.common.collect.SetMultimap;
Yuta HIGUCHI06dc6b92014-09-25 16:06:16 -07008
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -07009import org.apache.commons.lang3.concurrent.ConcurrentUtils;
tom5bcc9462014-09-19 10:11:31 -070010import org.apache.felix.scr.annotations.Activate;
tom35c0dc32014-09-19 10:00:58 -070011import org.apache.felix.scr.annotations.Component;
tom5bcc9462014-09-19 10:11:31 -070012import org.apache.felix.scr.annotations.Deactivate;
tom35c0dc32014-09-19 10:00:58 -070013import org.apache.felix.scr.annotations.Service;
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070014import org.onlab.onos.net.AnnotationsUtil;
tomeadbb462014-09-07 16:10:19 -070015import org.onlab.onos.net.ConnectPoint;
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070016import org.onlab.onos.net.DefaultAnnotations;
tomeadbb462014-09-07 16:10:19 -070017import org.onlab.onos.net.DefaultLink;
18import org.onlab.onos.net.DeviceId;
19import org.onlab.onos.net.Link;
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070020import org.onlab.onos.net.SparseAnnotations;
21import org.onlab.onos.net.Link.Type;
Yuta HIGUCHI06dc6b92014-09-25 16:06:16 -070022import org.onlab.onos.net.LinkKey;
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070023import org.onlab.onos.net.Provided;
24import org.onlab.onos.net.link.DefaultLinkDescription;
tomeadbb462014-09-07 16:10:19 -070025import org.onlab.onos.net.link.LinkDescription;
26import org.onlab.onos.net.link.LinkEvent;
tom35c0dc32014-09-19 10:00:58 -070027import org.onlab.onos.net.link.LinkStore;
tomf80c9722014-09-24 14:49:18 -070028import org.onlab.onos.net.link.LinkStoreDelegate;
tomeadbb462014-09-07 16:10:19 -070029import org.onlab.onos.net.provider.ProviderId;
tomf80c9722014-09-24 14:49:18 -070030import org.onlab.onos.store.AbstractStore;
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070031import org.onlab.util.NewConcurrentHashMap;
tom5bcc9462014-09-19 10:11:31 -070032import org.slf4j.Logger;
tomeadbb462014-09-07 16:10:19 -070033
34import java.util.Collections;
35import java.util.HashSet;
tomeadbb462014-09-07 16:10:19 -070036import java.util.Set;
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070037import java.util.Map.Entry;
tomeadbb462014-09-07 16:10:19 -070038import java.util.concurrent.ConcurrentHashMap;
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070039import java.util.concurrent.ConcurrentMap;
tomeadbb462014-09-07 16:10:19 -070040
Yuta HIGUCHI885be1d2014-10-04 21:47:26 -070041import static org.onlab.onos.net.DefaultAnnotations.union;
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070042import static org.onlab.onos.net.DefaultAnnotations.merge;
tomd176fc42014-09-08 00:12:30 -070043import static org.onlab.onos.net.Link.Type.DIRECT;
44import static org.onlab.onos.net.Link.Type.INDIRECT;
Yuta HIGUCHI18ab8a92014-10-13 11:16:19 -070045import static org.onlab.onos.net.LinkKey.linkKey;
tom35c0dc32014-09-19 10:00:58 -070046import static org.onlab.onos.net.link.LinkEvent.Type.*;
tom5bcc9462014-09-19 10:11:31 -070047import static org.slf4j.LoggerFactory.getLogger;
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070048import static com.google.common.collect.Multimaps.synchronizedSetMultimap;
49import static com.google.common.base.Predicates.notNull;
tomd176fc42014-09-08 00:12:30 -070050
tom4c6606f2014-09-07 11:11:21 -070051/**
tomcbff9392014-09-10 00:45:23 -070052 * Manages inventory of infrastructure links using trivial in-memory structures
tom4c6606f2014-09-07 11:11:21 -070053 * implementation.
54 */
tom35c0dc32014-09-19 10:00:58 -070055@Component(immediate = true)
56@Service
tomf80c9722014-09-24 14:49:18 -070057public class SimpleLinkStore
58 extends AbstractStore<LinkEvent, LinkStoreDelegate>
59 implements LinkStore {
tomeadbb462014-09-07 16:10:19 -070060
tom5bcc9462014-09-19 10:11:31 -070061 private final Logger log = getLogger(getClass());
62
tomeadbb462014-09-07 16:10:19 -070063 // Link inventory
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070064 private final ConcurrentMap<LinkKey,
65 ConcurrentMap<ProviderId, LinkDescription>>
66 linkDescs = new ConcurrentHashMap<>();
67
68 // Link instance cache
69 private final ConcurrentMap<LinkKey, Link> links = new ConcurrentHashMap<>();
tomeadbb462014-09-07 16:10:19 -070070
71 // Egress and ingress link sets
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070072 private final SetMultimap<DeviceId, LinkKey> srcLinks = createSynchronizedHashMultiMap();
73 private final SetMultimap<DeviceId, LinkKey> dstLinks = createSynchronizedHashMultiMap();
74
tomeadbb462014-09-07 16:10:19 -070075
tom5bcc9462014-09-19 10:11:31 -070076 @Activate
77 public void activate() {
78 log.info("Started");
79 }
80
81 @Deactivate
82 public void deactivate() {
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070083 linkDescs.clear();
84 links.clear();
85 srcLinks.clear();
86 dstLinks.clear();
tom5bcc9462014-09-19 10:11:31 -070087 log.info("Stopped");
88 }
tomeadbb462014-09-07 16:10:19 -070089
tom35c0dc32014-09-19 10:00:58 -070090 @Override
91 public int getLinkCount() {
tomeadbb462014-09-07 16:10:19 -070092 return links.size();
93 }
94
tom35c0dc32014-09-19 10:00:58 -070095 @Override
96 public Iterable<Link> getLinks() {
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070097 return Collections.unmodifiableCollection(links.values());
tomeadbb462014-09-07 16:10:19 -070098 }
99
tom35c0dc32014-09-19 10:00:58 -0700100 @Override
101 public Set<Link> getDeviceEgressLinks(DeviceId deviceId) {
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700102 // lock for iteration
103 synchronized (srcLinks) {
104 return FluentIterable.from(srcLinks.get(deviceId))
105 .transform(lookupLink())
106 .filter(notNull())
107 .toSet();
108 }
tomeadbb462014-09-07 16:10:19 -0700109 }
110
tom35c0dc32014-09-19 10:00:58 -0700111 @Override
112 public Set<Link> getDeviceIngressLinks(DeviceId deviceId) {
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700113 // lock for iteration
114 synchronized (dstLinks) {
115 return FluentIterable.from(dstLinks.get(deviceId))
116 .transform(lookupLink())
117 .filter(notNull())
118 .toSet();
119 }
tomeadbb462014-09-07 16:10:19 -0700120 }
121
tom35c0dc32014-09-19 10:00:58 -0700122 @Override
123 public Link getLink(ConnectPoint src, ConnectPoint dst) {
Yuta HIGUCHI18ab8a92014-10-13 11:16:19 -0700124 return links.get(linkKey(src, dst));
tomd176fc42014-09-08 00:12:30 -0700125 }
126
tom35c0dc32014-09-19 10:00:58 -0700127 @Override
128 public Set<Link> getEgressLinks(ConnectPoint src) {
tomeadbb462014-09-07 16:10:19 -0700129 Set<Link> egress = new HashSet<>();
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700130 for (LinkKey linkKey : srcLinks.get(src.deviceId())) {
131 if (linkKey.src().equals(src)) {
132 egress.add(links.get(linkKey));
tomeadbb462014-09-07 16:10:19 -0700133 }
134 }
135 return egress;
136 }
137
tom35c0dc32014-09-19 10:00:58 -0700138 @Override
139 public Set<Link> getIngressLinks(ConnectPoint dst) {
tomeadbb462014-09-07 16:10:19 -0700140 Set<Link> ingress = new HashSet<>();
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700141 for (LinkKey linkKey : dstLinks.get(dst.deviceId())) {
142 if (linkKey.dst().equals(dst)) {
143 ingress.add(links.get(linkKey));
tomeadbb462014-09-07 16:10:19 -0700144 }
145 }
146 return ingress;
147 }
148
tom35c0dc32014-09-19 10:00:58 -0700149 @Override
tomeadbb462014-09-07 16:10:19 -0700150 public LinkEvent createOrUpdateLink(ProviderId providerId,
151 LinkDescription linkDescription) {
Yuta HIGUCHI990aecc2014-10-13 22:21:42 -0700152 LinkKey key = linkKey(linkDescription.src(), linkDescription.dst());
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700153
154 ConcurrentMap<ProviderId, LinkDescription> descs = getLinkDescriptions(key);
155 synchronized (descs) {
156 final Link oldLink = links.get(key);
157 // update description
158 createOrUpdateLinkDescription(descs, providerId, linkDescription);
159 final Link newLink = composeLink(descs);
160 if (oldLink == null) {
161 return createLink(key, newLink);
162 }
163 return updateLink(key, oldLink, newLink);
tomeadbb462014-09-07 16:10:19 -0700164 }
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700165 }
166
167 // Guarded by linkDescs value (=locking each Link)
168 private LinkDescription createOrUpdateLinkDescription(
169 ConcurrentMap<ProviderId, LinkDescription> descs,
170 ProviderId providerId,
171 LinkDescription linkDescription) {
172
173 // merge existing attributes and merge
174 LinkDescription oldDesc = descs.get(providerId);
175 LinkDescription newDesc = linkDescription;
176 if (oldDesc != null) {
Yuta HIGUCHI885be1d2014-10-04 21:47:26 -0700177 SparseAnnotations merged = union(oldDesc.annotations(),
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700178 linkDescription.annotations());
179 newDesc = new DefaultLinkDescription(
180 linkDescription.src(),
181 linkDescription.dst(),
182 linkDescription.type(), merged);
183 }
184 return descs.put(providerId, newDesc);
tomeadbb462014-09-07 16:10:19 -0700185 }
186
187 // Creates and stores the link and returns the appropriate event.
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700188 // Guarded by linkDescs value (=locking each Link)
189 private LinkEvent createLink(LinkKey key, Link newLink) {
190
191 if (newLink.providerId().isAncillary()) {
192 // TODO: revisit ancillary only Link handling
193
194 // currently treating ancillary only as down (not visible outside)
195 return null;
tomeadbb462014-09-07 16:10:19 -0700196 }
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700197
198 links.put(key, newLink);
199 srcLinks.put(newLink.src().deviceId(), key);
200 dstLinks.put(newLink.dst().deviceId(), key);
201 return new LinkEvent(LINK_ADDED, newLink);
tomeadbb462014-09-07 16:10:19 -0700202 }
203
204 // Updates, if necessary the specified link and returns the appropriate event.
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700205 // Guarded by linkDescs value (=locking each Link)
206 private LinkEvent updateLink(LinkKey key, Link oldLink, Link newLink) {
tomd176fc42014-09-08 00:12:30 -0700207
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700208 if (newLink.providerId().isAncillary()) {
209 // TODO: revisit ancillary only Link handling
210
211 // currently treating ancillary only as down (not visible outside)
212 return null;
213 }
214
215 if ((oldLink.type() == INDIRECT && newLink.type() == DIRECT) ||
216 !AnnotationsUtil.isEqual(oldLink.annotations(), newLink.annotations())) {
217
218 links.put(key, newLink);
219 // strictly speaking following can be ommitted
220 srcLinks.put(oldLink.src().deviceId(), key);
221 dstLinks.put(oldLink.dst().deviceId(), key);
222 return new LinkEvent(LINK_UPDATED, newLink);
tomd176fc42014-09-08 00:12:30 -0700223 }
tomeadbb462014-09-07 16:10:19 -0700224 return null;
225 }
226
tom35c0dc32014-09-19 10:00:58 -0700227 @Override
228 public LinkEvent removeLink(ConnectPoint src, ConnectPoint dst) {
Yuta HIGUCHI18ab8a92014-10-13 11:16:19 -0700229 final LinkKey key = linkKey(src, dst);
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700230 ConcurrentMap<ProviderId, LinkDescription> descs = getLinkDescriptions(key);
231 synchronized (descs) {
232 Link link = links.remove(key);
Yuta HIGUCHI0d6a5e62014-10-03 15:54:09 -0700233 descs.clear();
tomd176fc42014-09-08 00:12:30 -0700234 if (link != null) {
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700235 srcLinks.remove(link.src().deviceId(), key);
236 dstLinks.remove(link.dst().deviceId(), key);
tomd176fc42014-09-08 00:12:30 -0700237 return new LinkEvent(LINK_REMOVED, link);
238 }
239 return null;
tomeadbb462014-09-07 16:10:19 -0700240 }
241 }
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700242
243 private static <K, V> SetMultimap<K, V> createSynchronizedHashMultiMap() {
244 return synchronizedSetMultimap(HashMultimap.<K, V>create());
245 }
246
247 /**
248 * @return primary ProviderID, or randomly chosen one if none exists
249 */
250 private ProviderId pickPrimaryPID(
251 ConcurrentMap<ProviderId, LinkDescription> providerDescs) {
252
253 ProviderId fallBackPrimary = null;
254 for (Entry<ProviderId, LinkDescription> e : providerDescs.entrySet()) {
255 if (!e.getKey().isAncillary()) {
256 return e.getKey();
257 } else if (fallBackPrimary == null) {
258 // pick randomly as a fallback in case there is no primary
259 fallBackPrimary = e.getKey();
260 }
261 }
262 return fallBackPrimary;
263 }
264
265 private Link composeLink(ConcurrentMap<ProviderId, LinkDescription> descs) {
266 ProviderId primary = pickPrimaryPID(descs);
267 LinkDescription base = descs.get(primary);
268
269 ConnectPoint src = base.src();
270 ConnectPoint dst = base.dst();
271 Type type = base.type();
Yuta HIGUCHI885be1d2014-10-04 21:47:26 -0700272 DefaultAnnotations annotations = DefaultAnnotations.builder().build();
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700273 annotations = merge(annotations, base.annotations());
274
275 for (Entry<ProviderId, LinkDescription> e : descs.entrySet()) {
276 if (primary.equals(e.getKey())) {
277 continue;
278 }
279
280 // TODO: should keep track of Description timestamp
281 // and only merge conflicting keys when timestamp is newer
282 // Currently assuming there will never be a key conflict between
283 // providers
284
285 // annotation merging. not so efficient, should revisit later
286 annotations = merge(annotations, e.getValue().annotations());
287 }
288
289 return new DefaultLink(primary , src, dst, type, annotations);
290 }
291
292 private ConcurrentMap<ProviderId, LinkDescription> getLinkDescriptions(LinkKey key) {
293 return ConcurrentUtils.createIfAbsentUnchecked(linkDescs, key,
294 NewConcurrentHashMap.<ProviderId, LinkDescription>ifNeeded());
295 }
296
297 private final Function<LinkKey, Link> lookupLink = new LookupLink();
298 private Function<LinkKey, Link> lookupLink() {
299 return lookupLink;
300 }
301
302 private final class LookupLink implements Function<LinkKey, Link> {
303 @Override
304 public Link apply(LinkKey input) {
305 return links.get(input);
306 }
307 }
308
309 private static final Predicate<Provided> IS_PRIMARY = new IsPrimary();
310 private static final Predicate<Provided> isPrimary() {
311 return IS_PRIMARY;
312 }
313
314 private static final class IsPrimary implements Predicate<Provided> {
315
316 @Override
317 public boolean apply(Provided input) {
318 return !input.providerId().isAncillary();
319 }
320 }
tom4c6606f2014-09-07 11:11:21 -0700321}