Madan Jampani

Support for optimistic replication on GossipHostStore

1 -package org.onlab.onos.store.host.impl;
2 -
3 -import com.google.common.collect.HashMultimap;
4 -import com.google.common.collect.ImmutableSet;
5 -import com.google.common.collect.Multimap;
6 -import com.google.common.collect.Sets;
7 -import org.apache.felix.scr.annotations.Activate;
8 -import org.apache.felix.scr.annotations.Component;
9 -import org.apache.felix.scr.annotations.Deactivate;
10 -import org.apache.felix.scr.annotations.Service;
11 -import org.onlab.onos.net.Annotations;
12 -import org.onlab.onos.net.ConnectPoint;
13 -import org.onlab.onos.net.DefaultHost;
14 -import org.onlab.onos.net.DeviceId;
15 -import org.onlab.onos.net.Host;
16 -import org.onlab.onos.net.HostId;
17 -import org.onlab.onos.net.HostLocation;
18 -import org.onlab.onos.net.host.HostDescription;
19 -import org.onlab.onos.net.host.HostEvent;
20 -import org.onlab.onos.net.host.HostStore;
21 -import org.onlab.onos.net.host.HostStoreDelegate;
22 -import org.onlab.onos.net.host.PortAddresses;
23 -import org.onlab.onos.net.provider.ProviderId;
24 -import org.onlab.onos.store.AbstractStore;
25 -import org.onlab.packet.IpPrefix;
26 -import org.onlab.packet.MacAddress;
27 -import org.onlab.packet.VlanId;
28 -import org.slf4j.Logger;
29 -
30 -import java.util.HashSet;
31 -import java.util.Map;
32 -import java.util.Set;
33 -import java.util.concurrent.ConcurrentHashMap;
34 -
35 -import static org.onlab.onos.net.host.HostEvent.Type.*;
36 -import static org.slf4j.LoggerFactory.getLogger;
37 -
38 -/**
39 - * Manages inventory of end-station hosts using trivial in-memory
40 - * implementation.
41 - */
42 -//FIXME: I LIE I AM NOT DISTRIBUTED
43 -@Component(immediate = true)
44 -@Service
45 -public class DistributedHostStore
46 - extends AbstractStore<HostEvent, HostStoreDelegate>
47 - implements HostStore {
48 -
49 - private final Logger log = getLogger(getClass());
50 -
51 - // Host inventory
52 - private final Map<HostId, StoredHost> hosts = new ConcurrentHashMap<>(2000000, 0.75f, 16);
53 -
54 - // Hosts tracked by their location
55 - private final Multimap<ConnectPoint, Host> locations = HashMultimap.create();
56 -
57 - private final Map<ConnectPoint, PortAddresses> portAddresses =
58 - new ConcurrentHashMap<>();
59 -
60 - @Activate
61 - public void activate() {
62 - log.info("Started");
63 - }
64 -
65 - @Deactivate
66 - public void deactivate() {
67 - log.info("Stopped");
68 - }
69 -
70 - @Override
71 - public HostEvent createOrUpdateHost(ProviderId providerId, HostId hostId,
72 - HostDescription hostDescription) {
73 - StoredHost host = hosts.get(hostId);
74 - if (host == null) {
75 - return createHost(providerId, hostId, hostDescription);
76 - }
77 - return updateHost(providerId, host, hostDescription);
78 - }
79 -
80 - // creates a new host and sends HOST_ADDED
81 - private HostEvent createHost(ProviderId providerId, HostId hostId,
82 - HostDescription descr) {
83 - StoredHost newhost = new StoredHost(providerId, hostId,
84 - descr.hwAddress(),
85 - descr.vlan(),
86 - descr.location(),
87 - ImmutableSet.of(descr.ipAddress()));
88 - synchronized (this) {
89 - hosts.put(hostId, newhost);
90 - locations.put(descr.location(), newhost);
91 - }
92 - return new HostEvent(HOST_ADDED, newhost);
93 - }
94 -
95 - // checks for type of update to host, sends appropriate event
96 - private HostEvent updateHost(ProviderId providerId, StoredHost host,
97 - HostDescription descr) {
98 - HostEvent event;
99 - if (!host.location().equals(descr.location())) {
100 - host.setLocation(descr.location());
101 - return new HostEvent(HOST_MOVED, host);
102 - }
103 -
104 - if (host.ipAddresses().contains(descr.ipAddress())) {
105 - return null;
106 - }
107 -
108 - Set<IpPrefix> addresses = new HashSet<>(host.ipAddresses());
109 - addresses.add(descr.ipAddress());
110 - StoredHost updated = new StoredHost(providerId, host.id(),
111 - host.mac(), host.vlan(),
112 - descr.location(), addresses);
113 - event = new HostEvent(HOST_UPDATED, updated);
114 - synchronized (this) {
115 - hosts.put(host.id(), updated);
116 - locations.remove(host.location(), host);
117 - locations.put(updated.location(), updated);
118 - }
119 - return event;
120 - }
121 -
122 - @Override
123 - public HostEvent removeHost(HostId hostId) {
124 - synchronized (this) {
125 - Host host = hosts.remove(hostId);
126 - if (host != null) {
127 - locations.remove((host.location()), host);
128 - return new HostEvent(HOST_REMOVED, host);
129 - }
130 - return null;
131 - }
132 - }
133 -
134 - @Override
135 - public int getHostCount() {
136 - return hosts.size();
137 - }
138 -
139 - @Override
140 - public Iterable<Host> getHosts() {
141 - return ImmutableSet.<Host>copyOf(hosts.values());
142 - }
143 -
144 - @Override
145 - public Host getHost(HostId hostId) {
146 - return hosts.get(hostId);
147 - }
148 -
149 - @Override
150 - public Set<Host> getHosts(VlanId vlanId) {
151 - Set<Host> vlanset = new HashSet<>();
152 - for (Host h : hosts.values()) {
153 - if (h.vlan().equals(vlanId)) {
154 - vlanset.add(h);
155 - }
156 - }
157 - return vlanset;
158 - }
159 -
160 - @Override
161 - public Set<Host> getHosts(MacAddress mac) {
162 - Set<Host> macset = new HashSet<>();
163 - for (Host h : hosts.values()) {
164 - if (h.mac().equals(mac)) {
165 - macset.add(h);
166 - }
167 - }
168 - return macset;
169 - }
170 -
171 - @Override
172 - public Set<Host> getHosts(IpPrefix ip) {
173 - Set<Host> ipset = new HashSet<>();
174 - for (Host h : hosts.values()) {
175 - if (h.ipAddresses().contains(ip)) {
176 - ipset.add(h);
177 - }
178 - }
179 - return ipset;
180 - }
181 -
182 - @Override
183 - public Set<Host> getConnectedHosts(ConnectPoint connectPoint) {
184 - return ImmutableSet.copyOf(locations.get(connectPoint));
185 - }
186 -
187 - @Override
188 - public Set<Host> getConnectedHosts(DeviceId deviceId) {
189 - Set<Host> hostset = new HashSet<>();
190 - for (ConnectPoint p : locations.keySet()) {
191 - if (p.deviceId().equals(deviceId)) {
192 - hostset.addAll(locations.get(p));
193 - }
194 - }
195 - return hostset;
196 - }
197 -
198 - @Override
199 - public void updateAddressBindings(PortAddresses addresses) {
200 - synchronized (portAddresses) {
201 - PortAddresses existing = portAddresses.get(addresses.connectPoint());
202 - if (existing == null) {
203 - portAddresses.put(addresses.connectPoint(), addresses);
204 - } else {
205 - Set<IpPrefix> union = Sets.union(existing.ips(), addresses.ips())
206 - .immutableCopy();
207 -
208 - MacAddress newMac = (addresses.mac() == null) ? existing.mac()
209 - : addresses.mac();
210 -
211 - PortAddresses newAddresses =
212 - new PortAddresses(addresses.connectPoint(), union, newMac);
213 -
214 - portAddresses.put(newAddresses.connectPoint(), newAddresses);
215 - }
216 - }
217 - }
218 -
219 - @Override
220 - public void removeAddressBindings(PortAddresses addresses) {
221 - synchronized (portAddresses) {
222 - PortAddresses existing = portAddresses.get(addresses.connectPoint());
223 - if (existing != null) {
224 - Set<IpPrefix> difference =
225 - Sets.difference(existing.ips(), addresses.ips()).immutableCopy();
226 -
227 - // If they removed the existing mac, set the new mac to null.
228 - // Otherwise, keep the existing mac.
229 - MacAddress newMac = existing.mac();
230 - if (addresses.mac() != null && addresses.mac().equals(existing.mac())) {
231 - newMac = null;
232 - }
233 -
234 - PortAddresses newAddresses =
235 - new PortAddresses(addresses.connectPoint(), difference, newMac);
236 -
237 - portAddresses.put(newAddresses.connectPoint(), newAddresses);
238 - }
239 - }
240 - }
241 -
242 - @Override
243 - public void clearAddressBindings(ConnectPoint connectPoint) {
244 - synchronized (portAddresses) {
245 - portAddresses.remove(connectPoint);
246 - }
247 - }
248 -
249 - @Override
250 - public Set<PortAddresses> getAddressBindings() {
251 - synchronized (portAddresses) {
252 - return new HashSet<>(portAddresses.values());
253 - }
254 - }
255 -
256 - @Override
257 - public PortAddresses getAddressBindingsForPort(ConnectPoint connectPoint) {
258 - PortAddresses addresses;
259 -
260 - synchronized (portAddresses) {
261 - addresses = portAddresses.get(connectPoint);
262 - }
263 -
264 - if (addresses == null) {
265 - addresses = new PortAddresses(connectPoint, null, null);
266 - }
267 -
268 - return addresses;
269 - }
270 -
271 - // Auxiliary extension to allow location to mutate.
272 - private class StoredHost extends DefaultHost {
273 - private HostLocation location;
274 -
275 - /**
276 - * Creates an end-station host using the supplied information.
277 - *
278 - * @param providerId provider identity
279 - * @param id host identifier
280 - * @param mac host MAC address
281 - * @param vlan host VLAN identifier
282 - * @param location host location
283 - * @param ips host IP addresses
284 - * @param annotations optional key/value annotations
285 - */
286 - public StoredHost(ProviderId providerId, HostId id,
287 - MacAddress mac, VlanId vlan, HostLocation location,
288 - Set<IpPrefix> ips, Annotations... annotations) {
289 - super(providerId, id, mac, vlan, location, ips, annotations);
290 - this.location = location;
291 - }
292 -
293 - void setLocation(HostLocation location) {
294 - this.location = location;
295 - }
296 -
297 - @Override
298 - public HostLocation location() {
299 - return location;
300 - }
301 - }
302 -}
...@@ -29,12 +29,18 @@ import org.onlab.onos.net.provider.ProviderId; ...@@ -29,12 +29,18 @@ import org.onlab.onos.net.provider.ProviderId;
29 import org.onlab.onos.store.AbstractStore; 29 import org.onlab.onos.store.AbstractStore;
30 import org.onlab.onos.store.Timestamp; 30 import org.onlab.onos.store.Timestamp;
31 import org.onlab.onos.store.cluster.messaging.ClusterCommunicationService; 31 import org.onlab.onos.store.cluster.messaging.ClusterCommunicationService;
32 +import org.onlab.onos.store.cluster.messaging.ClusterMessage;
33 +import org.onlab.onos.store.cluster.messaging.MessageSubject;
32 import org.onlab.onos.store.common.impl.Timestamped; 34 import org.onlab.onos.store.common.impl.Timestamped;
35 +import org.onlab.onos.store.serializers.DistributedStoreSerializers;
36 +import org.onlab.onos.store.serializers.KryoSerializer;
33 import org.onlab.packet.IpPrefix; 37 import org.onlab.packet.IpPrefix;
34 import org.onlab.packet.MacAddress; 38 import org.onlab.packet.MacAddress;
35 import org.onlab.packet.VlanId; 39 import org.onlab.packet.VlanId;
40 +import org.onlab.util.KryoPool;
36 import org.slf4j.Logger; 41 import org.slf4j.Logger;
37 42
43 +import java.io.IOException;
38 import java.util.HashSet; 44 import java.util.HashSet;
39 import java.util.Map; 45 import java.util.Map;
40 import java.util.Set; 46 import java.util.Set;
...@@ -76,6 +82,17 @@ public class GossipHostStore ...@@ -76,6 +82,17 @@ public class GossipHostStore
76 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 82 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
77 protected ClusterService clusterService; 83 protected ClusterService clusterService;
78 84
85 + private static final KryoSerializer SERIALIZER = new KryoSerializer() {
86 + @Override
87 + protected void setupKryoPool() {
88 + serializerPool = KryoPool.newBuilder()
89 + .register(DistributedStoreSerializers.COMMON)
90 + .register(InternalHostRemovedEvent.class)
91 + .build()
92 + .populate(1);
93 + }
94 + };
95 +
79 @Activate 96 @Activate
80 public void activate() { 97 public void activate() {
81 log.info("Started"); 98 log.info("Started");
...@@ -90,8 +107,18 @@ public class GossipHostStore ...@@ -90,8 +107,18 @@ public class GossipHostStore
90 public HostEvent createOrUpdateHost(ProviderId providerId, HostId hostId, 107 public HostEvent createOrUpdateHost(ProviderId providerId, HostId hostId,
91 HostDescription hostDescription) { 108 HostDescription hostDescription) {
92 Timestamp timestamp = hostClockService.getTimestamp(hostId); 109 Timestamp timestamp = hostClockService.getTimestamp(hostId);
93 - return createOrUpdateHostInternal(providerId, hostId, hostDescription, timestamp); 110 + HostEvent event = createOrUpdateHostInternal(providerId, hostId, hostDescription, timestamp);
94 - // TODO: tell peers. 111 + if (event != null) {
112 + log.info("Notifying peers of a host topology event for providerId: "
113 + + "{}; hostId: {}; hostDescription: {}", providerId, hostId, hostDescription);
114 + try {
115 + notifyPeers(new InternalHostEvent(providerId, hostId, hostDescription, timestamp));
116 + } catch (IOException e) {
117 + log.error("Failed to notify peers of a host topology event for providerId: "
118 + + "{}; hostId: {}; hostDescription: {}", providerId, hostId, hostDescription);
119 + }
120 + }
121 + return event;
95 } 122 }
96 123
97 private HostEvent createOrUpdateHostInternal(ProviderId providerId, HostId hostId, 124 private HostEvent createOrUpdateHostInternal(ProviderId providerId, HostId hostId,
...@@ -157,8 +184,16 @@ public class GossipHostStore ...@@ -157,8 +184,16 @@ public class GossipHostStore
157 @Override 184 @Override
158 public HostEvent removeHost(HostId hostId) { 185 public HostEvent removeHost(HostId hostId) {
159 Timestamp timestamp = hostClockService.getTimestamp(hostId); 186 Timestamp timestamp = hostClockService.getTimestamp(hostId);
160 - return removeHostInternal(hostId, timestamp); 187 + HostEvent event = removeHostInternal(hostId, timestamp);
161 - // TODO: tell peers 188 + if (event != null) {
189 + log.info("Notifying peers of a host removed topology event for hostId: {}", hostId);
190 + try {
191 + notifyPeers(new InternalHostRemovedEvent(hostId, timestamp));
192 + } catch (IOException e) {
193 + log.info("Failed to notify peers of a host removed topology event for hostId: {}", hostId);
194 + }
195 + }
196 + return event;
162 } 197 }
163 198
164 private HostEvent removeHostInternal(HostId hostId, Timestamp timestamp) { 199 private HostEvent removeHostInternal(HostId hostId, Timestamp timestamp) {
...@@ -341,4 +376,20 @@ public class GossipHostStore ...@@ -341,4 +376,20 @@ public class GossipHostStore
341 return location.value(); 376 return location.value();
342 } 377 }
343 } 378 }
379 +
380 + private void notifyPeers(InternalHostRemovedEvent event) throws IOException {
381 + broadcastMessage(GossipHostStoreMessageSubjects.HOST_REMOVED, event);
382 + }
383 +
384 + private void notifyPeers(InternalHostEvent event) throws IOException {
385 + broadcastMessage(GossipHostStoreMessageSubjects.HOST_UPDATED, event);
386 + }
387 +
388 + private void broadcastMessage(MessageSubject subject, Object event) throws IOException {
389 + ClusterMessage message = new ClusterMessage(
390 + clusterService.getLocalNode().id(),
391 + subject,
392 + SERIALIZER.encode(event));
393 + clusterCommunicator.broadcast(message);
394 + }
344 } 395 }
......
1 +package org.onlab.onos.store.host.impl;
2 +
3 +import org.onlab.onos.store.cluster.messaging.MessageSubject;
4 +
5 +public final class GossipHostStoreMessageSubjects {
6 + private GossipHostStoreMessageSubjects() {}
7 + public static final MessageSubject HOST_UPDATED = new MessageSubject("peer-host-updated");
8 + public static final MessageSubject HOST_REMOVED = new MessageSubject("peer-host-removed");
9 +}
1 +package org.onlab.onos.store.host.impl;
2 +
3 +import org.onlab.onos.net.HostId;
4 +import org.onlab.onos.net.host.HostDescription;
5 +import org.onlab.onos.net.provider.ProviderId;
6 +import org.onlab.onos.store.Timestamp;
7 +
8 +/**
9 + * Information published by GossipHostStore to notify peers of a host
10 + * change (create/update) event.
11 + */
12 +public class InternalHostEvent {
13 +
14 + private final ProviderId providerId;
15 + private final HostId hostId;
16 + private final HostDescription hostDescription;
17 + private final Timestamp timestamp;
18 +
19 + public InternalHostEvent(ProviderId providerId, HostId hostId,
20 + HostDescription hostDescription, Timestamp timestamp) {
21 + this.providerId = providerId;
22 + this.hostId = hostId;
23 + this.hostDescription = hostDescription;
24 + this.timestamp = timestamp;
25 + }
26 +
27 + public ProviderId providerId() {
28 + return providerId;
29 + }
30 +
31 + public HostId hostId() {
32 + return hostId;
33 + }
34 +
35 + public HostDescription hostDescription() {
36 + return hostDescription;
37 + }
38 +
39 + public Timestamp timestamp() {
40 + return timestamp;
41 + }
42 +
43 + // Needed for serialization.
44 + @SuppressWarnings("unused")
45 + private InternalHostEvent() {
46 + providerId = null;
47 + hostId = null;
48 + hostDescription = null;
49 + timestamp = null;
50 + }
51 +}
1 +package org.onlab.onos.store.host.impl;
2 +
3 +import org.onlab.onos.net.HostId;
4 +import org.onlab.onos.store.Timestamp;
5 +
6 +/**
7 + * Information published by GossipHostStore to notify peers of a host
8 + * removed event.
9 + */
10 +public class InternalHostRemovedEvent {
11 +
12 + private final HostId hostId;
13 + private final Timestamp timestamp;
14 +
15 + public InternalHostRemovedEvent(HostId hostId, Timestamp timestamp) {
16 + this.hostId = hostId;
17 + this.timestamp = timestamp;
18 + }
19 +
20 + public HostId hostId() {
21 + return hostId;
22 + }
23 +
24 + public Timestamp timestamp() {
25 + return timestamp;
26 + }
27 +
28 + // for serialization.
29 + @SuppressWarnings("unused")
30 + private InternalHostRemovedEvent() {
31 + hostId = null;
32 + timestamp = null;
33 + }
34 +}