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