blob: 0241b41ac76295716db3249f7c6c45c5c5d70a2f [file] [log] [blame]
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
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
47import java.util.Collections;
Yuta HIGUCHIdd841b72014-10-16 13:46:09 -070048import java.util.HashMap;
tomeadbb462014-09-07 16:10:19 -070049import java.util.HashSet;
Yuta HIGUCHIdd841b72014-10-16 13:46:09 -070050import java.util.Map;
Thomas Vachuska57126fe2014-11-11 17:13:24 -080051import java.util.Map.Entry;
Thomas Vachuska29a6a782014-11-10 21:31:41 -080052import java.util.Objects;
tomeadbb462014-09-07 16:10:19 -070053import java.util.Set;
54import java.util.concurrent.ConcurrentHashMap;
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070055import java.util.concurrent.ConcurrentMap;
tomeadbb462014-09-07 16:10:19 -070056
Thomas Vachuska57126fe2014-11-11 17:13:24 -080057import static com.google.common.base.Predicates.notNull;
58import static com.google.common.base.Verify.verifyNotNull;
59import static com.google.common.collect.Multimaps.synchronizedSetMultimap;
Brian O'Connorabafb502014-12-02 22:26:20 -080060import static org.onosproject.net.DefaultAnnotations.merge;
61import static org.onosproject.net.DefaultAnnotations.union;
62import static org.onosproject.net.Link.State.ACTIVE;
63import static org.onosproject.net.Link.State.INACTIVE;
64import static org.onosproject.net.Link.Type.DIRECT;
65import static org.onosproject.net.Link.Type.INDIRECT;
66import static org.onosproject.net.LinkKey.linkKey;
67import static org.onosproject.net.link.LinkEvent.Type.*;
tom5bcc9462014-09-19 10:11:31 -070068import static org.slf4j.LoggerFactory.getLogger;
tomd176fc42014-09-08 00:12:30 -070069
tom4c6606f2014-09-07 11:11:21 -070070/**
tomcbff9392014-09-10 00:45:23 -070071 * Manages inventory of infrastructure links using trivial in-memory structures
tom4c6606f2014-09-07 11:11:21 -070072 * implementation.
73 */
tom35c0dc32014-09-19 10:00:58 -070074@Component(immediate = true)
75@Service
tomf80c9722014-09-24 14:49:18 -070076public class SimpleLinkStore
77 extends AbstractStore<LinkEvent, LinkStoreDelegate>
78 implements LinkStore {
tomeadbb462014-09-07 16:10:19 -070079
tom5bcc9462014-09-19 10:11:31 -070080 private final Logger log = getLogger(getClass());
81
tomeadbb462014-09-07 16:10:19 -070082 // Link inventory
Yuta HIGUCHIdd841b72014-10-16 13:46:09 -070083 private final ConcurrentMap<LinkKey, Map<ProviderId, LinkDescription>>
Thomas Vachuska57126fe2014-11-11 17:13:24 -080084 linkDescs = new ConcurrentHashMap<>();
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070085
86 // Link instance cache
87 private final ConcurrentMap<LinkKey, Link> links = new ConcurrentHashMap<>();
tomeadbb462014-09-07 16:10:19 -070088
89 // Egress and ingress link sets
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070090 private final SetMultimap<DeviceId, LinkKey> srcLinks = createSynchronizedHashMultiMap();
91 private final SetMultimap<DeviceId, LinkKey> dstLinks = createSynchronizedHashMultiMap();
92
tomeadbb462014-09-07 16:10:19 -070093
tom5bcc9462014-09-19 10:11:31 -070094 @Activate
95 public void activate() {
96 log.info("Started");
97 }
98
99 @Deactivate
100 public void deactivate() {
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700101 linkDescs.clear();
102 links.clear();
103 srcLinks.clear();
104 dstLinks.clear();
tom5bcc9462014-09-19 10:11:31 -0700105 log.info("Stopped");
106 }
tomeadbb462014-09-07 16:10:19 -0700107
tom35c0dc32014-09-19 10:00:58 -0700108 @Override
109 public int getLinkCount() {
tomeadbb462014-09-07 16:10:19 -0700110 return links.size();
111 }
112
tom35c0dc32014-09-19 10:00:58 -0700113 @Override
114 public Iterable<Link> getLinks() {
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700115 return Collections.unmodifiableCollection(links.values());
tomeadbb462014-09-07 16:10:19 -0700116 }
117
tom35c0dc32014-09-19 10:00:58 -0700118 @Override
119 public Set<Link> getDeviceEgressLinks(DeviceId deviceId) {
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700120 // lock for iteration
121 synchronized (srcLinks) {
122 return FluentIterable.from(srcLinks.get(deviceId))
Thomas Vachuska57126fe2014-11-11 17:13:24 -0800123 .transform(lookupLink())
124 .filter(notNull())
125 .toSet();
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700126 }
tomeadbb462014-09-07 16:10:19 -0700127 }
128
tom35c0dc32014-09-19 10:00:58 -0700129 @Override
130 public Set<Link> getDeviceIngressLinks(DeviceId deviceId) {
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700131 // lock for iteration
132 synchronized (dstLinks) {
133 return FluentIterable.from(dstLinks.get(deviceId))
Thomas Vachuska57126fe2014-11-11 17:13:24 -0800134 .transform(lookupLink())
135 .filter(notNull())
136 .toSet();
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700137 }
tomeadbb462014-09-07 16:10:19 -0700138 }
139
tom35c0dc32014-09-19 10:00:58 -0700140 @Override
141 public Link getLink(ConnectPoint src, ConnectPoint dst) {
Yuta HIGUCHI18ab8a92014-10-13 11:16:19 -0700142 return links.get(linkKey(src, dst));
tomd176fc42014-09-08 00:12:30 -0700143 }
144
tom35c0dc32014-09-19 10:00:58 -0700145 @Override
146 public Set<Link> getEgressLinks(ConnectPoint src) {
tomeadbb462014-09-07 16:10:19 -0700147 Set<Link> egress = new HashSet<>();
HIGUCHI Yuta4d973eb2015-02-26 14:28:49 -0800148 synchronized (srcLinks) {
149 for (LinkKey linkKey : srcLinks.get(src.deviceId())) {
150 if (linkKey.src().equals(src)) {
151 egress.add(links.get(linkKey));
152 }
tomeadbb462014-09-07 16:10:19 -0700153 }
154 }
155 return egress;
156 }
157
tom35c0dc32014-09-19 10:00:58 -0700158 @Override
159 public Set<Link> getIngressLinks(ConnectPoint dst) {
tomeadbb462014-09-07 16:10:19 -0700160 Set<Link> ingress = new HashSet<>();
HIGUCHI Yuta4d973eb2015-02-26 14:28:49 -0800161 synchronized (dstLinks) {
162 for (LinkKey linkKey : dstLinks.get(dst.deviceId())) {
163 if (linkKey.dst().equals(dst)) {
164 ingress.add(links.get(linkKey));
165 }
tomeadbb462014-09-07 16:10:19 -0700166 }
167 }
168 return ingress;
169 }
170
tom35c0dc32014-09-19 10:00:58 -0700171 @Override
tomeadbb462014-09-07 16:10:19 -0700172 public LinkEvent createOrUpdateLink(ProviderId providerId,
173 LinkDescription linkDescription) {
Yuta HIGUCHI990aecc2014-10-13 22:21:42 -0700174 LinkKey key = linkKey(linkDescription.src(), linkDescription.dst());
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700175
Yuta HIGUCHIdd841b72014-10-16 13:46:09 -0700176 Map<ProviderId, LinkDescription> descs = getOrCreateLinkDescriptions(key);
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700177 synchronized (descs) {
178 final Link oldLink = links.get(key);
179 // update description
180 createOrUpdateLinkDescription(descs, providerId, linkDescription);
181 final Link newLink = composeLink(descs);
182 if (oldLink == null) {
183 return createLink(key, newLink);
184 }
185 return updateLink(key, oldLink, newLink);
tomeadbb462014-09-07 16:10:19 -0700186 }
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700187 }
188
Thomas Vachuska57126fe2014-11-11 17:13:24 -0800189 @Override
190 public LinkEvent removeOrDownLink(ConnectPoint src, ConnectPoint dst) {
191 Link link = getLink(src, dst);
192 if (link == null) {
193 return null;
194 }
195
Ray Milkey8521f812017-05-24 09:49:26 -0700196 if (link.isExpected()) {
Thomas Vachuska57126fe2014-11-11 17:13:24 -0800197 return link.state() == INACTIVE ? null :
198 updateLink(linkKey(link.src(), link.dst()), link,
Ray Milkey2693bda2016-01-22 16:08:14 -0800199 DefaultLink.builder()
200 .providerId(link.providerId())
201 .src(link.src())
202 .dst(link.dst())
203 .type(link.type())
204 .state(INACTIVE)
205 .isExpected(link.isExpected())
206 .annotations(link.annotations()).build());
Thomas Vachuska57126fe2014-11-11 17:13:24 -0800207 }
208 return removeLink(src, dst);
209 }
210
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700211 // Guarded by linkDescs value (=locking each Link)
212 private LinkDescription createOrUpdateLinkDescription(
Thomas Vachuska57126fe2014-11-11 17:13:24 -0800213 Map<ProviderId, LinkDescription> descs,
214 ProviderId providerId,
215 LinkDescription linkDescription) {
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700216
217 // merge existing attributes and merge
218 LinkDescription oldDesc = descs.get(providerId);
219 LinkDescription newDesc = linkDescription;
220 if (oldDesc != null) {
Yuta HIGUCHIa85542b2014-10-21 19:29:49 -0700221 // we only allow transition from INDIRECT -> DIRECT
222 final Type newType;
223 if (oldDesc.type() == DIRECT) {
224 newType = DIRECT;
225 } else {
226 newType = linkDescription.type();
227 }
Yuta HIGUCHI885be1d2014-10-04 21:47:26 -0700228 SparseAnnotations merged = union(oldDesc.annotations(),
Thomas Vachuska57126fe2014-11-11 17:13:24 -0800229 linkDescription.annotations());
230 newDesc = new DefaultLinkDescription(linkDescription.src(),
231 linkDescription.dst(),
232 newType, merged);
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700233 }
234 return descs.put(providerId, newDesc);
tomeadbb462014-09-07 16:10:19 -0700235 }
236
237 // Creates and stores the link and returns the appropriate event.
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700238 // Guarded by linkDescs value (=locking each Link)
239 private LinkEvent createLink(LinkKey key, Link newLink) {
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700240 links.put(key, newLink);
241 srcLinks.put(newLink.src().deviceId(), key);
242 dstLinks.put(newLink.dst().deviceId(), key);
243 return new LinkEvent(LINK_ADDED, newLink);
tomeadbb462014-09-07 16:10:19 -0700244 }
245
246 // Updates, if necessary the specified link and returns the appropriate event.
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700247 // Guarded by linkDescs value (=locking each Link)
248 private LinkEvent updateLink(LinkKey key, Link oldLink, Link newLink) {
Thomas Vachuska57126fe2014-11-11 17:13:24 -0800249 if (oldLink.state() != newLink.state() ||
250 (oldLink.type() == INDIRECT && newLink.type() == DIRECT) ||
251 !AnnotationsUtil.isEqual(oldLink.annotations(), newLink.annotations())) {
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700252
253 links.put(key, newLink);
Frank Wang3a98e0a2017-08-11 11:09:30 +0800254 // strictly speaking following can be omitted
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700255 srcLinks.put(oldLink.src().deviceId(), key);
256 dstLinks.put(oldLink.dst().deviceId(), key);
257 return new LinkEvent(LINK_UPDATED, newLink);
tomd176fc42014-09-08 00:12:30 -0700258 }
tomeadbb462014-09-07 16:10:19 -0700259 return null;
260 }
261
tom35c0dc32014-09-19 10:00:58 -0700262 @Override
263 public LinkEvent removeLink(ConnectPoint src, ConnectPoint dst) {
Yuta HIGUCHI18ab8a92014-10-13 11:16:19 -0700264 final LinkKey key = linkKey(src, dst);
Yuta HIGUCHIdd841b72014-10-16 13:46:09 -0700265 Map<ProviderId, LinkDescription> descs = getOrCreateLinkDescriptions(key);
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700266 synchronized (descs) {
Thomas Vachuska57126fe2014-11-11 17:13:24 -0800267 Link link = links.remove(key);
Yuta HIGUCHI0d6a5e62014-10-03 15:54:09 -0700268 descs.clear();
tomd176fc42014-09-08 00:12:30 -0700269 if (link != null) {
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700270 srcLinks.remove(link.src().deviceId(), key);
271 dstLinks.remove(link.dst().deviceId(), key);
tomd176fc42014-09-08 00:12:30 -0700272 return new LinkEvent(LINK_REMOVED, link);
273 }
274 return null;
tomeadbb462014-09-07 16:10:19 -0700275 }
276 }
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700277
Yuta HIGUCHI800fac62014-12-11 19:23:01 -0800278 /**
279 * Creates concurrent readable, synchronized HashMultimap.
280 *
281 * @return SetMultimap
282 */
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700283 private static <K, V> SetMultimap<K, V> createSynchronizedHashMultiMap() {
Yuta HIGUCHI800fac62014-12-11 19:23:01 -0800284 return synchronizedSetMultimap(
Sho SHIMIZU7a4087b2015-09-10 09:23:16 -0700285 Multimaps.newSetMultimap(new ConcurrentHashMap<>(),
Yuta HIGUCHI800fac62014-12-11 19:23:01 -0800286 () -> Sets.newConcurrentHashSet()));
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700287 }
288
289 /**
290 * @return primary ProviderID, or randomly chosen one if none exists
291 */
Yuta HIGUCHIdd841b72014-10-16 13:46:09 -0700292 // Guarded by linkDescs value (=locking each Link)
293 private ProviderId getBaseProviderId(Map<ProviderId, LinkDescription> providerDescs) {
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700294
295 ProviderId fallBackPrimary = null;
296 for (Entry<ProviderId, LinkDescription> e : providerDescs.entrySet()) {
297 if (!e.getKey().isAncillary()) {
298 return e.getKey();
299 } else if (fallBackPrimary == null) {
300 // pick randomly as a fallback in case there is no primary
301 fallBackPrimary = e.getKey();
302 }
303 }
304 return fallBackPrimary;
305 }
306
Yuta HIGUCHIdd841b72014-10-16 13:46:09 -0700307 // Guarded by linkDescs value (=locking each Link)
308 private Link composeLink(Map<ProviderId, LinkDescription> descs) {
309 ProviderId primary = getBaseProviderId(descs);
310 LinkDescription base = descs.get(verifyNotNull(primary));
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700311
312 ConnectPoint src = base.src();
313 ConnectPoint dst = base.dst();
314 Type type = base.type();
Yuta HIGUCHI885be1d2014-10-04 21:47:26 -0700315 DefaultAnnotations annotations = DefaultAnnotations.builder().build();
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700316 annotations = merge(annotations, base.annotations());
317
318 for (Entry<ProviderId, LinkDescription> e : descs.entrySet()) {
319 if (primary.equals(e.getKey())) {
320 continue;
321 }
322
323 // TODO: should keep track of Description timestamp
324 // and only merge conflicting keys when timestamp is newer
325 // Currently assuming there will never be a key conflict between
326 // providers
327
328 // annotation merging. not so efficient, should revisit later
329 annotations = merge(annotations, e.getValue().annotations());
330 }
331
Thomas Vachuska57126fe2014-11-11 17:13:24 -0800332 boolean isDurable = Objects.equals(annotations.value(AnnotationKeys.DURABLE), "true");
Ray Milkey2693bda2016-01-22 16:08:14 -0800333 return DefaultLink.builder()
334 .providerId(primary)
335 .src(src)
336 .dst(dst)
337 .type(type)
338 .state(ACTIVE)
339 .isExpected(isDurable)
340 .annotations(annotations)
341 .build();
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700342 }
343
Yuta HIGUCHIdd841b72014-10-16 13:46:09 -0700344 private Map<ProviderId, LinkDescription> getOrCreateLinkDescriptions(LinkKey key) {
345 Map<ProviderId, LinkDescription> r;
346 r = linkDescs.get(key);
347 if (r != null) {
348 return r;
349 }
350 r = new HashMap<>();
351 final Map<ProviderId, LinkDescription> concurrentlyAdded;
352 concurrentlyAdded = linkDescs.putIfAbsent(key, r);
353 if (concurrentlyAdded == null) {
354 return r;
355 } else {
356 return concurrentlyAdded;
357 }
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700358 }
359
360 private final Function<LinkKey, Link> lookupLink = new LookupLink();
Thomas Vachuska57126fe2014-11-11 17:13:24 -0800361
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700362 private Function<LinkKey, Link> lookupLink() {
363 return lookupLink;
364 }
365
366 private final class LookupLink implements Function<LinkKey, Link> {
367 @Override
368 public Link apply(LinkKey input) {
Yuta HIGUCHIdd841b72014-10-16 13:46:09 -0700369 if (input == null) {
370 return null;
371 } else {
372 return links.get(input);
373 }
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700374 }
375 }
tom4c6606f2014-09-07 11:11:21 -0700376}