blob: 8b5db8ad44db0880547fe5e655748641297db11b [file] [log] [blame]
Madan Jampaniab994042014-10-13 15:34:04 -07001package org.onlab.onos.store.host.impl;
2
3import com.google.common.collect.HashMultimap;
4import com.google.common.collect.ImmutableSet;
5import com.google.common.collect.Multimap;
6import com.google.common.collect.Sets;
7
8import org.apache.felix.scr.annotations.Activate;
9import org.apache.felix.scr.annotations.Component;
10import org.apache.felix.scr.annotations.Deactivate;
11import org.apache.felix.scr.annotations.Reference;
12import org.apache.felix.scr.annotations.ReferenceCardinality;
13import org.apache.felix.scr.annotations.Service;
14import org.onlab.onos.cluster.ClusterService;
15import org.onlab.onos.net.Annotations;
16import org.onlab.onos.net.ConnectPoint;
17import org.onlab.onos.net.DefaultHost;
18import org.onlab.onos.net.DeviceId;
19import org.onlab.onos.net.Host;
20import org.onlab.onos.net.HostId;
21import org.onlab.onos.net.HostLocation;
22import org.onlab.onos.net.host.HostClockService;
23import org.onlab.onos.net.host.HostDescription;
24import org.onlab.onos.net.host.HostEvent;
25import org.onlab.onos.net.host.HostStore;
26import org.onlab.onos.net.host.HostStoreDelegate;
27import org.onlab.onos.net.host.PortAddresses;
28import org.onlab.onos.net.provider.ProviderId;
29import org.onlab.onos.store.AbstractStore;
30import org.onlab.onos.store.Timestamp;
31import org.onlab.onos.store.cluster.messaging.ClusterCommunicationService;
32import org.onlab.onos.store.common.impl.Timestamped;
33import org.onlab.packet.IpPrefix;
34import org.onlab.packet.MacAddress;
35import org.onlab.packet.VlanId;
36import org.slf4j.Logger;
37
38import java.util.HashSet;
39import java.util.Map;
40import java.util.Set;
41import java.util.concurrent.ConcurrentHashMap;
42
43import static org.onlab.onos.net.host.HostEvent.Type.*;
44import static org.slf4j.LoggerFactory.getLogger;
45
46/**
47 * Manages inventory of end-station hosts in distributed data store
48 * that uses optimistic replication and gossip based techniques.
49 */
50@Component(immediate = true)
51@Service
52public class GossipHostStore
53 extends AbstractStore<HostEvent, HostStoreDelegate>
54 implements HostStore {
55
56 private final Logger log = getLogger(getClass());
57
58 // Host inventory
59 private final Map<HostId, StoredHost> hosts = new ConcurrentHashMap<>(2000000, 0.75f, 16);
60
61 private final Map<HostId, Timestamped<Host>> removedHosts = new ConcurrentHashMap<>(2000000, 0.75f, 16);
62
63 // Hosts tracked by their location
64 private final Multimap<ConnectPoint, Host> locations = HashMultimap.create();
65
66 private final Map<ConnectPoint, PortAddresses> portAddresses =
67 new ConcurrentHashMap<>();
68
69 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
70 protected HostClockService hostClockService;
71
72 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
73 protected ClusterCommunicationService clusterCommunicator;
74
75 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
76 protected ClusterService clusterService;
77
78 @Activate
79 public void activate() {
80 log.info("Started");
81 }
82
83 @Deactivate
84 public void deactivate() {
85 log.info("Stopped");
86 }
87
88 @Override
89 public HostEvent createOrUpdateHost(ProviderId providerId, HostId hostId,
90 HostDescription hostDescription) {
91 Timestamp timestamp = hostClockService.getTimestamp(hostId);
92 return createOrUpdateHostInternal(providerId, hostId, hostDescription, timestamp);
93 // TODO: tell peers.
94 }
95
96 private HostEvent createOrUpdateHostInternal(ProviderId providerId, HostId hostId,
97 HostDescription hostDescription, Timestamp timestamp) {
98 StoredHost host = hosts.get(hostId);
99 if (host == null) {
100 return createHost(providerId, hostId, hostDescription, timestamp);
101 }
102 return updateHost(providerId, host, hostDescription, timestamp);
103 }
104
105 // creates a new host and sends HOST_ADDED
106 private HostEvent createHost(ProviderId providerId, HostId hostId,
107 HostDescription descr, Timestamp timestamp) {
108 synchronized (this) {
109 // If this host was previously removed, first ensure
110 // this new request is "newer"
111 if (removedHosts.containsKey(hostId)) {
112 if (removedHosts.get(hostId).isNewer(timestamp)) {
113 return null;
114 } else {
115 removedHosts.remove(hostId);
116 }
117 }
118 StoredHost newhost = new StoredHost(providerId, hostId,
119 descr.hwAddress(),
120 descr.vlan(),
121 new Timestamped<>(descr.location(), timestamp),
122 ImmutableSet.of(descr.ipAddress()));
123 hosts.put(hostId, newhost);
124 locations.put(descr.location(), newhost);
125 return new HostEvent(HOST_ADDED, newhost);
126 }
127 }
128
129 // checks for type of update to host, sends appropriate event
130 private HostEvent updateHost(ProviderId providerId, StoredHost host,
131 HostDescription descr, Timestamp timestamp) {
132 HostEvent event;
133 if (!host.location.isNewer(timestamp) && !host.location().equals(descr.location())) {
134 host.setLocation(new Timestamped<>(descr.location(), timestamp));
135 return new HostEvent(HOST_MOVED, host);
136 }
137
138 if (host.ipAddresses().contains(descr.ipAddress())) {
139 return null;
140 }
141
142 Set<IpPrefix> addresses = new HashSet<>(host.ipAddresses());
143 addresses.add(descr.ipAddress());
144 StoredHost updated = new StoredHost(providerId, host.id(),
145 host.mac(), host.vlan(),
146 host.location, addresses);
147 event = new HostEvent(HOST_UPDATED, updated);
148 synchronized (this) {
149 hosts.put(host.id(), updated);
150 locations.remove(host.location(), host);
151 locations.put(updated.location(), updated);
152 }
153 return event;
154 }
155
156 @Override
157 public HostEvent removeHost(HostId hostId) {
158 Timestamp timestamp = hostClockService.getTimestamp(hostId);
159 return removeHostInternal(hostId, timestamp);
160 // TODO: tell peers
161 }
162
163 private HostEvent removeHostInternal(HostId hostId, Timestamp timestamp) {
164 synchronized (this) {
165 Host host = hosts.remove(hostId);
166 if (host != null) {
167 locations.remove((host.location()), host);
168 removedHosts.put(hostId, new Timestamped<>(host, timestamp));
169 return new HostEvent(HOST_REMOVED, host);
170 }
171 return null;
172 }
173 }
174
175 @Override
176 public int getHostCount() {
177 return hosts.size();
178 }
179
180 @Override
181 public Iterable<Host> getHosts() {
182 return ImmutableSet.<Host>copyOf(hosts.values());
183 }
184
185 @Override
186 public Host getHost(HostId hostId) {
187 return hosts.get(hostId);
188 }
189
190 @Override
191 public Set<Host> getHosts(VlanId vlanId) {
192 Set<Host> vlanset = new HashSet<>();
193 for (Host h : hosts.values()) {
194 if (h.vlan().equals(vlanId)) {
195 vlanset.add(h);
196 }
197 }
198 return vlanset;
199 }
200
201 @Override
202 public Set<Host> getHosts(MacAddress mac) {
203 Set<Host> macset = new HashSet<>();
204 for (Host h : hosts.values()) {
205 if (h.mac().equals(mac)) {
206 macset.add(h);
207 }
208 }
209 return macset;
210 }
211
212 @Override
213 public Set<Host> getHosts(IpPrefix ip) {
214 Set<Host> ipset = new HashSet<>();
215 for (Host h : hosts.values()) {
216 if (h.ipAddresses().contains(ip)) {
217 ipset.add(h);
218 }
219 }
220 return ipset;
221 }
222
223 @Override
224 public Set<Host> getConnectedHosts(ConnectPoint connectPoint) {
225 return ImmutableSet.copyOf(locations.get(connectPoint));
226 }
227
228 @Override
229 public Set<Host> getConnectedHosts(DeviceId deviceId) {
230 Set<Host> hostset = new HashSet<>();
231 for (ConnectPoint p : locations.keySet()) {
232 if (p.deviceId().equals(deviceId)) {
233 hostset.addAll(locations.get(p));
234 }
235 }
236 return hostset;
237 }
238
239 @Override
240 public void updateAddressBindings(PortAddresses addresses) {
241 synchronized (portAddresses) {
242 PortAddresses existing = portAddresses.get(addresses.connectPoint());
243 if (existing == null) {
244 portAddresses.put(addresses.connectPoint(), addresses);
245 } else {
246 Set<IpPrefix> union = Sets.union(existing.ips(), addresses.ips())
247 .immutableCopy();
248
249 MacAddress newMac = (addresses.mac() == null) ? existing.mac()
250 : addresses.mac();
251
252 PortAddresses newAddresses =
253 new PortAddresses(addresses.connectPoint(), union, newMac);
254
255 portAddresses.put(newAddresses.connectPoint(), newAddresses);
256 }
257 }
258 }
259
260 @Override
261 public void removeAddressBindings(PortAddresses addresses) {
262 synchronized (portAddresses) {
263 PortAddresses existing = portAddresses.get(addresses.connectPoint());
264 if (existing != null) {
265 Set<IpPrefix> difference =
266 Sets.difference(existing.ips(), addresses.ips()).immutableCopy();
267
268 // If they removed the existing mac, set the new mac to null.
269 // Otherwise, keep the existing mac.
270 MacAddress newMac = existing.mac();
271 if (addresses.mac() != null && addresses.mac().equals(existing.mac())) {
272 newMac = null;
273 }
274
275 PortAddresses newAddresses =
276 new PortAddresses(addresses.connectPoint(), difference, newMac);
277
278 portAddresses.put(newAddresses.connectPoint(), newAddresses);
279 }
280 }
281 }
282
283 @Override
284 public void clearAddressBindings(ConnectPoint connectPoint) {
285 synchronized (portAddresses) {
286 portAddresses.remove(connectPoint);
287 }
288 }
289
290 @Override
291 public Set<PortAddresses> getAddressBindings() {
292 synchronized (portAddresses) {
293 return new HashSet<>(portAddresses.values());
294 }
295 }
296
297 @Override
298 public PortAddresses getAddressBindingsForPort(ConnectPoint connectPoint) {
299 PortAddresses addresses;
300
301 synchronized (portAddresses) {
302 addresses = portAddresses.get(connectPoint);
303 }
304
305 if (addresses == null) {
306 addresses = new PortAddresses(connectPoint, null, null);
307 }
308
309 return addresses;
310 }
311
312 // Auxiliary extension to allow location to mutate.
313 private class StoredHost extends DefaultHost {
314 private Timestamped<HostLocation> location;
315
316 /**
317 * Creates an end-station host using the supplied information.
318 *
319 * @param providerId provider identity
320 * @param id host identifier
321 * @param mac host MAC address
322 * @param vlan host VLAN identifier
323 * @param location host location
324 * @param ips host IP addresses
325 * @param annotations optional key/value annotations
326 */
327 public StoredHost(ProviderId providerId, HostId id,
328 MacAddress mac, VlanId vlan, Timestamped<HostLocation> location,
329 Set<IpPrefix> ips, Annotations... annotations) {
330 super(providerId, id, mac, vlan, location.value(), ips, annotations);
331 this.location = location;
332 }
333
334 void setLocation(Timestamped<HostLocation> location) {
335 this.location = location;
336 }
337
338 @Override
339 public HostLocation location() {
340 return location.value();
341 }
342 }
343}