blob: 90ae6fe87c30f0418df4933d7b45a0bcda34fe0c [file] [log] [blame]
Yuta HIGUCHIa8a53eb2014-09-25 17:47:55 -07001package org.onlab.onos.store.link.impl;
2
3import static com.google.common.cache.CacheBuilder.newBuilder;
4import static org.onlab.onos.net.Link.Type.DIRECT;
5import static org.onlab.onos.net.Link.Type.INDIRECT;
Yuta HIGUCHI18ab8a92014-10-13 11:16:19 -07006import static org.onlab.onos.net.LinkKey.linkKey;
Yuta HIGUCHIa8a53eb2014-09-25 17:47:55 -07007import static org.onlab.onos.net.link.LinkEvent.Type.LINK_ADDED;
8import static org.onlab.onos.net.link.LinkEvent.Type.LINK_REMOVED;
9import static org.onlab.onos.net.link.LinkEvent.Type.LINK_UPDATED;
10import static org.slf4j.LoggerFactory.getLogger;
11
12import java.util.HashSet;
13import java.util.Set;
Yuta HIGUCHIb5df76d2014-09-27 20:54:00 -070014
Yuta HIGUCHIa8a53eb2014-09-25 17:47:55 -070015import org.apache.felix.scr.annotations.Activate;
16import org.apache.felix.scr.annotations.Component;
17import org.apache.felix.scr.annotations.Deactivate;
18import org.apache.felix.scr.annotations.Service;
19import org.onlab.onos.net.ConnectPoint;
20import org.onlab.onos.net.DefaultLink;
21import org.onlab.onos.net.DeviceId;
22import org.onlab.onos.net.Link;
23import org.onlab.onos.net.LinkKey;
24import org.onlab.onos.net.link.LinkDescription;
25import org.onlab.onos.net.link.LinkEvent;
26import org.onlab.onos.net.link.LinkStore;
27import org.onlab.onos.net.link.LinkStoreDelegate;
28import org.onlab.onos.net.provider.ProviderId;
Yuta HIGUCHIb5df76d2014-09-27 20:54:00 -070029import org.onlab.onos.store.common.AbsentInvalidatingLoadingCache;
30import org.onlab.onos.store.common.AbstractHazelcastStore;
31import org.onlab.onos.store.common.OptionalCacheLoader;
Yuta HIGUCHIa8a53eb2014-09-25 17:47:55 -070032import org.slf4j.Logger;
33
34import com.google.common.base.Optional;
35import com.google.common.cache.LoadingCache;
36import com.google.common.collect.HashMultimap;
37import com.google.common.collect.ImmutableSet;
38import com.google.common.collect.Multimap;
39import com.google.common.collect.ImmutableSet.Builder;
40import com.hazelcast.core.IMap;
41
Yuta HIGUCHIc99a8d32014-10-02 17:16:34 -070042//TODO: Add support for multiple provider and annotations
Yuta HIGUCHIa8a53eb2014-09-25 17:47:55 -070043/**
44 * Manages inventory of infrastructure links using Hazelcast-backed map.
45 */
46@Component(immediate = true)
47@Service
48public class DistributedLinkStore
Yuta HIGUCHI2e963892014-09-27 13:00:39 -070049 extends AbstractHazelcastStore<LinkEvent, LinkStoreDelegate>
Yuta HIGUCHIa8a53eb2014-09-25 17:47:55 -070050 implements LinkStore {
51
52 private final Logger log = getLogger(getClass());
53
54 // Link inventory
55 private IMap<byte[], byte[]> rawLinks;
56 private LoadingCache<LinkKey, Optional<DefaultLink>> links;
57
58 // TODO synchronize?
59 // Egress and ingress link sets
60 private final Multimap<DeviceId, Link> srcLinks = HashMultimap.create();
61 private final Multimap<DeviceId, Link> dstLinks = HashMultimap.create();
62
Yuta HIGUCHI63ab3ea2014-09-28 22:08:41 -070063 private String linksListener;
64
Yuta HIGUCHIa8a53eb2014-09-25 17:47:55 -070065 @Override
66 @Activate
67 public void activate() {
68 super.activate();
69
70 boolean includeValue = true;
71
72 // TODO decide on Map name scheme to avoid collision
73 rawLinks = theInstance.getMap("links");
74 final OptionalCacheLoader<LinkKey, DefaultLink> linkLoader
Yuta HIGUCHI672488d2014-10-07 09:23:43 -070075 = new OptionalCacheLoader<>(serializer, rawLinks);
Yuta HIGUCHIa8a53eb2014-09-25 17:47:55 -070076 links = new AbsentInvalidatingLoadingCache<>(newBuilder().build(linkLoader));
77 // refresh/populate cache based on notification from other instance
Yuta HIGUCHI63ab3ea2014-09-28 22:08:41 -070078 linksListener = rawLinks.addEntryListener(new RemoteLinkEventHandler(links), includeValue);
Yuta HIGUCHIa8a53eb2014-09-25 17:47:55 -070079
80 loadLinkCache();
81
82 log.info("Started");
83 }
84
85 @Deactivate
86 public void deactivate() {
Yuta HIGUCHI63ab3ea2014-09-28 22:08:41 -070087 rawLinks.removeEntryListener(linksListener);
Yuta HIGUCHIa8a53eb2014-09-25 17:47:55 -070088 log.info("Stopped");
89 }
90
91 private void loadLinkCache() {
92 for (byte[] keyBytes : rawLinks.keySet()) {
93 final LinkKey id = deserialize(keyBytes);
94 links.refresh(id);
95 }
96 }
97
98 @Override
99 public int getLinkCount() {
100 return links.asMap().size();
101 }
102
103 @Override
104 public Iterable<Link> getLinks() {
105 Builder<Link> builder = ImmutableSet.builder();
106 for (Optional<DefaultLink> e : links.asMap().values()) {
107 if (e.isPresent()) {
108 builder.add(e.get());
109 }
110 }
111 return builder.build();
112 }
113
114 @Override
115 public Set<Link> getDeviceEgressLinks(DeviceId deviceId) {
116 return ImmutableSet.copyOf(srcLinks.get(deviceId));
117 }
118
119 @Override
120 public Set<Link> getDeviceIngressLinks(DeviceId deviceId) {
121 return ImmutableSet.copyOf(dstLinks.get(deviceId));
122 }
123
124 @Override
125 public Link getLink(ConnectPoint src, ConnectPoint dst) {
Yuta HIGUCHI18ab8a92014-10-13 11:16:19 -0700126 return links.getUnchecked(linkKey(src, dst)).orNull();
Yuta HIGUCHIa8a53eb2014-09-25 17:47:55 -0700127 }
128
129 @Override
130 public Set<Link> getEgressLinks(ConnectPoint src) {
131 Set<Link> egress = new HashSet<>();
132 for (Link link : srcLinks.get(src.deviceId())) {
133 if (link.src().equals(src)) {
134 egress.add(link);
135 }
136 }
137 return egress;
138 }
139
140 @Override
141 public Set<Link> getIngressLinks(ConnectPoint dst) {
142 Set<Link> ingress = new HashSet<>();
143 for (Link link : dstLinks.get(dst.deviceId())) {
144 if (link.dst().equals(dst)) {
145 ingress.add(link);
146 }
147 }
148 return ingress;
149 }
150
151 @Override
152 public LinkEvent createOrUpdateLink(ProviderId providerId,
153 LinkDescription linkDescription) {
Yuta HIGUCHI990aecc2014-10-13 22:21:42 -0700154 LinkKey key = linkKey(linkDescription.src(), linkDescription.dst());
Yuta HIGUCHIa8a53eb2014-09-25 17:47:55 -0700155 Optional<DefaultLink> link = links.getUnchecked(key);
156 if (!link.isPresent()) {
157 return createLink(providerId, key, linkDescription);
158 }
159 return updateLink(providerId, link.get(), key, linkDescription);
160 }
161
162 // Creates and stores the link and returns the appropriate event.
163 private LinkEvent createLink(ProviderId providerId, LinkKey key,
164 LinkDescription linkDescription) {
165 DefaultLink link = new DefaultLink(providerId, key.src(), key.dst(),
166 linkDescription.type());
167 synchronized (this) {
168 final byte[] keyBytes = serialize(key);
169 rawLinks.put(keyBytes, serialize(link));
170 links.asMap().putIfAbsent(key, Optional.of(link));
171
172 addNewLink(link);
173 }
174 return new LinkEvent(LINK_ADDED, link);
175 }
176
177 // update Egress and ingress link sets
178 private void addNewLink(DefaultLink link) {
179 synchronized (this) {
180 srcLinks.put(link.src().deviceId(), link);
181 dstLinks.put(link.dst().deviceId(), link);
182 }
183 }
184
185 // Updates, if necessary the specified link and returns the appropriate event.
186 private LinkEvent updateLink(ProviderId providerId, DefaultLink link,
187 LinkKey key, LinkDescription linkDescription) {
188 // FIXME confirm Link update condition is OK
189 if (link.type() == INDIRECT && linkDescription.type() == DIRECT) {
190 synchronized (this) {
191
192 DefaultLink updated =
193 new DefaultLink(providerId, link.src(), link.dst(),
194 linkDescription.type());
195 final byte[] keyBytes = serialize(key);
196 rawLinks.put(keyBytes, serialize(updated));
197 links.asMap().replace(key, Optional.of(link), Optional.of(updated));
198
199 replaceLink(link, updated);
200 return new LinkEvent(LINK_UPDATED, updated);
201 }
202 }
203 return null;
204 }
205
206 // update Egress and ingress link sets
207 private void replaceLink(DefaultLink link, DefaultLink updated) {
208 synchronized (this) {
209 srcLinks.remove(link.src().deviceId(), link);
210 dstLinks.remove(link.dst().deviceId(), link);
211
212 srcLinks.put(link.src().deviceId(), updated);
213 dstLinks.put(link.dst().deviceId(), updated);
214 }
215 }
216
217 @Override
218 public LinkEvent removeLink(ConnectPoint src, ConnectPoint dst) {
219 synchronized (this) {
Yuta HIGUCHI18ab8a92014-10-13 11:16:19 -0700220 LinkKey key = linkKey(src, dst);
Yuta HIGUCHIa8a53eb2014-09-25 17:47:55 -0700221 byte[] keyBytes = serialize(key);
222 Link link = deserialize(rawLinks.remove(keyBytes));
223 links.invalidate(key);
224 if (link != null) {
225 removeLink(link);
226 return new LinkEvent(LINK_REMOVED, link);
227 }
228 return null;
229 }
230 }
231
232 // update Egress and ingress link sets
233 private void removeLink(Link link) {
234 synchronized (this) {
235 srcLinks.remove(link.src().deviceId(), link);
236 dstLinks.remove(link.dst().deviceId(), link);
237 }
238 }
239
Yuta HIGUCHIfec9e192014-09-28 14:58:02 -0700240 private class RemoteLinkEventHandler extends RemoteCacheEventHandler<LinkKey, DefaultLink> {
Yuta HIGUCHIa8a53eb2014-09-25 17:47:55 -0700241 public RemoteLinkEventHandler(LoadingCache<LinkKey, Optional<DefaultLink>> cache) {
242 super(cache);
243 }
244
245 @Override
246 protected void onAdd(LinkKey key, DefaultLink newVal) {
247 addNewLink(newVal);
248 notifyDelegate(new LinkEvent(LINK_ADDED, newVal));
249 }
250
251 @Override
252 protected void onUpdate(LinkKey key, DefaultLink oldVal, DefaultLink newVal) {
253 replaceLink(oldVal, newVal);
254 notifyDelegate(new LinkEvent(LINK_UPDATED, newVal));
255 }
256
257 @Override
258 protected void onRemove(LinkKey key, DefaultLink val) {
259 removeLink(val);
260 notifyDelegate(new LinkEvent(LINK_REMOVED, val));
261 }
262 }
263}