blob: a2e8a0984215571c246abba50461be61501de247 [file] [log] [blame]
Madan Jampani38a88212015-09-15 11:21:27 -07001/*
2 * Copyright 2015 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 */
16package org.onosproject.store.host.impl;
17
alshabib8a4a6002015-11-25 14:31:16 -080018import com.google.common.collect.ImmutableSet;
19import com.google.common.collect.Sets;
Madan Jampani38a88212015-09-15 11:21:27 -070020import org.apache.felix.scr.annotations.Activate;
21import org.apache.felix.scr.annotations.Component;
22import org.apache.felix.scr.annotations.Deactivate;
23import org.apache.felix.scr.annotations.Reference;
24import org.apache.felix.scr.annotations.ReferenceCardinality;
25import org.apache.felix.scr.annotations.Service;
26import org.onlab.packet.IpAddress;
27import org.onlab.packet.MacAddress;
28import org.onlab.packet.VlanId;
29import org.onlab.util.KryoNamespace;
30import org.onosproject.net.Annotations;
31import org.onosproject.net.ConnectPoint;
32import org.onosproject.net.DefaultAnnotations;
33import org.onosproject.net.DefaultHost;
34import org.onosproject.net.DeviceId;
35import org.onosproject.net.Host;
36import org.onosproject.net.HostId;
Brian O'Connorf107bd72015-09-21 15:31:03 -070037import org.onosproject.net.HostLocation;
Madan Jampani38a88212015-09-15 11:21:27 -070038import org.onosproject.net.host.HostDescription;
39import org.onosproject.net.host.HostEvent;
40import org.onosproject.net.host.HostStore;
41import org.onosproject.net.host.HostStoreDelegate;
Madan Jampani38a88212015-09-15 11:21:27 -070042import org.onosproject.net.provider.ProviderId;
43import org.onosproject.store.AbstractStore;
44import org.onosproject.store.serializers.KryoNamespaces;
alshabib8a4a6002015-11-25 14:31:16 -080045import org.onosproject.store.service.ConsistentMap;
46import org.onosproject.store.service.MapEvent;
47import org.onosproject.store.service.MapEventListener;
48import org.onosproject.store.service.Serializer;
Madan Jampani38a88212015-09-15 11:21:27 -070049import org.onosproject.store.service.StorageService;
50import org.slf4j.Logger;
51
alshabib8a4a6002015-11-25 14:31:16 -080052import java.util.Collection;
53import java.util.HashSet;
54import java.util.Map;
55import java.util.Objects;
56import java.util.Set;
57import java.util.concurrent.ConcurrentHashMap;
58import java.util.function.Predicate;
59import java.util.stream.Collectors;
60
61import static com.google.common.base.Preconditions.checkNotNull;
62import static com.google.common.base.Preconditions.checkState;
63import static org.onosproject.net.DefaultAnnotations.merge;
64import static org.onosproject.net.host.HostEvent.Type.*;
65import static org.slf4j.LoggerFactory.getLogger;
Madan Jampani38a88212015-09-15 11:21:27 -070066
67/**
68 * Manages the inventory of hosts using a {@code EventuallyConsistentMap}.
69 */
70@Component(immediate = true)
71@Service
alshabib8a4a6002015-11-25 14:31:16 -080072public class DistributedHostStore
Madan Jampani38a88212015-09-15 11:21:27 -070073 extends AbstractStore<HostEvent, HostStoreDelegate>
74 implements HostStore {
75
76 private final Logger log = getLogger(getClass());
77
78 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
79 protected StorageService storageService;
80
alshabib8a4a6002015-11-25 14:31:16 -080081 private ConsistentMap<HostId, DefaultHost> host;
82 private Map<HostId, DefaultHost> hosts;
Madan Jampani38a88212015-09-15 11:21:27 -070083
Charles Chan33f28a92015-11-13 13:12:38 -080084 private final ConcurrentHashMap<HostId, DefaultHost> prevHosts =
Charles Chan009c3082015-11-10 14:18:04 -080085 new ConcurrentHashMap<>();
86
alshabib8a4a6002015-11-25 14:31:16 -080087 private MapEventListener<HostId, DefaultHost> hostLocationTracker =
Madan Jampani38a88212015-09-15 11:21:27 -070088 new HostLocationTracker();
89
90 @Activate
91 public void activate() {
92 KryoNamespace.Builder hostSerializer = KryoNamespace.newBuilder()
93 .register(KryoNamespaces.API);
94
alshabib8a4a6002015-11-25 14:31:16 -080095 host = storageService.<HostId, DefaultHost>consistentMapBuilder()
Madan Jampani38a88212015-09-15 11:21:27 -070096 .withName("onos-hosts")
alshabib8a4a6002015-11-25 14:31:16 -080097 .withRelaxedReadConsistency()
98 .withSerializer(Serializer.using(hostSerializer.build()))
Madan Jampani38a88212015-09-15 11:21:27 -070099 .build();
100
alshabib8a4a6002015-11-25 14:31:16 -0800101 hosts = host.asJavaMap();
102
103 host.addListener(hostLocationTracker);
Madan Jampani38a88212015-09-15 11:21:27 -0700104
105 log.info("Started");
106 }
107
108 @Deactivate
109 public void deactivate() {
alshabib8a4a6002015-11-25 14:31:16 -0800110 host.removeListener(hostLocationTracker);
Charles Chan33f28a92015-11-13 13:12:38 -0800111 prevHosts.clear();
Madan Jampani38a88212015-09-15 11:21:27 -0700112
113 log.info("Stopped");
114 }
115
Brian O'Connordab09742015-12-07 20:06:29 -0800116 private boolean shouldUpdate(DefaultHost existingHost,
117 ProviderId providerId,
118 HostId hostId,
119 HostDescription hostDescription,
120 boolean replaceIPs) {
121 if (existingHost == null) {
122 return true;
123 }
124
125 if (!Objects.equals(existingHost.providerId(), providerId) ||
126 !Objects.equals(existingHost.mac(), hostDescription.hwAddress()) ||
127 !Objects.equals(existingHost.vlan(), hostDescription.vlan()) ||
128 !Objects.equals(existingHost.location(), hostDescription.location())) {
129 return true;
130 }
131
132 if (replaceIPs) {
133 if (!Objects.equals(hostDescription.ipAddress(),
134 existingHost.ipAddresses())) {
135 return true;
136 }
137 } else {
138 if (!existingHost.ipAddresses().containsAll(hostDescription.ipAddress())) {
139 return true;
140 }
141 }
142
143 // check to see if any of the annotations provided by hostDescription
144 // differ from those in the existing host
145 return hostDescription.annotations().keys().stream()
146 .anyMatch(k -> !Objects.equals(hostDescription.annotations().value(k),
147 existingHost.annotations().value(k)));
148
149
150 }
151
Charles Chan009c3082015-11-10 14:18:04 -0800152 // TODO No longer need to return HostEvent
Madan Jampani38a88212015-09-15 11:21:27 -0700153 @Override
154 public HostEvent createOrUpdateHost(ProviderId providerId,
Brian O'Connorf107bd72015-09-21 15:31:03 -0700155 HostId hostId,
156 HostDescription hostDescription,
157 boolean replaceIPs) {
158 // TODO: We need a way to detect conflicting changes and abort update.
Brian O'Connordab09742015-12-07 20:06:29 -0800159 host.computeIf(hostId,
160 existingHost -> shouldUpdate(existingHost, providerId, hostId,
161 hostDescription, replaceIPs),
162 (id, existingHost) -> {
Brian O'Connorf107bd72015-09-21 15:31:03 -0700163 HostLocation location = hostDescription.location();
164
165 final Set<IpAddress> addresses;
166 if (existingHost == null || replaceIPs) {
167 addresses = ImmutableSet.copyOf(hostDescription.ipAddress());
168 } else {
169 addresses = Sets.newHashSet(existingHost.ipAddresses());
170 addresses.addAll(hostDescription.ipAddress());
171 }
172
173 final Annotations annotations;
174 if (existingHost != null) {
175 annotations = merge((DefaultAnnotations) existingHost.annotations(),
176 hostDescription.annotations());
177 } else {
178 annotations = hostDescription.annotations();
179 }
180
Brian O'Connorf107bd72015-09-21 15:31:03 -0700181 return new DefaultHost(providerId,
182 hostId,
183 hostDescription.hwAddress(),
184 hostDescription.vlan(),
185 location,
186 addresses,
187 annotations);
188 });
189
Charles Chan009c3082015-11-10 14:18:04 -0800190 return null;
Madan Jampani38a88212015-09-15 11:21:27 -0700191 }
192
Charles Chan009c3082015-11-10 14:18:04 -0800193 // TODO No longer need to return HostEvent
Madan Jampani38a88212015-09-15 11:21:27 -0700194 @Override
195 public HostEvent removeHost(HostId hostId) {
Charles Chan009c3082015-11-10 14:18:04 -0800196 hosts.remove(hostId);
197 return null;
Madan Jampani38a88212015-09-15 11:21:27 -0700198 }
199
Charles Chan009c3082015-11-10 14:18:04 -0800200 // TODO No longer need to return HostEvent
Madan Jampani38a88212015-09-15 11:21:27 -0700201 @Override
samanwita palc40e5ed2015-09-24 11:01:51 -0700202 public HostEvent removeIp(HostId hostId, IpAddress ipAddress) {
Charles Chan009c3082015-11-10 14:18:04 -0800203 hosts.compute(hostId, (id, existingHost) -> {
samanwita palc40e5ed2015-09-24 11:01:51 -0700204 if (existingHost != null) {
205 checkState(Objects.equals(hostId.mac(), existingHost.mac()),
206 "Existing and new MAC addresses differ.");
207 checkState(Objects.equals(hostId.vlanId(), existingHost.vlan()),
208 "Existing and new VLANs differ.");
209
samanwita pale7c08de2015-09-24 21:59:49 -0700210 Set<IpAddress> addresses = existingHost.ipAddresses();
samanwita palc40e5ed2015-09-24 11:01:51 -0700211 if (addresses != null && addresses.contains(ipAddress)) {
samanwita pale7c08de2015-09-24 21:59:49 -0700212 addresses = new HashSet<>(existingHost.ipAddresses());
samanwita palc40e5ed2015-09-24 11:01:51 -0700213 addresses.remove(ipAddress);
214 return new DefaultHost(existingHost.providerId(),
215 hostId,
216 existingHost.mac(),
217 existingHost.vlan(),
218 existingHost.location(),
219 ImmutableSet.copyOf(addresses),
220 existingHost.annotations());
221 } else {
222 return existingHost;
223 }
224 }
225 return null;
226 });
Charles Chan009c3082015-11-10 14:18:04 -0800227 return null;
samanwita palc40e5ed2015-09-24 11:01:51 -0700228 }
229
230 @Override
Madan Jampani38a88212015-09-15 11:21:27 -0700231 public int getHostCount() {
232 return hosts.size();
233 }
234
235 @Override
236 public Iterable<Host> getHosts() {
237 return ImmutableSet.copyOf(hosts.values());
238 }
239
240 @Override
241 public Host getHost(HostId hostId) {
242 return hosts.get(hostId);
243 }
244
245 @Override
246 public Set<Host> getHosts(VlanId vlanId) {
247 return filter(hosts.values(), host -> Objects.equals(host.vlan(), vlanId));
248 }
249
250 @Override
251 public Set<Host> getHosts(MacAddress mac) {
252 return filter(hosts.values(), host -> Objects.equals(host.mac(), mac));
253 }
254
255 @Override
256 public Set<Host> getHosts(IpAddress ip) {
257 return filter(hosts.values(), host -> host.ipAddresses().contains(ip));
258 }
259
260 @Override
261 public Set<Host> getConnectedHosts(ConnectPoint connectPoint) {
Charles Chan009c3082015-11-10 14:18:04 -0800262 Set<Host> filtered = hosts.entrySet().stream()
263 .filter(entry -> entry.getValue().location().equals(connectPoint))
264 .map(Map.Entry::getValue)
265 .collect(Collectors.toSet());
266 return ImmutableSet.copyOf(filtered);
Madan Jampani38a88212015-09-15 11:21:27 -0700267 }
268
269 @Override
270 public Set<Host> getConnectedHosts(DeviceId deviceId) {
Charles Chan009c3082015-11-10 14:18:04 -0800271 Set<Host> filtered = hosts.entrySet().stream()
272 .filter(entry -> entry.getValue().location().deviceId().equals(deviceId))
273 .map(Map.Entry::getValue)
274 .collect(Collectors.toSet());
HIGUCHI Yutafe2122c2015-09-30 13:46:22 -0700275 return ImmutableSet.copyOf(filtered);
Madan Jampani38a88212015-09-15 11:21:27 -0700276 }
277
Madan Jampani38a88212015-09-15 11:21:27 -0700278 private Set<Host> filter(Collection<DefaultHost> collection, Predicate<DefaultHost> predicate) {
279 return collection.stream().filter(predicate).collect(Collectors.toSet());
280 }
281
alshabib8a4a6002015-11-25 14:31:16 -0800282 private class HostLocationTracker implements MapEventListener<HostId, DefaultHost> {
Madan Jampani38a88212015-09-15 11:21:27 -0700283 @Override
alshabib8a4a6002015-11-25 14:31:16 -0800284 public void event(MapEvent<HostId, DefaultHost> event) {
285 DefaultHost host = checkNotNull(event.value().value());
286 if (event.type() == MapEvent.Type.INSERT) {
Charles Chan33f28a92015-11-13 13:12:38 -0800287 Host prevHost = prevHosts.put(host.id(), host);
288 if (prevHost == null) {
Charles Chan009c3082015-11-10 14:18:04 -0800289 notifyDelegate(new HostEvent(HOST_ADDED, host));
Charles Chan33f28a92015-11-13 13:12:38 -0800290 } else if (!Objects.equals(prevHost.location(), host.location())) {
291 notifyDelegate(new HostEvent(HOST_MOVED, host, prevHost));
292 } else if (!Objects.equals(prevHost, host)) {
293 notifyDelegate(new HostEvent(HOST_UPDATED, host, prevHost));
Charles Chan009c3082015-11-10 14:18:04 -0800294 }
alshabib8a4a6002015-11-25 14:31:16 -0800295 } else if (event.type() == MapEvent.Type.REMOVE) {
Charles Chan33f28a92015-11-13 13:12:38 -0800296 if (prevHosts.remove(host.id()) != null) {
Madan Jampani38a88212015-09-15 11:21:27 -0700297 notifyDelegate(new HostEvent(HOST_REMOVED, host));
298 }
Madan Jampani38a88212015-09-15 11:21:27 -0700299 }
300 }
301 }
302}