blob: e833b2da49b1c4009a2926ed03ee79a60a67dfec [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;
19import static org.onosproject.net.DefaultAnnotations.merge;
20import static org.onosproject.net.host.HostEvent.Type.HOST_ADDED;
21import static org.onosproject.net.host.HostEvent.Type.HOST_REMOVED;
22import static org.onosproject.net.host.HostEvent.Type.HOST_UPDATED;
23import static org.onosproject.store.service.EventuallyConsistentMapEvent.Type.PUT;
24import static org.onosproject.store.service.EventuallyConsistentMapEvent.Type.REMOVE;
25import static org.slf4j.LoggerFactory.getLogger;
26
27import java.util.Collection;
28import java.util.Collections;
29import java.util.Objects;
30import java.util.Set;
31import java.util.function.Predicate;
32import java.util.stream.Collectors;
33
34import org.apache.felix.scr.annotations.Activate;
35import org.apache.felix.scr.annotations.Component;
36import org.apache.felix.scr.annotations.Deactivate;
37import org.apache.felix.scr.annotations.Reference;
38import org.apache.felix.scr.annotations.ReferenceCardinality;
39import org.apache.felix.scr.annotations.Service;
40import org.onlab.packet.IpAddress;
41import org.onlab.packet.MacAddress;
42import org.onlab.packet.VlanId;
43import org.onlab.util.KryoNamespace;
44import org.onosproject.net.Annotations;
45import org.onosproject.net.ConnectPoint;
46import org.onosproject.net.DefaultAnnotations;
47import org.onosproject.net.DefaultHost;
48import org.onosproject.net.DeviceId;
49import org.onosproject.net.Host;
50import org.onosproject.net.HostId;
51import org.onosproject.net.host.HostDescription;
52import org.onosproject.net.host.HostEvent;
53import org.onosproject.net.host.HostStore;
54import org.onosproject.net.host.HostStoreDelegate;
55import org.onosproject.net.host.PortAddresses;
56import org.onosproject.net.host.HostEvent.Type;
57import org.onosproject.net.provider.ProviderId;
58import org.onosproject.store.AbstractStore;
59import org.onosproject.store.serializers.KryoNamespaces;
60import org.onosproject.store.service.EventuallyConsistentMap;
61import org.onosproject.store.service.EventuallyConsistentMapEvent;
62import org.onosproject.store.service.EventuallyConsistentMapListener;
63import org.onosproject.store.service.LogicalClockService;
64import org.onosproject.store.service.StorageService;
65import org.slf4j.Logger;
66
67import com.google.common.collect.HashMultimap;
68import com.google.common.collect.ImmutableMultimap;
69import com.google.common.collect.ImmutableSet;
70import com.google.common.collect.Multimaps;
71import com.google.common.collect.SetMultimap;
72import com.google.common.collect.Sets;
73
74/**
75 * Manages the inventory of hosts using a {@code EventuallyConsistentMap}.
76 */
77@Component(immediate = true)
78@Service
79public class ECHostStore
80 extends AbstractStore<HostEvent, HostStoreDelegate>
81 implements HostStore {
82
83 private final Logger log = getLogger(getClass());
84
85 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
86 protected StorageService storageService;
87
88 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
89 protected LogicalClockService clockService;
90
91 // Hosts tracked by their location
92 private final SetMultimap<ConnectPoint, Host> locations =
93 Multimaps.synchronizedSetMultimap(
94 HashMultimap.<ConnectPoint, Host>create());
95
96 private final SetMultimap<ConnectPoint, PortAddresses> portAddresses =
97 Multimaps.synchronizedSetMultimap(
98 HashMultimap.<ConnectPoint, PortAddresses>create());
99
100 private EventuallyConsistentMap<HostId, DefaultHost> hosts;
101
102 private EventuallyConsistentMapListener<HostId, DefaultHost> hostLocationTracker =
103 new HostLocationTracker();
104
105 @Activate
106 public void activate() {
107 KryoNamespace.Builder hostSerializer = KryoNamespace.newBuilder()
108 .register(KryoNamespaces.API);
109
110 hosts = storageService.<HostId, DefaultHost>eventuallyConsistentMapBuilder()
111 .withName("onos-hosts")
112 .withSerializer(hostSerializer)
113 .withTimestampProvider((k, v) -> clockService.getTimestamp())
114 .build();
115
116 hosts.addListener(hostLocationTracker);
117
118 log.info("Started");
119 }
120
121 @Deactivate
122 public void deactivate() {
123 hosts.removeListener(hostLocationTracker);
124 hosts.destroy();
125 locations.clear();
126 portAddresses.clear();
127
128 log.info("Stopped");
129 }
130
131 @Override
132 public HostEvent createOrUpdateHost(ProviderId providerId,
133 HostId hostId,
134 HostDescription hostDescription) {
135 DefaultHost currentHost = hosts.get(hostId);
136 if (currentHost == null) {
137 DefaultHost newhost = new DefaultHost(
138 providerId,
139 hostId,
140 hostDescription.hwAddress(),
141 hostDescription.vlan(),
142 hostDescription.location(),
143 ImmutableSet.copyOf(hostDescription.ipAddress()),
144 hostDescription.annotations());
145 hosts.put(hostId, newhost);
146 return new HostEvent(HOST_ADDED, newhost);
147 }
148 return updateHost(providerId, hostId, hostDescription, currentHost);
149 }
150
151 @Override
152 public HostEvent removeHost(HostId hostId) {
153 Host host = hosts.remove(hostId);
154 return host != null ? new HostEvent(HOST_REMOVED, host) : null;
155 }
156
157 @Override
158 public int getHostCount() {
159 return hosts.size();
160 }
161
162 @Override
163 public Iterable<Host> getHosts() {
164 return ImmutableSet.copyOf(hosts.values());
165 }
166
167 @Override
168 public Host getHost(HostId hostId) {
169 return hosts.get(hostId);
170 }
171
172 @Override
173 public Set<Host> getHosts(VlanId vlanId) {
174 return filter(hosts.values(), host -> Objects.equals(host.vlan(), vlanId));
175 }
176
177 @Override
178 public Set<Host> getHosts(MacAddress mac) {
179 return filter(hosts.values(), host -> Objects.equals(host.mac(), mac));
180 }
181
182 @Override
183 public Set<Host> getHosts(IpAddress ip) {
184 return filter(hosts.values(), host -> host.ipAddresses().contains(ip));
185 }
186
187 @Override
188 public Set<Host> getConnectedHosts(ConnectPoint connectPoint) {
189 return ImmutableSet.copyOf(locations.get(connectPoint));
190 }
191
192 @Override
193 public Set<Host> getConnectedHosts(DeviceId deviceId) {
194 return ImmutableMultimap.copyOf(locations)
195 .entries()
196 .stream()
197 .filter(entry -> entry.getKey().deviceId().equals(deviceId))
198 .map(entry -> entry.getValue())
199 .collect(Collectors.toSet());
200 }
201
202 @Override
203 public void updateAddressBindings(PortAddresses addresses) {
204 portAddresses.put(addresses.connectPoint(), addresses);
205 }
206
207 @Override
208 public void removeAddressBindings(PortAddresses addresses) {
209 portAddresses.remove(addresses.connectPoint(), addresses);
210 }
211
212 @Override
213 public void clearAddressBindings(ConnectPoint connectPoint) {
214 portAddresses.removeAll(connectPoint);
215 }
216
217 @Override
218 public Set<PortAddresses> getAddressBindings() {
219 return ImmutableSet.copyOf(portAddresses.values());
220 }
221
222 @Override
223 public Set<PortAddresses> getAddressBindingsForPort(ConnectPoint connectPoint) {
224 synchronized (portAddresses) {
225 Set<PortAddresses> addresses = portAddresses.get(connectPoint);
226 return addresses == null ? Collections.emptySet() : ImmutableSet.copyOf(addresses);
227 }
228 }
229
230 private Set<Host> filter(Collection<DefaultHost> collection, Predicate<DefaultHost> predicate) {
231 return collection.stream().filter(predicate).collect(Collectors.toSet());
232 }
233
234 // checks for type of update to host, sends appropriate event
235 private HostEvent updateHost(ProviderId providerId,
236 HostId hostId,
237 HostDescription descr,
238 DefaultHost currentHost) {
239
240 final boolean hostMoved = !currentHost.location().equals(descr.location());
241 if (hostMoved ||
242 !currentHost.ipAddresses().containsAll(descr.ipAddress()) ||
243 !descr.annotations().keys().isEmpty()) {
244
245 Set<IpAddress> addresses = Sets.newHashSet(currentHost.ipAddresses());
246 addresses.addAll(descr.ipAddress());
247 Annotations annotations = merge((DefaultAnnotations) currentHost.annotations(),
248 descr.annotations());
249
250 DefaultHost updatedHost = new DefaultHost(providerId, currentHost.id(),
251 currentHost.mac(), currentHost.vlan(),
252 descr.location(),
253 addresses,
254 annotations);
255
256 // TODO: We need a way to detect conflicting changes and abort update.
257 hosts.put(hostId, updatedHost);
258 locations.remove(currentHost.location(), currentHost);
259 locations.put(updatedHost.location(), updatedHost);
260
261 HostEvent.Type eventType = hostMoved ? Type.HOST_MOVED : Type.HOST_UPDATED;
262 return new HostEvent(eventType, updatedHost);
263 }
264 return null;
265 }
266
267 private class HostLocationTracker implements EventuallyConsistentMapListener<HostId, DefaultHost> {
268 @Override
269 public void event(EventuallyConsistentMapEvent<HostId, DefaultHost> event) {
270 DefaultHost host = checkNotNull(event.value());
271 if (event.type() == PUT) {
272 boolean isNew = locations.put(host.location(), host);
273 notifyDelegate(new HostEvent(isNew ? HOST_ADDED : HOST_UPDATED, host));
274 } else if (event.type() == REMOVE) {
275 if (locations.remove(host.location(), host)) {
276 notifyDelegate(new HostEvent(HOST_REMOVED, host));
277 }
278
279 }
280 }
281 }
282}