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