blob: d0be2b1f47fab5cc6d3870cf6e1e6191f95e67a6 [file] [log] [blame]
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07001/*
Ray Milkey34c95902015-04-15 09:47:53 -07002 * Copyright 2014-2015 Open Networking Laboratory
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Thomas Vachuskac97aa612015-06-23 16:00:18 -070016package org.onosproject.store.trivial;
tom4c6606f2014-09-07 11:11:21 -070017
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070018import com.google.common.base.Function;
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070019import com.google.common.collect.FluentIterable;
Yuta HIGUCHI800fac62014-12-11 19:23:01 -080020import com.google.common.collect.Multimaps;
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070021import com.google.common.collect.SetMultimap;
Yuta HIGUCHI800fac62014-12-11 19:23:01 -080022import com.google.common.collect.Sets;
23
tom5bcc9462014-09-19 10:11:31 -070024import org.apache.felix.scr.annotations.Activate;
tom35c0dc32014-09-19 10:00:58 -070025import org.apache.felix.scr.annotations.Component;
tom5bcc9462014-09-19 10:11:31 -070026import org.apache.felix.scr.annotations.Deactivate;
tom35c0dc32014-09-19 10:00:58 -070027import org.apache.felix.scr.annotations.Service;
Brian O'Connorabafb502014-12-02 22:26:20 -080028import org.onosproject.net.AnnotationKeys;
29import org.onosproject.net.AnnotationsUtil;
30import org.onosproject.net.ConnectPoint;
31import org.onosproject.net.DefaultAnnotations;
32import org.onosproject.net.DefaultLink;
33import org.onosproject.net.DeviceId;
34import org.onosproject.net.Link;
35import org.onosproject.net.Link.Type;
36import org.onosproject.net.LinkKey;
37import org.onosproject.net.SparseAnnotations;
38import org.onosproject.net.link.DefaultLinkDescription;
39import org.onosproject.net.link.LinkDescription;
40import org.onosproject.net.link.LinkEvent;
41import org.onosproject.net.link.LinkStore;
42import org.onosproject.net.link.LinkStoreDelegate;
43import org.onosproject.net.provider.ProviderId;
44import org.onosproject.store.AbstractStore;
tom5bcc9462014-09-19 10:11:31 -070045import org.slf4j.Logger;
tomeadbb462014-09-07 16:10:19 -070046
Yuta HIGUCHI800fac62014-12-11 19:23:01 -080047import java.util.Collection;
tomeadbb462014-09-07 16:10:19 -070048import java.util.Collections;
Yuta HIGUCHIdd841b72014-10-16 13:46:09 -070049import java.util.HashMap;
tomeadbb462014-09-07 16:10:19 -070050import java.util.HashSet;
Yuta HIGUCHIdd841b72014-10-16 13:46:09 -070051import java.util.Map;
Thomas Vachuska57126fe2014-11-11 17:13:24 -080052import java.util.Map.Entry;
Thomas Vachuska29a6a782014-11-10 21:31:41 -080053import java.util.Objects;
tomeadbb462014-09-07 16:10:19 -070054import java.util.Set;
55import java.util.concurrent.ConcurrentHashMap;
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070056import java.util.concurrent.ConcurrentMap;
tomeadbb462014-09-07 16:10:19 -070057
Thomas Vachuska57126fe2014-11-11 17:13:24 -080058import static com.google.common.base.Predicates.notNull;
59import static com.google.common.base.Verify.verifyNotNull;
60import static com.google.common.collect.Multimaps.synchronizedSetMultimap;
Brian O'Connorabafb502014-12-02 22:26:20 -080061import static org.onosproject.net.DefaultAnnotations.merge;
62import static org.onosproject.net.DefaultAnnotations.union;
63import static org.onosproject.net.Link.State.ACTIVE;
64import static org.onosproject.net.Link.State.INACTIVE;
65import static org.onosproject.net.Link.Type.DIRECT;
66import static org.onosproject.net.Link.Type.INDIRECT;
67import static org.onosproject.net.LinkKey.linkKey;
68import static org.onosproject.net.link.LinkEvent.Type.*;
tom5bcc9462014-09-19 10:11:31 -070069import static org.slf4j.LoggerFactory.getLogger;
tomd176fc42014-09-08 00:12:30 -070070
tom4c6606f2014-09-07 11:11:21 -070071/**
tomcbff9392014-09-10 00:45:23 -070072 * Manages inventory of infrastructure links using trivial in-memory structures
tom4c6606f2014-09-07 11:11:21 -070073 * implementation.
74 */
tom35c0dc32014-09-19 10:00:58 -070075@Component(immediate = true)
76@Service
tomf80c9722014-09-24 14:49:18 -070077public class SimpleLinkStore
78 extends AbstractStore<LinkEvent, LinkStoreDelegate>
79 implements LinkStore {
tomeadbb462014-09-07 16:10:19 -070080
tom5bcc9462014-09-19 10:11:31 -070081 private final Logger log = getLogger(getClass());
82
tomeadbb462014-09-07 16:10:19 -070083 // Link inventory
Yuta HIGUCHIdd841b72014-10-16 13:46:09 -070084 private final ConcurrentMap<LinkKey, Map<ProviderId, LinkDescription>>
Thomas Vachuska57126fe2014-11-11 17:13:24 -080085 linkDescs = new ConcurrentHashMap<>();
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070086
87 // Link instance cache
88 private final ConcurrentMap<LinkKey, Link> links = new ConcurrentHashMap<>();
tomeadbb462014-09-07 16:10:19 -070089
90 // Egress and ingress link sets
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070091 private final SetMultimap<DeviceId, LinkKey> srcLinks = createSynchronizedHashMultiMap();
92 private final SetMultimap<DeviceId, LinkKey> dstLinks = createSynchronizedHashMultiMap();
93
tomeadbb462014-09-07 16:10:19 -070094
tom5bcc9462014-09-19 10:11:31 -070095 @Activate
96 public void activate() {
97 log.info("Started");
98 }
99
100 @Deactivate
101 public void deactivate() {
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700102 linkDescs.clear();
103 links.clear();
104 srcLinks.clear();
105 dstLinks.clear();
tom5bcc9462014-09-19 10:11:31 -0700106 log.info("Stopped");
107 }
tomeadbb462014-09-07 16:10:19 -0700108
tom35c0dc32014-09-19 10:00:58 -0700109 @Override
110 public int getLinkCount() {
tomeadbb462014-09-07 16:10:19 -0700111 return links.size();
112 }
113
tom35c0dc32014-09-19 10:00:58 -0700114 @Override
115 public Iterable<Link> getLinks() {
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700116 return Collections.unmodifiableCollection(links.values());
tomeadbb462014-09-07 16:10:19 -0700117 }
118
tom35c0dc32014-09-19 10:00:58 -0700119 @Override
120 public Set<Link> getDeviceEgressLinks(DeviceId deviceId) {
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700121 // lock for iteration
122 synchronized (srcLinks) {
123 return FluentIterable.from(srcLinks.get(deviceId))
Thomas Vachuska57126fe2014-11-11 17:13:24 -0800124 .transform(lookupLink())
125 .filter(notNull())
126 .toSet();
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700127 }
tomeadbb462014-09-07 16:10:19 -0700128 }
129
tom35c0dc32014-09-19 10:00:58 -0700130 @Override
131 public Set<Link> getDeviceIngressLinks(DeviceId deviceId) {
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700132 // lock for iteration
133 synchronized (dstLinks) {
134 return FluentIterable.from(dstLinks.get(deviceId))
Thomas Vachuska57126fe2014-11-11 17:13:24 -0800135 .transform(lookupLink())
136 .filter(notNull())
137 .toSet();
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700138 }
tomeadbb462014-09-07 16:10:19 -0700139 }
140
tom35c0dc32014-09-19 10:00:58 -0700141 @Override
142 public Link getLink(ConnectPoint src, ConnectPoint dst) {
Yuta HIGUCHI18ab8a92014-10-13 11:16:19 -0700143 return links.get(linkKey(src, dst));
tomd176fc42014-09-08 00:12:30 -0700144 }
145
tom35c0dc32014-09-19 10:00:58 -0700146 @Override
147 public Set<Link> getEgressLinks(ConnectPoint src) {
tomeadbb462014-09-07 16:10:19 -0700148 Set<Link> egress = new HashSet<>();
HIGUCHI Yuta4d973eb2015-02-26 14:28:49 -0800149 synchronized (srcLinks) {
150 for (LinkKey linkKey : srcLinks.get(src.deviceId())) {
151 if (linkKey.src().equals(src)) {
152 egress.add(links.get(linkKey));
153 }
tomeadbb462014-09-07 16:10:19 -0700154 }
155 }
156 return egress;
157 }
158
tom35c0dc32014-09-19 10:00:58 -0700159 @Override
160 public Set<Link> getIngressLinks(ConnectPoint dst) {
tomeadbb462014-09-07 16:10:19 -0700161 Set<Link> ingress = new HashSet<>();
HIGUCHI Yuta4d973eb2015-02-26 14:28:49 -0800162 synchronized (dstLinks) {
163 for (LinkKey linkKey : dstLinks.get(dst.deviceId())) {
164 if (linkKey.dst().equals(dst)) {
165 ingress.add(links.get(linkKey));
166 }
tomeadbb462014-09-07 16:10:19 -0700167 }
168 }
169 return ingress;
170 }
171
tom35c0dc32014-09-19 10:00:58 -0700172 @Override
tomeadbb462014-09-07 16:10:19 -0700173 public LinkEvent createOrUpdateLink(ProviderId providerId,
174 LinkDescription linkDescription) {
Yuta HIGUCHI990aecc2014-10-13 22:21:42 -0700175 LinkKey key = linkKey(linkDescription.src(), linkDescription.dst());
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700176
Yuta HIGUCHIdd841b72014-10-16 13:46:09 -0700177 Map<ProviderId, LinkDescription> descs = getOrCreateLinkDescriptions(key);
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700178 synchronized (descs) {
179 final Link oldLink = links.get(key);
180 // update description
181 createOrUpdateLinkDescription(descs, providerId, linkDescription);
182 final Link newLink = composeLink(descs);
183 if (oldLink == null) {
184 return createLink(key, newLink);
185 }
186 return updateLink(key, oldLink, newLink);
tomeadbb462014-09-07 16:10:19 -0700187 }
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700188 }
189
Thomas Vachuska57126fe2014-11-11 17:13:24 -0800190 @Override
191 public LinkEvent removeOrDownLink(ConnectPoint src, ConnectPoint dst) {
192 Link link = getLink(src, dst);
193 if (link == null) {
194 return null;
195 }
196
197 if (link.isDurable()) {
198 return link.state() == INACTIVE ? null :
199 updateLink(linkKey(link.src(), link.dst()), link,
200 new DefaultLink(link.providerId(),
201 link.src(), link.dst(),
202 link.type(), INACTIVE,
203 link.isDurable(),
204 link.annotations()));
205 }
206 return removeLink(src, dst);
207 }
208
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700209 // Guarded by linkDescs value (=locking each Link)
210 private LinkDescription createOrUpdateLinkDescription(
Thomas Vachuska57126fe2014-11-11 17:13:24 -0800211 Map<ProviderId, LinkDescription> descs,
212 ProviderId providerId,
213 LinkDescription linkDescription) {
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700214
215 // merge existing attributes and merge
216 LinkDescription oldDesc = descs.get(providerId);
217 LinkDescription newDesc = linkDescription;
218 if (oldDesc != null) {
Yuta HIGUCHIa85542b2014-10-21 19:29:49 -0700219 // we only allow transition from INDIRECT -> DIRECT
220 final Type newType;
221 if (oldDesc.type() == DIRECT) {
222 newType = DIRECT;
223 } else {
224 newType = linkDescription.type();
225 }
Yuta HIGUCHI885be1d2014-10-04 21:47:26 -0700226 SparseAnnotations merged = union(oldDesc.annotations(),
Thomas Vachuska57126fe2014-11-11 17:13:24 -0800227 linkDescription.annotations());
228 newDesc = new DefaultLinkDescription(linkDescription.src(),
229 linkDescription.dst(),
230 newType, merged);
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700231 }
232 return descs.put(providerId, newDesc);
tomeadbb462014-09-07 16:10:19 -0700233 }
234
235 // Creates and stores the link and returns the appropriate event.
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700236 // Guarded by linkDescs value (=locking each Link)
237 private LinkEvent createLink(LinkKey key, Link newLink) {
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700238 links.put(key, newLink);
239 srcLinks.put(newLink.src().deviceId(), key);
240 dstLinks.put(newLink.dst().deviceId(), key);
241 return new LinkEvent(LINK_ADDED, newLink);
tomeadbb462014-09-07 16:10:19 -0700242 }
243
244 // Updates, if necessary the specified link and returns the appropriate event.
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700245 // Guarded by linkDescs value (=locking each Link)
246 private LinkEvent updateLink(LinkKey key, Link oldLink, Link newLink) {
Thomas Vachuska57126fe2014-11-11 17:13:24 -0800247 if (oldLink.state() != newLink.state() ||
248 (oldLink.type() == INDIRECT && newLink.type() == DIRECT) ||
249 !AnnotationsUtil.isEqual(oldLink.annotations(), newLink.annotations())) {
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700250
251 links.put(key, newLink);
252 // strictly speaking following can be ommitted
253 srcLinks.put(oldLink.src().deviceId(), key);
254 dstLinks.put(oldLink.dst().deviceId(), key);
255 return new LinkEvent(LINK_UPDATED, newLink);
tomd176fc42014-09-08 00:12:30 -0700256 }
tomeadbb462014-09-07 16:10:19 -0700257 return null;
258 }
259
tom35c0dc32014-09-19 10:00:58 -0700260 @Override
261 public LinkEvent removeLink(ConnectPoint src, ConnectPoint dst) {
Yuta HIGUCHI18ab8a92014-10-13 11:16:19 -0700262 final LinkKey key = linkKey(src, dst);
Yuta HIGUCHIdd841b72014-10-16 13:46:09 -0700263 Map<ProviderId, LinkDescription> descs = getOrCreateLinkDescriptions(key);
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700264 synchronized (descs) {
Thomas Vachuska57126fe2014-11-11 17:13:24 -0800265 Link link = links.remove(key);
Yuta HIGUCHI0d6a5e62014-10-03 15:54:09 -0700266 descs.clear();
tomd176fc42014-09-08 00:12:30 -0700267 if (link != null) {
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700268 srcLinks.remove(link.src().deviceId(), key);
269 dstLinks.remove(link.dst().deviceId(), key);
tomd176fc42014-09-08 00:12:30 -0700270 return new LinkEvent(LINK_REMOVED, link);
271 }
272 return null;
tomeadbb462014-09-07 16:10:19 -0700273 }
274 }
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700275
Yuta HIGUCHI800fac62014-12-11 19:23:01 -0800276 /**
277 * Creates concurrent readable, synchronized HashMultimap.
278 *
279 * @return SetMultimap
280 */
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700281 private static <K, V> SetMultimap<K, V> createSynchronizedHashMultiMap() {
Yuta HIGUCHI800fac62014-12-11 19:23:01 -0800282 return synchronizedSetMultimap(
283 Multimaps.newSetMultimap(new ConcurrentHashMap<K, Collection<V>>(),
284 () -> Sets.newConcurrentHashSet()));
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700285 }
286
287 /**
288 * @return primary ProviderID, or randomly chosen one if none exists
289 */
Yuta HIGUCHIdd841b72014-10-16 13:46:09 -0700290 // Guarded by linkDescs value (=locking each Link)
291 private ProviderId getBaseProviderId(Map<ProviderId, LinkDescription> providerDescs) {
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700292
293 ProviderId fallBackPrimary = null;
294 for (Entry<ProviderId, LinkDescription> e : providerDescs.entrySet()) {
295 if (!e.getKey().isAncillary()) {
296 return e.getKey();
297 } else if (fallBackPrimary == null) {
298 // pick randomly as a fallback in case there is no primary
299 fallBackPrimary = e.getKey();
300 }
301 }
302 return fallBackPrimary;
303 }
304
Yuta HIGUCHIdd841b72014-10-16 13:46:09 -0700305 // Guarded by linkDescs value (=locking each Link)
306 private Link composeLink(Map<ProviderId, LinkDescription> descs) {
307 ProviderId primary = getBaseProviderId(descs);
308 LinkDescription base = descs.get(verifyNotNull(primary));
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700309
310 ConnectPoint src = base.src();
311 ConnectPoint dst = base.dst();
312 Type type = base.type();
Yuta HIGUCHI885be1d2014-10-04 21:47:26 -0700313 DefaultAnnotations annotations = DefaultAnnotations.builder().build();
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700314 annotations = merge(annotations, base.annotations());
315
316 for (Entry<ProviderId, LinkDescription> e : descs.entrySet()) {
317 if (primary.equals(e.getKey())) {
318 continue;
319 }
320
321 // TODO: should keep track of Description timestamp
322 // and only merge conflicting keys when timestamp is newer
323 // Currently assuming there will never be a key conflict between
324 // providers
325
326 // annotation merging. not so efficient, should revisit later
327 annotations = merge(annotations, e.getValue().annotations());
328 }
329
Thomas Vachuska57126fe2014-11-11 17:13:24 -0800330 boolean isDurable = Objects.equals(annotations.value(AnnotationKeys.DURABLE), "true");
Thomas Vachuskabadb93f2014-11-15 23:51:17 -0800331 return new DefaultLink(primary, src, dst, type, ACTIVE, isDurable, annotations);
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700332 }
333
Yuta HIGUCHIdd841b72014-10-16 13:46:09 -0700334 private Map<ProviderId, LinkDescription> getOrCreateLinkDescriptions(LinkKey key) {
335 Map<ProviderId, LinkDescription> r;
336 r = linkDescs.get(key);
337 if (r != null) {
338 return r;
339 }
340 r = new HashMap<>();
341 final Map<ProviderId, LinkDescription> concurrentlyAdded;
342 concurrentlyAdded = linkDescs.putIfAbsent(key, r);
343 if (concurrentlyAdded == null) {
344 return r;
345 } else {
346 return concurrentlyAdded;
347 }
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700348 }
349
350 private final Function<LinkKey, Link> lookupLink = new LookupLink();
Thomas Vachuska57126fe2014-11-11 17:13:24 -0800351
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700352 private Function<LinkKey, Link> lookupLink() {
353 return lookupLink;
354 }
355
356 private final class LookupLink implements Function<LinkKey, Link> {
357 @Override
358 public Link apply(LinkKey input) {
Yuta HIGUCHIdd841b72014-10-16 13:46:09 -0700359 if (input == null) {
360 return null;
361 } else {
362 return links.get(input);
363 }
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700364 }
365 }
tom4c6606f2014-09-07 11:11:21 -0700366}