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