blob: 26c6a84a444ed0251933677cb6a90e906196a6e0 [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;
Madan Jampani38a88212015-09-15 11:21:27 -070031import java.util.Objects;
32import java.util.Set;
Brian O'Connorf107bd72015-09-21 15:31:03 -070033import java.util.concurrent.atomic.AtomicReference;
Madan Jampani38a88212015-09-15 11:21:27 -070034import java.util.function.Predicate;
35import java.util.stream.Collectors;
36
37import org.apache.felix.scr.annotations.Activate;
38import org.apache.felix.scr.annotations.Component;
39import org.apache.felix.scr.annotations.Deactivate;
40import org.apache.felix.scr.annotations.Reference;
41import org.apache.felix.scr.annotations.ReferenceCardinality;
42import org.apache.felix.scr.annotations.Service;
43import org.onlab.packet.IpAddress;
44import org.onlab.packet.MacAddress;
45import org.onlab.packet.VlanId;
46import org.onlab.util.KryoNamespace;
47import org.onosproject.net.Annotations;
48import org.onosproject.net.ConnectPoint;
49import org.onosproject.net.DefaultAnnotations;
50import org.onosproject.net.DefaultHost;
51import org.onosproject.net.DeviceId;
52import org.onosproject.net.Host;
53import org.onosproject.net.HostId;
Brian O'Connorf107bd72015-09-21 15:31:03 -070054import org.onosproject.net.HostLocation;
Madan Jampani38a88212015-09-15 11:21:27 -070055import org.onosproject.net.host.HostDescription;
56import org.onosproject.net.host.HostEvent;
57import org.onosproject.net.host.HostStore;
58import org.onosproject.net.host.HostStoreDelegate;
Madan Jampani38a88212015-09-15 11:21:27 -070059import org.onosproject.net.host.HostEvent.Type;
60import 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
70import com.google.common.collect.HashMultimap;
71import com.google.common.collect.ImmutableMultimap;
72import com.google.common.collect.ImmutableSet;
73import com.google.common.collect.Multimaps;
74import com.google.common.collect.SetMultimap;
75import com.google.common.collect.Sets;
76
77/**
78 * Manages the inventory of hosts using a {@code EventuallyConsistentMap}.
79 */
80@Component(immediate = true)
81@Service
82public class ECHostStore
83 extends AbstractStore<HostEvent, HostStoreDelegate>
84 implements HostStore {
85
86 private final Logger log = getLogger(getClass());
87
88 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
89 protected StorageService storageService;
90
91 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
92 protected LogicalClockService clockService;
93
94 // Hosts tracked by their location
95 private final SetMultimap<ConnectPoint, Host> locations =
96 Multimaps.synchronizedSetMultimap(
97 HashMultimap.<ConnectPoint, Host>create());
98
Madan Jampani38a88212015-09-15 11:21:27 -070099 private EventuallyConsistentMap<HostId, DefaultHost> hosts;
100
101 private EventuallyConsistentMapListener<HostId, DefaultHost> hostLocationTracker =
102 new HostLocationTracker();
103
104 @Activate
105 public void activate() {
106 KryoNamespace.Builder hostSerializer = KryoNamespace.newBuilder()
107 .register(KryoNamespaces.API);
108
109 hosts = storageService.<HostId, DefaultHost>eventuallyConsistentMapBuilder()
110 .withName("onos-hosts")
111 .withSerializer(hostSerializer)
112 .withTimestampProvider((k, v) -> clockService.getTimestamp())
113 .build();
114
115 hosts.addListener(hostLocationTracker);
116
117 log.info("Started");
118 }
119
120 @Deactivate
121 public void deactivate() {
122 hosts.removeListener(hostLocationTracker);
123 hosts.destroy();
124 locations.clear();
Madan Jampani38a88212015-09-15 11:21:27 -0700125
126 log.info("Stopped");
127 }
128
129 @Override
130 public HostEvent createOrUpdateHost(ProviderId providerId,
Brian O'Connorf107bd72015-09-21 15:31:03 -0700131 HostId hostId,
132 HostDescription hostDescription,
133 boolean replaceIPs) {
134 // TODO: We need a way to detect conflicting changes and abort update.
135 // (BOC) Compute might do this for us.
136
137 final AtomicReference<Type> eventType = new AtomicReference<>();
138 final AtomicReference<DefaultHost> oldHost = new AtomicReference<>();
139 DefaultHost host = hosts.compute(hostId, (id, existingHost) -> {
140 if (existingHost != null) {
141 oldHost.set(existingHost);
142 checkState(Objects.equals(hostDescription.hwAddress(), existingHost.mac()),
143 "Existing and new MAC addresses differ.");
144 checkState(Objects.equals(hostDescription.vlan(), existingHost.vlan()),
145 "Existing and new VLANs differ.");
146 }
147
148 // TODO do we ever want the existing location?
149 HostLocation location = hostDescription.location();
150
151 final Set<IpAddress> addresses;
152 if (existingHost == null || replaceIPs) {
153 addresses = ImmutableSet.copyOf(hostDescription.ipAddress());
154 } else {
155 addresses = Sets.newHashSet(existingHost.ipAddresses());
156 addresses.addAll(hostDescription.ipAddress());
157 }
158
159 final Annotations annotations;
160 if (existingHost != null) {
161 annotations = merge((DefaultAnnotations) existingHost.annotations(),
162 hostDescription.annotations());
163 } else {
164 annotations = hostDescription.annotations();
165 }
166
167 if (existingHost == null) {
168 eventType.set(HOST_ADDED);
169 } else if (!Objects.equals(existingHost.location(), hostDescription.location())) {
170 eventType.set(HOST_MOVED);
171 } else if (!existingHost.ipAddresses().containsAll(hostDescription.ipAddress()) ||
172 !hostDescription.annotations().keys().isEmpty()) {
173 eventType.set(HOST_UPDATED);
174 } // else, eventType == null; this means we don't send an event
175
176 return new DefaultHost(providerId,
177 hostId,
178 hostDescription.hwAddress(),
179 hostDescription.vlan(),
180 location,
181 addresses,
182 annotations);
183 });
184
185 if (oldHost.get() != null) {
186 DefaultHost old = oldHost.get();
187 locations.remove(old.location(), old);
Madan Jampani38a88212015-09-15 11:21:27 -0700188 }
Brian O'Connorf107bd72015-09-21 15:31:03 -0700189 locations.put(host.location(), host);
190
191 return eventType.get() != null ? new HostEvent(eventType.get(), host) : null;
Madan Jampani38a88212015-09-15 11:21:27 -0700192 }
193
194 @Override
195 public HostEvent removeHost(HostId hostId) {
196 Host host = hosts.remove(hostId);
197 return host != null ? new HostEvent(HOST_REMOVED, host) : null;
198 }
199
200 @Override
samanwita palc40e5ed2015-09-24 11:01:51 -0700201 public HostEvent removeIp(HostId hostId, IpAddress ipAddress) {
202 DefaultHost host = hosts.compute(hostId, (id, existingHost) -> {
203 if (existingHost != null) {
204 checkState(Objects.equals(hostId.mac(), existingHost.mac()),
205 "Existing and new MAC addresses differ.");
206 checkState(Objects.equals(hostId.vlanId(), existingHost.vlan()),
207 "Existing and new VLANs differ.");
208
209 Set<IpAddress> addresses = new HashSet<>(existingHost.ipAddresses());
210 if (addresses != null && addresses.contains(ipAddress)) {
211 addresses.remove(ipAddress);
212 return new DefaultHost(existingHost.providerId(),
213 hostId,
214 existingHost.mac(),
215 existingHost.vlan(),
216 existingHost.location(),
217 ImmutableSet.copyOf(addresses),
218 existingHost.annotations());
219 } else {
220 return existingHost;
221 }
222 }
223 return null;
224 });
225 return host != null ? new HostEvent(HOST_UPDATED, host) : null;
226 }
227
228 @Override
Madan Jampani38a88212015-09-15 11:21:27 -0700229 public int getHostCount() {
230 return hosts.size();
231 }
232
233 @Override
234 public Iterable<Host> getHosts() {
235 return ImmutableSet.copyOf(hosts.values());
236 }
237
238 @Override
239 public Host getHost(HostId hostId) {
240 return hosts.get(hostId);
241 }
242
243 @Override
244 public Set<Host> getHosts(VlanId vlanId) {
245 return filter(hosts.values(), host -> Objects.equals(host.vlan(), vlanId));
246 }
247
248 @Override
249 public Set<Host> getHosts(MacAddress mac) {
250 return filter(hosts.values(), host -> Objects.equals(host.mac(), mac));
251 }
252
253 @Override
254 public Set<Host> getHosts(IpAddress ip) {
255 return filter(hosts.values(), host -> host.ipAddresses().contains(ip));
256 }
257
258 @Override
259 public Set<Host> getConnectedHosts(ConnectPoint connectPoint) {
260 return ImmutableSet.copyOf(locations.get(connectPoint));
261 }
262
263 @Override
264 public Set<Host> getConnectedHosts(DeviceId deviceId) {
265 return ImmutableMultimap.copyOf(locations)
266 .entries()
267 .stream()
268 .filter(entry -> entry.getKey().deviceId().equals(deviceId))
269 .map(entry -> entry.getValue())
270 .collect(Collectors.toSet());
271 }
272
Madan Jampani38a88212015-09-15 11:21:27 -0700273 private Set<Host> filter(Collection<DefaultHost> collection, Predicate<DefaultHost> predicate) {
274 return collection.stream().filter(predicate).collect(Collectors.toSet());
275 }
276
Madan Jampani38a88212015-09-15 11:21:27 -0700277 private class HostLocationTracker implements EventuallyConsistentMapListener<HostId, DefaultHost> {
278 @Override
279 public void event(EventuallyConsistentMapEvent<HostId, DefaultHost> event) {
280 DefaultHost host = checkNotNull(event.value());
281 if (event.type() == PUT) {
282 boolean isNew = locations.put(host.location(), host);
283 notifyDelegate(new HostEvent(isNew ? HOST_ADDED : HOST_UPDATED, host));
284 } else if (event.type() == REMOVE) {
285 if (locations.remove(host.location(), host)) {
286 notifyDelegate(new HostEvent(HOST_REMOVED, host));
287 }
288
289 }
290 }
291 }
292}