blob: 64d24a701c9b15b142be4860343f6ec1e084af3c [file] [log] [blame]
Madan Jampani38a88212015-09-15 11:21:27 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present Open Networking Laboratory
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
alshabib8a4a6002015-11-25 14:31:16 -080018import com.google.common.collect.ImmutableSet;
19import com.google.common.collect.Sets;
Madan Jampanic7f49f92015-12-10 11:35:06 -080020
Madan Jampani38a88212015-09-15 11:21:27 -070021import org.apache.felix.scr.annotations.Activate;
22import org.apache.felix.scr.annotations.Component;
23import org.apache.felix.scr.annotations.Deactivate;
24import org.apache.felix.scr.annotations.Reference;
25import org.apache.felix.scr.annotations.ReferenceCardinality;
26import org.apache.felix.scr.annotations.Service;
27import org.onlab.packet.IpAddress;
28import org.onlab.packet.MacAddress;
29import org.onlab.packet.VlanId;
30import org.onlab.util.KryoNamespace;
31import org.onosproject.net.Annotations;
32import org.onosproject.net.ConnectPoint;
33import org.onosproject.net.DefaultAnnotations;
34import org.onosproject.net.DefaultHost;
35import org.onosproject.net.DeviceId;
36import org.onosproject.net.Host;
37import org.onosproject.net.HostId;
Brian O'Connorf107bd72015-09-21 15:31:03 -070038import org.onosproject.net.HostLocation;
Madan Jampani38a88212015-09-15 11:21:27 -070039import org.onosproject.net.host.HostDescription;
40import org.onosproject.net.host.HostEvent;
41import org.onosproject.net.host.HostStore;
42import org.onosproject.net.host.HostStoreDelegate;
Madan Jampani38a88212015-09-15 11:21:27 -070043import org.onosproject.net.provider.ProviderId;
44import org.onosproject.store.AbstractStore;
45import org.onosproject.store.serializers.KryoNamespaces;
alshabib8a4a6002015-11-25 14:31:16 -080046import org.onosproject.store.service.ConsistentMap;
47import org.onosproject.store.service.MapEvent;
48import org.onosproject.store.service.MapEventListener;
49import org.onosproject.store.service.Serializer;
Madan Jampani38a88212015-09-15 11:21:27 -070050import org.onosproject.store.service.StorageService;
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +053051import org.onosproject.store.service.DistributedPrimitive.Status;
Madan Jampani38a88212015-09-15 11:21:27 -070052import org.slf4j.Logger;
53
alshabib8a4a6002015-11-25 14:31:16 -080054import java.util.Collection;
55import java.util.HashSet;
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +053056import java.util.Iterator;
alshabib8a4a6002015-11-25 14:31:16 -080057import java.util.Map;
58import java.util.Objects;
59import java.util.Set;
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +053060import java.util.concurrent.ConcurrentHashMap;
61import java.util.concurrent.ScheduledExecutorService;
62import java.util.function.Consumer;
alshabib8a4a6002015-11-25 14:31:16 -080063import java.util.function.Predicate;
64import java.util.stream.Collectors;
65
66import static com.google.common.base.Preconditions.checkNotNull;
67import static com.google.common.base.Preconditions.checkState;
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +053068import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
69import static org.onlab.util.Tools.groupedThreads;
alshabib8a4a6002015-11-25 14:31:16 -080070import static org.onosproject.net.DefaultAnnotations.merge;
71import static org.onosproject.net.host.HostEvent.Type.*;
72import static org.slf4j.LoggerFactory.getLogger;
Madan Jampani38a88212015-09-15 11:21:27 -070073
74/**
75 * Manages the inventory of hosts using a {@code EventuallyConsistentMap}.
76 */
77@Component(immediate = true)
78@Service
alshabib8a4a6002015-11-25 14:31:16 -080079public class DistributedHostStore
Madan Jampani38a88212015-09-15 11:21:27 -070080 extends AbstractStore<HostEvent, HostStoreDelegate>
81 implements HostStore {
82
83 private final Logger log = getLogger(getClass());
84
85 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
86 protected StorageService storageService;
87
Madan Jampanic6371882016-06-03 21:30:17 -070088 private ConsistentMap<HostId, DefaultHost> hostsConsistentMap;
alshabib8a4a6002015-11-25 14:31:16 -080089 private Map<HostId, DefaultHost> hosts;
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +053090 private Map<IpAddress, Set<Host>> hostsByIp;
Madan Jampani38a88212015-09-15 11:21:27 -070091
alshabib8a4a6002015-11-25 14:31:16 -080092 private MapEventListener<HostId, DefaultHost> hostLocationTracker =
Madan Jampani38a88212015-09-15 11:21:27 -070093 new HostLocationTracker();
94
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +053095 private ScheduledExecutorService executor;
96
97 private Consumer<Status> statusChangeListener;
98
Madan Jampani38a88212015-09-15 11:21:27 -070099 @Activate
100 public void activate() {
101 KryoNamespace.Builder hostSerializer = KryoNamespace.newBuilder()
Jonathan Hart38feb6e2016-08-29 22:54:16 +0000102 .register(KryoNamespaces.API);
Madan Jampani38a88212015-09-15 11:21:27 -0700103
Madan Jampanic6371882016-06-03 21:30:17 -0700104 hostsConsistentMap = storageService.<HostId, DefaultHost>consistentMapBuilder()
Madan Jampani38a88212015-09-15 11:21:27 -0700105 .withName("onos-hosts")
alshabib8a4a6002015-11-25 14:31:16 -0800106 .withRelaxedReadConsistency()
107 .withSerializer(Serializer.using(hostSerializer.build()))
Madan Jampani38a88212015-09-15 11:21:27 -0700108 .build();
109
Madan Jampanic6371882016-06-03 21:30:17 -0700110 hosts = hostsConsistentMap.asJavaMap();
alshabib8a4a6002015-11-25 14:31:16 -0800111
alshabib1400ce92015-12-16 15:05:47 -0800112
Madan Jampanic6371882016-06-03 21:30:17 -0700113 hostsConsistentMap.addListener(hostLocationTracker);
Madan Jampani38a88212015-09-15 11:21:27 -0700114
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +0530115 executor = newSingleThreadScheduledExecutor(groupedThreads("onos/hosts", "store", log));
116 statusChangeListener = status -> {
117 if (status == Status.ACTIVE) {
118 executor.execute(this::loadHostsByIp);
119 }
120 };
121 hostsConsistentMap.addStatusChangeListener(statusChangeListener);
122 loadHostsByIp();
Madan Jampani38a88212015-09-15 11:21:27 -0700123 log.info("Started");
124 }
125
126 @Deactivate
127 public void deactivate() {
Madan Jampanic6371882016-06-03 21:30:17 -0700128 hostsConsistentMap.removeListener(hostLocationTracker);
Madan Jampani38a88212015-09-15 11:21:27 -0700129
130 log.info("Stopped");
131 }
132
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +0530133 private void loadHostsByIp() {
134 hostsByIp = new ConcurrentHashMap<IpAddress, Set<Host>>();
135 hostsConsistentMap.asJavaMap().values().forEach(host -> {
136 host.ipAddresses().forEach(ip -> {
137 Set<Host> existingHosts = hostsByIp.get(ip);
138 if (existingHosts == null) {
139 hostsByIp.put(ip, addHosts(host));
140 } else {
141 existingHosts.add(host);
142 }
143 });
144 });
145 }
146
Brian O'Connordab09742015-12-07 20:06:29 -0800147 private boolean shouldUpdate(DefaultHost existingHost,
148 ProviderId providerId,
Brian O'Connordab09742015-12-07 20:06:29 -0800149 HostDescription hostDescription,
150 boolean replaceIPs) {
151 if (existingHost == null) {
152 return true;
153 }
154
Charles Chan69ebcbb2017-04-27 14:33:21 -0700155 // Avoid overriding configured host with learnt host
156 if (existingHost.configured() && !hostDescription.configured()) {
Charles Chan29ecdee2017-02-22 18:46:56 -0800157 return false;
158 }
159
Brian O'Connordab09742015-12-07 20:06:29 -0800160 if (!Objects.equals(existingHost.providerId(), providerId) ||
161 !Objects.equals(existingHost.mac(), hostDescription.hwAddress()) ||
162 !Objects.equals(existingHost.vlan(), hostDescription.vlan()) ||
Charles Chancd06c692017-04-27 20:46:06 -0700163 !Objects.equals(existingHost.locations(), hostDescription.locations())) {
Brian O'Connordab09742015-12-07 20:06:29 -0800164 return true;
165 }
166
167 if (replaceIPs) {
168 if (!Objects.equals(hostDescription.ipAddress(),
169 existingHost.ipAddresses())) {
170 return true;
171 }
172 } else {
173 if (!existingHost.ipAddresses().containsAll(hostDescription.ipAddress())) {
174 return true;
175 }
176 }
177
178 // check to see if any of the annotations provided by hostDescription
179 // differ from those in the existing host
180 return hostDescription.annotations().keys().stream()
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +0530181 .anyMatch(k -> !Objects.equals(hostDescription.annotations().value(k),
182 existingHost.annotations().value(k)));
Brian O'Connordab09742015-12-07 20:06:29 -0800183
184
185 }
186
Charles Chan009c3082015-11-10 14:18:04 -0800187 // TODO No longer need to return HostEvent
Madan Jampani38a88212015-09-15 11:21:27 -0700188 @Override
189 public HostEvent createOrUpdateHost(ProviderId providerId,
Brian O'Connorf107bd72015-09-21 15:31:03 -0700190 HostId hostId,
191 HostDescription hostDescription,
192 boolean replaceIPs) {
Madan Jampanic6371882016-06-03 21:30:17 -0700193 hostsConsistentMap.computeIf(hostId,
Charles Chan69ebcbb2017-04-27 14:33:21 -0700194 existingHost -> shouldUpdate(existingHost, providerId,
Brian O'Connordab09742015-12-07 20:06:29 -0800195 hostDescription, replaceIPs),
196 (id, existingHost) -> {
Brian O'Connorf107bd72015-09-21 15:31:03 -0700197
Madan Jampanic7f49f92015-12-10 11:35:06 -0800198 final Set<IpAddress> addresses;
199 if (existingHost == null || replaceIPs) {
200 addresses = ImmutableSet.copyOf(hostDescription.ipAddress());
201 } else {
202 addresses = Sets.newHashSet(existingHost.ipAddresses());
203 addresses.addAll(hostDescription.ipAddress());
204 }
Brian O'Connorf107bd72015-09-21 15:31:03 -0700205
Madan Jampanic7f49f92015-12-10 11:35:06 -0800206 final Annotations annotations;
sdn5e935452016-08-30 04:12:54 -0700207 final boolean configured;
Madan Jampanic7f49f92015-12-10 11:35:06 -0800208 if (existingHost != null) {
209 annotations = merge((DefaultAnnotations) existingHost.annotations(),
210 hostDescription.annotations());
sdn5e935452016-08-30 04:12:54 -0700211 configured = existingHost.configured();
Madan Jampanic7f49f92015-12-10 11:35:06 -0800212 } else {
213 annotations = hostDescription.annotations();
sdn5e935452016-08-30 04:12:54 -0700214 configured = hostDescription.configured();
Madan Jampanic7f49f92015-12-10 11:35:06 -0800215 }
Jonathan Hart38feb6e2016-08-29 22:54:16 +0000216
Madan Jampanic7f49f92015-12-10 11:35:06 -0800217 return new DefaultHost(providerId,
218 hostId,
219 hostDescription.hwAddress(),
220 hostDescription.vlan(),
Charles Chancd06c692017-04-27 20:46:06 -0700221 hostDescription.locations(),
Madan Jampanic7f49f92015-12-10 11:35:06 -0800222 addresses,
sdn5e935452016-08-30 04:12:54 -0700223 configured,
Madan Jampanic7f49f92015-12-10 11:35:06 -0800224 annotations);
225 });
Charles Chan009c3082015-11-10 14:18:04 -0800226 return null;
Madan Jampani38a88212015-09-15 11:21:27 -0700227 }
228
Charles Chan009c3082015-11-10 14:18:04 -0800229 // TODO No longer need to return HostEvent
Madan Jampani38a88212015-09-15 11:21:27 -0700230 @Override
231 public HostEvent removeHost(HostId hostId) {
Charles Chan009c3082015-11-10 14:18:04 -0800232 hosts.remove(hostId);
233 return null;
Madan Jampani38a88212015-09-15 11:21:27 -0700234 }
235
Charles Chan009c3082015-11-10 14:18:04 -0800236 // TODO No longer need to return HostEvent
Madan Jampani38a88212015-09-15 11:21:27 -0700237 @Override
samanwita palc40e5ed2015-09-24 11:01:51 -0700238 public HostEvent removeIp(HostId hostId, IpAddress ipAddress) {
Charles Chan009c3082015-11-10 14:18:04 -0800239 hosts.compute(hostId, (id, existingHost) -> {
samanwita palc40e5ed2015-09-24 11:01:51 -0700240 if (existingHost != null) {
241 checkState(Objects.equals(hostId.mac(), existingHost.mac()),
242 "Existing and new MAC addresses differ.");
243 checkState(Objects.equals(hostId.vlanId(), existingHost.vlan()),
244 "Existing and new VLANs differ.");
245
samanwita pale7c08de2015-09-24 21:59:49 -0700246 Set<IpAddress> addresses = existingHost.ipAddresses();
samanwita palc40e5ed2015-09-24 11:01:51 -0700247 if (addresses != null && addresses.contains(ipAddress)) {
samanwita pale7c08de2015-09-24 21:59:49 -0700248 addresses = new HashSet<>(existingHost.ipAddresses());
samanwita palc40e5ed2015-09-24 11:01:51 -0700249 addresses.remove(ipAddress);
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +0530250 removeIpFromHostsByIp(existingHost, ipAddress);
samanwita palc40e5ed2015-09-24 11:01:51 -0700251 return new DefaultHost(existingHost.providerId(),
252 hostId,
253 existingHost.mac(),
254 existingHost.vlan(),
Charles Chancd06c692017-04-27 20:46:06 -0700255 existingHost.locations(),
samanwita palc40e5ed2015-09-24 11:01:51 -0700256 ImmutableSet.copyOf(addresses),
Charles Chancd06c692017-04-27 20:46:06 -0700257 existingHost.configured(),
samanwita palc40e5ed2015-09-24 11:01:51 -0700258 existingHost.annotations());
259 } else {
260 return existingHost;
261 }
262 }
263 return null;
264 });
Charles Chan009c3082015-11-10 14:18:04 -0800265 return null;
samanwita palc40e5ed2015-09-24 11:01:51 -0700266 }
267
268 @Override
Madan Jampani38a88212015-09-15 11:21:27 -0700269 public int getHostCount() {
270 return hosts.size();
271 }
272
273 @Override
274 public Iterable<Host> getHosts() {
275 return ImmutableSet.copyOf(hosts.values());
276 }
277
278 @Override
279 public Host getHost(HostId hostId) {
280 return hosts.get(hostId);
281 }
282
283 @Override
284 public Set<Host> getHosts(VlanId vlanId) {
285 return filter(hosts.values(), host -> Objects.equals(host.vlan(), vlanId));
286 }
287
288 @Override
289 public Set<Host> getHosts(MacAddress mac) {
290 return filter(hosts.values(), host -> Objects.equals(host.mac(), mac));
291 }
292
293 @Override
294 public Set<Host> getHosts(IpAddress ip) {
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +0530295 Set<Host> hosts = hostsByIp.get(ip);
296 return hosts != null ? ImmutableSet.copyOf(hosts) : ImmutableSet.of();
Madan Jampani38a88212015-09-15 11:21:27 -0700297 }
298
299 @Override
300 public Set<Host> getConnectedHosts(ConnectPoint connectPoint) {
Charles Chan009c3082015-11-10 14:18:04 -0800301 Set<Host> filtered = hosts.entrySet().stream()
Charles Chancd06c692017-04-27 20:46:06 -0700302 .filter(entry -> entry.getValue().locations().contains(connectPoint))
Charles Chan009c3082015-11-10 14:18:04 -0800303 .map(Map.Entry::getValue)
304 .collect(Collectors.toSet());
305 return ImmutableSet.copyOf(filtered);
Madan Jampani38a88212015-09-15 11:21:27 -0700306 }
307
308 @Override
309 public Set<Host> getConnectedHosts(DeviceId deviceId) {
Charles Chan009c3082015-11-10 14:18:04 -0800310 Set<Host> filtered = hosts.entrySet().stream()
Charles Chancd06c692017-04-27 20:46:06 -0700311 .filter(entry -> entry.getValue().locations().stream()
312 .map(HostLocation::deviceId).anyMatch(dpid -> dpid.equals(deviceId)))
Charles Chan009c3082015-11-10 14:18:04 -0800313 .map(Map.Entry::getValue)
314 .collect(Collectors.toSet());
HIGUCHI Yutafe2122c2015-09-30 13:46:22 -0700315 return ImmutableSet.copyOf(filtered);
Madan Jampani38a88212015-09-15 11:21:27 -0700316 }
317
Madan Jampani38a88212015-09-15 11:21:27 -0700318 private Set<Host> filter(Collection<DefaultHost> collection, Predicate<DefaultHost> predicate) {
319 return collection.stream().filter(predicate).collect(Collectors.toSet());
320 }
321
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +0530322 private Set<Host> addHosts(Host host) {
323 Set<Host> hosts = Sets.newConcurrentHashSet();
324 hosts.add(host);
325 return hosts;
326 }
327
328 private Set<Host> updateHosts(Set<Host> existingHosts, Host host) {
329 Iterator<Host> iterator = existingHosts.iterator();
330 while (iterator.hasNext()) {
331 Host existingHost = iterator.next();
332 if (existingHost.id().equals(host.id())) {
333 iterator.remove();
334 }
335 }
336 existingHosts.add(host);
337 return existingHosts;
338 }
339
340 private Set<Host> removeHosts(Set<Host> existingHosts, Host host) {
341 if (existingHosts != null) {
342 Iterator<Host> iterator = existingHosts.iterator();
343 while (iterator.hasNext()) {
344 Host existingHost = iterator.next();
345 if (existingHost.id().equals(host.id())) {
346 iterator.remove();
347 }
348 }
349 }
350
351 if (existingHosts.isEmpty()) {
352 return null;
353 }
354 return existingHosts;
355 }
356
357 private void updateHostsByIp(DefaultHost host) {
358 host.ipAddresses().forEach(ip -> {
359 hostsByIp.compute(ip, (k, v) -> v == null ? addHosts(host)
360 : updateHosts(v, host));
361 });
362 }
363
364 private void removeHostsByIp(DefaultHost host) {
365 host.ipAddresses().forEach(ip -> {
366 hostsByIp.computeIfPresent(ip, (k, v) -> removeHosts(v, host));
367 });
368 }
369
370 private void removeIpFromHostsByIp(DefaultHost host, IpAddress ip) {
371 hostsByIp.computeIfPresent(ip, (k, v) -> removeHosts(v, host));
372 }
373
alshabib8a4a6002015-11-25 14:31:16 -0800374 private class HostLocationTracker implements MapEventListener<HostId, DefaultHost> {
Madan Jampani38a88212015-09-15 11:21:27 -0700375 @Override
alshabib8a4a6002015-11-25 14:31:16 -0800376 public void event(MapEvent<HostId, DefaultHost> event) {
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +0530377 DefaultHost host = checkNotNull(event.value().value());
alshabib1400ce92015-12-16 15:05:47 -0800378 switch (event.type()) {
379 case INSERT:
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +0530380 updateHostsByIp(host);
Charles Chan009c3082015-11-10 14:18:04 -0800381 notifyDelegate(new HostEvent(HOST_ADDED, host));
alshabib1400ce92015-12-16 15:05:47 -0800382 break;
383 case UPDATE:
Deepa Vaddireddy0a71c8b2017-01-19 21:20:45 +0530384 updateHostsByIp(host);
385 DefaultHost prevHost = checkNotNull(event.oldValue().value());
Charles Chancd06c692017-04-27 20:46:06 -0700386 if (!Objects.equals(prevHost.locations(), host.locations())) {
alshabib1400ce92015-12-16 15:05:47 -0800387 notifyDelegate(new HostEvent(HOST_MOVED, host, prevHost));
388 } else if (!Objects.equals(prevHost, host)) {
389 notifyDelegate(new HostEvent(HOST_UPDATED, host, prevHost));
390 }
391 break;
392 case REMOVE:
Charles Chan21720342017-05-13 00:19:09 -0700393 removeHostsByIp(host);
Yuta HIGUCHI215a7e42016-07-20 19:54:06 -0700394 notifyDelegate(new HostEvent(HOST_REMOVED, host));
alshabib1400ce92015-12-16 15:05:47 -0800395 break;
396 default:
397 log.warn("Unknown map event type: {}", event.type());
Madan Jampani38a88212015-09-15 11:21:27 -0700398 }
399 }
400 }
401}