blob: 7066b21bb24edfad21b61b5e1f5e3be7063e5085 [file] [log] [blame]
Madan Jampani38a88212015-09-15 11:21:27 -07001/*
2 * Copyright 2015 Open Networking Laboratory
3 *
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
18import static com.google.common.base.Preconditions.checkNotNull;
Brian O'Connorf107bd72015-09-21 15:31:03 -070019import static com.google.common.base.Preconditions.checkState;
Madan Jampani38a88212015-09-15 11:21:27 -070020import static org.onosproject.net.DefaultAnnotations.merge;
21import static org.onosproject.net.host.HostEvent.Type.HOST_ADDED;
22import static org.onosproject.net.host.HostEvent.Type.HOST_REMOVED;
Brian O'Connorf107bd72015-09-21 15:31:03 -070023import static org.onosproject.net.host.HostEvent.Type.HOST_MOVED;
Madan Jampani38a88212015-09-15 11:21:27 -070024import static org.onosproject.net.host.HostEvent.Type.HOST_UPDATED;
25import static org.onosproject.store.service.EventuallyConsistentMapEvent.Type.PUT;
26import static org.onosproject.store.service.EventuallyConsistentMapEvent.Type.REMOVE;
27import static org.slf4j.LoggerFactory.getLogger;
28
29import java.util.Collection;
samanwita palc40e5ed2015-09-24 11:01:51 -070030import java.util.HashSet;
Charles Chan009c3082015-11-10 14:18:04 -080031import java.util.Map;
Madan Jampani38a88212015-09-15 11:21:27 -070032import java.util.Objects;
33import java.util.Set;
Charles Chan009c3082015-11-10 14:18:04 -080034import java.util.concurrent.ConcurrentHashMap;
Madan Jampani38a88212015-09-15 11:21:27 -070035import java.util.function.Predicate;
36import java.util.stream.Collectors;
37
38import org.apache.felix.scr.annotations.Activate;
39import org.apache.felix.scr.annotations.Component;
40import org.apache.felix.scr.annotations.Deactivate;
41import org.apache.felix.scr.annotations.Reference;
42import org.apache.felix.scr.annotations.ReferenceCardinality;
43import org.apache.felix.scr.annotations.Service;
44import org.onlab.packet.IpAddress;
45import org.onlab.packet.MacAddress;
46import org.onlab.packet.VlanId;
47import org.onlab.util.KryoNamespace;
48import org.onosproject.net.Annotations;
49import org.onosproject.net.ConnectPoint;
50import org.onosproject.net.DefaultAnnotations;
51import org.onosproject.net.DefaultHost;
52import org.onosproject.net.DeviceId;
53import org.onosproject.net.Host;
54import org.onosproject.net.HostId;
Brian O'Connorf107bd72015-09-21 15:31:03 -070055import org.onosproject.net.HostLocation;
Madan Jampani38a88212015-09-15 11:21:27 -070056import org.onosproject.net.host.HostDescription;
57import org.onosproject.net.host.HostEvent;
58import org.onosproject.net.host.HostStore;
59import org.onosproject.net.host.HostStoreDelegate;
Madan Jampani38a88212015-09-15 11:21:27 -070060import org.onosproject.net.provider.ProviderId;
61import org.onosproject.store.AbstractStore;
62import org.onosproject.store.serializers.KryoNamespaces;
63import org.onosproject.store.service.EventuallyConsistentMap;
64import org.onosproject.store.service.EventuallyConsistentMapEvent;
65import org.onosproject.store.service.EventuallyConsistentMapListener;
66import org.onosproject.store.service.LogicalClockService;
67import org.onosproject.store.service.StorageService;
68import org.slf4j.Logger;
69
Madan Jampani38a88212015-09-15 11:21:27 -070070import com.google.common.collect.ImmutableSet;
Madan Jampani38a88212015-09-15 11:21:27 -070071import com.google.common.collect.Sets;
72
73/**
74 * Manages the inventory of hosts using a {@code EventuallyConsistentMap}.
75 */
76@Component(immediate = true)
77@Service
78public class ECHostStore
79 extends AbstractStore<HostEvent, HostStoreDelegate>
80 implements HostStore {
81
82 private final Logger log = getLogger(getClass());
83
84 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
85 protected StorageService storageService;
86
87 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
88 protected LogicalClockService clockService;
89
Madan Jampani38a88212015-09-15 11:21:27 -070090 private EventuallyConsistentMap<HostId, DefaultHost> hosts;
91
Charles Chan009c3082015-11-10 14:18:04 -080092 private final ConcurrentHashMap<HostId, ConnectPoint> locations =
93 new ConcurrentHashMap<>();
94
Madan Jampani38a88212015-09-15 11:21:27 -070095 private EventuallyConsistentMapListener<HostId, DefaultHost> hostLocationTracker =
96 new HostLocationTracker();
97
98 @Activate
99 public void activate() {
100 KryoNamespace.Builder hostSerializer = KryoNamespace.newBuilder()
101 .register(KryoNamespaces.API);
102
103 hosts = storageService.<HostId, DefaultHost>eventuallyConsistentMapBuilder()
104 .withName("onos-hosts")
105 .withSerializer(hostSerializer)
106 .withTimestampProvider((k, v) -> clockService.getTimestamp())
107 .build();
108
109 hosts.addListener(hostLocationTracker);
110
111 log.info("Started");
112 }
113
114 @Deactivate
115 public void deactivate() {
116 hosts.removeListener(hostLocationTracker);
117 hosts.destroy();
118 locations.clear();
Madan Jampani38a88212015-09-15 11:21:27 -0700119
120 log.info("Stopped");
121 }
122
Charles Chan009c3082015-11-10 14:18:04 -0800123 // TODO No longer need to return HostEvent
Madan Jampani38a88212015-09-15 11:21:27 -0700124 @Override
125 public HostEvent createOrUpdateHost(ProviderId providerId,
Brian O'Connorf107bd72015-09-21 15:31:03 -0700126 HostId hostId,
127 HostDescription hostDescription,
128 boolean replaceIPs) {
129 // TODO: We need a way to detect conflicting changes and abort update.
130 // (BOC) Compute might do this for us.
131
Charles Chan009c3082015-11-10 14:18:04 -0800132 hosts.compute(hostId, (id, existingHost) -> {
Brian O'Connorf107bd72015-09-21 15:31:03 -0700133 HostLocation location = hostDescription.location();
134
135 final Set<IpAddress> addresses;
136 if (existingHost == null || replaceIPs) {
137 addresses = ImmutableSet.copyOf(hostDescription.ipAddress());
138 } else {
139 addresses = Sets.newHashSet(existingHost.ipAddresses());
140 addresses.addAll(hostDescription.ipAddress());
141 }
142
143 final Annotations annotations;
144 if (existingHost != null) {
145 annotations = merge((DefaultAnnotations) existingHost.annotations(),
146 hostDescription.annotations());
147 } else {
148 annotations = hostDescription.annotations();
149 }
150
Brian O'Connorf107bd72015-09-21 15:31:03 -0700151 return new DefaultHost(providerId,
152 hostId,
153 hostDescription.hwAddress(),
154 hostDescription.vlan(),
155 location,
156 addresses,
157 annotations);
158 });
159
Charles Chan009c3082015-11-10 14:18:04 -0800160 return null;
Madan Jampani38a88212015-09-15 11:21:27 -0700161 }
162
Charles Chan009c3082015-11-10 14:18:04 -0800163 // TODO No longer need to return HostEvent
Madan Jampani38a88212015-09-15 11:21:27 -0700164 @Override
165 public HostEvent removeHost(HostId hostId) {
Charles Chan009c3082015-11-10 14:18:04 -0800166 hosts.remove(hostId);
167 return null;
Madan Jampani38a88212015-09-15 11:21:27 -0700168 }
169
Charles Chan009c3082015-11-10 14:18:04 -0800170 // TODO No longer need to return HostEvent
Madan Jampani38a88212015-09-15 11:21:27 -0700171 @Override
samanwita palc40e5ed2015-09-24 11:01:51 -0700172 public HostEvent removeIp(HostId hostId, IpAddress ipAddress) {
Charles Chan009c3082015-11-10 14:18:04 -0800173 hosts.compute(hostId, (id, existingHost) -> {
samanwita palc40e5ed2015-09-24 11:01:51 -0700174 if (existingHost != null) {
175 checkState(Objects.equals(hostId.mac(), existingHost.mac()),
176 "Existing and new MAC addresses differ.");
177 checkState(Objects.equals(hostId.vlanId(), existingHost.vlan()),
178 "Existing and new VLANs differ.");
179
samanwita pale7c08de2015-09-24 21:59:49 -0700180 Set<IpAddress> addresses = existingHost.ipAddresses();
samanwita palc40e5ed2015-09-24 11:01:51 -0700181 if (addresses != null && addresses.contains(ipAddress)) {
samanwita pale7c08de2015-09-24 21:59:49 -0700182 addresses = new HashSet<>(existingHost.ipAddresses());
samanwita palc40e5ed2015-09-24 11:01:51 -0700183 addresses.remove(ipAddress);
184 return new DefaultHost(existingHost.providerId(),
185 hostId,
186 existingHost.mac(),
187 existingHost.vlan(),
188 existingHost.location(),
189 ImmutableSet.copyOf(addresses),
190 existingHost.annotations());
191 } else {
192 return existingHost;
193 }
194 }
195 return null;
196 });
Charles Chan009c3082015-11-10 14:18:04 -0800197 return null;
samanwita palc40e5ed2015-09-24 11:01:51 -0700198 }
199
200 @Override
Madan Jampani38a88212015-09-15 11:21:27 -0700201 public int getHostCount() {
202 return hosts.size();
203 }
204
205 @Override
206 public Iterable<Host> getHosts() {
207 return ImmutableSet.copyOf(hosts.values());
208 }
209
210 @Override
211 public Host getHost(HostId hostId) {
212 return hosts.get(hostId);
213 }
214
215 @Override
216 public Set<Host> getHosts(VlanId vlanId) {
217 return filter(hosts.values(), host -> Objects.equals(host.vlan(), vlanId));
218 }
219
220 @Override
221 public Set<Host> getHosts(MacAddress mac) {
222 return filter(hosts.values(), host -> Objects.equals(host.mac(), mac));
223 }
224
225 @Override
226 public Set<Host> getHosts(IpAddress ip) {
227 return filter(hosts.values(), host -> host.ipAddresses().contains(ip));
228 }
229
230 @Override
231 public Set<Host> getConnectedHosts(ConnectPoint connectPoint) {
Charles Chan009c3082015-11-10 14:18:04 -0800232 Set<Host> filtered = hosts.entrySet().stream()
233 .filter(entry -> entry.getValue().location().equals(connectPoint))
234 .map(Map.Entry::getValue)
235 .collect(Collectors.toSet());
236 return ImmutableSet.copyOf(filtered);
Madan Jampani38a88212015-09-15 11:21:27 -0700237 }
238
239 @Override
240 public Set<Host> getConnectedHosts(DeviceId deviceId) {
Charles Chan009c3082015-11-10 14:18:04 -0800241 Set<Host> filtered = hosts.entrySet().stream()
242 .filter(entry -> entry.getValue().location().deviceId().equals(deviceId))
243 .map(Map.Entry::getValue)
244 .collect(Collectors.toSet());
HIGUCHI Yutafe2122c2015-09-30 13:46:22 -0700245 return ImmutableSet.copyOf(filtered);
Madan Jampani38a88212015-09-15 11:21:27 -0700246 }
247
Madan Jampani38a88212015-09-15 11:21:27 -0700248 private Set<Host> filter(Collection<DefaultHost> collection, Predicate<DefaultHost> predicate) {
249 return collection.stream().filter(predicate).collect(Collectors.toSet());
250 }
251
Madan Jampani38a88212015-09-15 11:21:27 -0700252 private class HostLocationTracker implements EventuallyConsistentMapListener<HostId, DefaultHost> {
253 @Override
254 public void event(EventuallyConsistentMapEvent<HostId, DefaultHost> event) {
255 DefaultHost host = checkNotNull(event.value());
256 if (event.type() == PUT) {
Charles Chan009c3082015-11-10 14:18:04 -0800257 ConnectPoint prevLocation = locations.put(host.id(), host.location());
258 if (prevLocation == null) {
259 notifyDelegate(new HostEvent(HOST_ADDED, host));
260 } else if (!Objects.equals(prevLocation, host.location())) {
261 notifyDelegate(new HostEvent(HOST_MOVED, host));
262 } else {
263 notifyDelegate(new HostEvent(HOST_UPDATED, host));
264 }
Madan Jampani38a88212015-09-15 11:21:27 -0700265 } else if (event.type() == REMOVE) {
Charles Chan009c3082015-11-10 14:18:04 -0800266 if (locations.remove(host.id()) != null) {
Madan Jampani38a88212015-09-15 11:21:27 -0700267 notifyDelegate(new HostEvent(HOST_REMOVED, host));
268 }
Madan Jampani38a88212015-09-15 11:21:27 -0700269 }
270 }
271 }
272}