Brian O'Connor
Committed by Gerrit Code Review

ONOS-2947 Improvements to HostService

- Added ability to replace IPs for existing hosts to HostProviderService
- Moved createOrUpdateHost to use compute() (rather than get/set) in HostStore

Change-Id: I8ac035d010ae65f23158d2237f9fc97c8f811dd4
...@@ -330,7 +330,7 @@ public class DhcpManagerTest { ...@@ -330,7 +330,7 @@ public class DhcpManagerTest {
330 } 330 }
331 331
332 @Override 332 @Override
333 - public void hostDetected(HostId hostId, HostDescription hostDescription) { 333 + public void hostDetected(HostId hostId, HostDescription hostDescription, boolean replaceIps) {
334 } 334 }
335 335
336 @Override 336 @Override
......
...@@ -30,7 +30,20 @@ public interface HostProviderService extends ProviderService<HostProvider> { ...@@ -30,7 +30,20 @@ public interface HostProviderService extends ProviderService<HostProvider> {
30 * @param hostId id of the host that been detected 30 * @param hostId id of the host that been detected
31 * @param hostDescription description of host and its location 31 * @param hostDescription description of host and its location
32 */ 32 */
33 - void hostDetected(HostId hostId, HostDescription hostDescription); 33 + @Deprecated
34 + default void hostDetected(HostId hostId, HostDescription hostDescription) {
35 + hostDetected(hostId, hostDescription, false);
36 + }
37 +
38 + /**
39 + * Notifies the core when a host has been detected on a network along with
40 + * information that identifies the host location.
41 + *
42 + * @param hostId id of the host that been detected
43 + * @param hostDescription description of host and its location
44 + * @param replaceIps replace IP set if true, merge IP set otherwise
45 + */
46 + void hostDetected(HostId hostId, HostDescription hostDescription, boolean replaceIps);
34 47
35 /** 48 /**
36 * Notifies the core when a host is no longer detected on a network. 49 * Notifies the core when a host is no longer detected on a network.
......
...@@ -39,12 +39,13 @@ public interface HostStore extends Store<HostEvent, HostStoreDelegate> { ...@@ -39,12 +39,13 @@ public interface HostStore extends Store<HostEvent, HostStoreDelegate> {
39 * @param providerId provider identification 39 * @param providerId provider identification
40 * @param hostId host identification 40 * @param hostId host identification
41 * @param hostDescription host description data 41 * @param hostDescription host description data
42 + * @param replaceIps replace IP set if true, merge IP set otherwise
42 * @return appropriate event or null if no change resulted 43 * @return appropriate event or null if no change resulted
43 */ 44 */
44 HostEvent createOrUpdateHost(ProviderId providerId, HostId hostId, 45 HostEvent createOrUpdateHost(ProviderId providerId, HostId hostId,
45 - HostDescription hostDescription); 46 + HostDescription hostDescription,
47 + boolean replaceIps);
46 48
47 - // FIXME: API to remove only IpAddress is missing
48 /** 49 /**
49 * Removes the specified host from the inventory. 50 * Removes the specified host from the inventory.
50 * 51 *
......
...@@ -84,12 +84,14 @@ public class SimpleHostStore ...@@ -84,12 +84,14 @@ public class SimpleHostStore
84 84
85 @Override 85 @Override
86 public HostEvent createOrUpdateHost(ProviderId providerId, HostId hostId, 86 public HostEvent createOrUpdateHost(ProviderId providerId, HostId hostId,
87 - HostDescription hostDescription) { 87 + HostDescription hostDescription,
88 + boolean replaceIps) {
89 + //TODO We need a way to detect conflicting changes and abort update.
88 StoredHost host = hosts.get(hostId); 90 StoredHost host = hosts.get(hostId);
89 if (host == null) { 91 if (host == null) {
90 return createHost(providerId, hostId, hostDescription); 92 return createHost(providerId, hostId, hostDescription);
91 } 93 }
92 - return updateHost(providerId, host, hostDescription); 94 + return updateHost(providerId, host, hostDescription, replaceIps);
93 } 95 }
94 96
95 // creates a new host and sends HOST_ADDED 97 // creates a new host and sends HOST_ADDED
...@@ -110,7 +112,7 @@ public class SimpleHostStore ...@@ -110,7 +112,7 @@ public class SimpleHostStore
110 112
111 // checks for type of update to host, sends appropriate event 113 // checks for type of update to host, sends appropriate event
112 private HostEvent updateHost(ProviderId providerId, StoredHost host, 114 private HostEvent updateHost(ProviderId providerId, StoredHost host,
113 - HostDescription descr) { 115 + HostDescription descr, boolean replaceIps) {
114 HostEvent event; 116 HostEvent event;
115 if (!host.location().equals(descr.location())) { 117 if (!host.location().equals(descr.location())) {
116 host.setLocation(descr.location()); 118 host.setLocation(descr.location());
...@@ -122,8 +124,14 @@ public class SimpleHostStore ...@@ -122,8 +124,14 @@ public class SimpleHostStore
122 return null; 124 return null;
123 } 125 }
124 126
125 - Set<IpAddress> addresses = new HashSet<>(host.ipAddresses()); 127 + final Set<IpAddress> addresses;
128 + if (replaceIps) {
129 + addresses = ImmutableSet.copyOf(descr.ipAddress());
130 + } else {
131 + addresses = new HashSet<>(host.ipAddresses());
126 addresses.addAll(descr.ipAddress()); 132 addresses.addAll(descr.ipAddress());
133 + }
134 +
127 Annotations annotations = merge((DefaultAnnotations) host.annotations(), 135 Annotations annotations = merge((DefaultAnnotations) host.annotations(),
128 descr.annotations()); 136 descr.annotations());
129 StoredHost updated = new StoredHost(providerId, host.id(), 137 StoredHost updated = new StoredHost(providerId, host.id(),
......
...@@ -207,12 +207,12 @@ public class HostManager ...@@ -207,12 +207,12 @@ public class HostManager
207 } 207 }
208 208
209 @Override 209 @Override
210 - public void hostDetected(HostId hostId, HostDescription hostDescription) { 210 + public void hostDetected(HostId hostId, HostDescription hostDescription, boolean replaceIps) {
211 checkNotNull(hostId, HOST_ID_NULL); 211 checkNotNull(hostId, HOST_ID_NULL);
212 checkValidity(); 212 checkValidity();
213 hostDescription = validateHost(hostDescription, hostId); 213 hostDescription = validateHost(hostDescription, hostId);
214 HostEvent event = store.createOrUpdateHost(provider().id(), hostId, 214 HostEvent event = store.createOrUpdateHost(provider().id(), hostId,
215 - hostDescription); 215 + hostDescription, replaceIps);
216 if (event != null) { 216 if (event != null) {
217 post(event); 217 post(event);
218 } 218 }
......
...@@ -16,9 +16,11 @@ ...@@ -16,9 +16,11 @@
16 package org.onosproject.store.host.impl; 16 package org.onosproject.store.host.impl;
17 17
18 import static com.google.common.base.Preconditions.checkNotNull; 18 import static com.google.common.base.Preconditions.checkNotNull;
19 +import static com.google.common.base.Preconditions.checkState;
19 import static org.onosproject.net.DefaultAnnotations.merge; 20 import static org.onosproject.net.DefaultAnnotations.merge;
20 import static org.onosproject.net.host.HostEvent.Type.HOST_ADDED; 21 import static org.onosproject.net.host.HostEvent.Type.HOST_ADDED;
21 import static org.onosproject.net.host.HostEvent.Type.HOST_REMOVED; 22 import static org.onosproject.net.host.HostEvent.Type.HOST_REMOVED;
23 +import static org.onosproject.net.host.HostEvent.Type.HOST_MOVED;
22 import static org.onosproject.net.host.HostEvent.Type.HOST_UPDATED; 24 import static org.onosproject.net.host.HostEvent.Type.HOST_UPDATED;
23 import static org.onosproject.store.service.EventuallyConsistentMapEvent.Type.PUT; 25 import static org.onosproject.store.service.EventuallyConsistentMapEvent.Type.PUT;
24 import static org.onosproject.store.service.EventuallyConsistentMapEvent.Type.REMOVE; 26 import static org.onosproject.store.service.EventuallyConsistentMapEvent.Type.REMOVE;
...@@ -27,6 +29,7 @@ import static org.slf4j.LoggerFactory.getLogger; ...@@ -27,6 +29,7 @@ import static org.slf4j.LoggerFactory.getLogger;
27 import java.util.Collection; 29 import java.util.Collection;
28 import java.util.Objects; 30 import java.util.Objects;
29 import java.util.Set; 31 import java.util.Set;
32 +import java.util.concurrent.atomic.AtomicReference;
30 import java.util.function.Predicate; 33 import java.util.function.Predicate;
31 import java.util.stream.Collectors; 34 import java.util.stream.Collectors;
32 35
...@@ -47,6 +50,7 @@ import org.onosproject.net.DefaultHost; ...@@ -47,6 +50,7 @@ import org.onosproject.net.DefaultHost;
47 import org.onosproject.net.DeviceId; 50 import org.onosproject.net.DeviceId;
48 import org.onosproject.net.Host; 51 import org.onosproject.net.Host;
49 import org.onosproject.net.HostId; 52 import org.onosproject.net.HostId;
53 +import org.onosproject.net.HostLocation;
50 import org.onosproject.net.host.HostDescription; 54 import org.onosproject.net.host.HostDescription;
51 import org.onosproject.net.host.HostEvent; 55 import org.onosproject.net.host.HostEvent;
52 import org.onosproject.net.host.HostStore; 56 import org.onosproject.net.host.HostStore;
...@@ -124,21 +128,66 @@ public class ECHostStore ...@@ -124,21 +128,66 @@ public class ECHostStore
124 @Override 128 @Override
125 public HostEvent createOrUpdateHost(ProviderId providerId, 129 public HostEvent createOrUpdateHost(ProviderId providerId,
126 HostId hostId, 130 HostId hostId,
127 - HostDescription hostDescription) { 131 + HostDescription hostDescription,
128 - DefaultHost currentHost = hosts.get(hostId); 132 + boolean replaceIPs) {
129 - if (currentHost == null) { 133 + // TODO: We need a way to detect conflicting changes and abort update.
130 - DefaultHost newhost = new DefaultHost( 134 + // (BOC) Compute might do this for us.
131 - providerId, 135 +
136 + final AtomicReference<Type> eventType = new AtomicReference<>();
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();
149 +
150 + final Set<IpAddress> addresses;
151 + if (existingHost == null || replaceIPs) {
152 + addresses = ImmutableSet.copyOf(hostDescription.ipAddress());
153 + } else {
154 + addresses = Sets.newHashSet(existingHost.ipAddresses());
155 + addresses.addAll(hostDescription.ipAddress());
156 + }
157 +
158 + final Annotations annotations;
159 + if (existingHost != null) {
160 + annotations = merge((DefaultAnnotations) existingHost.annotations(),
161 + hostDescription.annotations());
162 + } else {
163 + annotations = hostDescription.annotations();
164 + }
165 +
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,
132 hostId, 176 hostId,
133 hostDescription.hwAddress(), 177 hostDescription.hwAddress(),
134 hostDescription.vlan(), 178 hostDescription.vlan(),
135 - hostDescription.location(), 179 + location,
136 - ImmutableSet.copyOf(hostDescription.ipAddress()), 180 + addresses,
137 - hostDescription.annotations()); 181 + annotations);
138 - hosts.put(hostId, newhost); 182 + });
139 - return new HostEvent(HOST_ADDED, newhost); 183 +
184 + if (oldHost.get() != null) {
185 + DefaultHost old = oldHost.get();
186 + locations.remove(old.location(), old);
140 } 187 }
141 - return updateHost(providerId, hostId, hostDescription, currentHost); 188 + locations.put(host.location(), host);
189 +
190 + return eventType.get() != null ? new HostEvent(eventType.get(), host) : null;
142 } 191 }
143 192
144 @Override 193 @Override
...@@ -196,39 +245,6 @@ public class ECHostStore ...@@ -196,39 +245,6 @@ public class ECHostStore
196 return collection.stream().filter(predicate).collect(Collectors.toSet()); 245 return collection.stream().filter(predicate).collect(Collectors.toSet());
197 } 246 }
198 247
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> { 248 private class HostLocationTracker implements EventuallyConsistentMapListener<HostId, DefaultHost> {
233 @Override 249 @Override
234 public void event(EventuallyConsistentMapEvent<HostId, DefaultHost> event) { 250 public void event(EventuallyConsistentMapEvent<HostId, DefaultHost> event) {
......
...@@ -243,7 +243,7 @@ public class HostLocationProviderTest { ...@@ -243,7 +243,7 @@ public class HostLocationProviderTest {
243 } 243 }
244 244
245 @Override 245 @Override
246 - public void hostDetected(HostId hostId, HostDescription hostDescription) { 246 + public void hostDetected(HostId hostId, HostDescription hostDescription, boolean replaceIps) {
247 DeviceId descr = hostDescription.location().deviceId(); 247 DeviceId descr = hostDescription.location().deviceId();
248 if (added == null) { 248 if (added == null) {
249 added = descr; 249 added = descr;
......
...@@ -143,7 +143,7 @@ public class OvsdbHostProviderTest { ...@@ -143,7 +143,7 @@ public class OvsdbHostProviderTest {
143 } 143 }
144 144
145 @Override 145 @Override
146 - public void hostDetected(HostId hostId, HostDescription hostDescription) { 146 + public void hostDetected(HostId hostId, HostDescription hostDescription, boolean replaceIps) {
147 DeviceId descr = hostDescription.location().deviceId(); 147 DeviceId descr = hostDescription.location().deviceId();
148 if (added == null) { 148 if (added == null) {
149 added = descr; 149 added = descr;
......