implemented annotation merging on SimpleDeviceStore.
- Added annotation support to PortsDescriptions Change-Id: I157e4fb93b8f387b405722b8d004501d993decda
Showing
6 changed files
with
237 additions
and
34 deletions
1 | package org.onlab.onos.net; | 1 | package org.onlab.onos.net; |
2 | 2 | ||
3 | +import java.util.Collections; | ||
3 | import java.util.HashMap; | 4 | import java.util.HashMap; |
4 | import java.util.Map; | 5 | import java.util.Map; |
5 | import java.util.Objects; | 6 | import java.util.Objects; |
... | @@ -71,9 +72,33 @@ public final class DefaultAnnotations implements SparseAnnotations { | ... | @@ -71,9 +72,33 @@ public final class DefaultAnnotations implements SparseAnnotations { |
71 | return new DefaultAnnotations(merged); | 72 | return new DefaultAnnotations(merged); |
72 | } | 73 | } |
73 | 74 | ||
75 | + /** | ||
76 | + * Convert Annotations to DefaultAnnotations if needed and merges. | ||
77 | + * | ||
78 | + * @see #merge(DefaultAnnotations, SparseAnnotations) | ||
79 | + * | ||
80 | + * @param annotations base annotations | ||
81 | + * @param sparseAnnotations additional sparse annotations | ||
82 | + * @return combined annotations or the original base annotations if there | ||
83 | + * are not additional annotations | ||
84 | + */ | ||
85 | + public static DefaultAnnotations merge(Annotations annotations, | ||
86 | + SparseAnnotations sparseAnnotations) { | ||
87 | + if (annotations instanceof DefaultAnnotations) { | ||
88 | + return merge((DefaultAnnotations) annotations, sparseAnnotations); | ||
89 | + } | ||
90 | + | ||
91 | + DefaultAnnotations.Builder builder = DefaultAnnotations.builder(); | ||
92 | + for (String key : annotations.keys()) { | ||
93 | + builder.set(key, annotations.value(key)); | ||
94 | + } | ||
95 | + return merge(builder.build(), sparseAnnotations); | ||
96 | + } | ||
97 | + | ||
74 | @Override | 98 | @Override |
75 | public Set<String> keys() { | 99 | public Set<String> keys() { |
76 | - return map.keySet(); | 100 | + // TODO: unmodifiable to be removed after switching to ImmutableMap; |
101 | + return Collections.unmodifiableSet(map.keySet()); | ||
77 | } | 102 | } |
78 | 103 | ||
79 | @Override | 104 | @Override | ... | ... |
... | @@ -45,6 +45,18 @@ public class DefaultDeviceDescription extends AbstractDescription | ... | @@ -45,6 +45,18 @@ public class DefaultDeviceDescription extends AbstractDescription |
45 | this.serialNumber = serialNumber; | 45 | this.serialNumber = serialNumber; |
46 | } | 46 | } |
47 | 47 | ||
48 | + /** | ||
49 | + * Creates a device description using the supplied information. | ||
50 | + * @param base DeviceDescription to basic information | ||
51 | + * @param annotations Annotations to use. | ||
52 | + */ | ||
53 | + public DefaultDeviceDescription(DeviceDescription base, | ||
54 | + SparseAnnotations... annotations) { | ||
55 | + this(base.deviceURI(), base.type(), base.manufacturer(), | ||
56 | + base.hwVersion(), base.swVersion(), base.serialNumber(), | ||
57 | + annotations); | ||
58 | + } | ||
59 | + | ||
48 | @Override | 60 | @Override |
49 | public URI deviceURI() { | 61 | public URI deviceURI() { |
50 | return uri; | 62 | return uri; | ... | ... |
1 | package org.onlab.onos.net.device; | 1 | package org.onlab.onos.net.device; |
2 | 2 | ||
3 | +import org.onlab.onos.net.AbstractDescription; | ||
3 | import org.onlab.onos.net.PortNumber; | 4 | import org.onlab.onos.net.PortNumber; |
5 | +import org.onlab.onos.net.SparseAnnotations; | ||
4 | 6 | ||
5 | /** | 7 | /** |
6 | * Default implementation of immutable port description. | 8 | * Default implementation of immutable port description. |
7 | */ | 9 | */ |
8 | -public class DefaultPortDescription implements PortDescription { | 10 | +public class DefaultPortDescription extends AbstractDescription |
11 | + implements PortDescription { | ||
9 | 12 | ||
10 | private final PortNumber number; | 13 | private final PortNumber number; |
11 | private final boolean isEnabled; | 14 | private final boolean isEnabled; |
12 | 15 | ||
13 | - public DefaultPortDescription(PortNumber number, boolean isEnabled) { | 16 | + /** |
17 | + * Creates a port description using the supplied information. | ||
18 | + * | ||
19 | + * @param number port number | ||
20 | + * @param isEnabled port enabled state | ||
21 | + * @param annotations optional key/value annotations map | ||
22 | + */ | ||
23 | + public DefaultPortDescription(PortNumber number, boolean isEnabled, | ||
24 | + SparseAnnotations... annotations) { | ||
25 | + super(annotations); | ||
14 | this.number = number; | 26 | this.number = number; |
15 | this.isEnabled = isEnabled; | 27 | this.isEnabled = isEnabled; |
16 | } | 28 | } |
17 | 29 | ||
30 | + /** | ||
31 | + * Creates a port description using the supplied information. | ||
32 | + * | ||
33 | + * @param base PortDescription to get basic information from | ||
34 | + * @param annotations optional key/value annotations map | ||
35 | + */ | ||
36 | + public DefaultPortDescription(PortDescription base, | ||
37 | + SparseAnnotations annotations) { | ||
38 | + this(base.portNumber(), base.isEnabled(), annotations); | ||
39 | + } | ||
40 | + | ||
18 | @Override | 41 | @Override |
19 | public PortNumber portNumber() { | 42 | public PortNumber portNumber() { |
20 | return number; | 43 | return number; | ... | ... |
1 | package org.onlab.onos.net.device; | 1 | package org.onlab.onos.net.device; |
2 | 2 | ||
3 | +import org.onlab.onos.net.Description; | ||
3 | import org.onlab.onos.net.PortNumber; | 4 | import org.onlab.onos.net.PortNumber; |
4 | 5 | ||
5 | /** | 6 | /** |
6 | * Information about a port. | 7 | * Information about a port. |
7 | */ | 8 | */ |
8 | -public interface PortDescription { | 9 | +public interface PortDescription extends Description { |
9 | 10 | ||
10 | // TODO: possibly relocate this to a common ground so that this can also used by host tracking if required | 11 | // TODO: possibly relocate this to a common ground so that this can also used by host tracking if required |
11 | 12 | ... | ... |
... | @@ -9,6 +9,8 @@ import org.apache.felix.scr.annotations.Activate; | ... | @@ -9,6 +9,8 @@ import org.apache.felix.scr.annotations.Activate; |
9 | import org.apache.felix.scr.annotations.Component; | 9 | import org.apache.felix.scr.annotations.Component; |
10 | import org.apache.felix.scr.annotations.Deactivate; | 10 | import org.apache.felix.scr.annotations.Deactivate; |
11 | import org.apache.felix.scr.annotations.Service; | 11 | import org.apache.felix.scr.annotations.Service; |
12 | +import org.onlab.onos.net.Annotations; | ||
13 | +import org.onlab.onos.net.DefaultAnnotations; | ||
12 | import org.onlab.onos.net.DefaultDevice; | 14 | import org.onlab.onos.net.DefaultDevice; |
13 | import org.onlab.onos.net.DefaultPort; | 15 | import org.onlab.onos.net.DefaultPort; |
14 | import org.onlab.onos.net.Device; | 16 | import org.onlab.onos.net.Device; |
... | @@ -16,6 +18,9 @@ import org.onlab.onos.net.Device.Type; | ... | @@ -16,6 +18,9 @@ import org.onlab.onos.net.Device.Type; |
16 | import org.onlab.onos.net.DeviceId; | 18 | import org.onlab.onos.net.DeviceId; |
17 | import org.onlab.onos.net.Port; | 19 | import org.onlab.onos.net.Port; |
18 | import org.onlab.onos.net.PortNumber; | 20 | import org.onlab.onos.net.PortNumber; |
21 | +import org.onlab.onos.net.SparseAnnotations; | ||
22 | +import org.onlab.onos.net.device.DefaultDeviceDescription; | ||
23 | +import org.onlab.onos.net.device.DefaultPortDescription; | ||
19 | import org.onlab.onos.net.device.DeviceDescription; | 24 | import org.onlab.onos.net.device.DeviceDescription; |
20 | import org.onlab.onos.net.device.DeviceEvent; | 25 | import org.onlab.onos.net.device.DeviceEvent; |
21 | import org.onlab.onos.net.device.DeviceStore; | 26 | import org.onlab.onos.net.device.DeviceStore; |
... | @@ -45,6 +50,7 @@ import static com.google.common.base.Predicates.notNull; | ... | @@ -45,6 +50,7 @@ import static com.google.common.base.Predicates.notNull; |
45 | import static org.onlab.onos.net.device.DeviceEvent.Type.*; | 50 | import static org.onlab.onos.net.device.DeviceEvent.Type.*; |
46 | import static org.slf4j.LoggerFactory.getLogger; | 51 | import static org.slf4j.LoggerFactory.getLogger; |
47 | import static org.apache.commons.lang3.concurrent.ConcurrentUtils.createIfAbsentUnchecked; | 52 | import static org.apache.commons.lang3.concurrent.ConcurrentUtils.createIfAbsentUnchecked; |
53 | +import static org.onlab.onos.net.DefaultAnnotations.merge; | ||
48 | 54 | ||
49 | // TODO: synchronization should be done in more fine-grained manner. | 55 | // TODO: synchronization should be done in more fine-grained manner. |
50 | /** | 56 | /** |
... | @@ -112,8 +118,8 @@ public class SimpleDeviceStore | ... | @@ -112,8 +118,8 @@ public class SimpleDeviceStore |
112 | = createIfAbsentUnchecked(providerDescs, providerId, | 118 | = createIfAbsentUnchecked(providerDescs, providerId, |
113 | new InitDeviceDescs(deviceDescription)); | 119 | new InitDeviceDescs(deviceDescription)); |
114 | 120 | ||
121 | + // update description | ||
115 | descs.putDeviceDesc(deviceDescription); | 122 | descs.putDeviceDesc(deviceDescription); |
116 | - | ||
117 | Device newDevice = composeDevice(deviceId, providerDescs); | 123 | Device newDevice = composeDevice(deviceId, providerDescs); |
118 | 124 | ||
119 | if (oldDevice == null) { | 125 | if (oldDevice == null) { |
... | @@ -144,7 +150,8 @@ public class SimpleDeviceStore | ... | @@ -144,7 +150,8 @@ public class SimpleDeviceStore |
144 | 150 | ||
145 | // We allow only certain attributes to trigger update | 151 | // We allow only certain attributes to trigger update |
146 | if (!Objects.equals(oldDevice.hwVersion(), newDevice.hwVersion()) || | 152 | if (!Objects.equals(oldDevice.hwVersion(), newDevice.hwVersion()) || |
147 | - !Objects.equals(oldDevice.swVersion(), newDevice.swVersion())) { | 153 | + !Objects.equals(oldDevice.swVersion(), newDevice.swVersion()) || |
154 | + !isAnnotationsEqual(oldDevice.annotations(), newDevice.annotations())) { | ||
148 | 155 | ||
149 | synchronized (this) { | 156 | synchronized (this) { |
150 | devices.replace(newDevice.id(), oldDevice, newDevice); | 157 | devices.replace(newDevice.id(), oldDevice, newDevice); |
... | @@ -203,7 +210,7 @@ public class SimpleDeviceStore | ... | @@ -203,7 +210,7 @@ public class SimpleDeviceStore |
203 | PortNumber number = portDescription.portNumber(); | 210 | PortNumber number = portDescription.portNumber(); |
204 | Port oldPort = ports.get(number); | 211 | Port oldPort = ports.get(number); |
205 | // update description | 212 | // update description |
206 | - descs.putPortDesc(number, portDescription); | 213 | + descs.putPortDesc(portDescription); |
207 | Port newPort = composePort(device, number, descsMap); | 214 | Port newPort = composePort(device, number, descsMap); |
208 | 215 | ||
209 | events.add(oldPort == null ? | 216 | events.add(oldPort == null ? |
... | @@ -225,12 +232,14 @@ public class SimpleDeviceStore | ... | @@ -225,12 +232,14 @@ public class SimpleDeviceStore |
225 | return new DeviceEvent(PORT_ADDED, device, newPort); | 232 | return new DeviceEvent(PORT_ADDED, device, newPort); |
226 | } | 233 | } |
227 | 234 | ||
228 | - // CHecks if the specified port requires update and if so, it replaces the | 235 | + // Checks if the specified port requires update and if so, it replaces the |
229 | // existing entry in the map and returns corresponding event. | 236 | // existing entry in the map and returns corresponding event. |
230 | private DeviceEvent updatePort(Device device, Port oldPort, | 237 | private DeviceEvent updatePort(Device device, Port oldPort, |
231 | Port newPort, | 238 | Port newPort, |
232 | ConcurrentMap<PortNumber, Port> ports) { | 239 | ConcurrentMap<PortNumber, Port> ports) { |
233 | - if (oldPort.isEnabled() != newPort.isEnabled()) { | 240 | + if (oldPort.isEnabled() != newPort.isEnabled() || |
241 | + !isAnnotationsEqual(oldPort.annotations(), newPort.annotations())) { | ||
242 | + | ||
234 | ports.put(oldPort.number(), newPort); | 243 | ports.put(oldPort.number(), newPort); |
235 | return new DeviceEvent(PORT_UPDATED, device, newPort); | 244 | return new DeviceEvent(PORT_UPDATED, device, newPort); |
236 | } | 245 | } |
... | @@ -272,17 +281,17 @@ public class SimpleDeviceStore | ... | @@ -272,17 +281,17 @@ public class SimpleDeviceStore |
272 | checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId); | 281 | checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId); |
273 | 282 | ||
274 | DeviceDescriptions descs = descsMap.get(providerId); | 283 | DeviceDescriptions descs = descsMap.get(providerId); |
284 | + // assuming all providers must to give DeviceDescription | ||
275 | checkArgument(descs != null, | 285 | checkArgument(descs != null, |
276 | "Device description for Device ID %s from Provider %s was not found", | 286 | "Device description for Device ID %s from Provider %s was not found", |
277 | deviceId, providerId); | 287 | deviceId, providerId); |
278 | 288 | ||
279 | - // TODO: implement multi-provider | ||
280 | synchronized (this) { | 289 | synchronized (this) { |
281 | ConcurrentMap<PortNumber, Port> ports = getPortMap(deviceId); | 290 | ConcurrentMap<PortNumber, Port> ports = getPortMap(deviceId); |
282 | final PortNumber number = portDescription.portNumber(); | 291 | final PortNumber number = portDescription.portNumber(); |
283 | Port oldPort = ports.get(number); | 292 | Port oldPort = ports.get(number); |
284 | // update description | 293 | // update description |
285 | - descs.putPortDesc(number, portDescription); | 294 | + descs.putPortDesc(portDescription); |
286 | Port newPort = composePort(device, number, descsMap); | 295 | Port newPort = composePort(device, number, descsMap); |
287 | if (oldPort == null) { | 296 | if (oldPort == null) { |
288 | return createPort(device, newPort, ports); | 297 | return createPort(device, newPort, ports); |
... | @@ -321,6 +330,26 @@ public class SimpleDeviceStore | ... | @@ -321,6 +330,26 @@ public class SimpleDeviceStore |
321 | } | 330 | } |
322 | } | 331 | } |
323 | 332 | ||
333 | + private static boolean isAnnotationsEqual(Annotations lhs, Annotations rhs) { | ||
334 | + if (lhs == rhs) { | ||
335 | + return true; | ||
336 | + } | ||
337 | + if (lhs == null || rhs == null) { | ||
338 | + return false; | ||
339 | + } | ||
340 | + | ||
341 | + if (!lhs.keys().equals(rhs.keys())) { | ||
342 | + return false; | ||
343 | + } | ||
344 | + | ||
345 | + for (String key : lhs.keys()) { | ||
346 | + if (!lhs.value(key).equals(rhs.value(key))) { | ||
347 | + return false; | ||
348 | + } | ||
349 | + } | ||
350 | + return true; | ||
351 | + } | ||
352 | + | ||
324 | /** | 353 | /** |
325 | * Returns a Device, merging description given from multiple Providers. | 354 | * Returns a Device, merging description given from multiple Providers. |
326 | * | 355 | * |
... | @@ -336,46 +365,67 @@ public class SimpleDeviceStore | ... | @@ -336,46 +365,67 @@ public class SimpleDeviceStore |
336 | ProviderId primary = pickPrimaryPID(providerDescs); | 365 | ProviderId primary = pickPrimaryPID(providerDescs); |
337 | 366 | ||
338 | DeviceDescriptions desc = providerDescs.get(primary); | 367 | DeviceDescriptions desc = providerDescs.get(primary); |
368 | + | ||
369 | + // base | ||
339 | Type type = desc.getDeviceDesc().type(); | 370 | Type type = desc.getDeviceDesc().type(); |
340 | String manufacturer = desc.getDeviceDesc().manufacturer(); | 371 | String manufacturer = desc.getDeviceDesc().manufacturer(); |
341 | String hwVersion = desc.getDeviceDesc().hwVersion(); | 372 | String hwVersion = desc.getDeviceDesc().hwVersion(); |
342 | String swVersion = desc.getDeviceDesc().swVersion(); | 373 | String swVersion = desc.getDeviceDesc().swVersion(); |
343 | String serialNumber = desc.getDeviceDesc().serialNumber(); | 374 | String serialNumber = desc.getDeviceDesc().serialNumber(); |
375 | + DefaultAnnotations annotations = DefaultAnnotations.builder().build(); | ||
376 | + annotations = merge(annotations, desc.getDeviceDesc().annotations()); | ||
344 | 377 | ||
345 | for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) { | 378 | for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) { |
346 | if (e.getKey().equals(primary)) { | 379 | if (e.getKey().equals(primary)) { |
347 | continue; | 380 | continue; |
348 | } | 381 | } |
349 | - // FIXME: implement attribute merging once we have K-V attributes | 382 | + // TODO: should keep track of Description timestamp |
383 | + // and only merge conflicting keys when timestamp is newer | ||
384 | + // Currently assuming there will never be a key conflict between | ||
385 | + // providers | ||
386 | + | ||
387 | + // annotation merging. not so efficient, should revisit later | ||
388 | + annotations = merge(annotations, e.getValue().getDeviceDesc().annotations()); | ||
350 | } | 389 | } |
351 | 390 | ||
352 | - return new DefaultDevice(primary, deviceId , type, manufacturer, hwVersion, swVersion, serialNumber); | 391 | + return new DefaultDevice(primary, deviceId , type, manufacturer, |
392 | + hwVersion, swVersion, serialNumber, annotations); | ||
353 | } | 393 | } |
354 | 394 | ||
355 | - // probably want composePorts | 395 | + // probably want composePort"s" also |
356 | private Port composePort(Device device, PortNumber number, | 396 | private Port composePort(Device device, PortNumber number, |
357 | ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs) { | 397 | ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs) { |
358 | 398 | ||
359 | ProviderId primary = pickPrimaryPID(providerDescs); | 399 | ProviderId primary = pickPrimaryPID(providerDescs); |
360 | DeviceDescriptions primDescs = providerDescs.get(primary); | 400 | DeviceDescriptions primDescs = providerDescs.get(primary); |
401 | + // if no primary, assume not enabled | ||
402 | + // TODO: revisit this default port enabled/disabled behavior | ||
403 | + boolean isEnabled = false; | ||
404 | + DefaultAnnotations annotations = DefaultAnnotations.builder().build(); | ||
405 | + | ||
361 | final PortDescription portDesc = primDescs.getPortDesc(number); | 406 | final PortDescription portDesc = primDescs.getPortDesc(number); |
362 | - boolean isEnabled; | ||
363 | if (portDesc != null) { | 407 | if (portDesc != null) { |
364 | isEnabled = portDesc.isEnabled(); | 408 | isEnabled = portDesc.isEnabled(); |
365 | - } else { | 409 | + annotations = merge(annotations, portDesc.annotations()); |
366 | - // if no primary, assume not enabled | ||
367 | - // TODO: revisit this port enabled/disabled behavior | ||
368 | - isEnabled = false; | ||
369 | } | 410 | } |
370 | 411 | ||
371 | for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) { | 412 | for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) { |
372 | if (e.getKey().equals(primary)) { | 413 | if (e.getKey().equals(primary)) { |
373 | continue; | 414 | continue; |
374 | } | 415 | } |
375 | - // FIXME: implement attribute merging once we have K-V attributes | 416 | + // TODO: should keep track of Description timestamp |
417 | + // and only merge conflicting keys when timestamp is newer | ||
418 | + // Currently assuming there will never be a key conflict between | ||
419 | + // providers | ||
420 | + | ||
421 | + // annotation merging. not so efficient, should revisit later | ||
422 | + final PortDescription otherPortDesc = e.getValue().getPortDesc(number); | ||
423 | + if (otherPortDesc != null) { | ||
424 | + annotations = merge(annotations, otherPortDesc.annotations()); | ||
425 | + } | ||
376 | } | 426 | } |
377 | 427 | ||
378 | - return new DefaultPort(device, number, isEnabled); | 428 | + return new DefaultPort(device, number, isEnabled, annotations); |
379 | } | 429 | } |
380 | 430 | ||
381 | /** | 431 | /** |
... | @@ -428,7 +478,7 @@ public class SimpleDeviceStore | ... | @@ -428,7 +478,7 @@ public class SimpleDeviceStore |
428 | private final ConcurrentMap<PortNumber, PortDescription> portDescs; | 478 | private final ConcurrentMap<PortNumber, PortDescription> portDescs; |
429 | 479 | ||
430 | public DeviceDescriptions(DeviceDescription desc) { | 480 | public DeviceDescriptions(DeviceDescription desc) { |
431 | - this.deviceDesc = new AtomicReference<>(desc); | 481 | + this.deviceDesc = new AtomicReference<>(checkNotNull(desc)); |
432 | this.portDescs = new ConcurrentHashMap<>(); | 482 | this.portDescs = new ConcurrentHashMap<>(); |
433 | } | 483 | } |
434 | 484 | ||
... | @@ -444,12 +494,38 @@ public class SimpleDeviceStore | ... | @@ -444,12 +494,38 @@ public class SimpleDeviceStore |
444 | return Collections.unmodifiableCollection(portDescs.values()); | 494 | return Collections.unmodifiableCollection(portDescs.values()); |
445 | } | 495 | } |
446 | 496 | ||
447 | - public DeviceDescription putDeviceDesc(DeviceDescription newDesc) { | 497 | + /** |
448 | - return deviceDesc.getAndSet(newDesc); | 498 | + * Puts DeviceDescription, merging annotations as necessary. |
499 | + * | ||
500 | + * @param newDesc new DeviceDescription | ||
501 | + * @return previous DeviceDescription | ||
502 | + */ | ||
503 | + public synchronized DeviceDescription putDeviceDesc(DeviceDescription newDesc) { | ||
504 | + DeviceDescription oldOne = deviceDesc.get(); | ||
505 | + DeviceDescription newOne = newDesc; | ||
506 | + if (oldOne != null) { | ||
507 | + SparseAnnotations merged = merge(oldOne.annotations(), | ||
508 | + newDesc.annotations()); | ||
509 | + newOne = new DefaultDeviceDescription(newOne, merged); | ||
510 | + } | ||
511 | + return deviceDesc.getAndSet(newOne); | ||
449 | } | 512 | } |
450 | 513 | ||
451 | - public PortDescription putPortDesc(PortNumber number, PortDescription newDesc) { | 514 | + /** |
452 | - return portDescs.put(number, newDesc); | 515 | + * Puts PortDescription, merging annotations as necessary. |
516 | + * | ||
517 | + * @param newDesc new PortDescription | ||
518 | + * @return previous PortDescription | ||
519 | + */ | ||
520 | + public synchronized PortDescription putPortDesc(PortDescription newDesc) { | ||
521 | + PortDescription oldOne = portDescs.get(newDesc.portNumber()); | ||
522 | + PortDescription newOne = newDesc; | ||
523 | + if (oldOne != null) { | ||
524 | + SparseAnnotations merged = merge(oldOne.annotations(), | ||
525 | + newDesc.annotations()); | ||
526 | + newOne = new DefaultPortDescription(newOne, merged); | ||
527 | + } | ||
528 | + return portDescs.put(newOne.portNumber(), newOne); | ||
453 | } | 529 | } |
454 | } | 530 | } |
455 | } | 531 | } | ... | ... |
... | @@ -22,10 +22,13 @@ import org.junit.Before; | ... | @@ -22,10 +22,13 @@ import org.junit.Before; |
22 | import org.junit.BeforeClass; | 22 | import org.junit.BeforeClass; |
23 | import org.junit.Ignore; | 23 | import org.junit.Ignore; |
24 | import org.junit.Test; | 24 | import org.junit.Test; |
25 | +import org.onlab.onos.net.Annotations; | ||
26 | +import org.onlab.onos.net.DefaultAnnotations; | ||
25 | import org.onlab.onos.net.Device; | 27 | import org.onlab.onos.net.Device; |
26 | import org.onlab.onos.net.DeviceId; | 28 | import org.onlab.onos.net.DeviceId; |
27 | import org.onlab.onos.net.Port; | 29 | import org.onlab.onos.net.Port; |
28 | import org.onlab.onos.net.PortNumber; | 30 | import org.onlab.onos.net.PortNumber; |
31 | +import org.onlab.onos.net.SparseAnnotations; | ||
29 | import org.onlab.onos.net.device.DefaultDeviceDescription; | 32 | import org.onlab.onos.net.device.DefaultDeviceDescription; |
30 | import org.onlab.onos.net.device.DefaultPortDescription; | 33 | import org.onlab.onos.net.device.DefaultPortDescription; |
31 | import org.onlab.onos.net.device.DeviceDescription; | 34 | import org.onlab.onos.net.device.DeviceDescription; |
... | @@ -57,6 +60,23 @@ public class SimpleDeviceStoreTest { | ... | @@ -57,6 +60,23 @@ public class SimpleDeviceStoreTest { |
57 | private static final PortNumber P2 = PortNumber.portNumber(2); | 60 | private static final PortNumber P2 = PortNumber.portNumber(2); |
58 | private static final PortNumber P3 = PortNumber.portNumber(3); | 61 | private static final PortNumber P3 = PortNumber.portNumber(3); |
59 | 62 | ||
63 | + private static final SparseAnnotations A1 = DefaultAnnotations.builder() | ||
64 | + .set("A1", "a1") | ||
65 | + .set("B1", "b1") | ||
66 | + .build(); | ||
67 | + private static final SparseAnnotations A1_2 = DefaultAnnotations.builder() | ||
68 | + .remove("A1") | ||
69 | + .set("B3", "b3") | ||
70 | + .build(); | ||
71 | + private static final SparseAnnotations A2 = DefaultAnnotations.builder() | ||
72 | + .set("A2", "a2") | ||
73 | + .set("B2", "b2") | ||
74 | + .build(); | ||
75 | + private static final SparseAnnotations A2_2 = DefaultAnnotations.builder() | ||
76 | + .remove("A2") | ||
77 | + .set("B4", "b4") | ||
78 | + .build(); | ||
79 | + | ||
60 | private SimpleDeviceStore simpleDeviceStore; | 80 | private SimpleDeviceStore simpleDeviceStore; |
61 | private DeviceStore deviceStore; | 81 | private DeviceStore deviceStore; |
62 | 82 | ||
... | @@ -106,6 +126,24 @@ public class SimpleDeviceStoreTest { | ... | @@ -106,6 +126,24 @@ public class SimpleDeviceStoreTest { |
106 | assertEquals(SN, device.serialNumber()); | 126 | assertEquals(SN, device.serialNumber()); |
107 | } | 127 | } |
108 | 128 | ||
129 | + /** | ||
130 | + * Verifies that Annotations created by merging {@code annotations} is | ||
131 | + * equal to actual Annotations. | ||
132 | + * | ||
133 | + * @param actual Annotations to check | ||
134 | + * @param annotations | ||
135 | + */ | ||
136 | + private static void assertAnnotationsEquals(Annotations actual, SparseAnnotations... annotations) { | ||
137 | + DefaultAnnotations expected = DefaultAnnotations.builder().build(); | ||
138 | + for (SparseAnnotations a : annotations) { | ||
139 | + expected = DefaultAnnotations.merge(expected, a); | ||
140 | + } | ||
141 | + assertEquals(expected.keys(), actual.keys()); | ||
142 | + for (String key : expected.keys()) { | ||
143 | + assertEquals(expected.value(key), actual.value(key)); | ||
144 | + } | ||
145 | + } | ||
146 | + | ||
109 | @Test | 147 | @Test |
110 | public final void testGetDeviceCount() { | 148 | public final void testGetDeviceCount() { |
111 | assertEquals("initialy empty", 0, deviceStore.getDeviceCount()); | 149 | assertEquals("initialy empty", 0, deviceStore.getDeviceCount()); |
... | @@ -171,26 +209,41 @@ public class SimpleDeviceStoreTest { | ... | @@ -171,26 +209,41 @@ public class SimpleDeviceStoreTest { |
171 | public final void testCreateOrUpdateDeviceAncillary() { | 209 | public final void testCreateOrUpdateDeviceAncillary() { |
172 | DeviceDescription description = | 210 | DeviceDescription description = |
173 | new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR, | 211 | new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR, |
174 | - HW, SW1, SN); | 212 | + HW, SW1, SN, A2); |
175 | DeviceEvent event = deviceStore.createOrUpdateDevice(PIDA, DID1, description); | 213 | DeviceEvent event = deviceStore.createOrUpdateDevice(PIDA, DID1, description); |
176 | assertEquals(DEVICE_ADDED, event.type()); | 214 | assertEquals(DEVICE_ADDED, event.type()); |
177 | assertDevice(DID1, SW1, event.subject()); | 215 | assertDevice(DID1, SW1, event.subject()); |
178 | assertEquals(PIDA, event.subject().providerId()); | 216 | assertEquals(PIDA, event.subject().providerId()); |
217 | + assertAnnotationsEquals(event.subject().annotations(), A2); | ||
179 | assertFalse("Ancillary will not bring device up", deviceStore.isAvailable(DID1)); | 218 | assertFalse("Ancillary will not bring device up", deviceStore.isAvailable(DID1)); |
180 | 219 | ||
181 | DeviceDescription description2 = | 220 | DeviceDescription description2 = |
182 | new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR, | 221 | new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR, |
183 | - HW, SW2, SN); | 222 | + HW, SW2, SN, A1); |
184 | DeviceEvent event2 = deviceStore.createOrUpdateDevice(PID, DID1, description2); | 223 | DeviceEvent event2 = deviceStore.createOrUpdateDevice(PID, DID1, description2); |
185 | assertEquals(DEVICE_UPDATED, event2.type()); | 224 | assertEquals(DEVICE_UPDATED, event2.type()); |
186 | assertDevice(DID1, SW2, event2.subject()); | 225 | assertDevice(DID1, SW2, event2.subject()); |
187 | assertEquals(PID, event2.subject().providerId()); | 226 | assertEquals(PID, event2.subject().providerId()); |
227 | + assertAnnotationsEquals(event2.subject().annotations(), A1, A2); | ||
188 | assertTrue(deviceStore.isAvailable(DID1)); | 228 | assertTrue(deviceStore.isAvailable(DID1)); |
189 | 229 | ||
190 | assertNull("No change expected", deviceStore.createOrUpdateDevice(PID, DID1, description2)); | 230 | assertNull("No change expected", deviceStore.createOrUpdateDevice(PID, DID1, description2)); |
191 | 231 | ||
192 | // For now, Ancillary is ignored once primary appears | 232 | // For now, Ancillary is ignored once primary appears |
193 | assertNull("No change expected", deviceStore.createOrUpdateDevice(PIDA, DID1, description)); | 233 | assertNull("No change expected", deviceStore.createOrUpdateDevice(PIDA, DID1, description)); |
234 | + | ||
235 | + // But, Ancillary annotations will be in effect | ||
236 | + DeviceDescription description3 = | ||
237 | + new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR, | ||
238 | + HW, SW1, SN, A2_2); | ||
239 | + DeviceEvent event3 = deviceStore.createOrUpdateDevice(PIDA, DID1, description3); | ||
240 | + assertEquals(DEVICE_UPDATED, event3.type()); | ||
241 | + // basic information will be the one from Primary | ||
242 | + assertDevice(DID1, SW2, event3.subject()); | ||
243 | + assertEquals(PID, event3.subject().providerId()); | ||
244 | + // but annotation from Ancillary will be merged | ||
245 | + assertAnnotationsEquals(event3.subject().annotations(), A1, A2, A2_2); | ||
246 | + assertTrue(deviceStore.isAvailable(DID1)); | ||
194 | } | 247 | } |
195 | 248 | ||
196 | 249 | ||
... | @@ -299,27 +352,40 @@ public class SimpleDeviceStoreTest { | ... | @@ -299,27 +352,40 @@ public class SimpleDeviceStoreTest { |
299 | putDeviceAncillary(DID1, SW1); | 352 | putDeviceAncillary(DID1, SW1); |
300 | putDevice(DID1, SW1); | 353 | putDevice(DID1, SW1); |
301 | List<PortDescription> pds = Arrays.<PortDescription>asList( | 354 | List<PortDescription> pds = Arrays.<PortDescription>asList( |
302 | - new DefaultPortDescription(P1, true) | 355 | + new DefaultPortDescription(P1, true, A1) |
303 | ); | 356 | ); |
304 | deviceStore.updatePorts(PID, DID1, pds); | 357 | deviceStore.updatePorts(PID, DID1, pds); |
305 | 358 | ||
306 | DeviceEvent event = deviceStore.updatePortStatus(PID, DID1, | 359 | DeviceEvent event = deviceStore.updatePortStatus(PID, DID1, |
307 | - new DefaultPortDescription(P1, false)); | 360 | + new DefaultPortDescription(P1, false, A1_2)); |
308 | assertEquals(PORT_UPDATED, event.type()); | 361 | assertEquals(PORT_UPDATED, event.type()); |
309 | assertDevice(DID1, SW1, event.subject()); | 362 | assertDevice(DID1, SW1, event.subject()); |
310 | assertEquals(P1, event.port().number()); | 363 | assertEquals(P1, event.port().number()); |
364 | + assertAnnotationsEquals(event.port().annotations(), A1, A1_2); | ||
311 | assertFalse("Port is disabled", event.port().isEnabled()); | 365 | assertFalse("Port is disabled", event.port().isEnabled()); |
312 | 366 | ||
313 | DeviceEvent event2 = deviceStore.updatePortStatus(PIDA, DID1, | 367 | DeviceEvent event2 = deviceStore.updatePortStatus(PIDA, DID1, |
314 | new DefaultPortDescription(P1, true)); | 368 | new DefaultPortDescription(P1, true)); |
315 | assertNull("Ancillary is ignored if primary exists", event2); | 369 | assertNull("Ancillary is ignored if primary exists", event2); |
316 | 370 | ||
371 | + // but, Ancillary annotation update will be notified | ||
317 | DeviceEvent event3 = deviceStore.updatePortStatus(PIDA, DID1, | 372 | DeviceEvent event3 = deviceStore.updatePortStatus(PIDA, DID1, |
318 | - new DefaultPortDescription(P2, true)); | 373 | + new DefaultPortDescription(P1, true, A2)); |
319 | - assertEquals(PORT_ADDED, event3.type()); | 374 | + assertEquals(PORT_UPDATED, event3.type()); |
320 | assertDevice(DID1, SW1, event3.subject()); | 375 | assertDevice(DID1, SW1, event3.subject()); |
321 | - assertEquals(P2, event3.port().number()); | 376 | + assertEquals(P1, event3.port().number()); |
322 | - assertFalse("Port is disabled if not given from provider", event3.port().isEnabled()); | 377 | + assertAnnotationsEquals(event3.port().annotations(), A1, A1_2, A2); |
378 | + assertFalse("Port is disabled", event3.port().isEnabled()); | ||
379 | + | ||
380 | + // port only reported from Ancillary will be notified as down | ||
381 | + DeviceEvent event4 = deviceStore.updatePortStatus(PIDA, DID1, | ||
382 | + new DefaultPortDescription(P2, true)); | ||
383 | + assertEquals(PORT_ADDED, event4.type()); | ||
384 | + assertDevice(DID1, SW1, event4.subject()); | ||
385 | + assertEquals(P2, event4.port().number()); | ||
386 | + assertAnnotationsEquals(event4.port().annotations()); | ||
387 | + assertFalse("Port is disabled if not given from primary provider", | ||
388 | + event4.port().isEnabled()); | ||
323 | } | 389 | } |
324 | 390 | ||
325 | @Test | 391 | @Test | ... | ... |
-
Please register or login to post a comment