blob: aa811bade0c1fa480c0995c82a21e3b9ae3ef9a6 [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 Chan47933752017-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;
Jordan Haltermandc49cfd2018-03-09 11:31:09 -050050import org.onosproject.store.service.AtomicCounter;
alshabib8a4a6002015-11-25 14:31:16 -080051import org.onosproject.store.service.ConsistentMap;
52import org.onosproject.store.service.MapEvent;
53import org.onosproject.store.service.MapEventListener;
54import org.onosproject.store.service.Serializer;
Madan Jampani38a88212015-09-15 11:21:27 -070055import org.onosproject.store.service.StorageService;
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +053056import org.onosproject.store.service.DistributedPrimitive.Status;
Charles Chan35a32322017-08-14 11:42:11 -070057import org.onosproject.store.service.Versioned;
Madan Jampani38a88212015-09-15 11:21:27 -070058import org.slf4j.Logger;
59
alshabib8a4a6002015-11-25 14:31:16 -080060import java.util.Collection;
61import java.util.HashSet;
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +053062import java.util.Iterator;
alshabib8a4a6002015-11-25 14:31:16 -080063import java.util.Map;
64import java.util.Objects;
Charles Chan47933752017-11-30 15:37:50 -080065import java.util.Optional;
alshabib8a4a6002015-11-25 14:31:16 -080066import java.util.Set;
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +053067import java.util.concurrent.ConcurrentHashMap;
68import java.util.concurrent.ScheduledExecutorService;
Charles Chan35a32322017-08-14 11:42:11 -070069import java.util.concurrent.TimeUnit;
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +053070import java.util.function.Consumer;
alshabib8a4a6002015-11-25 14:31:16 -080071import java.util.function.Predicate;
72import java.util.stream.Collectors;
73
74import static com.google.common.base.Preconditions.checkNotNull;
75import static com.google.common.base.Preconditions.checkState;
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +053076import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
77import static org.onlab.util.Tools.groupedThreads;
alshabib8a4a6002015-11-25 14:31:16 -080078import static org.onosproject.net.DefaultAnnotations.merge;
79import static org.onosproject.net.host.HostEvent.Type.*;
80import static org.slf4j.LoggerFactory.getLogger;
Madan Jampani38a88212015-09-15 11:21:27 -070081
82/**
83 * Manages the inventory of hosts using a {@code EventuallyConsistentMap}.
84 */
85@Component(immediate = true)
86@Service
alshabib8a4a6002015-11-25 14:31:16 -080087public class DistributedHostStore
Madan Jampani38a88212015-09-15 11:21:27 -070088 extends AbstractStore<HostEvent, HostStoreDelegate>
89 implements HostStore {
90
91 private final Logger log = getLogger(getClass());
92
93 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
94 protected StorageService storageService;
95
Jordan Haltermandc49cfd2018-03-09 11:31:09 -050096 private AtomicCounter hostProbeIndex;
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;
Charles Chan35c06ac2018-04-19 15:11:23 -0700109 private ScheduledExecutorService cacheCleaner;
110 private ScheduledExecutorService locationRemover;
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +0530111
112 private Consumer<Status> statusChangeListener;
113
Charles Chan35a32322017-08-14 11:42:11 -0700114 // TODO make this configurable
Charles Chan8277b6b2017-12-03 13:48:46 -0800115 private static final int PROBE_TIMEOUT_MS = 3000;
Charles Chan35a32322017-08-14 11:42:11 -0700116
117 private Cache<MacAddress, PendingHostLocation> pendingHostsCache = CacheBuilder.newBuilder()
118 .expireAfterWrite(PROBE_TIMEOUT_MS, TimeUnit.MILLISECONDS)
119 .removalListener((RemovalNotification<MacAddress, PendingHostLocation> notification) -> {
120 switch (notification.getCause()) {
121 case EXPIRED:
122 PendingHostLocation expired = notification.getValue();
123 if (expired != null) {
Charles Chan8e786b52017-09-12 18:57:47 -0700124 if (timeoutPendingHostLocation(notification.getKey())) {
125 log.info("Evict {} from pendingHosts due to probe timeout", notification.getValue());
126 }
Charles Chan35a32322017-08-14 11:42:11 -0700127 }
128 break;
129 case EXPLICIT:
130 break;
131 default:
132 log.warn("Remove {} from pendingHostLocations for unexpected reason {}",
133 notification.getKey(), notification.getCause());
134 }
135 }).build();
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 Chan47933752017-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
Jordan Haltermandc49cfd2018-03-09 11:31:09 -0500161 hostProbeIndex = storageService.atomicCounterBuilder()
162 .withName("onos-hosts-probe-index")
163 .build()
164 .asAtomicCounter();
165
Charles Chan35c06ac2018-04-19 15:11:23 -0700166 cacheCleaner = newSingleThreadScheduledExecutor(groupedThreads("onos/hosts", "cache-cleaner", log));
167 cacheCleaner.scheduleAtFixedRate(pendingHostsCache::cleanUp, 0, PROBE_TIMEOUT_MS, TimeUnit.MILLISECONDS);
Madan Jampani38a88212015-09-15 11:21:27 -0700168
Charles Chan35c06ac2018-04-19 15:11:23 -0700169 locationRemover = newSingleThreadScheduledExecutor(groupedThreads("onos/hosts", "loc-remover", log));
170
171 executor = newSingleThreadScheduledExecutor(groupedThreads("onos/hosts", "status-listener", log));
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +0530172 statusChangeListener = status -> {
173 if (status == Status.ACTIVE) {
174 executor.execute(this::loadHostsByIp);
175 }
176 };
177 hostsConsistentMap.addStatusChangeListener(statusChangeListener);
178 loadHostsByIp();
Madan Jampani38a88212015-09-15 11:21:27 -0700179 log.info("Started");
180 }
181
182 @Deactivate
183 public void deactivate() {
Madan Jampanic6371882016-06-03 21:30:17 -0700184 hostsConsistentMap.removeListener(hostLocationTracker);
Madan Jampani38a88212015-09-15 11:21:27 -0700185
Charles Chan35a32322017-08-14 11:42:11 -0700186 cacheCleaner.shutdown();
Charles Chan35c06ac2018-04-19 15:11:23 -0700187 locationRemover.shutdown();
188 executor.shutdown();
Charles Chan35a32322017-08-14 11:42:11 -0700189
Madan Jampani38a88212015-09-15 11:21:27 -0700190 log.info("Stopped");
191 }
192
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +0530193 private void loadHostsByIp() {
194 hostsByIp = new ConcurrentHashMap<IpAddress, Set<Host>>();
195 hostsConsistentMap.asJavaMap().values().forEach(host -> {
196 host.ipAddresses().forEach(ip -> {
197 Set<Host> existingHosts = hostsByIp.get(ip);
198 if (existingHosts == null) {
199 hostsByIp.put(ip, addHosts(host));
200 } else {
201 existingHosts.add(host);
202 }
203 });
204 });
205 }
206
Brian O'Connordab09742015-12-07 20:06:29 -0800207 private boolean shouldUpdate(DefaultHost existingHost,
208 ProviderId providerId,
Brian O'Connordab09742015-12-07 20:06:29 -0800209 HostDescription hostDescription,
210 boolean replaceIPs) {
211 if (existingHost == null) {
212 return true;
213 }
214
Charles Chan69ebcbb2017-04-27 14:33:21 -0700215 // Avoid overriding configured host with learnt host
216 if (existingHost.configured() && !hostDescription.configured()) {
Charles Chan29ecdee2017-02-22 18:46:56 -0800217 return false;
218 }
219
Brian O'Connordab09742015-12-07 20:06:29 -0800220 if (!Objects.equals(existingHost.providerId(), providerId) ||
221 !Objects.equals(existingHost.mac(), hostDescription.hwAddress()) ||
222 !Objects.equals(existingHost.vlan(), hostDescription.vlan()) ||
Jonghwan Hyun2c95acf2018-03-14 16:47:34 -0700223 !Objects.equals(existingHost.innerVlan(), hostDescription.innerVlan()) ||
224 !Objects.equals(existingHost.tpid(), hostDescription.tpid()) ||
Charles Chancd06c692017-04-27 20:46:06 -0700225 !Objects.equals(existingHost.locations(), hostDescription.locations())) {
Brian O'Connordab09742015-12-07 20:06:29 -0800226 return true;
227 }
228
229 if (replaceIPs) {
230 if (!Objects.equals(hostDescription.ipAddress(),
231 existingHost.ipAddresses())) {
232 return true;
233 }
234 } else {
235 if (!existingHost.ipAddresses().containsAll(hostDescription.ipAddress())) {
236 return true;
237 }
238 }
239
240 // check to see if any of the annotations provided by hostDescription
241 // differ from those in the existing host
242 return hostDescription.annotations().keys().stream()
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +0530243 .anyMatch(k -> !Objects.equals(hostDescription.annotations().value(k),
244 existingHost.annotations().value(k)));
Brian O'Connordab09742015-12-07 20:06:29 -0800245
246
247 }
248
Charles Chan009c3082015-11-10 14:18:04 -0800249 // TODO No longer need to return HostEvent
Madan Jampani38a88212015-09-15 11:21:27 -0700250 @Override
251 public HostEvent createOrUpdateHost(ProviderId providerId,
Brian O'Connorf107bd72015-09-21 15:31:03 -0700252 HostId hostId,
253 HostDescription hostDescription,
254 boolean replaceIPs) {
Madan Jampanic6371882016-06-03 21:30:17 -0700255 hostsConsistentMap.computeIf(hostId,
Charles Chan69ebcbb2017-04-27 14:33:21 -0700256 existingHost -> shouldUpdate(existingHost, providerId,
Brian O'Connordab09742015-12-07 20:06:29 -0800257 hostDescription, replaceIPs),
258 (id, existingHost) -> {
Brian O'Connorf107bd72015-09-21 15:31:03 -0700259
Madan Jampanic7f49f92015-12-10 11:35:06 -0800260 final Set<IpAddress> addresses;
261 if (existingHost == null || replaceIPs) {
262 addresses = ImmutableSet.copyOf(hostDescription.ipAddress());
263 } else {
264 addresses = Sets.newHashSet(existingHost.ipAddresses());
265 addresses.addAll(hostDescription.ipAddress());
266 }
Brian O'Connorf107bd72015-09-21 15:31:03 -0700267
Madan Jampanic7f49f92015-12-10 11:35:06 -0800268 final Annotations annotations;
269 if (existingHost != null) {
270 annotations = merge((DefaultAnnotations) existingHost.annotations(),
271 hostDescription.annotations());
272 } else {
273 annotations = hostDescription.annotations();
274 }
Jonathan Hart38feb6e2016-08-29 22:54:16 +0000275
Madan Jampanic7f49f92015-12-10 11:35:06 -0800276 return new DefaultHost(providerId,
277 hostId,
278 hostDescription.hwAddress(),
279 hostDescription.vlan(),
Charles Chancd06c692017-04-27 20:46:06 -0700280 hostDescription.locations(),
Madan Jampanic7f49f92015-12-10 11:35:06 -0800281 addresses,
Jonghwan Hyun2c95acf2018-03-14 16:47:34 -0700282 hostDescription.innerVlan(),
283 hostDescription.tpid(),
Charles Chanb1e99242017-07-07 14:11:09 -0700284 hostDescription.configured(),
Madan Jampanic7f49f92015-12-10 11:35:06 -0800285 annotations);
286 });
Charles Chan009c3082015-11-10 14:18:04 -0800287 return null;
Madan Jampani38a88212015-09-15 11:21:27 -0700288 }
289
Charles Chan009c3082015-11-10 14:18:04 -0800290 // TODO No longer need to return HostEvent
Madan Jampani38a88212015-09-15 11:21:27 -0700291 @Override
292 public HostEvent removeHost(HostId hostId) {
Charles Chan009c3082015-11-10 14:18:04 -0800293 hosts.remove(hostId);
294 return null;
Madan Jampani38a88212015-09-15 11:21:27 -0700295 }
296
Charles Chan009c3082015-11-10 14:18:04 -0800297 // TODO No longer need to return HostEvent
Madan Jampani38a88212015-09-15 11:21:27 -0700298 @Override
samanwita palc40e5ed2015-09-24 11:01:51 -0700299 public HostEvent removeIp(HostId hostId, IpAddress ipAddress) {
Charles Chan009c3082015-11-10 14:18:04 -0800300 hosts.compute(hostId, (id, existingHost) -> {
samanwita palc40e5ed2015-09-24 11:01:51 -0700301 if (existingHost != null) {
302 checkState(Objects.equals(hostId.mac(), existingHost.mac()),
303 "Existing and new MAC addresses differ.");
304 checkState(Objects.equals(hostId.vlanId(), existingHost.vlan()),
305 "Existing and new VLANs differ.");
306
samanwita pale7c08de2015-09-24 21:59:49 -0700307 Set<IpAddress> addresses = existingHost.ipAddresses();
samanwita palc40e5ed2015-09-24 11:01:51 -0700308 if (addresses != null && addresses.contains(ipAddress)) {
samanwita pale7c08de2015-09-24 21:59:49 -0700309 addresses = new HashSet<>(existingHost.ipAddresses());
samanwita palc40e5ed2015-09-24 11:01:51 -0700310 addresses.remove(ipAddress);
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +0530311 removeIpFromHostsByIp(existingHost, ipAddress);
samanwita palc40e5ed2015-09-24 11:01:51 -0700312 return new DefaultHost(existingHost.providerId(),
313 hostId,
314 existingHost.mac(),
315 existingHost.vlan(),
Charles Chancd06c692017-04-27 20:46:06 -0700316 existingHost.locations(),
samanwita palc40e5ed2015-09-24 11:01:51 -0700317 ImmutableSet.copyOf(addresses),
Charles Chancd06c692017-04-27 20:46:06 -0700318 existingHost.configured(),
samanwita palc40e5ed2015-09-24 11:01:51 -0700319 existingHost.annotations());
320 } else {
321 return existingHost;
322 }
323 }
324 return null;
325 });
Charles Chan009c3082015-11-10 14:18:04 -0800326 return null;
samanwita palc40e5ed2015-09-24 11:01:51 -0700327 }
328
329 @Override
Charles Chan47933752017-11-30 15:37:50 -0800330 public void appendLocation(HostId hostId, HostLocation location) {
331 log.debug("Appending location {} to host {}", location, hostId);
332 hosts.compute(hostId, (id, existingHost) -> {
333 if (existingHost != null) {
334 checkState(Objects.equals(hostId.mac(), existingHost.mac()),
335 "Existing and new MAC addresses differ.");
336 checkState(Objects.equals(hostId.vlanId(), existingHost.vlan()),
337 "Existing and new VLANs differ.");
338
Charles Chan9bd0e5a2018-04-25 18:51:46 -0400339 // Move within the same switch
340 // Simply replace old location that is on the same device
341 Set<HostLocation> newLocations = Sets.newHashSet(location);
342 existingHost.locations().stream().filter(loc -> !loc.deviceId().equals(location.deviceId()))
343 .forEach(newLocations::add);
Charles Chan47933752017-11-30 15:37:50 -0800344
345 return new DefaultHost(existingHost.providerId(),
346 hostId, existingHost.mac(), existingHost.vlan(),
Charles Chan9bd0e5a2018-04-25 18:51:46 -0400347 newLocations, existingHost.ipAddresses(),
Charles Chan47933752017-11-30 15:37:50 -0800348 existingHost.configured(), existingHost.annotations());
349 }
350 return null;
351 });
352 }
353
354 @Override
Charles Chan888e20a2017-05-01 15:44:23 -0700355 public void removeLocation(HostId hostId, HostLocation location) {
Charles Chan47933752017-11-30 15:37:50 -0800356 log.debug("Removing location {} from host {}", location, hostId);
Charles Chan888e20a2017-05-01 15:44:23 -0700357 hosts.compute(hostId, (id, existingHost) -> {
358 if (existingHost != null) {
359 checkState(Objects.equals(hostId.mac(), existingHost.mac()),
360 "Existing and new MAC addresses differ.");
361 checkState(Objects.equals(hostId.vlanId(), existingHost.vlan()),
362 "Existing and new VLANs differ.");
363
364 Set<HostLocation> locations = new HashSet<>(existingHost.locations());
365 locations.remove(location);
366
367 // Remove entire host if we are removing the last location
368 return locations.isEmpty() ? null :
369 new DefaultHost(existingHost.providerId(),
370 hostId, existingHost.mac(), existingHost.vlan(),
371 locations, existingHost.ipAddresses(),
372 existingHost.configured(), existingHost.annotations());
373 }
374 return null;
375 });
376 }
377
378 @Override
Madan Jampani38a88212015-09-15 11:21:27 -0700379 public int getHostCount() {
380 return hosts.size();
381 }
382
383 @Override
384 public Iterable<Host> getHosts() {
385 return ImmutableSet.copyOf(hosts.values());
386 }
387
388 @Override
389 public Host getHost(HostId hostId) {
390 return hosts.get(hostId);
391 }
392
393 @Override
394 public Set<Host> getHosts(VlanId vlanId) {
395 return filter(hosts.values(), host -> Objects.equals(host.vlan(), vlanId));
396 }
397
398 @Override
399 public Set<Host> getHosts(MacAddress mac) {
400 return filter(hosts.values(), host -> Objects.equals(host.mac(), mac));
401 }
402
403 @Override
404 public Set<Host> getHosts(IpAddress ip) {
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +0530405 Set<Host> hosts = hostsByIp.get(ip);
406 return hosts != null ? ImmutableSet.copyOf(hosts) : ImmutableSet.of();
Madan Jampani38a88212015-09-15 11:21:27 -0700407 }
408
409 @Override
410 public Set<Host> getConnectedHosts(ConnectPoint connectPoint) {
Charles Chan009c3082015-11-10 14:18:04 -0800411 Set<Host> filtered = hosts.entrySet().stream()
Charles Chancd06c692017-04-27 20:46:06 -0700412 .filter(entry -> entry.getValue().locations().contains(connectPoint))
Charles Chan009c3082015-11-10 14:18:04 -0800413 .map(Map.Entry::getValue)
414 .collect(Collectors.toSet());
415 return ImmutableSet.copyOf(filtered);
Madan Jampani38a88212015-09-15 11:21:27 -0700416 }
417
418 @Override
419 public Set<Host> getConnectedHosts(DeviceId deviceId) {
Charles Chan009c3082015-11-10 14:18:04 -0800420 Set<Host> filtered = hosts.entrySet().stream()
Charles Chancd06c692017-04-27 20:46:06 -0700421 .filter(entry -> entry.getValue().locations().stream()
422 .map(HostLocation::deviceId).anyMatch(dpid -> dpid.equals(deviceId)))
Charles Chan009c3082015-11-10 14:18:04 -0800423 .map(Map.Entry::getValue)
424 .collect(Collectors.toSet());
HIGUCHI Yutafe2122c2015-09-30 13:46:22 -0700425 return ImmutableSet.copyOf(filtered);
Madan Jampani38a88212015-09-15 11:21:27 -0700426 }
427
Charles Chan35a32322017-08-14 11:42:11 -0700428 @Override
Charles Chan47933752017-11-30 15:37:50 -0800429 public MacAddress addPendingHostLocation(HostId hostId, ConnectPoint connectPoint, ProbeMode probeMode) {
Charles Chan35a32322017-08-14 11:42:11 -0700430 // Use ONLab OUI (3 bytes) + atomic counter (3 bytes) as the source MAC of the probe
Jordan Haltermandc49cfd2018-03-09 11:31:09 -0500431 long nextIndex = hostProbeIndex.getAndIncrement();
Charles Chan35a32322017-08-14 11:42:11 -0700432 MacAddress probeMac = MacAddress.valueOf(MacAddress.NONE.toLong() + nextIndex);
Charles Chan47933752017-11-30 15:37:50 -0800433 PendingHostLocation phl = new PendingHostLocation(hostId, connectPoint, probeMode);
Charles Chan35a32322017-08-14 11:42:11 -0700434
435 pendingHostsCache.put(probeMac, phl);
436 pendingHosts.put(probeMac, phl);
437
438 return probeMac;
439 }
440
441 @Override
442 public void removePendingHostLocation(MacAddress probeMac) {
Charles Chan47933752017-11-30 15:37:50 -0800443 // Add the host location if probe replied in-time in DISCOVER mode
444 Optional.ofNullable(pendingHosts.get(probeMac)).ifPresent(phl -> {
445 if (phl.probeMode() == ProbeMode.DISCOVER) {
446 HostLocation newLocation = new HostLocation(phl.connectPoint(), System.currentTimeMillis());
447 appendLocation(phl.hostId(), newLocation);
448 }
449 });
450
Charles Chan35a32322017-08-14 11:42:11 -0700451 pendingHostsCache.invalidate(probeMac);
452 pendingHosts.remove(probeMac);
453 }
454
Charles Chan8e786b52017-09-12 18:57:47 -0700455 private boolean timeoutPendingHostLocation(MacAddress probeMac) {
456 PendingHostLocation phl = pendingHosts.computeIfPresent(probeMac, (k, v) -> {
Charles Chan35a32322017-08-14 11:42:11 -0700457 v.setExpired(true);
458 return v;
459 });
Charles Chan8e786b52017-09-12 18:57:47 -0700460 return phl != null;
Charles Chan35a32322017-08-14 11:42:11 -0700461 }
462
Madan Jampani38a88212015-09-15 11:21:27 -0700463 private Set<Host> filter(Collection<DefaultHost> collection, Predicate<DefaultHost> predicate) {
464 return collection.stream().filter(predicate).collect(Collectors.toSet());
465 }
466
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +0530467 private Set<Host> addHosts(Host host) {
468 Set<Host> hosts = Sets.newConcurrentHashSet();
469 hosts.add(host);
470 return hosts;
471 }
472
473 private Set<Host> updateHosts(Set<Host> existingHosts, Host host) {
474 Iterator<Host> iterator = existingHosts.iterator();
475 while (iterator.hasNext()) {
476 Host existingHost = iterator.next();
477 if (existingHost.id().equals(host.id())) {
478 iterator.remove();
479 }
480 }
481 existingHosts.add(host);
482 return existingHosts;
483 }
484
485 private Set<Host> removeHosts(Set<Host> existingHosts, Host host) {
486 if (existingHosts != null) {
487 Iterator<Host> iterator = existingHosts.iterator();
488 while (iterator.hasNext()) {
489 Host existingHost = iterator.next();
490 if (existingHost.id().equals(host.id())) {
491 iterator.remove();
492 }
493 }
494 }
495
Ray Milkey74e59132018-01-17 15:24:52 -0800496 if (existingHosts == null || existingHosts.isEmpty()) {
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +0530497 return null;
498 }
499 return existingHosts;
500 }
501
502 private void updateHostsByIp(DefaultHost host) {
503 host.ipAddresses().forEach(ip -> {
504 hostsByIp.compute(ip, (k, v) -> v == null ? addHosts(host)
505 : updateHosts(v, host));
506 });
507 }
508
509 private void removeHostsByIp(DefaultHost host) {
510 host.ipAddresses().forEach(ip -> {
511 hostsByIp.computeIfPresent(ip, (k, v) -> removeHosts(v, host));
512 });
513 }
514
515 private void removeIpFromHostsByIp(DefaultHost host, IpAddress ip) {
516 hostsByIp.computeIfPresent(ip, (k, v) -> removeHosts(v, host));
517 }
518
alshabib8a4a6002015-11-25 14:31:16 -0800519 private class HostLocationTracker implements MapEventListener<HostId, DefaultHost> {
Madan Jampani38a88212015-09-15 11:21:27 -0700520 @Override
alshabib8a4a6002015-11-25 14:31:16 -0800521 public void event(MapEvent<HostId, DefaultHost> event) {
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +0530522 DefaultHost host = checkNotNull(event.value().value());
alshabib1400ce92015-12-16 15:05:47 -0800523 switch (event.type()) {
524 case INSERT:
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +0530525 updateHostsByIp(host);
Charles Chan009c3082015-11-10 14:18:04 -0800526 notifyDelegate(new HostEvent(HOST_ADDED, host));
alshabib1400ce92015-12-16 15:05:47 -0800527 break;
528 case UPDATE:
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +0530529 updateHostsByIp(host);
530 DefaultHost prevHost = checkNotNull(event.oldValue().value());
Charles Chancd06c692017-04-27 20:46:06 -0700531 if (!Objects.equals(prevHost.locations(), host.locations())) {
alshabib1400ce92015-12-16 15:05:47 -0800532 notifyDelegate(new HostEvent(HOST_MOVED, host, prevHost));
533 } else if (!Objects.equals(prevHost, host)) {
534 notifyDelegate(new HostEvent(HOST_UPDATED, host, prevHost));
535 }
536 break;
537 case REMOVE:
Charles Chan21720342017-05-13 00:19:09 -0700538 removeHostsByIp(host);
Yuta HIGUCHI215a7e42016-07-20 19:54:06 -0700539 notifyDelegate(new HostEvent(HOST_REMOVED, host));
alshabib1400ce92015-12-16 15:05:47 -0800540 break;
541 default:
542 log.warn("Unknown map event type: {}", event.type());
Madan Jampani38a88212015-09-15 11:21:27 -0700543 }
544 }
545 }
Charles Chan35a32322017-08-14 11:42:11 -0700546
547 private class PendingHostListener implements MapEventListener<MacAddress, PendingHostLocation> {
548 @Override
549 public void event(MapEvent<MacAddress, PendingHostLocation> event) {
550 Versioned<PendingHostLocation> newValue = event.newValue();
551 switch (event.type()) {
552 case INSERT:
553 break;
554 case UPDATE:
Charles Chan47933752017-11-30 15:37:50 -0800555 // Remove the host location if probe timeout in VERIFY mode
556 if (newValue.value().expired() && newValue.value().probeMode() == ProbeMode.VERIFY) {
Charles Chan35a32322017-08-14 11:42:11 -0700557 locationRemover.execute(() -> {
558 pendingHosts.remove(event.key());
Charles Chan47933752017-11-30 15:37:50 -0800559 removeLocation(newValue.value().hostId(),
560 new HostLocation(newValue.value().connectPoint(), 0L));
Charles Chan35a32322017-08-14 11:42:11 -0700561 });
562 }
563 break;
564 case REMOVE:
565 break;
566 default:
567 log.warn("Unknown map event type: {}", event.type());
568 }
569 }
570 }
Madan Jampani38a88212015-09-15 11:21:27 -0700571}