Charles Chan

Fix duplicate host event

Change-Id: I632a1482e7b1b768ce7a0243cc8b6b398b9825b7
...@@ -88,10 +88,14 @@ public class SimpleHostStore ...@@ -88,10 +88,14 @@ public class SimpleHostStore
88 boolean replaceIps) { 88 boolean replaceIps) {
89 //TODO We need a way to detect conflicting changes and abort update. 89 //TODO We need a way to detect conflicting changes and abort update.
90 StoredHost host = hosts.get(hostId); 90 StoredHost host = hosts.get(hostId);
91 + HostEvent hostEvent;
91 if (host == null) { 92 if (host == null) {
92 - return createHost(providerId, hostId, hostDescription); 93 + hostEvent = createHost(providerId, hostId, hostDescription);
94 + } else {
95 + hostEvent = updateHost(providerId, host, hostDescription, replaceIps);
93 } 96 }
94 - return updateHost(providerId, host, hostDescription, replaceIps); 97 + notifyDelegate(hostEvent);
98 + return hostEvent;
95 } 99 }
96 100
97 // creates a new host and sends HOST_ADDED 101 // creates a new host and sends HOST_ADDED
...@@ -153,7 +157,9 @@ public class SimpleHostStore ...@@ -153,7 +157,9 @@ public class SimpleHostStore
153 Host host = hosts.remove(hostId); 157 Host host = hosts.remove(hostId);
154 if (host != null) { 158 if (host != null) {
155 locations.remove((host.location()), host); 159 locations.remove((host.location()), host);
156 - return new HostEvent(HOST_REMOVED, host); 160 + HostEvent hostEvent = new HostEvent(HOST_REMOVED, host);
161 + notifyDelegate(hostEvent);
162 + return hostEvent;
157 } 163 }
158 return null; 164 return null;
159 } 165 }
......
...@@ -192,10 +192,7 @@ public class HostManager ...@@ -192,10 +192,7 @@ public class HostManager
192 @Override 192 @Override
193 public void removeHost(HostId hostId) { 193 public void removeHost(HostId hostId) {
194 checkNotNull(hostId, HOST_ID_NULL); 194 checkNotNull(hostId, HOST_ID_NULL);
195 - HostEvent event = store.removeHost(hostId); 195 + store.removeHost(hostId);
196 - if (event != null) {
197 - post(event);
198 - }
199 } 196 }
200 197
201 // Personalized host provider service issued to the supplied provider. 198 // Personalized host provider service issued to the supplied provider.
...@@ -211,11 +208,8 @@ public class HostManager ...@@ -211,11 +208,8 @@ public class HostManager
211 checkNotNull(hostId, HOST_ID_NULL); 208 checkNotNull(hostId, HOST_ID_NULL);
212 checkValidity(); 209 checkValidity();
213 hostDescription = validateHost(hostDescription, hostId); 210 hostDescription = validateHost(hostDescription, hostId);
214 - HostEvent event = store.createOrUpdateHost(provider().id(), hostId, 211 + store.createOrUpdateHost(provider().id(), hostId,
215 hostDescription, replaceIps); 212 hostDescription, replaceIps);
216 - if (event != null) {
217 - post(event);
218 - }
219 } 213 }
220 214
221 // returns a HostDescription made from the union of the BasicHostConfig 215 // returns a HostDescription made from the union of the BasicHostConfig
...@@ -231,20 +225,14 @@ public class HostManager ...@@ -231,20 +225,14 @@ public class HostManager
231 public void hostVanished(HostId hostId) { 225 public void hostVanished(HostId hostId) {
232 checkNotNull(hostId, HOST_ID_NULL); 226 checkNotNull(hostId, HOST_ID_NULL);
233 checkValidity(); 227 checkValidity();
234 - HostEvent event = store.removeHost(hostId); 228 + store.removeHost(hostId);
235 - if (event != null) {
236 - post(event);
237 - }
238 } 229 }
239 230
240 @Override 231 @Override
241 public void removeIpFromHost(HostId hostId, IpAddress ipAddress) { 232 public void removeIpFromHost(HostId hostId, IpAddress ipAddress) {
242 checkNotNull(hostId, HOST_ID_NULL); 233 checkNotNull(hostId, HOST_ID_NULL);
243 checkValidity(); 234 checkValidity();
244 - HostEvent event = store.removeIp(hostId, ipAddress); 235 + store.removeIp(hostId, ipAddress);
245 - if (event != null) {
246 - post(event);
247 - }
248 } 236 }
249 } 237 }
250 238
......
...@@ -20,6 +20,7 @@ import com.google.common.collect.Sets; ...@@ -20,6 +20,7 @@ import com.google.common.collect.Sets;
20 import org.junit.After; 20 import org.junit.After;
21 import org.junit.Before; 21 import org.junit.Before;
22 import org.junit.Test; 22 import org.junit.Test;
23 +import org.onlab.junit.TestTools;
23 import org.onlab.packet.IpAddress; 24 import org.onlab.packet.IpAddress;
24 import org.onlab.packet.MacAddress; 25 import org.onlab.packet.MacAddress;
25 import org.onlab.packet.VlanId; 26 import org.onlab.packet.VlanId;
...@@ -129,13 +130,15 @@ public class HostManagerTest { ...@@ -129,13 +130,15 @@ public class HostManagerTest {
129 } 130 }
130 131
131 private void validateEvents(Enum... types) { 132 private void validateEvents(Enum... types) {
132 - int i = 0; 133 + TestTools.assertAfter(100, () -> {
133 - assertEquals("wrong events received", types.length, listener.events.size()); 134 + int i = 0;
134 - for (Event event : listener.events) { 135 + assertEquals("wrong events received", types.length, listener.events.size());
135 - assertEquals("incorrect event type", types[i], event.type()); 136 + for (Event event : listener.events) {
136 - i++; 137 + assertEquals("incorrect event type", types[i], event.type());
137 - } 138 + i++;
138 - listener.events.clear(); 139 + }
140 + listener.events.clear();
141 + });
139 } 142 }
140 143
141 @Test 144 @Test
......
...@@ -28,9 +28,10 @@ import static org.slf4j.LoggerFactory.getLogger; ...@@ -28,9 +28,10 @@ import static org.slf4j.LoggerFactory.getLogger;
28 28
29 import java.util.Collection; 29 import java.util.Collection;
30 import java.util.HashSet; 30 import java.util.HashSet;
31 +import java.util.Map;
31 import java.util.Objects; 32 import java.util.Objects;
32 import java.util.Set; 33 import java.util.Set;
33 -import java.util.concurrent.atomic.AtomicReference; 34 +import java.util.concurrent.ConcurrentHashMap;
34 import java.util.function.Predicate; 35 import java.util.function.Predicate;
35 import java.util.stream.Collectors; 36 import java.util.stream.Collectors;
36 37
...@@ -56,7 +57,6 @@ import org.onosproject.net.host.HostDescription; ...@@ -56,7 +57,6 @@ import org.onosproject.net.host.HostDescription;
56 import org.onosproject.net.host.HostEvent; 57 import org.onosproject.net.host.HostEvent;
57 import org.onosproject.net.host.HostStore; 58 import org.onosproject.net.host.HostStore;
58 import org.onosproject.net.host.HostStoreDelegate; 59 import org.onosproject.net.host.HostStoreDelegate;
59 -import org.onosproject.net.host.HostEvent.Type;
60 import org.onosproject.net.provider.ProviderId; 60 import org.onosproject.net.provider.ProviderId;
61 import org.onosproject.store.AbstractStore; 61 import org.onosproject.store.AbstractStore;
62 import org.onosproject.store.serializers.KryoNamespaces; 62 import org.onosproject.store.serializers.KryoNamespaces;
...@@ -67,10 +67,7 @@ import org.onosproject.store.service.LogicalClockService; ...@@ -67,10 +67,7 @@ import org.onosproject.store.service.LogicalClockService;
67 import org.onosproject.store.service.StorageService; 67 import org.onosproject.store.service.StorageService;
68 import org.slf4j.Logger; 68 import org.slf4j.Logger;
69 69
70 -import com.google.common.collect.HashMultimap;
71 import com.google.common.collect.ImmutableSet; 70 import com.google.common.collect.ImmutableSet;
72 -import com.google.common.collect.Multimaps;
73 -import com.google.common.collect.SetMultimap;
74 import com.google.common.collect.Sets; 71 import com.google.common.collect.Sets;
75 72
76 /** 73 /**
...@@ -90,13 +87,11 @@ public class ECHostStore ...@@ -90,13 +87,11 @@ public class ECHostStore
90 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 87 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
91 protected LogicalClockService clockService; 88 protected LogicalClockService clockService;
92 89
93 - // Hosts tracked by their location
94 - private final SetMultimap<ConnectPoint, Host> locations =
95 - Multimaps.synchronizedSetMultimap(
96 - HashMultimap.<ConnectPoint, Host>create());
97 -
98 private EventuallyConsistentMap<HostId, DefaultHost> hosts; 90 private EventuallyConsistentMap<HostId, DefaultHost> hosts;
99 91
92 + private final ConcurrentHashMap<HostId, ConnectPoint> locations =
93 + new ConcurrentHashMap<>();
94 +
100 private EventuallyConsistentMapListener<HostId, DefaultHost> hostLocationTracker = 95 private EventuallyConsistentMapListener<HostId, DefaultHost> hostLocationTracker =
101 new HostLocationTracker(); 96 new HostLocationTracker();
102 97
...@@ -125,6 +120,7 @@ public class ECHostStore ...@@ -125,6 +120,7 @@ public class ECHostStore
125 log.info("Stopped"); 120 log.info("Stopped");
126 } 121 }
127 122
123 + // TODO No longer need to return HostEvent
128 @Override 124 @Override
129 public HostEvent createOrUpdateHost(ProviderId providerId, 125 public HostEvent createOrUpdateHost(ProviderId providerId,
130 HostId hostId, 126 HostId hostId,
...@@ -133,18 +129,7 @@ public class ECHostStore ...@@ -133,18 +129,7 @@ public class ECHostStore
133 // TODO: We need a way to detect conflicting changes and abort update. 129 // TODO: We need a way to detect conflicting changes and abort update.
134 // (BOC) Compute might do this for us. 130 // (BOC) Compute might do this for us.
135 131
136 - final AtomicReference<Type> eventType = new AtomicReference<>(); 132 + hosts.compute(hostId, (id, existingHost) -> {
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(); 133 HostLocation location = hostDescription.location();
149 134
150 final Set<IpAddress> addresses; 135 final Set<IpAddress> addresses;
...@@ -163,15 +148,6 @@ public class ECHostStore ...@@ -163,15 +148,6 @@ public class ECHostStore
163 annotations = hostDescription.annotations(); 148 annotations = hostDescription.annotations();
164 } 149 }
165 150
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, 151 return new DefaultHost(providerId,
176 hostId, 152 hostId,
177 hostDescription.hwAddress(), 153 hostDescription.hwAddress(),
...@@ -181,24 +157,20 @@ public class ECHostStore ...@@ -181,24 +157,20 @@ public class ECHostStore
181 annotations); 157 annotations);
182 }); 158 });
183 159
184 - if (oldHost.get() != null) { 160 + return null;
185 - DefaultHost old = oldHost.get();
186 - locations.remove(old.location(), old);
187 - }
188 - locations.put(host.location(), host);
189 -
190 - return eventType.get() != null ? new HostEvent(eventType.get(), host) : null;
191 } 161 }
192 162
163 + // TODO No longer need to return HostEvent
193 @Override 164 @Override
194 public HostEvent removeHost(HostId hostId) { 165 public HostEvent removeHost(HostId hostId) {
195 - Host host = hosts.remove(hostId); 166 + hosts.remove(hostId);
196 - return host != null ? new HostEvent(HOST_REMOVED, host) : null; 167 + return null;
197 } 168 }
198 169
170 + // TODO No longer need to return HostEvent
199 @Override 171 @Override
200 public HostEvent removeIp(HostId hostId, IpAddress ipAddress) { 172 public HostEvent removeIp(HostId hostId, IpAddress ipAddress) {
201 - DefaultHost host = hosts.compute(hostId, (id, existingHost) -> { 173 + hosts.compute(hostId, (id, existingHost) -> {
202 if (existingHost != null) { 174 if (existingHost != null) {
203 checkState(Objects.equals(hostId.mac(), existingHost.mac()), 175 checkState(Objects.equals(hostId.mac(), existingHost.mac()),
204 "Existing and new MAC addresses differ."); 176 "Existing and new MAC addresses differ.");
...@@ -222,7 +194,7 @@ public class ECHostStore ...@@ -222,7 +194,7 @@ public class ECHostStore
222 } 194 }
223 return null; 195 return null;
224 }); 196 });
225 - return host != null ? new HostEvent(HOST_UPDATED, host) : null; 197 + return null;
226 } 198 }
227 199
228 @Override 200 @Override
...@@ -257,22 +229,19 @@ public class ECHostStore ...@@ -257,22 +229,19 @@ public class ECHostStore
257 229
258 @Override 230 @Override
259 public Set<Host> getConnectedHosts(ConnectPoint connectPoint) { 231 public Set<Host> getConnectedHosts(ConnectPoint connectPoint) {
260 - synchronized (locations) { 232 + Set<Host> filtered = hosts.entrySet().stream()
261 - return ImmutableSet.copyOf(locations.get(connectPoint)); 233 + .filter(entry -> entry.getValue().location().equals(connectPoint))
262 - } 234 + .map(Map.Entry::getValue)
235 + .collect(Collectors.toSet());
236 + return ImmutableSet.copyOf(filtered);
263 } 237 }
264 238
265 @Override 239 @Override
266 public Set<Host> getConnectedHosts(DeviceId deviceId) { 240 public Set<Host> getConnectedHosts(DeviceId deviceId) {
267 - Set<Host> filtered; 241 + Set<Host> filtered = hosts.entrySet().stream()
268 - synchronized (locations) { 242 + .filter(entry -> entry.getValue().location().deviceId().equals(deviceId))
269 - filtered = locations 243 + .map(Map.Entry::getValue)
270 - .entries() 244 + .collect(Collectors.toSet());
271 - .stream()
272 - .filter(entry -> entry.getKey().deviceId().equals(deviceId))
273 - .map(entry -> entry.getValue())
274 - .collect(Collectors.toSet());
275 - }
276 return ImmutableSet.copyOf(filtered); 245 return ImmutableSet.copyOf(filtered);
277 } 246 }
278 247
...@@ -285,13 +254,18 @@ public class ECHostStore ...@@ -285,13 +254,18 @@ public class ECHostStore
285 public void event(EventuallyConsistentMapEvent<HostId, DefaultHost> event) { 254 public void event(EventuallyConsistentMapEvent<HostId, DefaultHost> event) {
286 DefaultHost host = checkNotNull(event.value()); 255 DefaultHost host = checkNotNull(event.value());
287 if (event.type() == PUT) { 256 if (event.type() == PUT) {
288 - boolean isNew = locations.put(host.location(), host); 257 + ConnectPoint prevLocation = locations.put(host.id(), host.location());
289 - notifyDelegate(new HostEvent(isNew ? HOST_ADDED : HOST_UPDATED, host)); 258 + if (prevLocation == null) {
259 + notifyDelegate(new HostEvent(HOST_ADDED, host));
260 + } else if (!Objects.equals(prevLocation, host.location())) {
261 + notifyDelegate(new HostEvent(HOST_MOVED, host));
262 + } else {
263 + notifyDelegate(new HostEvent(HOST_UPDATED, host));
264 + }
290 } else if (event.type() == REMOVE) { 265 } else if (event.type() == REMOVE) {
291 - if (locations.remove(host.location(), host)) { 266 + if (locations.remove(host.id()) != null) {
292 notifyDelegate(new HostEvent(HOST_REMOVED, host)); 267 notifyDelegate(new HostEvent(HOST_REMOVED, host));
293 } 268 }
294 -
295 } 269 }
296 } 270 }
297 } 271 }
......