blob: 9cf9856321db1a48eb9159315701bb8e25e49960 [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;
Charles Chanba90df12017-11-30 15:37:50 -080046import org.onosproject.net.host.HostLocationProbingService.ProbeMode;
Madan Jampani38a88212015-09-15 11:21:27 -070047import org.onosproject.net.provider.ProviderId;
48import org.onosproject.store.AbstractStore;
49import org.onosproject.store.serializers.KryoNamespaces;
alshabib8a4a6002015-11-25 14:31:16 -080050import org.onosproject.store.service.ConsistentMap;
51import org.onosproject.store.service.MapEvent;
52import org.onosproject.store.service.MapEventListener;
53import org.onosproject.store.service.Serializer;
Madan Jampani38a88212015-09-15 11:21:27 -070054import org.onosproject.store.service.StorageService;
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +053055import org.onosproject.store.service.DistributedPrimitive.Status;
Charles Chan35a32322017-08-14 11:42:11 -070056import org.onosproject.store.service.Versioned;
Madan Jampani38a88212015-09-15 11:21:27 -070057import org.slf4j.Logger;
58
alshabib8a4a6002015-11-25 14:31:16 -080059import java.util.Collection;
60import java.util.HashSet;
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +053061import java.util.Iterator;
alshabib8a4a6002015-11-25 14:31:16 -080062import java.util.Map;
63import java.util.Objects;
Charles Chanba90df12017-11-30 15:37:50 -080064import java.util.Optional;
alshabib8a4a6002015-11-25 14:31:16 -080065import java.util.Set;
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +053066import java.util.concurrent.ConcurrentHashMap;
Charles Chan35a32322017-08-14 11:42:11 -070067import java.util.concurrent.Executor;
68import java.util.concurrent.Executors;
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +053069import java.util.concurrent.ScheduledExecutorService;
Charles Chan35a32322017-08-14 11:42:11 -070070import java.util.concurrent.TimeUnit;
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +053071import java.util.function.Consumer;
alshabib8a4a6002015-11-25 14:31:16 -080072import java.util.function.Predicate;
73import java.util.stream.Collectors;
74
75import static com.google.common.base.Preconditions.checkNotNull;
76import static com.google.common.base.Preconditions.checkState;
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +053077import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
78import static org.onlab.util.Tools.groupedThreads;
alshabib8a4a6002015-11-25 14:31:16 -080079import static org.onosproject.net.DefaultAnnotations.merge;
80import static org.onosproject.net.host.HostEvent.Type.*;
81import static org.slf4j.LoggerFactory.getLogger;
Madan Jampani38a88212015-09-15 11:21:27 -070082
83/**
84 * Manages the inventory of hosts using a {@code EventuallyConsistentMap}.
85 */
86@Component(immediate = true)
87@Service
alshabib8a4a6002015-11-25 14:31:16 -080088public class DistributedHostStore
Madan Jampani38a88212015-09-15 11:21:27 -070089 extends AbstractStore<HostEvent, HostStoreDelegate>
90 implements HostStore {
91
92 private final Logger log = getLogger(getClass());
93
94 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
95 protected StorageService storageService;
96
Madan Jampanic6371882016-06-03 21:30:17 -070097 private ConsistentMap<HostId, DefaultHost> hostsConsistentMap;
alshabib8a4a6002015-11-25 14:31:16 -080098 private Map<HostId, DefaultHost> hosts;
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +053099 private Map<IpAddress, Set<Host>> hostsByIp;
alshabib8a4a6002015-11-25 14:31:16 -0800100 private MapEventListener<HostId, DefaultHost> hostLocationTracker =
Madan Jampani38a88212015-09-15 11:21:27 -0700101 new HostLocationTracker();
102
Charles Chan35a32322017-08-14 11:42:11 -0700103 private ConsistentMap<MacAddress, PendingHostLocation> pendingHostsConsistentMap;
104 private Map<MacAddress, PendingHostLocation> pendingHosts;
105 private MapEventListener<MacAddress, PendingHostLocation> pendingHostListener =
106 new PendingHostListener();
107
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +0530108 private ScheduledExecutorService executor;
109
110 private Consumer<Status> statusChangeListener;
111
Charles Chan35a32322017-08-14 11:42:11 -0700112 // TODO make this configurable
Charles Chanc505f1e2017-12-03 13:48:46 -0800113 private static final int PROBE_TIMEOUT_MS = 3000;
Charles Chan35a32322017-08-14 11:42:11 -0700114
115 private Cache<MacAddress, PendingHostLocation> pendingHostsCache = CacheBuilder.newBuilder()
116 .expireAfterWrite(PROBE_TIMEOUT_MS, TimeUnit.MILLISECONDS)
117 .removalListener((RemovalNotification<MacAddress, PendingHostLocation> notification) -> {
118 switch (notification.getCause()) {
119 case EXPIRED:
120 PendingHostLocation expired = notification.getValue();
121 if (expired != null) {
Charles Chan9ff637e2017-09-12 18:57:47 -0700122 if (timeoutPendingHostLocation(notification.getKey())) {
123 log.info("Evict {} from pendingHosts due to probe timeout", notification.getValue());
124 }
Charles Chan35a32322017-08-14 11:42:11 -0700125 }
126 break;
127 case EXPLICIT:
128 break;
129 default:
130 log.warn("Remove {} from pendingHostLocations for unexpected reason {}",
131 notification.getKey(), notification.getCause());
132 }
133 }).build();
134
135 private ScheduledExecutorService cacheCleaner = Executors.newSingleThreadScheduledExecutor();
136
Madan Jampani38a88212015-09-15 11:21:27 -0700137 @Activate
138 public void activate() {
139 KryoNamespace.Builder hostSerializer = KryoNamespace.newBuilder()
Jonathan Hart38feb6e2016-08-29 22:54:16 +0000140 .register(KryoNamespaces.API);
Madan Jampanic6371882016-06-03 21:30:17 -0700141 hostsConsistentMap = storageService.<HostId, DefaultHost>consistentMapBuilder()
Madan Jampani38a88212015-09-15 11:21:27 -0700142 .withName("onos-hosts")
alshabib8a4a6002015-11-25 14:31:16 -0800143 .withRelaxedReadConsistency()
144 .withSerializer(Serializer.using(hostSerializer.build()))
Madan Jampani38a88212015-09-15 11:21:27 -0700145 .build();
Charles Chan35a32322017-08-14 11:42:11 -0700146 hostsConsistentMap.addListener(hostLocationTracker);
Madan Jampanic6371882016-06-03 21:30:17 -0700147 hosts = hostsConsistentMap.asJavaMap();
alshabib8a4a6002015-11-25 14:31:16 -0800148
Charles Chan35a32322017-08-14 11:42:11 -0700149 KryoNamespace.Builder pendingHostSerializer = KryoNamespace.newBuilder()
150 .register(KryoNamespaces.API)
Charles Chanba90df12017-11-30 15:37:50 -0800151 .register(PendingHostLocation.class)
152 .register(ProbeMode.class);
Charles Chan35a32322017-08-14 11:42:11 -0700153 pendingHostsConsistentMap = storageService.<MacAddress, PendingHostLocation>consistentMapBuilder()
154 .withName("onos-hosts-pending")
155 .withRelaxedReadConsistency()
156 .withSerializer(Serializer.using(pendingHostSerializer.build()))
157 .build();
158 pendingHostsConsistentMap.addListener(pendingHostListener);
159 pendingHosts = pendingHostsConsistentMap.asJavaMap();
alshabib1400ce92015-12-16 15:05:47 -0800160
Charles Chan35a32322017-08-14 11:42:11 -0700161 cacheCleaner.scheduleAtFixedRate(pendingHostsCache::cleanUp, 0,
162 PROBE_TIMEOUT_MS, TimeUnit.MILLISECONDS);
Madan Jampani38a88212015-09-15 11:21:27 -0700163
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +0530164 executor = newSingleThreadScheduledExecutor(groupedThreads("onos/hosts", "store", log));
165 statusChangeListener = status -> {
166 if (status == Status.ACTIVE) {
167 executor.execute(this::loadHostsByIp);
168 }
169 };
170 hostsConsistentMap.addStatusChangeListener(statusChangeListener);
171 loadHostsByIp();
Madan Jampani38a88212015-09-15 11:21:27 -0700172 log.info("Started");
173 }
174
175 @Deactivate
176 public void deactivate() {
Madan Jampanic6371882016-06-03 21:30:17 -0700177 hostsConsistentMap.removeListener(hostLocationTracker);
Madan Jampani38a88212015-09-15 11:21:27 -0700178
Charles Chan35a32322017-08-14 11:42:11 -0700179 cacheCleaner.shutdown();
180
Madan Jampani38a88212015-09-15 11:21:27 -0700181 log.info("Stopped");
182 }
183
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +0530184 private void loadHostsByIp() {
185 hostsByIp = new ConcurrentHashMap<IpAddress, Set<Host>>();
186 hostsConsistentMap.asJavaMap().values().forEach(host -> {
187 host.ipAddresses().forEach(ip -> {
188 Set<Host> existingHosts = hostsByIp.get(ip);
189 if (existingHosts == null) {
190 hostsByIp.put(ip, addHosts(host));
191 } else {
192 existingHosts.add(host);
193 }
194 });
195 });
196 }
197
Brian O'Connordab09742015-12-07 20:06:29 -0800198 private boolean shouldUpdate(DefaultHost existingHost,
199 ProviderId providerId,
Brian O'Connordab09742015-12-07 20:06:29 -0800200 HostDescription hostDescription,
201 boolean replaceIPs) {
202 if (existingHost == null) {
203 return true;
204 }
205
Charles Chan69ebcbb2017-04-27 14:33:21 -0700206 // Avoid overriding configured host with learnt host
207 if (existingHost.configured() && !hostDescription.configured()) {
Charles Chan29ecdee2017-02-22 18:46:56 -0800208 return false;
209 }
210
Brian O'Connordab09742015-12-07 20:06:29 -0800211 if (!Objects.equals(existingHost.providerId(), providerId) ||
212 !Objects.equals(existingHost.mac(), hostDescription.hwAddress()) ||
213 !Objects.equals(existingHost.vlan(), hostDescription.vlan()) ||
Charles Chancd06c692017-04-27 20:46:06 -0700214 !Objects.equals(existingHost.locations(), hostDescription.locations())) {
Brian O'Connordab09742015-12-07 20:06:29 -0800215 return true;
216 }
217
218 if (replaceIPs) {
219 if (!Objects.equals(hostDescription.ipAddress(),
220 existingHost.ipAddresses())) {
221 return true;
222 }
223 } else {
224 if (!existingHost.ipAddresses().containsAll(hostDescription.ipAddress())) {
225 return true;
226 }
227 }
228
229 // check to see if any of the annotations provided by hostDescription
230 // differ from those in the existing host
231 return hostDescription.annotations().keys().stream()
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +0530232 .anyMatch(k -> !Objects.equals(hostDescription.annotations().value(k),
233 existingHost.annotations().value(k)));
Brian O'Connordab09742015-12-07 20:06:29 -0800234
235
236 }
237
Charles Chan009c3082015-11-10 14:18:04 -0800238 // TODO No longer need to return HostEvent
Madan Jampani38a88212015-09-15 11:21:27 -0700239 @Override
240 public HostEvent createOrUpdateHost(ProviderId providerId,
Brian O'Connorf107bd72015-09-21 15:31:03 -0700241 HostId hostId,
242 HostDescription hostDescription,
243 boolean replaceIPs) {
Madan Jampanic6371882016-06-03 21:30:17 -0700244 hostsConsistentMap.computeIf(hostId,
Charles Chan69ebcbb2017-04-27 14:33:21 -0700245 existingHost -> shouldUpdate(existingHost, providerId,
Brian O'Connordab09742015-12-07 20:06:29 -0800246 hostDescription, replaceIPs),
247 (id, existingHost) -> {
Brian O'Connorf107bd72015-09-21 15:31:03 -0700248
Madan Jampanic7f49f92015-12-10 11:35:06 -0800249 final Set<IpAddress> addresses;
250 if (existingHost == null || replaceIPs) {
251 addresses = ImmutableSet.copyOf(hostDescription.ipAddress());
252 } else {
253 addresses = Sets.newHashSet(existingHost.ipAddresses());
254 addresses.addAll(hostDescription.ipAddress());
255 }
Brian O'Connorf107bd72015-09-21 15:31:03 -0700256
Madan Jampanic7f49f92015-12-10 11:35:06 -0800257 final Annotations annotations;
258 if (existingHost != null) {
259 annotations = merge((DefaultAnnotations) existingHost.annotations(),
260 hostDescription.annotations());
261 } else {
262 annotations = hostDescription.annotations();
263 }
Jonathan Hart38feb6e2016-08-29 22:54:16 +0000264
Madan Jampanic7f49f92015-12-10 11:35:06 -0800265 return new DefaultHost(providerId,
266 hostId,
267 hostDescription.hwAddress(),
268 hostDescription.vlan(),
Charles Chancd06c692017-04-27 20:46:06 -0700269 hostDescription.locations(),
Madan Jampanic7f49f92015-12-10 11:35:06 -0800270 addresses,
Charles Chanb1e99242017-07-07 14:11:09 -0700271 hostDescription.configured(),
Madan Jampanic7f49f92015-12-10 11:35:06 -0800272 annotations);
273 });
Charles Chan009c3082015-11-10 14:18:04 -0800274 return null;
Madan Jampani38a88212015-09-15 11:21:27 -0700275 }
276
Charles Chan009c3082015-11-10 14:18:04 -0800277 // TODO No longer need to return HostEvent
Madan Jampani38a88212015-09-15 11:21:27 -0700278 @Override
279 public HostEvent removeHost(HostId hostId) {
Charles Chan009c3082015-11-10 14:18:04 -0800280 hosts.remove(hostId);
281 return null;
Madan Jampani38a88212015-09-15 11:21:27 -0700282 }
283
Charles Chan009c3082015-11-10 14:18:04 -0800284 // TODO No longer need to return HostEvent
Madan Jampani38a88212015-09-15 11:21:27 -0700285 @Override
samanwita palc40e5ed2015-09-24 11:01:51 -0700286 public HostEvent removeIp(HostId hostId, IpAddress ipAddress) {
Charles Chan009c3082015-11-10 14:18:04 -0800287 hosts.compute(hostId, (id, existingHost) -> {
samanwita palc40e5ed2015-09-24 11:01:51 -0700288 if (existingHost != null) {
289 checkState(Objects.equals(hostId.mac(), existingHost.mac()),
290 "Existing and new MAC addresses differ.");
291 checkState(Objects.equals(hostId.vlanId(), existingHost.vlan()),
292 "Existing and new VLANs differ.");
293
samanwita pale7c08de2015-09-24 21:59:49 -0700294 Set<IpAddress> addresses = existingHost.ipAddresses();
samanwita palc40e5ed2015-09-24 11:01:51 -0700295 if (addresses != null && addresses.contains(ipAddress)) {
samanwita pale7c08de2015-09-24 21:59:49 -0700296 addresses = new HashSet<>(existingHost.ipAddresses());
samanwita palc40e5ed2015-09-24 11:01:51 -0700297 addresses.remove(ipAddress);
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +0530298 removeIpFromHostsByIp(existingHost, ipAddress);
samanwita palc40e5ed2015-09-24 11:01:51 -0700299 return new DefaultHost(existingHost.providerId(),
300 hostId,
301 existingHost.mac(),
302 existingHost.vlan(),
Charles Chancd06c692017-04-27 20:46:06 -0700303 existingHost.locations(),
samanwita palc40e5ed2015-09-24 11:01:51 -0700304 ImmutableSet.copyOf(addresses),
Charles Chancd06c692017-04-27 20:46:06 -0700305 existingHost.configured(),
samanwita palc40e5ed2015-09-24 11:01:51 -0700306 existingHost.annotations());
307 } else {
308 return existingHost;
309 }
310 }
311 return null;
312 });
Charles Chan009c3082015-11-10 14:18:04 -0800313 return null;
samanwita palc40e5ed2015-09-24 11:01:51 -0700314 }
315
316 @Override
Charles Chanba90df12017-11-30 15:37:50 -0800317 public void appendLocation(HostId hostId, HostLocation location) {
318 log.debug("Appending location {} to host {}", location, hostId);
319 hosts.compute(hostId, (id, existingHost) -> {
320 if (existingHost != null) {
321 checkState(Objects.equals(hostId.mac(), existingHost.mac()),
322 "Existing and new MAC addresses differ.");
323 checkState(Objects.equals(hostId.vlanId(), existingHost.vlan()),
324 "Existing and new VLANs differ.");
325
326 Set<HostLocation> locations = new HashSet<>(existingHost.locations());
327 locations.add(location);
328
329 return new DefaultHost(existingHost.providerId(),
330 hostId, existingHost.mac(), existingHost.vlan(),
331 locations, existingHost.ipAddresses(),
332 existingHost.configured(), existingHost.annotations());
333 }
334 return null;
335 });
336 }
337
338 @Override
Charles Chan888e20a2017-05-01 15:44:23 -0700339 public void removeLocation(HostId hostId, HostLocation location) {
Charles Chanba90df12017-11-30 15:37:50 -0800340 log.debug("Removing location {} from host {}", location, hostId);
Charles Chan888e20a2017-05-01 15:44:23 -0700341 hosts.compute(hostId, (id, existingHost) -> {
342 if (existingHost != null) {
343 checkState(Objects.equals(hostId.mac(), existingHost.mac()),
344 "Existing and new MAC addresses differ.");
345 checkState(Objects.equals(hostId.vlanId(), existingHost.vlan()),
346 "Existing and new VLANs differ.");
347
348 Set<HostLocation> locations = new HashSet<>(existingHost.locations());
349 locations.remove(location);
350
351 // Remove entire host if we are removing the last location
352 return locations.isEmpty() ? null :
353 new DefaultHost(existingHost.providerId(),
354 hostId, existingHost.mac(), existingHost.vlan(),
355 locations, existingHost.ipAddresses(),
356 existingHost.configured(), existingHost.annotations());
357 }
358 return null;
359 });
360 }
361
362 @Override
Madan Jampani38a88212015-09-15 11:21:27 -0700363 public int getHostCount() {
364 return hosts.size();
365 }
366
367 @Override
368 public Iterable<Host> getHosts() {
369 return ImmutableSet.copyOf(hosts.values());
370 }
371
372 @Override
373 public Host getHost(HostId hostId) {
374 return hosts.get(hostId);
375 }
376
377 @Override
378 public Set<Host> getHosts(VlanId vlanId) {
379 return filter(hosts.values(), host -> Objects.equals(host.vlan(), vlanId));
380 }
381
382 @Override
383 public Set<Host> getHosts(MacAddress mac) {
384 return filter(hosts.values(), host -> Objects.equals(host.mac(), mac));
385 }
386
387 @Override
388 public Set<Host> getHosts(IpAddress ip) {
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +0530389 Set<Host> hosts = hostsByIp.get(ip);
390 return hosts != null ? ImmutableSet.copyOf(hosts) : ImmutableSet.of();
Madan Jampani38a88212015-09-15 11:21:27 -0700391 }
392
393 @Override
394 public Set<Host> getConnectedHosts(ConnectPoint connectPoint) {
Charles Chan009c3082015-11-10 14:18:04 -0800395 Set<Host> filtered = hosts.entrySet().stream()
Charles Chancd06c692017-04-27 20:46:06 -0700396 .filter(entry -> entry.getValue().locations().contains(connectPoint))
Charles Chan009c3082015-11-10 14:18:04 -0800397 .map(Map.Entry::getValue)
398 .collect(Collectors.toSet());
399 return ImmutableSet.copyOf(filtered);
Madan Jampani38a88212015-09-15 11:21:27 -0700400 }
401
402 @Override
403 public Set<Host> getConnectedHosts(DeviceId deviceId) {
Charles Chan009c3082015-11-10 14:18:04 -0800404 Set<Host> filtered = hosts.entrySet().stream()
Charles Chancd06c692017-04-27 20:46:06 -0700405 .filter(entry -> entry.getValue().locations().stream()
406 .map(HostLocation::deviceId).anyMatch(dpid -> dpid.equals(deviceId)))
Charles Chan009c3082015-11-10 14:18:04 -0800407 .map(Map.Entry::getValue)
408 .collect(Collectors.toSet());
HIGUCHI Yutafe2122c2015-09-30 13:46:22 -0700409 return ImmutableSet.copyOf(filtered);
Madan Jampani38a88212015-09-15 11:21:27 -0700410 }
411
Charles Chan35a32322017-08-14 11:42:11 -0700412 @Override
Charles Chanba90df12017-11-30 15:37:50 -0800413 public MacAddress addPendingHostLocation(HostId hostId, ConnectPoint connectPoint, ProbeMode probeMode) {
Charles Chan35a32322017-08-14 11:42:11 -0700414 // Use ONLab OUI (3 bytes) + atomic counter (3 bytes) as the source MAC of the probe
415 long nextIndex = storageService.getAtomicCounter("onos-hosts-probe-index").getAndIncrement();
416 MacAddress probeMac = MacAddress.valueOf(MacAddress.NONE.toLong() + nextIndex);
Charles Chanba90df12017-11-30 15:37:50 -0800417 PendingHostLocation phl = new PendingHostLocation(hostId, connectPoint, probeMode);
Charles Chan35a32322017-08-14 11:42:11 -0700418
419 pendingHostsCache.put(probeMac, phl);
420 pendingHosts.put(probeMac, phl);
421
422 return probeMac;
423 }
424
425 @Override
426 public void removePendingHostLocation(MacAddress probeMac) {
Charles Chanba90df12017-11-30 15:37:50 -0800427 // Add the host location if probe replied in-time in DISCOVER mode
428 Optional.ofNullable(pendingHosts.get(probeMac)).ifPresent(phl -> {
429 if (phl.probeMode() == ProbeMode.DISCOVER) {
430 HostLocation newLocation = new HostLocation(phl.connectPoint(), System.currentTimeMillis());
431 appendLocation(phl.hostId(), newLocation);
432 }
433 });
434
Charles Chan35a32322017-08-14 11:42:11 -0700435 pendingHostsCache.invalidate(probeMac);
436 pendingHosts.remove(probeMac);
437 }
438
Charles Chan9ff637e2017-09-12 18:57:47 -0700439 private boolean timeoutPendingHostLocation(MacAddress probeMac) {
440 PendingHostLocation phl = pendingHosts.computeIfPresent(probeMac, (k, v) -> {
Charles Chan35a32322017-08-14 11:42:11 -0700441 v.setExpired(true);
442 return v;
443 });
Charles Chan9ff637e2017-09-12 18:57:47 -0700444 return phl != null;
Charles Chan35a32322017-08-14 11:42:11 -0700445 }
446
Madan Jampani38a88212015-09-15 11:21:27 -0700447 private Set<Host> filter(Collection<DefaultHost> collection, Predicate<DefaultHost> predicate) {
448 return collection.stream().filter(predicate).collect(Collectors.toSet());
449 }
450
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +0530451 private Set<Host> addHosts(Host host) {
452 Set<Host> hosts = Sets.newConcurrentHashSet();
453 hosts.add(host);
454 return hosts;
455 }
456
457 private Set<Host> updateHosts(Set<Host> existingHosts, Host host) {
458 Iterator<Host> iterator = existingHosts.iterator();
459 while (iterator.hasNext()) {
460 Host existingHost = iterator.next();
461 if (existingHost.id().equals(host.id())) {
462 iterator.remove();
463 }
464 }
465 existingHosts.add(host);
466 return existingHosts;
467 }
468
469 private Set<Host> removeHosts(Set<Host> existingHosts, Host host) {
470 if (existingHosts != null) {
471 Iterator<Host> iterator = existingHosts.iterator();
472 while (iterator.hasNext()) {
473 Host existingHost = iterator.next();
474 if (existingHost.id().equals(host.id())) {
475 iterator.remove();
476 }
477 }
478 }
479
Ray Milkeyfd4f8d32018-01-17 15:24:52 -0800480 if (existingHosts == null || existingHosts.isEmpty()) {
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +0530481 return null;
482 }
483 return existingHosts;
484 }
485
486 private void updateHostsByIp(DefaultHost host) {
487 host.ipAddresses().forEach(ip -> {
488 hostsByIp.compute(ip, (k, v) -> v == null ? addHosts(host)
489 : updateHosts(v, host));
490 });
491 }
492
493 private void removeHostsByIp(DefaultHost host) {
494 host.ipAddresses().forEach(ip -> {
495 hostsByIp.computeIfPresent(ip, (k, v) -> removeHosts(v, host));
496 });
497 }
498
499 private void removeIpFromHostsByIp(DefaultHost host, IpAddress ip) {
500 hostsByIp.computeIfPresent(ip, (k, v) -> removeHosts(v, host));
501 }
502
alshabib8a4a6002015-11-25 14:31:16 -0800503 private class HostLocationTracker implements MapEventListener<HostId, DefaultHost> {
Madan Jampani38a88212015-09-15 11:21:27 -0700504 @Override
alshabib8a4a6002015-11-25 14:31:16 -0800505 public void event(MapEvent<HostId, DefaultHost> event) {
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +0530506 DefaultHost host = checkNotNull(event.value().value());
alshabib1400ce92015-12-16 15:05:47 -0800507 switch (event.type()) {
508 case INSERT:
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +0530509 updateHostsByIp(host);
Charles Chan009c3082015-11-10 14:18:04 -0800510 notifyDelegate(new HostEvent(HOST_ADDED, host));
alshabib1400ce92015-12-16 15:05:47 -0800511 break;
512 case UPDATE:
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +0530513 updateHostsByIp(host);
514 DefaultHost prevHost = checkNotNull(event.oldValue().value());
Charles Chancd06c692017-04-27 20:46:06 -0700515 if (!Objects.equals(prevHost.locations(), host.locations())) {
alshabib1400ce92015-12-16 15:05:47 -0800516 notifyDelegate(new HostEvent(HOST_MOVED, host, prevHost));
517 } else if (!Objects.equals(prevHost, host)) {
518 notifyDelegate(new HostEvent(HOST_UPDATED, host, prevHost));
519 }
520 break;
521 case REMOVE:
Charles Chan21720342017-05-13 00:19:09 -0700522 removeHostsByIp(host);
Yuta HIGUCHI215a7e42016-07-20 19:54:06 -0700523 notifyDelegate(new HostEvent(HOST_REMOVED, host));
alshabib1400ce92015-12-16 15:05:47 -0800524 break;
525 default:
526 log.warn("Unknown map event type: {}", event.type());
Madan Jampani38a88212015-09-15 11:21:27 -0700527 }
528 }
529 }
Charles Chan35a32322017-08-14 11:42:11 -0700530
531 private class PendingHostListener implements MapEventListener<MacAddress, PendingHostLocation> {
532 @Override
533 public void event(MapEvent<MacAddress, PendingHostLocation> event) {
534 Versioned<PendingHostLocation> newValue = event.newValue();
535 switch (event.type()) {
536 case INSERT:
537 break;
538 case UPDATE:
Charles Chanba90df12017-11-30 15:37:50 -0800539 // Remove the host location if probe timeout in VERIFY mode
540 if (newValue.value().expired() && newValue.value().probeMode() == ProbeMode.VERIFY) {
Charles Chan35a32322017-08-14 11:42:11 -0700541 Executor locationRemover = Executors.newSingleThreadScheduledExecutor();
542 locationRemover.execute(() -> {
543 pendingHosts.remove(event.key());
Charles Chanba90df12017-11-30 15:37:50 -0800544 removeLocation(newValue.value().hostId(),
545 new HostLocation(newValue.value().connectPoint(), 0L));
Charles Chan35a32322017-08-14 11:42:11 -0700546 });
547 }
548 break;
549 case REMOVE:
550 break;
551 default:
552 log.warn("Unknown map event type: {}", event.type());
553 }
554 }
555 }
Madan Jampani38a88212015-09-15 11:21:27 -0700556}