blob: bfb60c7e4ee0605b71996fc2676112868348cef0 [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;
51import org.slf4j.Logger;
52
alshabib8a4a6002015-11-25 14:31:16 -080053import java.util.Collection;
54import java.util.HashSet;
55import java.util.Map;
56import java.util.Objects;
57import java.util.Set;
alshabib8a4a6002015-11-25 14:31:16 -080058import java.util.function.Predicate;
59import java.util.stream.Collectors;
60
61import static com.google.common.base.Preconditions.checkNotNull;
62import static com.google.common.base.Preconditions.checkState;
63import static org.onosproject.net.DefaultAnnotations.merge;
64import static org.onosproject.net.host.HostEvent.Type.*;
65import static org.slf4j.LoggerFactory.getLogger;
Madan Jampani38a88212015-09-15 11:21:27 -070066
67/**
68 * Manages the inventory of hosts using a {@code EventuallyConsistentMap}.
69 */
70@Component(immediate = true)
71@Service
alshabib8a4a6002015-11-25 14:31:16 -080072public class DistributedHostStore
Madan Jampani38a88212015-09-15 11:21:27 -070073 extends AbstractStore<HostEvent, HostStoreDelegate>
74 implements HostStore {
75
76 private final Logger log = getLogger(getClass());
77
78 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
79 protected StorageService storageService;
80
Madan Jampanic6371882016-06-03 21:30:17 -070081 private ConsistentMap<HostId, DefaultHost> hostsConsistentMap;
alshabib8a4a6002015-11-25 14:31:16 -080082 private Map<HostId, DefaultHost> hosts;
Madan Jampani38a88212015-09-15 11:21:27 -070083
alshabib8a4a6002015-11-25 14:31:16 -080084 private MapEventListener<HostId, DefaultHost> hostLocationTracker =
Madan Jampani38a88212015-09-15 11:21:27 -070085 new HostLocationTracker();
86
87 @Activate
88 public void activate() {
89 KryoNamespace.Builder hostSerializer = KryoNamespace.newBuilder()
Jonathan Hart38feb6e2016-08-29 22:54:16 +000090 .register(KryoNamespaces.API);
Madan Jampani38a88212015-09-15 11:21:27 -070091
Madan Jampanic6371882016-06-03 21:30:17 -070092 hostsConsistentMap = storageService.<HostId, DefaultHost>consistentMapBuilder()
Madan Jampani38a88212015-09-15 11:21:27 -070093 .withName("onos-hosts")
alshabib8a4a6002015-11-25 14:31:16 -080094 .withRelaxedReadConsistency()
95 .withSerializer(Serializer.using(hostSerializer.build()))
Madan Jampani38a88212015-09-15 11:21:27 -070096 .build();
97
Madan Jampanic6371882016-06-03 21:30:17 -070098 hosts = hostsConsistentMap.asJavaMap();
alshabib8a4a6002015-11-25 14:31:16 -080099
alshabib1400ce92015-12-16 15:05:47 -0800100
Madan Jampanic6371882016-06-03 21:30:17 -0700101 hostsConsistentMap.addListener(hostLocationTracker);
Madan Jampani38a88212015-09-15 11:21:27 -0700102
103 log.info("Started");
104 }
105
106 @Deactivate
107 public void deactivate() {
Madan Jampanic6371882016-06-03 21:30:17 -0700108 hostsConsistentMap.removeListener(hostLocationTracker);
Madan Jampani38a88212015-09-15 11:21:27 -0700109
110 log.info("Stopped");
111 }
112
Brian O'Connordab09742015-12-07 20:06:29 -0800113 private boolean shouldUpdate(DefaultHost existingHost,
114 ProviderId providerId,
115 HostId hostId,
116 HostDescription hostDescription,
117 boolean replaceIPs) {
118 if (existingHost == null) {
119 return true;
120 }
121
Charles Chan29ecdee2017-02-22 18:46:56 -0800122 // Avoid overriding configured hosts
123 if (existingHost.configured()) {
124 return false;
125 }
126
Brian O'Connordab09742015-12-07 20:06:29 -0800127 if (!Objects.equals(existingHost.providerId(), providerId) ||
128 !Objects.equals(existingHost.mac(), hostDescription.hwAddress()) ||
129 !Objects.equals(existingHost.vlan(), hostDescription.vlan()) ||
Jonathan Hart38feb6e2016-08-29 22:54:16 +0000130 !Objects.equals(existingHost.location(), hostDescription.location())) {
Brian O'Connordab09742015-12-07 20:06:29 -0800131 return true;
132 }
133
134 if (replaceIPs) {
135 if (!Objects.equals(hostDescription.ipAddress(),
136 existingHost.ipAddresses())) {
137 return true;
138 }
139 } else {
140 if (!existingHost.ipAddresses().containsAll(hostDescription.ipAddress())) {
141 return true;
142 }
143 }
144
145 // check to see if any of the annotations provided by hostDescription
146 // differ from those in the existing host
147 return hostDescription.annotations().keys().stream()
148 .anyMatch(k -> !Objects.equals(hostDescription.annotations().value(k),
149 existingHost.annotations().value(k)));
150
151
152 }
153
Charles Chan009c3082015-11-10 14:18:04 -0800154 // TODO No longer need to return HostEvent
Madan Jampani38a88212015-09-15 11:21:27 -0700155 @Override
156 public HostEvent createOrUpdateHost(ProviderId providerId,
Brian O'Connorf107bd72015-09-21 15:31:03 -0700157 HostId hostId,
158 HostDescription hostDescription,
159 boolean replaceIPs) {
Madan Jampanic6371882016-06-03 21:30:17 -0700160 hostsConsistentMap.computeIf(hostId,
Brian O'Connordab09742015-12-07 20:06:29 -0800161 existingHost -> shouldUpdate(existingHost, providerId, hostId,
162 hostDescription, replaceIPs),
163 (id, existingHost) -> {
Madan Jampanic7f49f92015-12-10 11:35:06 -0800164 HostLocation location = hostDescription.location();
Brian O'Connorf107bd72015-09-21 15:31:03 -0700165
Madan Jampanic7f49f92015-12-10 11:35:06 -0800166 final Set<IpAddress> addresses;
167 if (existingHost == null || replaceIPs) {
168 addresses = ImmutableSet.copyOf(hostDescription.ipAddress());
169 } else {
170 addresses = Sets.newHashSet(existingHost.ipAddresses());
171 addresses.addAll(hostDescription.ipAddress());
172 }
Brian O'Connorf107bd72015-09-21 15:31:03 -0700173
Madan Jampanic7f49f92015-12-10 11:35:06 -0800174 final Annotations annotations;
sdn5e935452016-08-30 04:12:54 -0700175 final boolean configured;
Madan Jampanic7f49f92015-12-10 11:35:06 -0800176 if (existingHost != null) {
177 annotations = merge((DefaultAnnotations) existingHost.annotations(),
178 hostDescription.annotations());
sdn5e935452016-08-30 04:12:54 -0700179 configured = existingHost.configured();
Madan Jampanic7f49f92015-12-10 11:35:06 -0800180 } else {
181 annotations = hostDescription.annotations();
sdn5e935452016-08-30 04:12:54 -0700182 configured = hostDescription.configured();
Madan Jampanic7f49f92015-12-10 11:35:06 -0800183 }
Jonathan Hart38feb6e2016-08-29 22:54:16 +0000184
Madan Jampanic7f49f92015-12-10 11:35:06 -0800185 return new DefaultHost(providerId,
186 hostId,
187 hostDescription.hwAddress(),
188 hostDescription.vlan(),
189 location,
190 addresses,
sdn5e935452016-08-30 04:12:54 -0700191 configured,
Madan Jampanic7f49f92015-12-10 11:35:06 -0800192 annotations);
193 });
Charles Chan009c3082015-11-10 14:18:04 -0800194 return null;
Madan Jampani38a88212015-09-15 11:21:27 -0700195 }
196
Charles Chan009c3082015-11-10 14:18:04 -0800197 // TODO No longer need to return HostEvent
Madan Jampani38a88212015-09-15 11:21:27 -0700198 @Override
199 public HostEvent removeHost(HostId hostId) {
Charles Chan009c3082015-11-10 14:18:04 -0800200 hosts.remove(hostId);
201 return null;
Madan Jampani38a88212015-09-15 11:21:27 -0700202 }
203
Charles Chan009c3082015-11-10 14:18:04 -0800204 // TODO No longer need to return HostEvent
Madan Jampani38a88212015-09-15 11:21:27 -0700205 @Override
samanwita palc40e5ed2015-09-24 11:01:51 -0700206 public HostEvent removeIp(HostId hostId, IpAddress ipAddress) {
Charles Chan009c3082015-11-10 14:18:04 -0800207 hosts.compute(hostId, (id, existingHost) -> {
samanwita palc40e5ed2015-09-24 11:01:51 -0700208 if (existingHost != null) {
209 checkState(Objects.equals(hostId.mac(), existingHost.mac()),
210 "Existing and new MAC addresses differ.");
211 checkState(Objects.equals(hostId.vlanId(), existingHost.vlan()),
212 "Existing and new VLANs differ.");
213
samanwita pale7c08de2015-09-24 21:59:49 -0700214 Set<IpAddress> addresses = existingHost.ipAddresses();
samanwita palc40e5ed2015-09-24 11:01:51 -0700215 if (addresses != null && addresses.contains(ipAddress)) {
samanwita pale7c08de2015-09-24 21:59:49 -0700216 addresses = new HashSet<>(existingHost.ipAddresses());
samanwita palc40e5ed2015-09-24 11:01:51 -0700217 addresses.remove(ipAddress);
218 return new DefaultHost(existingHost.providerId(),
219 hostId,
220 existingHost.mac(),
221 existingHost.vlan(),
222 existingHost.location(),
223 ImmutableSet.copyOf(addresses),
224 existingHost.annotations());
225 } else {
226 return existingHost;
227 }
228 }
229 return null;
230 });
Charles Chan009c3082015-11-10 14:18:04 -0800231 return null;
samanwita palc40e5ed2015-09-24 11:01:51 -0700232 }
233
234 @Override
Madan Jampani38a88212015-09-15 11:21:27 -0700235 public int getHostCount() {
236 return hosts.size();
237 }
238
239 @Override
240 public Iterable<Host> getHosts() {
241 return ImmutableSet.copyOf(hosts.values());
242 }
243
244 @Override
245 public Host getHost(HostId hostId) {
246 return hosts.get(hostId);
247 }
248
249 @Override
250 public Set<Host> getHosts(VlanId vlanId) {
251 return filter(hosts.values(), host -> Objects.equals(host.vlan(), vlanId));
252 }
253
254 @Override
255 public Set<Host> getHosts(MacAddress mac) {
256 return filter(hosts.values(), host -> Objects.equals(host.mac(), mac));
257 }
258
259 @Override
260 public Set<Host> getHosts(IpAddress ip) {
261 return filter(hosts.values(), host -> host.ipAddresses().contains(ip));
262 }
263
264 @Override
265 public Set<Host> getConnectedHosts(ConnectPoint connectPoint) {
Charles Chan009c3082015-11-10 14:18:04 -0800266 Set<Host> filtered = hosts.entrySet().stream()
267 .filter(entry -> entry.getValue().location().equals(connectPoint))
268 .map(Map.Entry::getValue)
269 .collect(Collectors.toSet());
270 return ImmutableSet.copyOf(filtered);
Madan Jampani38a88212015-09-15 11:21:27 -0700271 }
272
273 @Override
274 public Set<Host> getConnectedHosts(DeviceId deviceId) {
Charles Chan009c3082015-11-10 14:18:04 -0800275 Set<Host> filtered = hosts.entrySet().stream()
276 .filter(entry -> entry.getValue().location().deviceId().equals(deviceId))
277 .map(Map.Entry::getValue)
278 .collect(Collectors.toSet());
HIGUCHI Yutafe2122c2015-09-30 13:46:22 -0700279 return ImmutableSet.copyOf(filtered);
Madan Jampani38a88212015-09-15 11:21:27 -0700280 }
281
Madan Jampani38a88212015-09-15 11:21:27 -0700282 private Set<Host> filter(Collection<DefaultHost> collection, Predicate<DefaultHost> predicate) {
283 return collection.stream().filter(predicate).collect(Collectors.toSet());
284 }
285
alshabib8a4a6002015-11-25 14:31:16 -0800286 private class HostLocationTracker implements MapEventListener<HostId, DefaultHost> {
Madan Jampani38a88212015-09-15 11:21:27 -0700287 @Override
alshabib8a4a6002015-11-25 14:31:16 -0800288 public void event(MapEvent<HostId, DefaultHost> event) {
Yuta HIGUCHI215a7e42016-07-20 19:54:06 -0700289 Host host;
alshabib1400ce92015-12-16 15:05:47 -0800290 switch (event.type()) {
291 case INSERT:
Yuta HIGUCHI215a7e42016-07-20 19:54:06 -0700292 host = checkNotNull(event.newValue().value());
Charles Chan009c3082015-11-10 14:18:04 -0800293 notifyDelegate(new HostEvent(HOST_ADDED, host));
alshabib1400ce92015-12-16 15:05:47 -0800294 break;
295 case UPDATE:
Yuta HIGUCHI215a7e42016-07-20 19:54:06 -0700296 host = checkNotNull(event.newValue().value());
297 Host prevHost = checkNotNull(event.oldValue().value());
alshabib1400ce92015-12-16 15:05:47 -0800298 if (!Objects.equals(prevHost.location(), host.location())) {
299 notifyDelegate(new HostEvent(HOST_MOVED, host, prevHost));
300 } else if (!Objects.equals(prevHost, host)) {
301 notifyDelegate(new HostEvent(HOST_UPDATED, host, prevHost));
302 }
303 break;
304 case REMOVE:
Yuta HIGUCHI215a7e42016-07-20 19:54:06 -0700305 host = checkNotNull(event.oldValue().value());
306 notifyDelegate(new HostEvent(HOST_REMOVED, host));
alshabib1400ce92015-12-16 15:05:47 -0800307 break;
308 default:
309 log.warn("Unknown map event type: {}", event.type());
Madan Jampani38a88212015-09-15 11:21:27 -0700310 }
311 }
312 }
313}