blob: 83c922dc426bbffd6905f852db0a6b11e37274c7 [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;
Madan Jampanic7f49f92015-12-10 11:35:06 -080031import org.onlab.util.Tools;
Madan Jampani38a88212015-09-15 11:21:27 -070032import org.onosproject.net.Annotations;
33import org.onosproject.net.ConnectPoint;
34import org.onosproject.net.DefaultAnnotations;
35import org.onosproject.net.DefaultHost;
36import org.onosproject.net.DeviceId;
37import org.onosproject.net.Host;
38import org.onosproject.net.HostId;
Brian O'Connorf107bd72015-09-21 15:31:03 -070039import org.onosproject.net.HostLocation;
Madan Jampani38a88212015-09-15 11:21:27 -070040import org.onosproject.net.host.HostDescription;
41import org.onosproject.net.host.HostEvent;
42import org.onosproject.net.host.HostStore;
43import org.onosproject.net.host.HostStoreDelegate;
Madan Jampani38a88212015-09-15 11:21:27 -070044import org.onosproject.net.provider.ProviderId;
45import org.onosproject.store.AbstractStore;
46import org.onosproject.store.serializers.KryoNamespaces;
alshabib8a4a6002015-11-25 14:31:16 -080047import org.onosproject.store.service.ConsistentMap;
Madan Jampanic7f49f92015-12-10 11:35:06 -080048import org.onosproject.store.service.ConsistentMapException;
alshabib8a4a6002015-11-25 14:31:16 -080049import org.onosproject.store.service.MapEvent;
50import org.onosproject.store.service.MapEventListener;
51import org.onosproject.store.service.Serializer;
Madan Jampani38a88212015-09-15 11:21:27 -070052import org.onosproject.store.service.StorageService;
Madan Jampanic7f49f92015-12-10 11:35:06 -080053import org.onosproject.store.service.Versioned;
Madan Jampani38a88212015-09-15 11:21:27 -070054import org.slf4j.Logger;
55
alshabib8a4a6002015-11-25 14:31:16 -080056import java.util.Collection;
57import java.util.HashSet;
58import java.util.Map;
59import java.util.Objects;
60import java.util.Set;
61import java.util.concurrent.ConcurrentHashMap;
62import java.util.function.Predicate;
Madan Jampanic7f49f92015-12-10 11:35:06 -080063import java.util.function.Supplier;
alshabib8a4a6002015-11-25 14:31:16 -080064import java.util.stream.Collectors;
65
66import static com.google.common.base.Preconditions.checkNotNull;
67import static com.google.common.base.Preconditions.checkState;
68import static org.onosproject.net.DefaultAnnotations.merge;
69import static org.onosproject.net.host.HostEvent.Type.*;
70import static org.slf4j.LoggerFactory.getLogger;
Madan Jampani38a88212015-09-15 11:21:27 -070071
72/**
73 * Manages the inventory of hosts using a {@code EventuallyConsistentMap}.
74 */
75@Component(immediate = true)
76@Service
alshabib8a4a6002015-11-25 14:31:16 -080077public class DistributedHostStore
Madan Jampani38a88212015-09-15 11:21:27 -070078 extends AbstractStore<HostEvent, HostStoreDelegate>
79 implements HostStore {
80
81 private final Logger log = getLogger(getClass());
82
83 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
84 protected StorageService storageService;
85
alshabib8a4a6002015-11-25 14:31:16 -080086 private ConsistentMap<HostId, DefaultHost> host;
87 private Map<HostId, DefaultHost> hosts;
Madan Jampani38a88212015-09-15 11:21:27 -070088
Charles Chan33f28a92015-11-13 13:12:38 -080089 private final ConcurrentHashMap<HostId, DefaultHost> prevHosts =
Charles Chan009c3082015-11-10 14:18:04 -080090 new ConcurrentHashMap<>();
91
alshabib8a4a6002015-11-25 14:31:16 -080092 private MapEventListener<HostId, DefaultHost> hostLocationTracker =
Madan Jampani38a88212015-09-15 11:21:27 -070093 new HostLocationTracker();
94
95 @Activate
96 public void activate() {
97 KryoNamespace.Builder hostSerializer = KryoNamespace.newBuilder()
98 .register(KryoNamespaces.API);
99
alshabib8a4a6002015-11-25 14:31:16 -0800100 host = storageService.<HostId, DefaultHost>consistentMapBuilder()
Madan Jampani38a88212015-09-15 11:21:27 -0700101 .withName("onos-hosts")
alshabib8a4a6002015-11-25 14:31:16 -0800102 .withRelaxedReadConsistency()
103 .withSerializer(Serializer.using(hostSerializer.build()))
Madan Jampani38a88212015-09-15 11:21:27 -0700104 .build();
105
alshabib8a4a6002015-11-25 14:31:16 -0800106 hosts = host.asJavaMap();
107
alshabib1400ce92015-12-16 15:05:47 -0800108 prevHosts.putAll(hosts);
109
alshabib8a4a6002015-11-25 14:31:16 -0800110 host.addListener(hostLocationTracker);
Madan Jampani38a88212015-09-15 11:21:27 -0700111
112 log.info("Started");
113 }
114
115 @Deactivate
116 public void deactivate() {
alshabib8a4a6002015-11-25 14:31:16 -0800117 host.removeListener(hostLocationTracker);
Charles Chan33f28a92015-11-13 13:12:38 -0800118 prevHosts.clear();
Madan Jampani38a88212015-09-15 11:21:27 -0700119
120 log.info("Stopped");
121 }
122
Brian O'Connordab09742015-12-07 20:06:29 -0800123 private boolean shouldUpdate(DefaultHost existingHost,
124 ProviderId providerId,
125 HostId hostId,
126 HostDescription hostDescription,
127 boolean replaceIPs) {
128 if (existingHost == null) {
129 return true;
130 }
131
132 if (!Objects.equals(existingHost.providerId(), providerId) ||
133 !Objects.equals(existingHost.mac(), hostDescription.hwAddress()) ||
134 !Objects.equals(existingHost.vlan(), hostDescription.vlan()) ||
135 !Objects.equals(existingHost.location(), hostDescription.location())) {
136 return true;
137 }
138
139 if (replaceIPs) {
140 if (!Objects.equals(hostDescription.ipAddress(),
141 existingHost.ipAddresses())) {
142 return true;
143 }
144 } else {
145 if (!existingHost.ipAddresses().containsAll(hostDescription.ipAddress())) {
146 return true;
147 }
148 }
149
150 // check to see if any of the annotations provided by hostDescription
151 // differ from those in the existing host
152 return hostDescription.annotations().keys().stream()
153 .anyMatch(k -> !Objects.equals(hostDescription.annotations().value(k),
154 existingHost.annotations().value(k)));
155
156
157 }
158
Charles Chan009c3082015-11-10 14:18:04 -0800159 // TODO No longer need to return HostEvent
Madan Jampani38a88212015-09-15 11:21:27 -0700160 @Override
161 public HostEvent createOrUpdateHost(ProviderId providerId,
Brian O'Connorf107bd72015-09-21 15:31:03 -0700162 HostId hostId,
163 HostDescription hostDescription,
164 boolean replaceIPs) {
Madan Jampanic7f49f92015-12-10 11:35:06 -0800165 Supplier<Versioned<DefaultHost>> supplier =
166 () -> host.computeIf(hostId,
Brian O'Connordab09742015-12-07 20:06:29 -0800167 existingHost -> shouldUpdate(existingHost, providerId, hostId,
168 hostDescription, replaceIPs),
169 (id, existingHost) -> {
Madan Jampanic7f49f92015-12-10 11:35:06 -0800170 HostLocation location = hostDescription.location();
Brian O'Connorf107bd72015-09-21 15:31:03 -0700171
Madan Jampanic7f49f92015-12-10 11:35:06 -0800172 final Set<IpAddress> addresses;
173 if (existingHost == null || replaceIPs) {
174 addresses = ImmutableSet.copyOf(hostDescription.ipAddress());
175 } else {
176 addresses = Sets.newHashSet(existingHost.ipAddresses());
177 addresses.addAll(hostDescription.ipAddress());
178 }
Brian O'Connorf107bd72015-09-21 15:31:03 -0700179
Madan Jampanic7f49f92015-12-10 11:35:06 -0800180 final Annotations annotations;
181 if (existingHost != null) {
182 annotations = merge((DefaultAnnotations) existingHost.annotations(),
183 hostDescription.annotations());
184 } else {
185 annotations = hostDescription.annotations();
186 }
Brian O'Connorf107bd72015-09-21 15:31:03 -0700187
Madan Jampanic7f49f92015-12-10 11:35:06 -0800188 return new DefaultHost(providerId,
189 hostId,
190 hostDescription.hwAddress(),
191 hostDescription.vlan(),
192 location,
193 addresses,
194 annotations);
195 });
196
197 Tools.retryable(supplier,
198 ConsistentMapException.ConcurrentModification.class,
199 Integer.MAX_VALUE,
200 50).get();
Brian O'Connorf107bd72015-09-21 15:31:03 -0700201
Charles Chan009c3082015-11-10 14:18:04 -0800202 return null;
Madan Jampani38a88212015-09-15 11:21:27 -0700203 }
204
Charles Chan009c3082015-11-10 14:18:04 -0800205 // TODO No longer need to return HostEvent
Madan Jampani38a88212015-09-15 11:21:27 -0700206 @Override
207 public HostEvent removeHost(HostId hostId) {
Charles Chan009c3082015-11-10 14:18:04 -0800208 hosts.remove(hostId);
209 return null;
Madan Jampani38a88212015-09-15 11:21:27 -0700210 }
211
Charles Chan009c3082015-11-10 14:18:04 -0800212 // TODO No longer need to return HostEvent
Madan Jampani38a88212015-09-15 11:21:27 -0700213 @Override
samanwita palc40e5ed2015-09-24 11:01:51 -0700214 public HostEvent removeIp(HostId hostId, IpAddress ipAddress) {
Charles Chan009c3082015-11-10 14:18:04 -0800215 hosts.compute(hostId, (id, existingHost) -> {
samanwita palc40e5ed2015-09-24 11:01:51 -0700216 if (existingHost != null) {
217 checkState(Objects.equals(hostId.mac(), existingHost.mac()),
218 "Existing and new MAC addresses differ.");
219 checkState(Objects.equals(hostId.vlanId(), existingHost.vlan()),
220 "Existing and new VLANs differ.");
221
samanwita pale7c08de2015-09-24 21:59:49 -0700222 Set<IpAddress> addresses = existingHost.ipAddresses();
samanwita palc40e5ed2015-09-24 11:01:51 -0700223 if (addresses != null && addresses.contains(ipAddress)) {
samanwita pale7c08de2015-09-24 21:59:49 -0700224 addresses = new HashSet<>(existingHost.ipAddresses());
samanwita palc40e5ed2015-09-24 11:01:51 -0700225 addresses.remove(ipAddress);
226 return new DefaultHost(existingHost.providerId(),
227 hostId,
228 existingHost.mac(),
229 existingHost.vlan(),
230 existingHost.location(),
231 ImmutableSet.copyOf(addresses),
232 existingHost.annotations());
233 } else {
234 return existingHost;
235 }
236 }
237 return null;
238 });
Charles Chan009c3082015-11-10 14:18:04 -0800239 return null;
samanwita palc40e5ed2015-09-24 11:01:51 -0700240 }
241
242 @Override
Madan Jampani38a88212015-09-15 11:21:27 -0700243 public int getHostCount() {
244 return hosts.size();
245 }
246
247 @Override
248 public Iterable<Host> getHosts() {
249 return ImmutableSet.copyOf(hosts.values());
250 }
251
252 @Override
253 public Host getHost(HostId hostId) {
254 return hosts.get(hostId);
255 }
256
257 @Override
258 public Set<Host> getHosts(VlanId vlanId) {
259 return filter(hosts.values(), host -> Objects.equals(host.vlan(), vlanId));
260 }
261
262 @Override
263 public Set<Host> getHosts(MacAddress mac) {
264 return filter(hosts.values(), host -> Objects.equals(host.mac(), mac));
265 }
266
267 @Override
268 public Set<Host> getHosts(IpAddress ip) {
269 return filter(hosts.values(), host -> host.ipAddresses().contains(ip));
270 }
271
272 @Override
273 public Set<Host> getConnectedHosts(ConnectPoint connectPoint) {
Charles Chan009c3082015-11-10 14:18:04 -0800274 Set<Host> filtered = hosts.entrySet().stream()
275 .filter(entry -> entry.getValue().location().equals(connectPoint))
276 .map(Map.Entry::getValue)
277 .collect(Collectors.toSet());
278 return ImmutableSet.copyOf(filtered);
Madan Jampani38a88212015-09-15 11:21:27 -0700279 }
280
281 @Override
282 public Set<Host> getConnectedHosts(DeviceId deviceId) {
Charles Chan009c3082015-11-10 14:18:04 -0800283 Set<Host> filtered = hosts.entrySet().stream()
284 .filter(entry -> entry.getValue().location().deviceId().equals(deviceId))
285 .map(Map.Entry::getValue)
286 .collect(Collectors.toSet());
HIGUCHI Yutafe2122c2015-09-30 13:46:22 -0700287 return ImmutableSet.copyOf(filtered);
Madan Jampani38a88212015-09-15 11:21:27 -0700288 }
289
Madan Jampani38a88212015-09-15 11:21:27 -0700290 private Set<Host> filter(Collection<DefaultHost> collection, Predicate<DefaultHost> predicate) {
291 return collection.stream().filter(predicate).collect(Collectors.toSet());
292 }
293
alshabib8a4a6002015-11-25 14:31:16 -0800294 private class HostLocationTracker implements MapEventListener<HostId, DefaultHost> {
Madan Jampani38a88212015-09-15 11:21:27 -0700295 @Override
alshabib8a4a6002015-11-25 14:31:16 -0800296 public void event(MapEvent<HostId, DefaultHost> event) {
297 DefaultHost host = checkNotNull(event.value().value());
alshabib1400ce92015-12-16 15:05:47 -0800298 Host prevHost = prevHosts.put(host.id(), host);
299 switch (event.type()) {
300 case INSERT:
Charles Chan009c3082015-11-10 14:18:04 -0800301 notifyDelegate(new HostEvent(HOST_ADDED, host));
alshabib1400ce92015-12-16 15:05:47 -0800302 break;
303 case UPDATE:
304 if (!Objects.equals(prevHost.location(), host.location())) {
305 notifyDelegate(new HostEvent(HOST_MOVED, host, prevHost));
306 } else if (!Objects.equals(prevHost, host)) {
307 notifyDelegate(new HostEvent(HOST_UPDATED, host, prevHost));
308 }
309 break;
310 case REMOVE:
311 if (prevHosts.remove(host.id()) != null) {
312 notifyDelegate(new HostEvent(HOST_REMOVED, host));
313 }
314 break;
315 default:
316 log.warn("Unknown map event type: {}", event.type());
Madan Jampani38a88212015-09-15 11:21:27 -0700317 }
318 }
319 }
320}