blob: 50f4e069378f58a36c2adafc24604792b1cb791e [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;
Yuta HIGUCHI06dc6b92014-09-25 16:06:16 -070022
tom5bcc9462014-09-19 10:11:31 -070023import org.apache.felix.scr.annotations.Activate;
tom35c0dc32014-09-19 10:00:58 -070024import org.apache.felix.scr.annotations.Component;
tom5bcc9462014-09-19 10:11:31 -070025import org.apache.felix.scr.annotations.Deactivate;
tom35c0dc32014-09-19 10:00:58 -070026import org.apache.felix.scr.annotations.Service;
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.SparseAnnotations;
34import org.onlab.onos.net.Link.Type;
Yuta HIGUCHI06dc6b92014-09-25 16:06:16 -070035import org.onlab.onos.net.LinkKey;
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 Vachuska29a6a782014-11-10 21:31:41 -080049import java.util.Objects;
tomeadbb462014-09-07 16:10:19 -070050import java.util.Set;
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070051import java.util.Map.Entry;
tomeadbb462014-09-07 16:10:19 -070052import java.util.concurrent.ConcurrentHashMap;
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070053import java.util.concurrent.ConcurrentMap;
tomeadbb462014-09-07 16:10:19 -070054
Yuta HIGUCHI885be1d2014-10-04 21:47:26 -070055import static org.onlab.onos.net.DefaultAnnotations.union;
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070056import static org.onlab.onos.net.DefaultAnnotations.merge;
tomd176fc42014-09-08 00:12:30 -070057import static org.onlab.onos.net.Link.Type.DIRECT;
58import static org.onlab.onos.net.Link.Type.INDIRECT;
Yuta HIGUCHI18ab8a92014-10-13 11:16:19 -070059import static org.onlab.onos.net.LinkKey.linkKey;
tom35c0dc32014-09-19 10:00:58 -070060import static org.onlab.onos.net.link.LinkEvent.Type.*;
tom5bcc9462014-09-19 10:11:31 -070061import static org.slf4j.LoggerFactory.getLogger;
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070062import static com.google.common.collect.Multimaps.synchronizedSetMultimap;
63import static com.google.common.base.Predicates.notNull;
Yuta HIGUCHIdd841b72014-10-16 13:46:09 -070064import static com.google.common.base.Verify.verifyNotNull;
tomd176fc42014-09-08 00:12:30 -070065
tom4c6606f2014-09-07 11:11:21 -070066/**
tomcbff9392014-09-10 00:45:23 -070067 * Manages inventory of infrastructure links using trivial in-memory structures
tom4c6606f2014-09-07 11:11:21 -070068 * implementation.
69 */
tom35c0dc32014-09-19 10:00:58 -070070@Component(immediate = true)
71@Service
tomf80c9722014-09-24 14:49:18 -070072public class SimpleLinkStore
73 extends AbstractStore<LinkEvent, LinkStoreDelegate>
74 implements LinkStore {
tomeadbb462014-09-07 16:10:19 -070075
tom5bcc9462014-09-19 10:11:31 -070076 private final Logger log = getLogger(getClass());
77
tomeadbb462014-09-07 16:10:19 -070078 // Link inventory
Yuta HIGUCHIdd841b72014-10-16 13:46:09 -070079 private final ConcurrentMap<LinkKey, Map<ProviderId, LinkDescription>>
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070080 linkDescs = new ConcurrentHashMap<>();
81
82 // Link instance cache
83 private final ConcurrentMap<LinkKey, Link> links = new ConcurrentHashMap<>();
tomeadbb462014-09-07 16:10:19 -070084
85 // Egress and ingress link sets
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070086 private final SetMultimap<DeviceId, LinkKey> srcLinks = createSynchronizedHashMultiMap();
87 private final SetMultimap<DeviceId, LinkKey> dstLinks = createSynchronizedHashMultiMap();
88
tomeadbb462014-09-07 16:10:19 -070089
tom5bcc9462014-09-19 10:11:31 -070090 @Activate
91 public void activate() {
92 log.info("Started");
93 }
94
95 @Deactivate
96 public void deactivate() {
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070097 linkDescs.clear();
98 links.clear();
99 srcLinks.clear();
100 dstLinks.clear();
tom5bcc9462014-09-19 10:11:31 -0700101 log.info("Stopped");
102 }
tomeadbb462014-09-07 16:10:19 -0700103
tom35c0dc32014-09-19 10:00:58 -0700104 @Override
105 public int getLinkCount() {
tomeadbb462014-09-07 16:10:19 -0700106 return links.size();
107 }
108
tom35c0dc32014-09-19 10:00:58 -0700109 @Override
110 public Iterable<Link> getLinks() {
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700111 return Collections.unmodifiableCollection(links.values());
tomeadbb462014-09-07 16:10:19 -0700112 }
113
tom35c0dc32014-09-19 10:00:58 -0700114 @Override
115 public Set<Link> getDeviceEgressLinks(DeviceId deviceId) {
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700116 // lock for iteration
117 synchronized (srcLinks) {
118 return FluentIterable.from(srcLinks.get(deviceId))
119 .transform(lookupLink())
120 .filter(notNull())
121 .toSet();
122 }
tomeadbb462014-09-07 16:10:19 -0700123 }
124
tom35c0dc32014-09-19 10:00:58 -0700125 @Override
126 public Set<Link> getDeviceIngressLinks(DeviceId deviceId) {
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700127 // lock for iteration
128 synchronized (dstLinks) {
129 return FluentIterable.from(dstLinks.get(deviceId))
130 .transform(lookupLink())
131 .filter(notNull())
132 .toSet();
133 }
tomeadbb462014-09-07 16:10:19 -0700134 }
135
tom35c0dc32014-09-19 10:00:58 -0700136 @Override
137 public Link getLink(ConnectPoint src, ConnectPoint dst) {
Yuta HIGUCHI18ab8a92014-10-13 11:16:19 -0700138 return links.get(linkKey(src, dst));
tomd176fc42014-09-08 00:12:30 -0700139 }
140
tom35c0dc32014-09-19 10:00:58 -0700141 @Override
142 public Set<Link> getEgressLinks(ConnectPoint src) {
tomeadbb462014-09-07 16:10:19 -0700143 Set<Link> egress = new HashSet<>();
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700144 for (LinkKey linkKey : srcLinks.get(src.deviceId())) {
145 if (linkKey.src().equals(src)) {
146 egress.add(links.get(linkKey));
tomeadbb462014-09-07 16:10:19 -0700147 }
148 }
149 return egress;
150 }
151
tom35c0dc32014-09-19 10:00:58 -0700152 @Override
153 public Set<Link> getIngressLinks(ConnectPoint dst) {
tomeadbb462014-09-07 16:10:19 -0700154 Set<Link> ingress = new HashSet<>();
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700155 for (LinkKey linkKey : dstLinks.get(dst.deviceId())) {
156 if (linkKey.dst().equals(dst)) {
157 ingress.add(links.get(linkKey));
tomeadbb462014-09-07 16:10:19 -0700158 }
159 }
160 return ingress;
161 }
162
tom35c0dc32014-09-19 10:00:58 -0700163 @Override
tomeadbb462014-09-07 16:10:19 -0700164 public LinkEvent createOrUpdateLink(ProviderId providerId,
165 LinkDescription linkDescription) {
Yuta HIGUCHI990aecc2014-10-13 22:21:42 -0700166 LinkKey key = linkKey(linkDescription.src(), linkDescription.dst());
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700167
Yuta HIGUCHIdd841b72014-10-16 13:46:09 -0700168 Map<ProviderId, LinkDescription> descs = getOrCreateLinkDescriptions(key);
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700169 synchronized (descs) {
170 final Link oldLink = links.get(key);
171 // update description
172 createOrUpdateLinkDescription(descs, providerId, linkDescription);
173 final Link newLink = composeLink(descs);
174 if (oldLink == null) {
175 return createLink(key, newLink);
176 }
177 return updateLink(key, oldLink, newLink);
tomeadbb462014-09-07 16:10:19 -0700178 }
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700179 }
180
181 // Guarded by linkDescs value (=locking each Link)
182 private LinkDescription createOrUpdateLinkDescription(
Yuta HIGUCHIdd841b72014-10-16 13:46:09 -0700183 Map<ProviderId, LinkDescription> descs,
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700184 ProviderId providerId,
185 LinkDescription linkDescription) {
186
187 // merge existing attributes and merge
188 LinkDescription oldDesc = descs.get(providerId);
189 LinkDescription newDesc = linkDescription;
190 if (oldDesc != null) {
Yuta HIGUCHIa85542b2014-10-21 19:29:49 -0700191 // we only allow transition from INDIRECT -> DIRECT
192 final Type newType;
193 if (oldDesc.type() == DIRECT) {
194 newType = DIRECT;
195 } else {
196 newType = linkDescription.type();
197 }
Yuta HIGUCHI885be1d2014-10-04 21:47:26 -0700198 SparseAnnotations merged = union(oldDesc.annotations(),
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700199 linkDescription.annotations());
200 newDesc = new DefaultLinkDescription(
201 linkDescription.src(),
202 linkDescription.dst(),
Yuta HIGUCHIa85542b2014-10-21 19:29:49 -0700203 newType, merged);
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700204 }
205 return descs.put(providerId, newDesc);
tomeadbb462014-09-07 16:10:19 -0700206 }
207
208 // Creates and stores the link and returns the appropriate event.
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700209 // Guarded by linkDescs value (=locking each Link)
210 private LinkEvent createLink(LinkKey key, Link newLink) {
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700211 links.put(key, newLink);
212 srcLinks.put(newLink.src().deviceId(), key);
213 dstLinks.put(newLink.dst().deviceId(), key);
214 return new LinkEvent(LINK_ADDED, newLink);
tomeadbb462014-09-07 16:10:19 -0700215 }
216
217 // Updates, if necessary the specified link and returns the appropriate event.
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700218 // Guarded by linkDescs value (=locking each Link)
219 private LinkEvent updateLink(LinkKey key, Link oldLink, Link newLink) {
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700220 if ((oldLink.type() == INDIRECT && newLink.type() == DIRECT) ||
221 !AnnotationsUtil.isEqual(oldLink.annotations(), newLink.annotations())) {
222
223 links.put(key, newLink);
224 // strictly speaking following can be ommitted
225 srcLinks.put(oldLink.src().deviceId(), key);
226 dstLinks.put(oldLink.dst().deviceId(), key);
227 return new LinkEvent(LINK_UPDATED, newLink);
tomd176fc42014-09-08 00:12:30 -0700228 }
tomeadbb462014-09-07 16:10:19 -0700229 return null;
230 }
231
tom35c0dc32014-09-19 10:00:58 -0700232 @Override
233 public LinkEvent removeLink(ConnectPoint src, ConnectPoint dst) {
Yuta HIGUCHI18ab8a92014-10-13 11:16:19 -0700234 final LinkKey key = linkKey(src, dst);
Yuta HIGUCHIdd841b72014-10-16 13:46:09 -0700235 Map<ProviderId, LinkDescription> descs = getOrCreateLinkDescriptions(key);
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700236 synchronized (descs) {
Thomas Vachuska29a6a782014-11-10 21:31:41 -0800237 Link link = links.get(key);
238 if (isDurable(link)) {
239 return null;
240 }
241 links.remove(key);
Yuta HIGUCHI0d6a5e62014-10-03 15:54:09 -0700242 descs.clear();
tomd176fc42014-09-08 00:12:30 -0700243 if (link != null) {
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700244 srcLinks.remove(link.src().deviceId(), key);
245 dstLinks.remove(link.dst().deviceId(), key);
tomd176fc42014-09-08 00:12:30 -0700246 return new LinkEvent(LINK_REMOVED, link);
247 }
248 return null;
tomeadbb462014-09-07 16:10:19 -0700249 }
250 }
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700251
Thomas Vachuska29a6a782014-11-10 21:31:41 -0800252 // Indicates if the link has been marked as durable via annotations.
253 private boolean isDurable(Link link) {
254 return link != null && Objects.equals(link.annotations().value("durable"), "true");
255 }
256
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700257 private static <K, V> SetMultimap<K, V> createSynchronizedHashMultiMap() {
258 return synchronizedSetMultimap(HashMultimap.<K, V>create());
259 }
260
261 /**
262 * @return primary ProviderID, or randomly chosen one if none exists
263 */
Yuta HIGUCHIdd841b72014-10-16 13:46:09 -0700264 // Guarded by linkDescs value (=locking each Link)
265 private ProviderId getBaseProviderId(Map<ProviderId, LinkDescription> providerDescs) {
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700266
267 ProviderId fallBackPrimary = null;
268 for (Entry<ProviderId, LinkDescription> e : providerDescs.entrySet()) {
269 if (!e.getKey().isAncillary()) {
270 return e.getKey();
271 } else if (fallBackPrimary == null) {
272 // pick randomly as a fallback in case there is no primary
273 fallBackPrimary = e.getKey();
274 }
275 }
276 return fallBackPrimary;
277 }
278
Yuta HIGUCHIdd841b72014-10-16 13:46:09 -0700279 // Guarded by linkDescs value (=locking each Link)
280 private Link composeLink(Map<ProviderId, LinkDescription> descs) {
281 ProviderId primary = getBaseProviderId(descs);
282 LinkDescription base = descs.get(verifyNotNull(primary));
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700283
284 ConnectPoint src = base.src();
285 ConnectPoint dst = base.dst();
286 Type type = base.type();
Yuta HIGUCHI885be1d2014-10-04 21:47:26 -0700287 DefaultAnnotations annotations = DefaultAnnotations.builder().build();
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700288 annotations = merge(annotations, base.annotations());
289
290 for (Entry<ProviderId, LinkDescription> e : descs.entrySet()) {
291 if (primary.equals(e.getKey())) {
292 continue;
293 }
294
295 // TODO: should keep track of Description timestamp
296 // and only merge conflicting keys when timestamp is newer
297 // Currently assuming there will never be a key conflict between
298 // providers
299
300 // annotation merging. not so efficient, should revisit later
301 annotations = merge(annotations, e.getValue().annotations());
302 }
303
304 return new DefaultLink(primary , src, dst, type, annotations);
305 }
306
Yuta HIGUCHIdd841b72014-10-16 13:46:09 -0700307 private Map<ProviderId, LinkDescription> getOrCreateLinkDescriptions(LinkKey key) {
308 Map<ProviderId, LinkDescription> r;
309 r = linkDescs.get(key);
310 if (r != null) {
311 return r;
312 }
313 r = new HashMap<>();
314 final Map<ProviderId, LinkDescription> concurrentlyAdded;
315 concurrentlyAdded = linkDescs.putIfAbsent(key, r);
316 if (concurrentlyAdded == null) {
317 return r;
318 } else {
319 return concurrentlyAdded;
320 }
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700321 }
322
323 private final Function<LinkKey, Link> lookupLink = new LookupLink();
324 private Function<LinkKey, Link> lookupLink() {
325 return lookupLink;
326 }
327
328 private final class LookupLink implements Function<LinkKey, Link> {
329 @Override
330 public Link apply(LinkKey input) {
Yuta HIGUCHIdd841b72014-10-16 13:46:09 -0700331 if (input == null) {
332 return null;
333 } else {
334 return links.get(input);
335 }
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700336 }
337 }
tom4c6606f2014-09-07 11:11:21 -0700338}