Thomas Vachuska

Fixed a defect that allowed ancillary device providers to overwrite primary provider's data.

...@@ -290,12 +290,17 @@ public class GossipDeviceStore ...@@ -290,12 +290,17 @@ public class GossipDeviceStore
290 private DeviceEvent updateDevice(ProviderId providerId, 290 private DeviceEvent updateDevice(ProviderId providerId,
291 Device oldDevice, 291 Device oldDevice,
292 Device newDevice, Timestamp newTimestamp) { 292 Device newDevice, Timestamp newTimestamp) {
293 -
294 // We allow only certain attributes to trigger update 293 // We allow only certain attributes to trigger update
295 - if (!Objects.equals(oldDevice.hwVersion(), newDevice.hwVersion()) || 294 + boolean propertiesChanged =
296 - !Objects.equals(oldDevice.swVersion(), newDevice.swVersion()) || 295 + !Objects.equals(oldDevice.hwVersion(), newDevice.hwVersion()) ||
297 - !AnnotationsUtil.isEqual(oldDevice.annotations(), newDevice.annotations())) { 296 + !Objects.equals(oldDevice.swVersion(), newDevice.swVersion());
298 - 297 + boolean annotationsChanged =
298 + !AnnotationsUtil.isEqual(oldDevice.annotations(), newDevice.annotations());
299 +
300 + // Primary providers can respond to all changes, but ancillary ones
301 + // should respond only to annotation changes.
302 + if ((providerId.isAncillary() && annotationsChanged) ||
303 + (!providerId.isAncillary() && (propertiesChanged || annotationsChanged))) {
299 boolean replaced = devices.replace(newDevice.id(), oldDevice, newDevice); 304 boolean replaced = devices.replace(newDevice.id(), oldDevice, newDevice);
300 if (!replaced) { 305 if (!replaced) {
301 verify(replaced, 306 verify(replaced,
......
...@@ -70,15 +70,16 @@ public class SimpleDeviceStore ...@@ -70,15 +70,16 @@ public class SimpleDeviceStore
70 70
71 public static final String DEVICE_NOT_FOUND = "Device with ID %s not found"; 71 public static final String DEVICE_NOT_FOUND = "Device with ID %s not found";
72 72
73 - // collection of Description given from various providers 73 + // Collection of Description given from various providers
74 private final ConcurrentMap<DeviceId, Map<ProviderId, DeviceDescriptions>> 74 private final ConcurrentMap<DeviceId, Map<ProviderId, DeviceDescriptions>>
75 - deviceDescs = Maps.newConcurrentMap(); 75 + deviceDescs = Maps.newConcurrentMap();
76 76
77 - // cache of Device and Ports generated by compositing descriptions from providers 77 + // Cache of Device and Ports generated by compositing descriptions from providers
78 private final ConcurrentMap<DeviceId, Device> devices = Maps.newConcurrentMap(); 78 private final ConcurrentMap<DeviceId, Device> devices = Maps.newConcurrentMap();
79 - private final ConcurrentMap<DeviceId, ConcurrentMap<PortNumber, Port>> devicePorts = Maps.newConcurrentMap(); 79 + private final ConcurrentMap<DeviceId, ConcurrentMap<PortNumber, Port>>
80 + devicePorts = Maps.newConcurrentMap();
80 81
81 - // available(=UP) devices 82 + // Available (=UP) devices
82 private final Set<DeviceId> availableDevices = Sets.newConcurrentHashSet(); 83 private final Set<DeviceId> availableDevices = Sets.newConcurrentHashSet();
83 84
84 85
...@@ -113,19 +114,17 @@ public class SimpleDeviceStore ...@@ -113,19 +114,17 @@ public class SimpleDeviceStore
113 114
114 @Override 115 @Override
115 public DeviceEvent createOrUpdateDevice(ProviderId providerId, 116 public DeviceEvent createOrUpdateDevice(ProviderId providerId,
116 - DeviceId deviceId, 117 + DeviceId deviceId,
117 - DeviceDescription deviceDescription) { 118 + DeviceDescription deviceDescription) {
118 -
119 Map<ProviderId, DeviceDescriptions> providerDescs 119 Map<ProviderId, DeviceDescriptions> providerDescs
120 - = getOrCreateDeviceDescriptions(deviceId); 120 + = getOrCreateDeviceDescriptions(deviceId);
121 121
122 synchronized (providerDescs) { 122 synchronized (providerDescs) {
123 // locking per device 123 // locking per device
124 -
125 DeviceDescriptions descs 124 DeviceDescriptions descs
126 - = getOrCreateProviderDeviceDescriptions(providerDescs, 125 + = getOrCreateProviderDeviceDescriptions(providerDescs,
127 - providerId, 126 + providerId,
128 - deviceDescription); 127 + deviceDescription);
129 128
130 Device oldDevice = devices.get(deviceId); 129 Device oldDevice = devices.get(deviceId);
131 // update description 130 // update description
...@@ -145,12 +144,11 @@ public class SimpleDeviceStore ...@@ -145,12 +144,11 @@ public class SimpleDeviceStore
145 // Creates the device and returns the appropriate event if necessary. 144 // Creates the device and returns the appropriate event if necessary.
146 // Guarded by deviceDescs value (=Device lock) 145 // Guarded by deviceDescs value (=Device lock)
147 private DeviceEvent createDevice(ProviderId providerId, Device newDevice) { 146 private DeviceEvent createDevice(ProviderId providerId, Device newDevice) {
148 -
149 // update composed device cache 147 // update composed device cache
150 Device oldDevice = devices.putIfAbsent(newDevice.id(), newDevice); 148 Device oldDevice = devices.putIfAbsent(newDevice.id(), newDevice);
151 verify(oldDevice == null, 149 verify(oldDevice == null,
152 - "Unexpected Device in cache. PID:%s [old=%s, new=%s]", 150 + "Unexpected Device in cache. PID:%s [old=%s, new=%s]",
153 - providerId, oldDevice, newDevice); 151 + providerId, oldDevice, newDevice);
154 152
155 if (!providerId.isAncillary()) { 153 if (!providerId.isAncillary()) {
156 availableDevices.add(newDevice.id()); 154 availableDevices.add(newDevice.id());
...@@ -162,17 +160,24 @@ public class SimpleDeviceStore ...@@ -162,17 +160,24 @@ public class SimpleDeviceStore
162 // Updates the device and returns the appropriate event if necessary. 160 // Updates the device and returns the appropriate event if necessary.
163 // Guarded by deviceDescs value (=Device lock) 161 // Guarded by deviceDescs value (=Device lock)
164 private DeviceEvent updateDevice(ProviderId providerId, Device oldDevice, Device newDevice) { 162 private DeviceEvent updateDevice(ProviderId providerId, Device oldDevice, Device newDevice) {
165 -
166 // We allow only certain attributes to trigger update 163 // We allow only certain attributes to trigger update
167 - if (!Objects.equals(oldDevice.hwVersion(), newDevice.hwVersion()) || 164 + boolean propertiesChanged =
168 - !Objects.equals(oldDevice.swVersion(), newDevice.swVersion()) || 165 + !Objects.equals(oldDevice.hwVersion(), newDevice.hwVersion()) ||
169 - !AnnotationsUtil.isEqual(oldDevice.annotations(), newDevice.annotations())) { 166 + !Objects.equals(oldDevice.swVersion(), newDevice.swVersion());
167 + boolean annotationsChanged =
168 + !AnnotationsUtil.isEqual(oldDevice.annotations(), newDevice.annotations());
169 +
170 + // Primary providers can respond to all changes, but ancillary ones
171 + // should respond only to annotation changes.
172 + if ((providerId.isAncillary() && annotationsChanged) ||
173 + (!providerId.isAncillary() && (propertiesChanged || annotationsChanged))) {
170 174
171 boolean replaced = devices.replace(newDevice.id(), oldDevice, newDevice); 175 boolean replaced = devices.replace(newDevice.id(), oldDevice, newDevice);
172 if (!replaced) { 176 if (!replaced) {
177 + // FIXME: Is the enclosing if required here?
173 verify(replaced, 178 verify(replaced,
174 - "Replacing devices cache failed. PID:%s [expected:%s, found:%s, new=%s]", 179 + "Replacing devices cache failed. PID:%s [expected:%s, found:%s, new=%s]",
175 - providerId, oldDevice, devices.get(newDevice.id()) 180 + providerId, oldDevice, devices.get(newDevice.id())
176 , newDevice); 181 , newDevice);
177 } 182 }
178 if (!providerId.isAncillary()) { 183 if (!providerId.isAncillary()) {
...@@ -193,7 +198,7 @@ public class SimpleDeviceStore ...@@ -193,7 +198,7 @@ public class SimpleDeviceStore
193 @Override 198 @Override
194 public DeviceEvent markOffline(DeviceId deviceId) { 199 public DeviceEvent markOffline(DeviceId deviceId) {
195 Map<ProviderId, DeviceDescriptions> providerDescs 200 Map<ProviderId, DeviceDescriptions> providerDescs
196 - = getOrCreateDeviceDescriptions(deviceId); 201 + = getOrCreateDeviceDescriptions(deviceId);
197 202
198 // locking device 203 // locking device
199 synchronized (providerDescs) { 204 synchronized (providerDescs) {
...@@ -212,9 +217,8 @@ public class SimpleDeviceStore ...@@ -212,9 +217,8 @@ public class SimpleDeviceStore
212 217
213 @Override 218 @Override
214 public List<DeviceEvent> updatePorts(ProviderId providerId, 219 public List<DeviceEvent> updatePorts(ProviderId providerId,
215 - DeviceId deviceId, 220 + DeviceId deviceId,
216 - List<PortDescription> portDescriptions) { 221 + List<PortDescription> portDescriptions) {
217 -
218 Device device = devices.get(deviceId); 222 Device device = devices.get(deviceId);
219 checkArgument(device != null, DEVICE_NOT_FOUND, deviceId); 223 checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
220 224
...@@ -226,8 +230,8 @@ public class SimpleDeviceStore ...@@ -226,8 +230,8 @@ public class SimpleDeviceStore
226 DeviceDescriptions descs = descsMap.get(providerId); 230 DeviceDescriptions descs = descsMap.get(providerId);
227 // every provider must provide DeviceDescription. 231 // every provider must provide DeviceDescription.
228 checkArgument(descs != null, 232 checkArgument(descs != null,
229 - "Device description for Device ID %s from Provider %s was not found", 233 + "Device description for Device ID %s from Provider %s was not found",
230 - deviceId, providerId); 234 + deviceId, providerId);
231 235
232 Map<PortNumber, Port> ports = getPortMap(deviceId); 236 Map<PortNumber, Port> ports = getPortMap(deviceId);
233 237
...@@ -247,8 +251,8 @@ public class SimpleDeviceStore ...@@ -247,8 +251,8 @@ public class SimpleDeviceStore
247 newPort = composePort(device, number, descsMap); 251 newPort = composePort(device, number, descsMap);
248 252
249 events.add(oldPort == null ? 253 events.add(oldPort == null ?
250 - createPort(device, newPort, ports) : 254 + createPort(device, newPort, ports) :
251 - updatePort(device, oldPort, newPort, ports)); 255 + updatePort(device, oldPort, newPort, ports));
252 } 256 }
253 257
254 events.addAll(pruneOldPorts(device, ports, processed)); 258 events.addAll(pruneOldPorts(device, ports, processed));
...@@ -272,7 +276,7 @@ public class SimpleDeviceStore ...@@ -272,7 +276,7 @@ public class SimpleDeviceStore
272 Port newPort, 276 Port newPort,
273 Map<PortNumber, Port> ports) { 277 Map<PortNumber, Port> ports) {
274 if (oldPort.isEnabled() != newPort.isEnabled() || 278 if (oldPort.isEnabled() != newPort.isEnabled() ||
275 - !AnnotationsUtil.isEqual(oldPort.annotations(), newPort.annotations())) { 279 + !AnnotationsUtil.isEqual(oldPort.annotations(), newPort.annotations())) {
276 280
277 ports.put(oldPort.number(), newPort); 281 ports.put(oldPort.number(), newPort);
278 return new DeviceEvent(PORT_UPDATED, device, newPort); 282 return new DeviceEvent(PORT_UPDATED, device, newPort);
...@@ -303,7 +307,7 @@ public class SimpleDeviceStore ...@@ -303,7 +307,7 @@ public class SimpleDeviceStore
303 // exist, it creates and registers a new one. 307 // exist, it creates and registers a new one.
304 private ConcurrentMap<PortNumber, Port> getPortMap(DeviceId deviceId) { 308 private ConcurrentMap<PortNumber, Port> getPortMap(DeviceId deviceId) {
305 return createIfAbsentUnchecked(devicePorts, deviceId, 309 return createIfAbsentUnchecked(devicePorts, deviceId,
306 - NewConcurrentHashMap.<PortNumber, Port>ifNeeded()); 310 + NewConcurrentHashMap.<PortNumber, Port>ifNeeded());
307 } 311 }
308 312
309 private Map<ProviderId, DeviceDescriptions> getOrCreateDeviceDescriptions( 313 private Map<ProviderId, DeviceDescriptions> getOrCreateDeviceDescriptions(
...@@ -325,9 +329,8 @@ public class SimpleDeviceStore ...@@ -325,9 +329,8 @@ public class SimpleDeviceStore
325 329
326 // Guarded by deviceDescs value (=Device lock) 330 // Guarded by deviceDescs value (=Device lock)
327 private DeviceDescriptions getOrCreateProviderDeviceDescriptions( 331 private DeviceDescriptions getOrCreateProviderDeviceDescriptions(
328 - Map<ProviderId, DeviceDescriptions> device, 332 + Map<ProviderId, DeviceDescriptions> device,
329 - ProviderId providerId, DeviceDescription deltaDesc) { 333 + ProviderId providerId, DeviceDescription deltaDesc) {
330 -
331 synchronized (device) { 334 synchronized (device) {
332 DeviceDescriptions r = device.get(providerId); 335 DeviceDescriptions r = device.get(providerId);
333 if (r == null) { 336 if (r == null) {
...@@ -340,7 +343,7 @@ public class SimpleDeviceStore ...@@ -340,7 +343,7 @@ public class SimpleDeviceStore
340 343
341 @Override 344 @Override
342 public DeviceEvent updatePortStatus(ProviderId providerId, DeviceId deviceId, 345 public DeviceEvent updatePortStatus(ProviderId providerId, DeviceId deviceId,
343 - PortDescription portDescription) { 346 + PortDescription portDescription) {
344 Device device = devices.get(deviceId); 347 Device device = devices.get(deviceId);
345 checkArgument(device != null, DEVICE_NOT_FOUND, deviceId); 348 checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
346 349
...@@ -351,8 +354,8 @@ public class SimpleDeviceStore ...@@ -351,8 +354,8 @@ public class SimpleDeviceStore
351 DeviceDescriptions descs = descsMap.get(providerId); 354 DeviceDescriptions descs = descsMap.get(providerId);
352 // assuming all providers must give DeviceDescription first 355 // assuming all providers must give DeviceDescription first
353 checkArgument(descs != null, 356 checkArgument(descs != null,
354 - "Device description for Device ID %s from Provider %s was not found", 357 + "Device description for Device ID %s from Provider %s was not found",
355 - deviceId, providerId); 358 + deviceId, providerId);
356 359
357 ConcurrentMap<PortNumber, Port> ports = getPortMap(deviceId); 360 ConcurrentMap<PortNumber, Port> ports = getPortMap(deviceId);
358 final PortNumber number = portDescription.portNumber(); 361 final PortNumber number = portDescription.portNumber();
...@@ -404,19 +407,19 @@ public class SimpleDeviceStore ...@@ -404,19 +407,19 @@ public class SimpleDeviceStore
404 availableDevices.remove(deviceId); 407 availableDevices.remove(deviceId);
405 descs.clear(); 408 descs.clear();
406 return device == null ? null : 409 return device == null ? null :
407 - new DeviceEvent(DEVICE_REMOVED, device, null); 410 + new DeviceEvent(DEVICE_REMOVED, device, null);
408 } 411 }
409 } 412 }
410 413
411 /** 414 /**
412 * Returns a Device, merging description given from multiple Providers. 415 * Returns a Device, merging description given from multiple Providers.
413 * 416 *
414 - * @param deviceId device identifier 417 + * @param deviceId device identifier
415 * @param providerDescs Collection of Descriptions from multiple providers 418 * @param providerDescs Collection of Descriptions from multiple providers
416 * @return Device instance 419 * @return Device instance
417 */ 420 */
418 private Device composeDevice(DeviceId deviceId, 421 private Device composeDevice(DeviceId deviceId,
419 - Map<ProviderId, DeviceDescriptions> providerDescs) { 422 + Map<ProviderId, DeviceDescriptions> providerDescs) {
420 423
421 checkArgument(!providerDescs.isEmpty(), "No Device descriptions supplied"); 424 checkArgument(!providerDescs.isEmpty(), "No Device descriptions supplied");
422 425
...@@ -447,21 +450,21 @@ public class SimpleDeviceStore ...@@ -447,21 +450,21 @@ public class SimpleDeviceStore
447 annotations = merge(annotations, e.getValue().getDeviceDesc().annotations()); 450 annotations = merge(annotations, e.getValue().getDeviceDesc().annotations());
448 } 451 }
449 452
450 - return new DefaultDevice(primary, deviceId , type, manufacturer, 453 + return new DefaultDevice(primary, deviceId, type, manufacturer,
451 - hwVersion, swVersion, serialNumber, 454 + hwVersion, swVersion, serialNumber,
452 - chassisId, annotations); 455 + chassisId, annotations);
453 } 456 }
454 457
455 /** 458 /**
456 * Returns a Port, merging description given from multiple Providers. 459 * Returns a Port, merging description given from multiple Providers.
457 * 460 *
458 - * @param device device the port is on 461 + * @param device device the port is on
459 - * @param number port number 462 + * @param number port number
460 * @param descsMap Collection of Descriptions from multiple providers 463 * @param descsMap Collection of Descriptions from multiple providers
461 * @return Port instance 464 * @return Port instance
462 */ 465 */
463 private Port composePort(Device device, PortNumber number, 466 private Port composePort(Device device, PortNumber number,
464 - Map<ProviderId, DeviceDescriptions> descsMap) { 467 + Map<ProviderId, DeviceDescriptions> descsMap) {
465 468
466 ProviderId primary = pickPrimaryPID(descsMap); 469 ProviderId primary = pickPrimaryPID(descsMap);
467 DeviceDescriptions primDescs = descsMap.get(primary); 470 DeviceDescriptions primDescs = descsMap.get(primary);
......