blob: 1d5828e0afcc958e83e83e278530281c483a0678 [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;
Brian O'Connorabafb502014-12-02 22:26:20 -080023import org.onosproject.net.AnnotationKeys;
24import org.onosproject.net.AnnotationsUtil;
25import org.onosproject.net.ConnectPoint;
26import org.onosproject.net.DefaultAnnotations;
27import org.onosproject.net.DefaultLink;
28import org.onosproject.net.DeviceId;
29import org.onosproject.net.Link;
30import org.onosproject.net.Link.Type;
31import org.onosproject.net.LinkKey;
32import org.onosproject.net.SparseAnnotations;
33import org.onosproject.net.link.DefaultLinkDescription;
34import org.onosproject.net.link.LinkDescription;
35import org.onosproject.net.link.LinkEvent;
36import org.onosproject.net.link.LinkStore;
37import org.onosproject.net.link.LinkStoreDelegate;
38import org.onosproject.net.provider.ProviderId;
39import org.onosproject.store.AbstractStore;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070040import org.osgi.service.component.annotations.Activate;
41import org.osgi.service.component.annotations.Component;
42import org.osgi.service.component.annotations.Deactivate;
tom5bcc9462014-09-19 10:11:31 -070043import org.slf4j.Logger;
tomeadbb462014-09-07 16:10:19 -070044
45import java.util.Collections;
Yuta HIGUCHIdd841b72014-10-16 13:46:09 -070046import java.util.HashMap;
tomeadbb462014-09-07 16:10:19 -070047import java.util.HashSet;
Yuta HIGUCHIdd841b72014-10-16 13:46:09 -070048import java.util.Map;
Thomas Vachuska57126fe2014-11-11 17:13:24 -080049import java.util.Map.Entry;
Thomas Vachuska29a6a782014-11-10 21:31:41 -080050import java.util.Objects;
tomeadbb462014-09-07 16:10:19 -070051import java.util.Set;
52import java.util.concurrent.ConcurrentHashMap;
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070053import java.util.concurrent.ConcurrentMap;
tomeadbb462014-09-07 16:10:19 -070054
Thomas Vachuska57126fe2014-11-11 17:13:24 -080055import static com.google.common.base.Predicates.notNull;
56import static com.google.common.base.Verify.verifyNotNull;
57import static com.google.common.collect.Multimaps.synchronizedSetMultimap;
Brian O'Connorabafb502014-12-02 22:26:20 -080058import static org.onosproject.net.DefaultAnnotations.merge;
59import static org.onosproject.net.DefaultAnnotations.union;
60import static org.onosproject.net.Link.State.ACTIVE;
61import static org.onosproject.net.Link.State.INACTIVE;
62import static org.onosproject.net.Link.Type.DIRECT;
63import static org.onosproject.net.Link.Type.INDIRECT;
64import static org.onosproject.net.LinkKey.linkKey;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070065import static org.onosproject.net.link.LinkEvent.Type.LINK_ADDED;
66import static org.onosproject.net.link.LinkEvent.Type.LINK_REMOVED;
67import static org.onosproject.net.link.LinkEvent.Type.LINK_UPDATED;
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 */
Ray Milkeyd84f89b2018-08-17 14:54:17 -070074@Component(immediate = true, service = LinkStore.class)
tomf80c9722014-09-24 14:49:18 -070075public class SimpleLinkStore
76 extends AbstractStore<LinkEvent, LinkStoreDelegate>
77 implements LinkStore {
tomeadbb462014-09-07 16:10:19 -070078
tom5bcc9462014-09-19 10:11:31 -070079 private final Logger log = getLogger(getClass());
80
tomeadbb462014-09-07 16:10:19 -070081 // Link inventory
Yuta HIGUCHIdd841b72014-10-16 13:46:09 -070082 private final ConcurrentMap<LinkKey, Map<ProviderId, LinkDescription>>
Thomas Vachuska57126fe2014-11-11 17:13:24 -080083 linkDescs = new ConcurrentHashMap<>();
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070084
85 // Link instance cache
86 private final ConcurrentMap<LinkKey, Link> links = new ConcurrentHashMap<>();
tomeadbb462014-09-07 16:10:19 -070087
88 // Egress and ingress link sets
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070089 private final SetMultimap<DeviceId, LinkKey> srcLinks = createSynchronizedHashMultiMap();
90 private final SetMultimap<DeviceId, LinkKey> dstLinks = createSynchronizedHashMultiMap();
91
tomeadbb462014-09-07 16:10:19 -070092
tom5bcc9462014-09-19 10:11:31 -070093 @Activate
94 public void activate() {
95 log.info("Started");
96 }
97
98 @Deactivate
99 public void deactivate() {
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700100 linkDescs.clear();
101 links.clear();
102 srcLinks.clear();
103 dstLinks.clear();
tom5bcc9462014-09-19 10:11:31 -0700104 log.info("Stopped");
105 }
tomeadbb462014-09-07 16:10:19 -0700106
tom35c0dc32014-09-19 10:00:58 -0700107 @Override
108 public int getLinkCount() {
tomeadbb462014-09-07 16:10:19 -0700109 return links.size();
110 }
111
tom35c0dc32014-09-19 10:00:58 -0700112 @Override
113 public Iterable<Link> getLinks() {
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700114 return Collections.unmodifiableCollection(links.values());
tomeadbb462014-09-07 16:10:19 -0700115 }
116
tom35c0dc32014-09-19 10:00:58 -0700117 @Override
118 public Set<Link> getDeviceEgressLinks(DeviceId deviceId) {
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700119 // lock for iteration
120 synchronized (srcLinks) {
121 return FluentIterable.from(srcLinks.get(deviceId))
Thomas Vachuska57126fe2014-11-11 17:13:24 -0800122 .transform(lookupLink())
123 .filter(notNull())
124 .toSet();
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700125 }
tomeadbb462014-09-07 16:10:19 -0700126 }
127
tom35c0dc32014-09-19 10:00:58 -0700128 @Override
129 public Set<Link> getDeviceIngressLinks(DeviceId deviceId) {
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700130 // lock for iteration
131 synchronized (dstLinks) {
132 return FluentIterable.from(dstLinks.get(deviceId))
Thomas Vachuska57126fe2014-11-11 17:13:24 -0800133 .transform(lookupLink())
134 .filter(notNull())
135 .toSet();
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700136 }
tomeadbb462014-09-07 16:10:19 -0700137 }
138
tom35c0dc32014-09-19 10:00:58 -0700139 @Override
140 public Link getLink(ConnectPoint src, ConnectPoint dst) {
Yuta HIGUCHI18ab8a92014-10-13 11:16:19 -0700141 return links.get(linkKey(src, dst));
tomd176fc42014-09-08 00:12:30 -0700142 }
143
tom35c0dc32014-09-19 10:00:58 -0700144 @Override
145 public Set<Link> getEgressLinks(ConnectPoint src) {
tomeadbb462014-09-07 16:10:19 -0700146 Set<Link> egress = new HashSet<>();
HIGUCHI Yuta4d973eb2015-02-26 14:28:49 -0800147 synchronized (srcLinks) {
148 for (LinkKey linkKey : srcLinks.get(src.deviceId())) {
149 if (linkKey.src().equals(src)) {
150 egress.add(links.get(linkKey));
151 }
tomeadbb462014-09-07 16:10:19 -0700152 }
153 }
154 return egress;
155 }
156
tom35c0dc32014-09-19 10:00:58 -0700157 @Override
158 public Set<Link> getIngressLinks(ConnectPoint dst) {
tomeadbb462014-09-07 16:10:19 -0700159 Set<Link> ingress = new HashSet<>();
HIGUCHI Yuta4d973eb2015-02-26 14:28:49 -0800160 synchronized (dstLinks) {
161 for (LinkKey linkKey : dstLinks.get(dst.deviceId())) {
162 if (linkKey.dst().equals(dst)) {
163 ingress.add(links.get(linkKey));
164 }
tomeadbb462014-09-07 16:10:19 -0700165 }
166 }
167 return ingress;
168 }
169
tom35c0dc32014-09-19 10:00:58 -0700170 @Override
tomeadbb462014-09-07 16:10:19 -0700171 public LinkEvent createOrUpdateLink(ProviderId providerId,
172 LinkDescription linkDescription) {
Yuta HIGUCHI990aecc2014-10-13 22:21:42 -0700173 LinkKey key = linkKey(linkDescription.src(), linkDescription.dst());
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700174
Yuta HIGUCHIdd841b72014-10-16 13:46:09 -0700175 Map<ProviderId, LinkDescription> descs = getOrCreateLinkDescriptions(key);
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700176 synchronized (descs) {
177 final Link oldLink = links.get(key);
178 // update description
179 createOrUpdateLinkDescription(descs, providerId, linkDescription);
180 final Link newLink = composeLink(descs);
181 if (oldLink == null) {
182 return createLink(key, newLink);
183 }
184 return updateLink(key, oldLink, newLink);
tomeadbb462014-09-07 16:10:19 -0700185 }
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700186 }
187
Thomas Vachuska57126fe2014-11-11 17:13:24 -0800188 @Override
189 public LinkEvent removeOrDownLink(ConnectPoint src, ConnectPoint dst) {
190 Link link = getLink(src, dst);
191 if (link == null) {
192 return null;
193 }
194
Ray Milkey8521f812017-05-24 09:49:26 -0700195 if (link.isExpected()) {
Thomas Vachuska57126fe2014-11-11 17:13:24 -0800196 return link.state() == INACTIVE ? null :
197 updateLink(linkKey(link.src(), link.dst()), link,
Ray Milkey2693bda2016-01-22 16:08:14 -0800198 DefaultLink.builder()
199 .providerId(link.providerId())
200 .src(link.src())
201 .dst(link.dst())
202 .type(link.type())
203 .state(INACTIVE)
204 .isExpected(link.isExpected())
205 .annotations(link.annotations()).build());
Thomas Vachuska57126fe2014-11-11 17:13:24 -0800206 }
207 return removeLink(src, dst);
208 }
209
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700210 // Guarded by linkDescs value (=locking each Link)
211 private LinkDescription createOrUpdateLinkDescription(
Thomas Vachuska57126fe2014-11-11 17:13:24 -0800212 Map<ProviderId, LinkDescription> descs,
213 ProviderId providerId,
214 LinkDescription linkDescription) {
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700215
216 // merge existing attributes and merge
217 LinkDescription oldDesc = descs.get(providerId);
218 LinkDescription newDesc = linkDescription;
219 if (oldDesc != null) {
Yuta HIGUCHIa85542b2014-10-21 19:29:49 -0700220 // we only allow transition from INDIRECT -> DIRECT
221 final Type newType;
222 if (oldDesc.type() == DIRECT) {
223 newType = DIRECT;
224 } else {
225 newType = linkDescription.type();
226 }
Yuta HIGUCHI885be1d2014-10-04 21:47:26 -0700227 SparseAnnotations merged = union(oldDesc.annotations(),
Thomas Vachuska57126fe2014-11-11 17:13:24 -0800228 linkDescription.annotations());
229 newDesc = new DefaultLinkDescription(linkDescription.src(),
230 linkDescription.dst(),
231 newType, merged);
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700232 }
233 return descs.put(providerId, newDesc);
tomeadbb462014-09-07 16:10:19 -0700234 }
235
236 // Creates and stores the link and returns the appropriate event.
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700237 // Guarded by linkDescs value (=locking each Link)
238 private LinkEvent createLink(LinkKey key, Link newLink) {
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700239 links.put(key, newLink);
240 srcLinks.put(newLink.src().deviceId(), key);
241 dstLinks.put(newLink.dst().deviceId(), key);
242 return new LinkEvent(LINK_ADDED, newLink);
tomeadbb462014-09-07 16:10:19 -0700243 }
244
245 // Updates, if necessary the specified link and returns the appropriate event.
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700246 // Guarded by linkDescs value (=locking each Link)
247 private LinkEvent updateLink(LinkKey key, Link oldLink, Link newLink) {
Thomas Vachuska57126fe2014-11-11 17:13:24 -0800248 if (oldLink.state() != newLink.state() ||
249 (oldLink.type() == INDIRECT && newLink.type() == DIRECT) ||
250 !AnnotationsUtil.isEqual(oldLink.annotations(), newLink.annotations())) {
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700251
252 links.put(key, newLink);
Frank Wangd8ab0962017-08-11 11:09:30 +0800253 // strictly speaking following can be omitted
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700254 srcLinks.put(oldLink.src().deviceId(), key);
255 dstLinks.put(oldLink.dst().deviceId(), key);
256 return new LinkEvent(LINK_UPDATED, newLink);
tomd176fc42014-09-08 00:12:30 -0700257 }
tomeadbb462014-09-07 16:10:19 -0700258 return null;
259 }
260
tom35c0dc32014-09-19 10:00:58 -0700261 @Override
262 public LinkEvent removeLink(ConnectPoint src, ConnectPoint dst) {
Yuta HIGUCHI18ab8a92014-10-13 11:16:19 -0700263 final LinkKey key = linkKey(src, dst);
Yuta HIGUCHIdd841b72014-10-16 13:46:09 -0700264 Map<ProviderId, LinkDescription> descs = getOrCreateLinkDescriptions(key);
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700265 synchronized (descs) {
Thomas Vachuska57126fe2014-11-11 17:13:24 -0800266 Link link = links.remove(key);
Yuta HIGUCHI0d6a5e62014-10-03 15:54:09 -0700267 descs.clear();
tomd176fc42014-09-08 00:12:30 -0700268 if (link != null) {
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700269 srcLinks.remove(link.src().deviceId(), key);
270 dstLinks.remove(link.dst().deviceId(), key);
tomd176fc42014-09-08 00:12:30 -0700271 return new LinkEvent(LINK_REMOVED, link);
272 }
273 return null;
tomeadbb462014-09-07 16:10:19 -0700274 }
275 }
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700276
Yuta HIGUCHI800fac62014-12-11 19:23:01 -0800277 /**
278 * Creates concurrent readable, synchronized HashMultimap.
279 *
280 * @return SetMultimap
281 */
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700282 private static <K, V> SetMultimap<K, V> createSynchronizedHashMultiMap() {
Yuta HIGUCHI800fac62014-12-11 19:23:01 -0800283 return synchronizedSetMultimap(
Sho SHIMIZU7a4087b2015-09-10 09:23:16 -0700284 Multimaps.newSetMultimap(new ConcurrentHashMap<>(),
Yuta HIGUCHI800fac62014-12-11 19:23:01 -0800285 () -> Sets.newConcurrentHashSet()));
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700286 }
287
288 /**
289 * @return primary ProviderID, or randomly chosen one if none exists
290 */
Yuta HIGUCHIdd841b72014-10-16 13:46:09 -0700291 // Guarded by linkDescs value (=locking each Link)
292 private ProviderId getBaseProviderId(Map<ProviderId, LinkDescription> providerDescs) {
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700293
294 ProviderId fallBackPrimary = null;
295 for (Entry<ProviderId, LinkDescription> e : providerDescs.entrySet()) {
296 if (!e.getKey().isAncillary()) {
297 return e.getKey();
298 } else if (fallBackPrimary == null) {
299 // pick randomly as a fallback in case there is no primary
300 fallBackPrimary = e.getKey();
301 }
302 }
303 return fallBackPrimary;
304 }
305
Yuta HIGUCHIdd841b72014-10-16 13:46:09 -0700306 // Guarded by linkDescs value (=locking each Link)
307 private Link composeLink(Map<ProviderId, LinkDescription> descs) {
308 ProviderId primary = getBaseProviderId(descs);
309 LinkDescription base = descs.get(verifyNotNull(primary));
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700310
311 ConnectPoint src = base.src();
312 ConnectPoint dst = base.dst();
313 Type type = base.type();
Yuta HIGUCHI885be1d2014-10-04 21:47:26 -0700314 DefaultAnnotations annotations = DefaultAnnotations.builder().build();
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700315 annotations = merge(annotations, base.annotations());
316
317 for (Entry<ProviderId, LinkDescription> e : descs.entrySet()) {
318 if (primary.equals(e.getKey())) {
319 continue;
320 }
321
322 // TODO: should keep track of Description timestamp
323 // and only merge conflicting keys when timestamp is newer
324 // Currently assuming there will never be a key conflict between
325 // providers
326
327 // annotation merging. not so efficient, should revisit later
328 annotations = merge(annotations, e.getValue().annotations());
329 }
330
Thomas Vachuska57126fe2014-11-11 17:13:24 -0800331 boolean isDurable = Objects.equals(annotations.value(AnnotationKeys.DURABLE), "true");
Ray Milkey2693bda2016-01-22 16:08:14 -0800332 return DefaultLink.builder()
333 .providerId(primary)
334 .src(src)
335 .dst(dst)
336 .type(type)
337 .state(ACTIVE)
338 .isExpected(isDurable)
339 .annotations(annotations)
340 .build();
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700341 }
342
Yuta HIGUCHIdd841b72014-10-16 13:46:09 -0700343 private Map<ProviderId, LinkDescription> getOrCreateLinkDescriptions(LinkKey key) {
344 Map<ProviderId, LinkDescription> r;
345 r = linkDescs.get(key);
346 if (r != null) {
347 return r;
348 }
349 r = new HashMap<>();
350 final Map<ProviderId, LinkDescription> concurrentlyAdded;
351 concurrentlyAdded = linkDescs.putIfAbsent(key, r);
352 if (concurrentlyAdded == null) {
353 return r;
354 } else {
355 return concurrentlyAdded;
356 }
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700357 }
358
359 private final Function<LinkKey, Link> lookupLink = new LookupLink();
Thomas Vachuska57126fe2014-11-11 17:13:24 -0800360
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700361 private Function<LinkKey, Link> lookupLink() {
362 return lookupLink;
363 }
364
365 private final class LookupLink implements Function<LinkKey, Link> {
366 @Override
367 public Link apply(LinkKey input) {
Yuta HIGUCHIdd841b72014-10-16 13:46:09 -0700368 if (input == null) {
369 return null;
370 } else {
371 return links.get(input);
372 }
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700373 }
374 }
tom4c6606f2014-09-07 11:11:21 -0700375}