blob: 5dec00c389a96b2022186da02d406fca33d536aa [file] [log] [blame]
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07001/*
2 * Copyright 2014 Open Networking Laboratory
3 *
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 */
tomea961ff2014-10-01 12:45:15 -070016package org.onlab.onos.store.trivial.impl;
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;
tomeadbb462014-09-07 16:10:19 -070020import com.google.common.collect.HashMultimap;
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070021import com.google.common.collect.SetMultimap;
tom5bcc9462014-09-19 10:11:31 -070022import org.apache.felix.scr.annotations.Activate;
tom35c0dc32014-09-19 10:00:58 -070023import org.apache.felix.scr.annotations.Component;
tom5bcc9462014-09-19 10:11:31 -070024import org.apache.felix.scr.annotations.Deactivate;
tom35c0dc32014-09-19 10:00:58 -070025import org.apache.felix.scr.annotations.Service;
Thomas Vachuska57126fe2014-11-11 17:13:24 -080026import org.onlab.onos.net.AnnotationKeys;
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070027import org.onlab.onos.net.AnnotationsUtil;
tomeadbb462014-09-07 16:10:19 -070028import org.onlab.onos.net.ConnectPoint;
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070029import org.onlab.onos.net.DefaultAnnotations;
tomeadbb462014-09-07 16:10:19 -070030import org.onlab.onos.net.DefaultLink;
31import org.onlab.onos.net.DeviceId;
32import org.onlab.onos.net.Link;
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070033import org.onlab.onos.net.Link.Type;
Yuta HIGUCHI06dc6b92014-09-25 16:06:16 -070034import org.onlab.onos.net.LinkKey;
Thomas Vachuska57126fe2014-11-11 17:13:24 -080035import org.onlab.onos.net.SparseAnnotations;
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070036import org.onlab.onos.net.link.DefaultLinkDescription;
tomeadbb462014-09-07 16:10:19 -070037import org.onlab.onos.net.link.LinkDescription;
38import org.onlab.onos.net.link.LinkEvent;
tom35c0dc32014-09-19 10:00:58 -070039import org.onlab.onos.net.link.LinkStore;
tomf80c9722014-09-24 14:49:18 -070040import org.onlab.onos.net.link.LinkStoreDelegate;
tomeadbb462014-09-07 16:10:19 -070041import org.onlab.onos.net.provider.ProviderId;
tomf80c9722014-09-24 14:49:18 -070042import org.onlab.onos.store.AbstractStore;
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;
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070058import static org.onlab.onos.net.DefaultAnnotations.merge;
Thomas Vachuska57126fe2014-11-11 17:13:24 -080059import static org.onlab.onos.net.DefaultAnnotations.union;
60import static org.onlab.onos.net.Link.State.ACTIVE;
61import static org.onlab.onos.net.Link.State.INACTIVE;
tomd176fc42014-09-08 00:12:30 -070062import static org.onlab.onos.net.Link.Type.DIRECT;
63import static org.onlab.onos.net.Link.Type.INDIRECT;
Yuta HIGUCHI18ab8a92014-10-13 11:16:19 -070064import static org.onlab.onos.net.LinkKey.linkKey;
tom35c0dc32014-09-19 10:00:58 -070065import static org.onlab.onos.net.link.LinkEvent.Type.*;
tom5bcc9462014-09-19 10:11:31 -070066import static org.slf4j.LoggerFactory.getLogger;
tomd176fc42014-09-08 00:12:30 -070067
tom4c6606f2014-09-07 11:11:21 -070068/**
tomcbff9392014-09-10 00:45:23 -070069 * Manages inventory of infrastructure links using trivial in-memory structures
tom4c6606f2014-09-07 11:11:21 -070070 * implementation.
71 */
tom35c0dc32014-09-19 10:00:58 -070072@Component(immediate = true)
73@Service
tomf80c9722014-09-24 14:49:18 -070074public class SimpleLinkStore
75 extends AbstractStore<LinkEvent, LinkStoreDelegate>
76 implements LinkStore {
tomeadbb462014-09-07 16:10:19 -070077
tom5bcc9462014-09-19 10:11:31 -070078 private final Logger log = getLogger(getClass());
79
tomeadbb462014-09-07 16:10:19 -070080 // Link inventory
Yuta HIGUCHIdd841b72014-10-16 13:46:09 -070081 private final ConcurrentMap<LinkKey, Map<ProviderId, LinkDescription>>
Thomas Vachuska57126fe2014-11-11 17:13:24 -080082 linkDescs = new ConcurrentHashMap<>();
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070083
84 // Link instance cache
85 private final ConcurrentMap<LinkKey, Link> links = new ConcurrentHashMap<>();
tomeadbb462014-09-07 16:10:19 -070086
87 // Egress and ingress link sets
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070088 private final SetMultimap<DeviceId, LinkKey> srcLinks = createSynchronizedHashMultiMap();
89 private final SetMultimap<DeviceId, LinkKey> dstLinks = createSynchronizedHashMultiMap();
90
tomeadbb462014-09-07 16:10:19 -070091
tom5bcc9462014-09-19 10:11:31 -070092 @Activate
93 public void activate() {
94 log.info("Started");
95 }
96
97 @Deactivate
98 public void deactivate() {
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070099 linkDescs.clear();
100 links.clear();
101 srcLinks.clear();
102 dstLinks.clear();
tom5bcc9462014-09-19 10:11:31 -0700103 log.info("Stopped");
104 }
tomeadbb462014-09-07 16:10:19 -0700105
tom35c0dc32014-09-19 10:00:58 -0700106 @Override
107 public int getLinkCount() {
tomeadbb462014-09-07 16:10:19 -0700108 return links.size();
109 }
110
tom35c0dc32014-09-19 10:00:58 -0700111 @Override
112 public Iterable<Link> getLinks() {
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700113 return Collections.unmodifiableCollection(links.values());
tomeadbb462014-09-07 16:10:19 -0700114 }
115
tom35c0dc32014-09-19 10:00:58 -0700116 @Override
117 public Set<Link> getDeviceEgressLinks(DeviceId deviceId) {
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700118 // lock for iteration
119 synchronized (srcLinks) {
120 return FluentIterable.from(srcLinks.get(deviceId))
Thomas Vachuska57126fe2014-11-11 17:13:24 -0800121 .transform(lookupLink())
122 .filter(notNull())
123 .toSet();
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700124 }
tomeadbb462014-09-07 16:10:19 -0700125 }
126
tom35c0dc32014-09-19 10:00:58 -0700127 @Override
128 public Set<Link> getDeviceIngressLinks(DeviceId deviceId) {
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700129 // lock for iteration
130 synchronized (dstLinks) {
131 return FluentIterable.from(dstLinks.get(deviceId))
Thomas Vachuska57126fe2014-11-11 17:13:24 -0800132 .transform(lookupLink())
133 .filter(notNull())
134 .toSet();
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700135 }
tomeadbb462014-09-07 16:10:19 -0700136 }
137
tom35c0dc32014-09-19 10:00:58 -0700138 @Override
139 public Link getLink(ConnectPoint src, ConnectPoint dst) {
Yuta HIGUCHI18ab8a92014-10-13 11:16:19 -0700140 return links.get(linkKey(src, dst));
tomd176fc42014-09-08 00:12:30 -0700141 }
142
tom35c0dc32014-09-19 10:00:58 -0700143 @Override
144 public Set<Link> getEgressLinks(ConnectPoint src) {
tomeadbb462014-09-07 16:10:19 -0700145 Set<Link> egress = new HashSet<>();
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700146 for (LinkKey linkKey : srcLinks.get(src.deviceId())) {
147 if (linkKey.src().equals(src)) {
148 egress.add(links.get(linkKey));
tomeadbb462014-09-07 16:10:19 -0700149 }
150 }
151 return egress;
152 }
153
tom35c0dc32014-09-19 10:00:58 -0700154 @Override
155 public Set<Link> getIngressLinks(ConnectPoint dst) {
tomeadbb462014-09-07 16:10:19 -0700156 Set<Link> ingress = new HashSet<>();
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700157 for (LinkKey linkKey : dstLinks.get(dst.deviceId())) {
158 if (linkKey.dst().equals(dst)) {
159 ingress.add(links.get(linkKey));
tomeadbb462014-09-07 16:10:19 -0700160 }
161 }
162 return ingress;
163 }
164
tom35c0dc32014-09-19 10:00:58 -0700165 @Override
tomeadbb462014-09-07 16:10:19 -0700166 public LinkEvent createOrUpdateLink(ProviderId providerId,
167 LinkDescription linkDescription) {
Yuta HIGUCHI990aecc2014-10-13 22:21:42 -0700168 LinkKey key = linkKey(linkDescription.src(), linkDescription.dst());
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700169
Yuta HIGUCHIdd841b72014-10-16 13:46:09 -0700170 Map<ProviderId, LinkDescription> descs = getOrCreateLinkDescriptions(key);
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700171 synchronized (descs) {
172 final Link oldLink = links.get(key);
173 // update description
174 createOrUpdateLinkDescription(descs, providerId, linkDescription);
175 final Link newLink = composeLink(descs);
176 if (oldLink == null) {
177 return createLink(key, newLink);
178 }
179 return updateLink(key, oldLink, newLink);
tomeadbb462014-09-07 16:10:19 -0700180 }
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700181 }
182
Thomas Vachuska57126fe2014-11-11 17:13:24 -0800183 @Override
184 public LinkEvent removeOrDownLink(ConnectPoint src, ConnectPoint dst) {
185 Link link = getLink(src, dst);
186 if (link == null) {
187 return null;
188 }
189
190 if (link.isDurable()) {
191 return link.state() == INACTIVE ? null :
192 updateLink(linkKey(link.src(), link.dst()), link,
193 new DefaultLink(link.providerId(),
194 link.src(), link.dst(),
195 link.type(), INACTIVE,
196 link.isDurable(),
197 link.annotations()));
198 }
199 return removeLink(src, dst);
200 }
201
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700202 // Guarded by linkDescs value (=locking each Link)
203 private LinkDescription createOrUpdateLinkDescription(
Thomas Vachuska57126fe2014-11-11 17:13:24 -0800204 Map<ProviderId, LinkDescription> descs,
205 ProviderId providerId,
206 LinkDescription linkDescription) {
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700207
208 // merge existing attributes and merge
209 LinkDescription oldDesc = descs.get(providerId);
210 LinkDescription newDesc = linkDescription;
211 if (oldDesc != null) {
Yuta HIGUCHIa85542b2014-10-21 19:29:49 -0700212 // we only allow transition from INDIRECT -> DIRECT
213 final Type newType;
214 if (oldDesc.type() == DIRECT) {
215 newType = DIRECT;
216 } else {
217 newType = linkDescription.type();
218 }
Yuta HIGUCHI885be1d2014-10-04 21:47:26 -0700219 SparseAnnotations merged = union(oldDesc.annotations(),
Thomas Vachuska57126fe2014-11-11 17:13:24 -0800220 linkDescription.annotations());
221 newDesc = new DefaultLinkDescription(linkDescription.src(),
222 linkDescription.dst(),
223 newType, merged);
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700224 }
225 return descs.put(providerId, newDesc);
tomeadbb462014-09-07 16:10:19 -0700226 }
227
228 // Creates and stores the link and returns the appropriate event.
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700229 // Guarded by linkDescs value (=locking each Link)
230 private LinkEvent createLink(LinkKey key, Link newLink) {
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700231 links.put(key, newLink);
232 srcLinks.put(newLink.src().deviceId(), key);
233 dstLinks.put(newLink.dst().deviceId(), key);
234 return new LinkEvent(LINK_ADDED, newLink);
tomeadbb462014-09-07 16:10:19 -0700235 }
236
237 // Updates, if necessary the specified link and returns the appropriate event.
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700238 // Guarded by linkDescs value (=locking each Link)
239 private LinkEvent updateLink(LinkKey key, Link oldLink, Link newLink) {
Thomas Vachuska57126fe2014-11-11 17:13:24 -0800240 if (oldLink.state() != newLink.state() ||
241 (oldLink.type() == INDIRECT && newLink.type() == DIRECT) ||
242 !AnnotationsUtil.isEqual(oldLink.annotations(), newLink.annotations())) {
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700243
244 links.put(key, newLink);
245 // strictly speaking following can be ommitted
246 srcLinks.put(oldLink.src().deviceId(), key);
247 dstLinks.put(oldLink.dst().deviceId(), key);
248 return new LinkEvent(LINK_UPDATED, newLink);
tomd176fc42014-09-08 00:12:30 -0700249 }
tomeadbb462014-09-07 16:10:19 -0700250 return null;
251 }
252
tom35c0dc32014-09-19 10:00:58 -0700253 @Override
254 public LinkEvent removeLink(ConnectPoint src, ConnectPoint dst) {
Yuta HIGUCHI18ab8a92014-10-13 11:16:19 -0700255 final LinkKey key = linkKey(src, dst);
Yuta HIGUCHIdd841b72014-10-16 13:46:09 -0700256 Map<ProviderId, LinkDescription> descs = getOrCreateLinkDescriptions(key);
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700257 synchronized (descs) {
Thomas Vachuska57126fe2014-11-11 17:13:24 -0800258 Link link = links.remove(key);
Yuta HIGUCHI0d6a5e62014-10-03 15:54:09 -0700259 descs.clear();
tomd176fc42014-09-08 00:12:30 -0700260 if (link != null) {
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700261 srcLinks.remove(link.src().deviceId(), key);
262 dstLinks.remove(link.dst().deviceId(), key);
tomd176fc42014-09-08 00:12:30 -0700263 return new LinkEvent(LINK_REMOVED, link);
264 }
265 return null;
tomeadbb462014-09-07 16:10:19 -0700266 }
267 }
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700268
269 private static <K, V> SetMultimap<K, V> createSynchronizedHashMultiMap() {
270 return synchronizedSetMultimap(HashMultimap.<K, V>create());
271 }
272
273 /**
274 * @return primary ProviderID, or randomly chosen one if none exists
275 */
Yuta HIGUCHIdd841b72014-10-16 13:46:09 -0700276 // Guarded by linkDescs value (=locking each Link)
277 private ProviderId getBaseProviderId(Map<ProviderId, LinkDescription> providerDescs) {
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700278
279 ProviderId fallBackPrimary = null;
280 for (Entry<ProviderId, LinkDescription> e : providerDescs.entrySet()) {
281 if (!e.getKey().isAncillary()) {
282 return e.getKey();
283 } else if (fallBackPrimary == null) {
284 // pick randomly as a fallback in case there is no primary
285 fallBackPrimary = e.getKey();
286 }
287 }
288 return fallBackPrimary;
289 }
290
Yuta HIGUCHIdd841b72014-10-16 13:46:09 -0700291 // Guarded by linkDescs value (=locking each Link)
292 private Link composeLink(Map<ProviderId, LinkDescription> descs) {
293 ProviderId primary = getBaseProviderId(descs);
294 LinkDescription base = descs.get(verifyNotNull(primary));
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700295
296 ConnectPoint src = base.src();
297 ConnectPoint dst = base.dst();
298 Type type = base.type();
Yuta HIGUCHI885be1d2014-10-04 21:47:26 -0700299 DefaultAnnotations annotations = DefaultAnnotations.builder().build();
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700300 annotations = merge(annotations, base.annotations());
301
302 for (Entry<ProviderId, LinkDescription> e : descs.entrySet()) {
303 if (primary.equals(e.getKey())) {
304 continue;
305 }
306
307 // TODO: should keep track of Description timestamp
308 // and only merge conflicting keys when timestamp is newer
309 // Currently assuming there will never be a key conflict between
310 // providers
311
312 // annotation merging. not so efficient, should revisit later
313 annotations = merge(annotations, e.getValue().annotations());
314 }
315
Thomas Vachuska57126fe2014-11-11 17:13:24 -0800316 boolean isDurable = Objects.equals(annotations.value(AnnotationKeys.DURABLE), "true");
Thomas Vachuskabadb93f2014-11-15 23:51:17 -0800317 return new DefaultLink(primary, src, dst, type, ACTIVE, isDurable, annotations);
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700318 }
319
Yuta HIGUCHIdd841b72014-10-16 13:46:09 -0700320 private Map<ProviderId, LinkDescription> getOrCreateLinkDescriptions(LinkKey key) {
321 Map<ProviderId, LinkDescription> r;
322 r = linkDescs.get(key);
323 if (r != null) {
324 return r;
325 }
326 r = new HashMap<>();
327 final Map<ProviderId, LinkDescription> concurrentlyAdded;
328 concurrentlyAdded = linkDescs.putIfAbsent(key, r);
329 if (concurrentlyAdded == null) {
330 return r;
331 } else {
332 return concurrentlyAdded;
333 }
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700334 }
335
336 private final Function<LinkKey, Link> lookupLink = new LookupLink();
Thomas Vachuska57126fe2014-11-11 17:13:24 -0800337
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700338 private Function<LinkKey, Link> lookupLink() {
339 return lookupLink;
340 }
341
342 private final class LookupLink implements Function<LinkKey, Link> {
343 @Override
344 public Link apply(LinkKey input) {
Yuta HIGUCHIdd841b72014-10-16 13:46:09 -0700345 if (input == null) {
346 return null;
347 } else {
348 return links.get(input);
349 }
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700350 }
351 }
tom4c6606f2014-09-07 11:11:21 -0700352}