blob: 8a913cda7492071dcbb2384e4ce3ea2b48de5662 [file] [log] [blame]
Madan Jampani38a88212015-09-15 11:21:27 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
Madan Jampani38a88212015-09-15 11:21:27 -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 */
16package org.onosproject.store.host.impl;
17
Charles Chan35a32322017-08-14 11:42:11 -070018import com.google.common.cache.Cache;
19import com.google.common.cache.CacheBuilder;
20import com.google.common.cache.RemovalNotification;
alshabib8a4a6002015-11-25 14:31:16 -080021import com.google.common.collect.ImmutableSet;
22import com.google.common.collect.Sets;
Madan Jampanic7f49f92015-12-10 11:35:06 -080023
Madan Jampani38a88212015-09-15 11:21:27 -070024import org.apache.felix.scr.annotations.Activate;
25import org.apache.felix.scr.annotations.Component;
26import org.apache.felix.scr.annotations.Deactivate;
27import org.apache.felix.scr.annotations.Reference;
28import org.apache.felix.scr.annotations.ReferenceCardinality;
29import org.apache.felix.scr.annotations.Service;
30import org.onlab.packet.IpAddress;
31import org.onlab.packet.MacAddress;
32import org.onlab.packet.VlanId;
33import org.onlab.util.KryoNamespace;
34import org.onosproject.net.Annotations;
35import org.onosproject.net.ConnectPoint;
36import org.onosproject.net.DefaultAnnotations;
37import org.onosproject.net.DefaultHost;
38import org.onosproject.net.DeviceId;
39import org.onosproject.net.Host;
40import org.onosproject.net.HostId;
Brian O'Connorf107bd72015-09-21 15:31:03 -070041import org.onosproject.net.HostLocation;
Madan Jampani38a88212015-09-15 11:21:27 -070042import org.onosproject.net.host.HostDescription;
43import org.onosproject.net.host.HostEvent;
44import org.onosproject.net.host.HostStore;
45import org.onosproject.net.host.HostStoreDelegate;
Madan Jampani38a88212015-09-15 11:21:27 -070046import org.onosproject.net.provider.ProviderId;
47import org.onosproject.store.AbstractStore;
48import org.onosproject.store.serializers.KryoNamespaces;
alshabib8a4a6002015-11-25 14:31:16 -080049import org.onosproject.store.service.ConsistentMap;
50import org.onosproject.store.service.MapEvent;
51import org.onosproject.store.service.MapEventListener;
52import org.onosproject.store.service.Serializer;
Madan Jampani38a88212015-09-15 11:21:27 -070053import org.onosproject.store.service.StorageService;
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +053054import org.onosproject.store.service.DistributedPrimitive.Status;
Charles Chan35a32322017-08-14 11:42:11 -070055import org.onosproject.store.service.Versioned;
Madan Jampani38a88212015-09-15 11:21:27 -070056import org.slf4j.Logger;
57
alshabib8a4a6002015-11-25 14:31:16 -080058import java.util.Collection;
59import java.util.HashSet;
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +053060import java.util.Iterator;
alshabib8a4a6002015-11-25 14:31:16 -080061import java.util.Map;
62import java.util.Objects;
63import java.util.Set;
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +053064import java.util.concurrent.ConcurrentHashMap;
Charles Chan35a32322017-08-14 11:42:11 -070065import java.util.concurrent.Executor;
66import java.util.concurrent.Executors;
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +053067import java.util.concurrent.ScheduledExecutorService;
Charles Chan35a32322017-08-14 11:42:11 -070068import java.util.concurrent.TimeUnit;
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +053069import java.util.function.Consumer;
alshabib8a4a6002015-11-25 14:31:16 -080070import java.util.function.Predicate;
71import java.util.stream.Collectors;
72
73import static com.google.common.base.Preconditions.checkNotNull;
74import static com.google.common.base.Preconditions.checkState;
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +053075import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
76import static org.onlab.util.Tools.groupedThreads;
alshabib8a4a6002015-11-25 14:31:16 -080077import static org.onosproject.net.DefaultAnnotations.merge;
78import static org.onosproject.net.host.HostEvent.Type.*;
79import static org.slf4j.LoggerFactory.getLogger;
Madan Jampani38a88212015-09-15 11:21:27 -070080
81/**
82 * Manages the inventory of hosts using a {@code EventuallyConsistentMap}.
83 */
84@Component(immediate = true)
85@Service
alshabib8a4a6002015-11-25 14:31:16 -080086public class DistributedHostStore
Madan Jampani38a88212015-09-15 11:21:27 -070087 extends AbstractStore<HostEvent, HostStoreDelegate>
88 implements HostStore {
89
90 private final Logger log = getLogger(getClass());
91
92 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
93 protected StorageService storageService;
94
Madan Jampanic6371882016-06-03 21:30:17 -070095 private ConsistentMap<HostId, DefaultHost> hostsConsistentMap;
alshabib8a4a6002015-11-25 14:31:16 -080096 private Map<HostId, DefaultHost> hosts;
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +053097 private Map<IpAddress, Set<Host>> hostsByIp;
alshabib8a4a6002015-11-25 14:31:16 -080098 private MapEventListener<HostId, DefaultHost> hostLocationTracker =
Madan Jampani38a88212015-09-15 11:21:27 -070099 new HostLocationTracker();
100
Charles Chan35a32322017-08-14 11:42:11 -0700101 private ConsistentMap<MacAddress, PendingHostLocation> pendingHostsConsistentMap;
102 private Map<MacAddress, PendingHostLocation> pendingHosts;
103 private MapEventListener<MacAddress, PendingHostLocation> pendingHostListener =
104 new PendingHostListener();
105
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +0530106 private ScheduledExecutorService executor;
107
108 private Consumer<Status> statusChangeListener;
109
Charles Chan35a32322017-08-14 11:42:11 -0700110 // TODO make this configurable
111 private static final int PROBE_TIMEOUT_MS = 1000;
112
113 private Cache<MacAddress, PendingHostLocation> pendingHostsCache = CacheBuilder.newBuilder()
114 .expireAfterWrite(PROBE_TIMEOUT_MS, TimeUnit.MILLISECONDS)
115 .removalListener((RemovalNotification<MacAddress, PendingHostLocation> notification) -> {
116 switch (notification.getCause()) {
117 case EXPIRED:
118 PendingHostLocation expired = notification.getValue();
119 if (expired != null) {
Charles Chan8e786b52017-09-12 18:57:47 -0700120 if (timeoutPendingHostLocation(notification.getKey())) {
121 log.info("Evict {} from pendingHosts due to probe timeout", notification.getValue());
122 }
Charles Chan35a32322017-08-14 11:42:11 -0700123 }
124 break;
125 case EXPLICIT:
126 break;
127 default:
128 log.warn("Remove {} from pendingHostLocations for unexpected reason {}",
129 notification.getKey(), notification.getCause());
130 }
131 }).build();
132
133 private ScheduledExecutorService cacheCleaner = Executors.newSingleThreadScheduledExecutor();
134
Madan Jampani38a88212015-09-15 11:21:27 -0700135 @Activate
136 public void activate() {
137 KryoNamespace.Builder hostSerializer = KryoNamespace.newBuilder()
Jonathan Hart38feb6e2016-08-29 22:54:16 +0000138 .register(KryoNamespaces.API);
Madan Jampanic6371882016-06-03 21:30:17 -0700139 hostsConsistentMap = storageService.<HostId, DefaultHost>consistentMapBuilder()
Madan Jampani38a88212015-09-15 11:21:27 -0700140 .withName("onos-hosts")
alshabib8a4a6002015-11-25 14:31:16 -0800141 .withRelaxedReadConsistency()
142 .withSerializer(Serializer.using(hostSerializer.build()))
Madan Jampani38a88212015-09-15 11:21:27 -0700143 .build();
Charles Chan35a32322017-08-14 11:42:11 -0700144 hostsConsistentMap.addListener(hostLocationTracker);
Madan Jampanic6371882016-06-03 21:30:17 -0700145 hosts = hostsConsistentMap.asJavaMap();
alshabib8a4a6002015-11-25 14:31:16 -0800146
Charles Chan35a32322017-08-14 11:42:11 -0700147 KryoNamespace.Builder pendingHostSerializer = KryoNamespace.newBuilder()
148 .register(KryoNamespaces.API)
149 .register(PendingHostLocation.class);
150 pendingHostsConsistentMap = storageService.<MacAddress, PendingHostLocation>consistentMapBuilder()
151 .withName("onos-hosts-pending")
152 .withRelaxedReadConsistency()
153 .withSerializer(Serializer.using(pendingHostSerializer.build()))
154 .build();
155 pendingHostsConsistentMap.addListener(pendingHostListener);
156 pendingHosts = pendingHostsConsistentMap.asJavaMap();
alshabib1400ce92015-12-16 15:05:47 -0800157
Charles Chan35a32322017-08-14 11:42:11 -0700158 cacheCleaner.scheduleAtFixedRate(pendingHostsCache::cleanUp, 0,
159 PROBE_TIMEOUT_MS, TimeUnit.MILLISECONDS);
Madan Jampani38a88212015-09-15 11:21:27 -0700160
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +0530161 executor = newSingleThreadScheduledExecutor(groupedThreads("onos/hosts", "store", log));
162 statusChangeListener = status -> {
163 if (status == Status.ACTIVE) {
164 executor.execute(this::loadHostsByIp);
165 }
166 };
167 hostsConsistentMap.addStatusChangeListener(statusChangeListener);
168 loadHostsByIp();
Madan Jampani38a88212015-09-15 11:21:27 -0700169 log.info("Started");
170 }
171
172 @Deactivate
173 public void deactivate() {
Madan Jampanic6371882016-06-03 21:30:17 -0700174 hostsConsistentMap.removeListener(hostLocationTracker);
Madan Jampani38a88212015-09-15 11:21:27 -0700175
Charles Chan35a32322017-08-14 11:42:11 -0700176 cacheCleaner.shutdown();
177
Madan Jampani38a88212015-09-15 11:21:27 -0700178 log.info("Stopped");
179 }
180
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +0530181 private void loadHostsByIp() {
182 hostsByIp = new ConcurrentHashMap<IpAddress, Set<Host>>();
183 hostsConsistentMap.asJavaMap().values().forEach(host -> {
184 host.ipAddresses().forEach(ip -> {
185 Set<Host> existingHosts = hostsByIp.get(ip);
186 if (existingHosts == null) {
187 hostsByIp.put(ip, addHosts(host));
188 } else {
189 existingHosts.add(host);
190 }
191 });
192 });
193 }
194
Brian O'Connordab09742015-12-07 20:06:29 -0800195 private boolean shouldUpdate(DefaultHost existingHost,
196 ProviderId providerId,
Brian O'Connordab09742015-12-07 20:06:29 -0800197 HostDescription hostDescription,
198 boolean replaceIPs) {
199 if (existingHost == null) {
200 return true;
201 }
202
Charles Chan69ebcbb2017-04-27 14:33:21 -0700203 // Avoid overriding configured host with learnt host
204 if (existingHost.configured() && !hostDescription.configured()) {
Charles Chan29ecdee2017-02-22 18:46:56 -0800205 return false;
206 }
207
Brian O'Connordab09742015-12-07 20:06:29 -0800208 if (!Objects.equals(existingHost.providerId(), providerId) ||
209 !Objects.equals(existingHost.mac(), hostDescription.hwAddress()) ||
210 !Objects.equals(existingHost.vlan(), hostDescription.vlan()) ||
Charles Chancd06c692017-04-27 20:46:06 -0700211 !Objects.equals(existingHost.locations(), hostDescription.locations())) {
Brian O'Connordab09742015-12-07 20:06:29 -0800212 return true;
213 }
214
215 if (replaceIPs) {
216 if (!Objects.equals(hostDescription.ipAddress(),
217 existingHost.ipAddresses())) {
218 return true;
219 }
220 } else {
221 if (!existingHost.ipAddresses().containsAll(hostDescription.ipAddress())) {
222 return true;
223 }
224 }
225
226 // check to see if any of the annotations provided by hostDescription
227 // differ from those in the existing host
228 return hostDescription.annotations().keys().stream()
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +0530229 .anyMatch(k -> !Objects.equals(hostDescription.annotations().value(k),
230 existingHost.annotations().value(k)));
Brian O'Connordab09742015-12-07 20:06:29 -0800231
232
233 }
234
Charles Chan009c3082015-11-10 14:18:04 -0800235 // TODO No longer need to return HostEvent
Madan Jampani38a88212015-09-15 11:21:27 -0700236 @Override
237 public HostEvent createOrUpdateHost(ProviderId providerId,
Brian O'Connorf107bd72015-09-21 15:31:03 -0700238 HostId hostId,
239 HostDescription hostDescription,
240 boolean replaceIPs) {
Madan Jampanic6371882016-06-03 21:30:17 -0700241 hostsConsistentMap.computeIf(hostId,
Charles Chan69ebcbb2017-04-27 14:33:21 -0700242 existingHost -> shouldUpdate(existingHost, providerId,
Brian O'Connordab09742015-12-07 20:06:29 -0800243 hostDescription, replaceIPs),
244 (id, existingHost) -> {
Brian O'Connorf107bd72015-09-21 15:31:03 -0700245
Madan Jampanic7f49f92015-12-10 11:35:06 -0800246 final Set<IpAddress> addresses;
247 if (existingHost == null || replaceIPs) {
248 addresses = ImmutableSet.copyOf(hostDescription.ipAddress());
249 } else {
250 addresses = Sets.newHashSet(existingHost.ipAddresses());
251 addresses.addAll(hostDescription.ipAddress());
252 }
Brian O'Connorf107bd72015-09-21 15:31:03 -0700253
Madan Jampanic7f49f92015-12-10 11:35:06 -0800254 final Annotations annotations;
255 if (existingHost != null) {
256 annotations = merge((DefaultAnnotations) existingHost.annotations(),
257 hostDescription.annotations());
258 } else {
259 annotations = hostDescription.annotations();
260 }
Jonathan Hart38feb6e2016-08-29 22:54:16 +0000261
Madan Jampanic7f49f92015-12-10 11:35:06 -0800262 return new DefaultHost(providerId,
263 hostId,
264 hostDescription.hwAddress(),
265 hostDescription.vlan(),
Charles Chancd06c692017-04-27 20:46:06 -0700266 hostDescription.locations(),
Madan Jampanic7f49f92015-12-10 11:35:06 -0800267 addresses,
Charles Chanb1e99242017-07-07 14:11:09 -0700268 hostDescription.configured(),
Madan Jampanic7f49f92015-12-10 11:35:06 -0800269 annotations);
270 });
Charles Chan009c3082015-11-10 14:18:04 -0800271 return null;
Madan Jampani38a88212015-09-15 11:21:27 -0700272 }
273
Charles Chan009c3082015-11-10 14:18:04 -0800274 // TODO No longer need to return HostEvent
Madan Jampani38a88212015-09-15 11:21:27 -0700275 @Override
276 public HostEvent removeHost(HostId hostId) {
Charles Chan009c3082015-11-10 14:18:04 -0800277 hosts.remove(hostId);
278 return null;
Madan Jampani38a88212015-09-15 11:21:27 -0700279 }
280
Charles Chan009c3082015-11-10 14:18:04 -0800281 // TODO No longer need to return HostEvent
Madan Jampani38a88212015-09-15 11:21:27 -0700282 @Override
samanwita palc40e5ed2015-09-24 11:01:51 -0700283 public HostEvent removeIp(HostId hostId, IpAddress ipAddress) {
Charles Chan009c3082015-11-10 14:18:04 -0800284 hosts.compute(hostId, (id, existingHost) -> {
samanwita palc40e5ed2015-09-24 11:01:51 -0700285 if (existingHost != null) {
286 checkState(Objects.equals(hostId.mac(), existingHost.mac()),
287 "Existing and new MAC addresses differ.");
288 checkState(Objects.equals(hostId.vlanId(), existingHost.vlan()),
289 "Existing and new VLANs differ.");
290
samanwita pale7c08de2015-09-24 21:59:49 -0700291 Set<IpAddress> addresses = existingHost.ipAddresses();
samanwita palc40e5ed2015-09-24 11:01:51 -0700292 if (addresses != null && addresses.contains(ipAddress)) {
samanwita pale7c08de2015-09-24 21:59:49 -0700293 addresses = new HashSet<>(existingHost.ipAddresses());
samanwita palc40e5ed2015-09-24 11:01:51 -0700294 addresses.remove(ipAddress);
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +0530295 removeIpFromHostsByIp(existingHost, ipAddress);
samanwita palc40e5ed2015-09-24 11:01:51 -0700296 return new DefaultHost(existingHost.providerId(),
297 hostId,
298 existingHost.mac(),
299 existingHost.vlan(),
Charles Chancd06c692017-04-27 20:46:06 -0700300 existingHost.locations(),
samanwita palc40e5ed2015-09-24 11:01:51 -0700301 ImmutableSet.copyOf(addresses),
Charles Chancd06c692017-04-27 20:46:06 -0700302 existingHost.configured(),
samanwita palc40e5ed2015-09-24 11:01:51 -0700303 existingHost.annotations());
304 } else {
305 return existingHost;
306 }
307 }
308 return null;
309 });
Charles Chan009c3082015-11-10 14:18:04 -0800310 return null;
samanwita palc40e5ed2015-09-24 11:01:51 -0700311 }
312
313 @Override
Charles Chan888e20a2017-05-01 15:44:23 -0700314 public void removeLocation(HostId hostId, HostLocation location) {
315 hosts.compute(hostId, (id, existingHost) -> {
316 if (existingHost != null) {
317 checkState(Objects.equals(hostId.mac(), existingHost.mac()),
318 "Existing and new MAC addresses differ.");
319 checkState(Objects.equals(hostId.vlanId(), existingHost.vlan()),
320 "Existing and new VLANs differ.");
321
322 Set<HostLocation> locations = new HashSet<>(existingHost.locations());
323 locations.remove(location);
324
325 // Remove entire host if we are removing the last location
326 return locations.isEmpty() ? null :
327 new DefaultHost(existingHost.providerId(),
328 hostId, existingHost.mac(), existingHost.vlan(),
329 locations, existingHost.ipAddresses(),
330 existingHost.configured(), existingHost.annotations());
331 }
332 return null;
333 });
334 }
335
336 @Override
Madan Jampani38a88212015-09-15 11:21:27 -0700337 public int getHostCount() {
338 return hosts.size();
339 }
340
341 @Override
342 public Iterable<Host> getHosts() {
343 return ImmutableSet.copyOf(hosts.values());
344 }
345
346 @Override
347 public Host getHost(HostId hostId) {
348 return hosts.get(hostId);
349 }
350
351 @Override
352 public Set<Host> getHosts(VlanId vlanId) {
353 return filter(hosts.values(), host -> Objects.equals(host.vlan(), vlanId));
354 }
355
356 @Override
357 public Set<Host> getHosts(MacAddress mac) {
358 return filter(hosts.values(), host -> Objects.equals(host.mac(), mac));
359 }
360
361 @Override
362 public Set<Host> getHosts(IpAddress ip) {
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +0530363 Set<Host> hosts = hostsByIp.get(ip);
364 return hosts != null ? ImmutableSet.copyOf(hosts) : ImmutableSet.of();
Madan Jampani38a88212015-09-15 11:21:27 -0700365 }
366
367 @Override
368 public Set<Host> getConnectedHosts(ConnectPoint connectPoint) {
Charles Chan009c3082015-11-10 14:18:04 -0800369 Set<Host> filtered = hosts.entrySet().stream()
Charles Chancd06c692017-04-27 20:46:06 -0700370 .filter(entry -> entry.getValue().locations().contains(connectPoint))
Charles Chan009c3082015-11-10 14:18:04 -0800371 .map(Map.Entry::getValue)
372 .collect(Collectors.toSet());
373 return ImmutableSet.copyOf(filtered);
Madan Jampani38a88212015-09-15 11:21:27 -0700374 }
375
376 @Override
377 public Set<Host> getConnectedHosts(DeviceId deviceId) {
Charles Chan009c3082015-11-10 14:18:04 -0800378 Set<Host> filtered = hosts.entrySet().stream()
Charles Chancd06c692017-04-27 20:46:06 -0700379 .filter(entry -> entry.getValue().locations().stream()
380 .map(HostLocation::deviceId).anyMatch(dpid -> dpid.equals(deviceId)))
Charles Chan009c3082015-11-10 14:18:04 -0800381 .map(Map.Entry::getValue)
382 .collect(Collectors.toSet());
HIGUCHI Yutafe2122c2015-09-30 13:46:22 -0700383 return ImmutableSet.copyOf(filtered);
Madan Jampani38a88212015-09-15 11:21:27 -0700384 }
385
Charles Chan35a32322017-08-14 11:42:11 -0700386 @Override
387 public MacAddress addPendingHostLocation(HostId hostId, HostLocation hostLocation) {
388 // Use ONLab OUI (3 bytes) + atomic counter (3 bytes) as the source MAC of the probe
389 long nextIndex = storageService.getAtomicCounter("onos-hosts-probe-index").getAndIncrement();
390 MacAddress probeMac = MacAddress.valueOf(MacAddress.NONE.toLong() + nextIndex);
391 PendingHostLocation phl = new PendingHostLocation(hostId, hostLocation);
392
393 pendingHostsCache.put(probeMac, phl);
394 pendingHosts.put(probeMac, phl);
395
396 return probeMac;
397 }
398
399 @Override
400 public void removePendingHostLocation(MacAddress probeMac) {
401 pendingHostsCache.invalidate(probeMac);
402 pendingHosts.remove(probeMac);
403 }
404
Charles Chan8e786b52017-09-12 18:57:47 -0700405 private boolean timeoutPendingHostLocation(MacAddress probeMac) {
406 PendingHostLocation phl = pendingHosts.computeIfPresent(probeMac, (k, v) -> {
Charles Chan35a32322017-08-14 11:42:11 -0700407 v.setExpired(true);
408 return v;
409 });
Charles Chan8e786b52017-09-12 18:57:47 -0700410 return phl != null;
Charles Chan35a32322017-08-14 11:42:11 -0700411 }
412
Madan Jampani38a88212015-09-15 11:21:27 -0700413 private Set<Host> filter(Collection<DefaultHost> collection, Predicate<DefaultHost> predicate) {
414 return collection.stream().filter(predicate).collect(Collectors.toSet());
415 }
416
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +0530417 private Set<Host> addHosts(Host host) {
418 Set<Host> hosts = Sets.newConcurrentHashSet();
419 hosts.add(host);
420 return hosts;
421 }
422
423 private Set<Host> updateHosts(Set<Host> existingHosts, Host host) {
424 Iterator<Host> iterator = existingHosts.iterator();
425 while (iterator.hasNext()) {
426 Host existingHost = iterator.next();
427 if (existingHost.id().equals(host.id())) {
428 iterator.remove();
429 }
430 }
431 existingHosts.add(host);
432 return existingHosts;
433 }
434
435 private Set<Host> removeHosts(Set<Host> existingHosts, Host host) {
436 if (existingHosts != null) {
437 Iterator<Host> iterator = existingHosts.iterator();
438 while (iterator.hasNext()) {
439 Host existingHost = iterator.next();
440 if (existingHost.id().equals(host.id())) {
441 iterator.remove();
442 }
443 }
444 }
445
446 if (existingHosts.isEmpty()) {
447 return null;
448 }
449 return existingHosts;
450 }
451
452 private void updateHostsByIp(DefaultHost host) {
453 host.ipAddresses().forEach(ip -> {
454 hostsByIp.compute(ip, (k, v) -> v == null ? addHosts(host)
455 : updateHosts(v, host));
456 });
457 }
458
459 private void removeHostsByIp(DefaultHost host) {
460 host.ipAddresses().forEach(ip -> {
461 hostsByIp.computeIfPresent(ip, (k, v) -> removeHosts(v, host));
462 });
463 }
464
465 private void removeIpFromHostsByIp(DefaultHost host, IpAddress ip) {
466 hostsByIp.computeIfPresent(ip, (k, v) -> removeHosts(v, host));
467 }
468
alshabib8a4a6002015-11-25 14:31:16 -0800469 private class HostLocationTracker implements MapEventListener<HostId, DefaultHost> {
Madan Jampani38a88212015-09-15 11:21:27 -0700470 @Override
alshabib8a4a6002015-11-25 14:31:16 -0800471 public void event(MapEvent<HostId, DefaultHost> event) {
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +0530472 DefaultHost host = checkNotNull(event.value().value());
alshabib1400ce92015-12-16 15:05:47 -0800473 switch (event.type()) {
474 case INSERT:
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +0530475 updateHostsByIp(host);
Charles Chan009c3082015-11-10 14:18:04 -0800476 notifyDelegate(new HostEvent(HOST_ADDED, host));
alshabib1400ce92015-12-16 15:05:47 -0800477 break;
478 case UPDATE:
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +0530479 updateHostsByIp(host);
480 DefaultHost prevHost = checkNotNull(event.oldValue().value());
Charles Chancd06c692017-04-27 20:46:06 -0700481 if (!Objects.equals(prevHost.locations(), host.locations())) {
alshabib1400ce92015-12-16 15:05:47 -0800482 notifyDelegate(new HostEvent(HOST_MOVED, host, prevHost));
483 } else if (!Objects.equals(prevHost, host)) {
484 notifyDelegate(new HostEvent(HOST_UPDATED, host, prevHost));
485 }
486 break;
487 case REMOVE:
Charles Chan21720342017-05-13 00:19:09 -0700488 removeHostsByIp(host);
Yuta HIGUCHI215a7e42016-07-20 19:54:06 -0700489 notifyDelegate(new HostEvent(HOST_REMOVED, host));
alshabib1400ce92015-12-16 15:05:47 -0800490 break;
491 default:
492 log.warn("Unknown map event type: {}", event.type());
Madan Jampani38a88212015-09-15 11:21:27 -0700493 }
494 }
495 }
Charles Chan35a32322017-08-14 11:42:11 -0700496
497 private class PendingHostListener implements MapEventListener<MacAddress, PendingHostLocation> {
498 @Override
499 public void event(MapEvent<MacAddress, PendingHostLocation> event) {
500 Versioned<PendingHostLocation> newValue = event.newValue();
501 switch (event.type()) {
502 case INSERT:
503 break;
504 case UPDATE:
505 if (newValue.value().expired()) {
506 Executor locationRemover = Executors.newSingleThreadScheduledExecutor();
507 locationRemover.execute(() -> {
508 pendingHosts.remove(event.key());
509 removeLocation(newValue.value().hostId(), newValue.value().location());
510 });
511 }
512 break;
513 case REMOVE:
514 break;
515 default:
516 log.warn("Unknown map event type: {}", event.type());
517 }
518 }
519 }
Madan Jampani38a88212015-09-15 11:21:27 -0700520}