blob: ac38e23be4ca892e6426d9cc9c91e2209aa1a59a [file] [log] [blame]
Yuta HIGUCHI67a527f2014-10-02 22:23:54 -07001package org.onlab.onos.store.device.impl;
2
3import com.google.common.collect.FluentIterable;
4import com.google.common.collect.ImmutableList;
5
6import org.apache.commons.lang3.concurrent.ConcurrentException;
7import org.apache.commons.lang3.concurrent.ConcurrentInitializer;
8import org.apache.felix.scr.annotations.Activate;
9import org.apache.felix.scr.annotations.Component;
10import org.apache.felix.scr.annotations.Deactivate;
11import org.apache.felix.scr.annotations.Reference;
12import org.apache.felix.scr.annotations.ReferenceCardinality;
13import org.apache.felix.scr.annotations.Service;
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070014import org.onlab.onos.net.AnnotationsUtil;
Yuta HIGUCHI67a527f2014-10-02 22:23:54 -070015import org.onlab.onos.net.DefaultAnnotations;
16import org.onlab.onos.net.DefaultDevice;
17import org.onlab.onos.net.DefaultPort;
18import org.onlab.onos.net.Device;
19import org.onlab.onos.net.Device.Type;
20import org.onlab.onos.net.DeviceId;
21import org.onlab.onos.net.Port;
22import org.onlab.onos.net.PortNumber;
23import org.onlab.onos.net.SparseAnnotations;
24import org.onlab.onos.net.device.DefaultDeviceDescription;
25import org.onlab.onos.net.device.DefaultPortDescription;
26import org.onlab.onos.net.device.DeviceDescription;
27import org.onlab.onos.net.device.DeviceEvent;
28import org.onlab.onos.net.device.DeviceStore;
29import org.onlab.onos.net.device.DeviceStoreDelegate;
30import org.onlab.onos.net.device.PortDescription;
31import org.onlab.onos.net.provider.ProviderId;
32import org.onlab.onos.store.AbstractStore;
33import org.onlab.onos.store.ClockService;
34import org.onlab.onos.store.Timestamp;
35import org.onlab.onos.store.common.impl.Timestamped;
Yuta HIGUCHI0d6a5e62014-10-03 15:54:09 -070036import org.onlab.util.NewConcurrentHashMap;
Yuta HIGUCHI67a527f2014-10-02 22:23:54 -070037import org.slf4j.Logger;
38
39import java.util.ArrayList;
40import java.util.Collections;
41import java.util.HashSet;
42import java.util.Iterator;
43import java.util.List;
44import java.util.Map;
45import java.util.Map.Entry;
46import java.util.Objects;
47import java.util.Set;
48import java.util.concurrent.ConcurrentHashMap;
49import java.util.concurrent.ConcurrentMap;
50import java.util.concurrent.atomic.AtomicReference;
51
52import static com.google.common.base.Preconditions.checkArgument;
53import static com.google.common.base.Preconditions.checkNotNull;
54import static com.google.common.base.Predicates.notNull;
55import static org.onlab.onos.net.device.DeviceEvent.Type.*;
56import static org.slf4j.LoggerFactory.getLogger;
57import static org.apache.commons.lang3.concurrent.ConcurrentUtils.createIfAbsentUnchecked;
58import static org.onlab.onos.net.DefaultAnnotations.merge;
59import static com.google.common.base.Verify.verify;
60
61// TODO: implement remove event handling and call *Internal
62/**
63 * Manages inventory of infrastructure devices using gossip protocol to distribute
64 * information.
65 */
66@Component(immediate = true)
67@Service
68public class GossipDeviceStore
69 extends AbstractStore<DeviceEvent, DeviceStoreDelegate>
70 implements DeviceStore {
71
72 private final Logger log = getLogger(getClass());
73
74 public static final String DEVICE_NOT_FOUND = "Device with ID %s not found";
75
76 // TODO: Check if inner Map can be replaced with plain Map
77 // innerMap is used to lock a Device, thus instance should never be replaced.
78 // collection of Description given from various providers
79 private final ConcurrentMap<DeviceId,
80 ConcurrentMap<ProviderId, DeviceDescriptions>>
81 deviceDescs = new ConcurrentHashMap<>();
82
83 // cache of Device and Ports generated by compositing descriptions from providers
84 private final ConcurrentMap<DeviceId, Device> devices = new ConcurrentHashMap<>();
85 private final ConcurrentMap<DeviceId, ConcurrentMap<PortNumber, Port>> devicePorts = new ConcurrentHashMap<>();
86
87 // available(=UP) devices
88 private final Set<DeviceId> availableDevices = new HashSet<>();
89
90 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
91 protected ClockService clockService;
92
93 @Activate
94 public void activate() {
95 log.info("Started");
96 }
97
98 @Deactivate
99 public void deactivate() {
100 deviceDescs.clear();
101 devices.clear();
102 devicePorts.clear();
103 availableDevices.clear();
104 log.info("Stopped");
105 }
106
107 @Override
108 public int getDeviceCount() {
109 return devices.size();
110 }
111
112 @Override
113 public Iterable<Device> getDevices() {
114 return Collections.unmodifiableCollection(devices.values());
115 }
116
117 @Override
118 public Device getDevice(DeviceId deviceId) {
119 return devices.get(deviceId);
120 }
121
122 @Override
123 public synchronized DeviceEvent createOrUpdateDevice(ProviderId providerId, DeviceId deviceId,
124 DeviceDescription deviceDescription) {
125 Timestamp newTimestamp = clockService.getTimestamp(deviceId);
126 final Timestamped<DeviceDescription> deltaDesc = new Timestamped<>(deviceDescription, newTimestamp);
127 DeviceEvent event = createOrUpdateDeviceInternal(providerId, deviceId, deltaDesc);
128 if (event != null) {
129 // FIXME: broadcast deltaDesc, UP
130 log.debug("broadcast deltaDesc");
131 }
132 return event;
133 }
134
135 private DeviceEvent createOrUpdateDeviceInternal(ProviderId providerId, DeviceId deviceId,
136 Timestamped<DeviceDescription> deltaDesc) {
137
138 // Collection of DeviceDescriptions for a Device
139 ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs
Yuta HIGUCHI0d6a5e62014-10-03 15:54:09 -0700140 = getDeviceDescriptions(deviceId);
Yuta HIGUCHI67a527f2014-10-02 22:23:54 -0700141
142
143 DeviceDescriptions descs
144 = createIfAbsentUnchecked(providerDescs, providerId,
145 new InitDeviceDescs(deltaDesc));
146
147 // update description
148 synchronized (providerDescs) {
149 // locking per device
150
151 final Device oldDevice = devices.get(deviceId);
152 final Device newDevice;
153
154 if (deltaDesc == descs.getDeviceDesc() ||
155 deltaDesc.isNewer(descs.getDeviceDesc())) {
156 // on new device or valid update
157 descs.putDeviceDesc(deltaDesc);
158 newDevice = composeDevice(deviceId, providerDescs);
159 } else {
160 // outdated event, ignored.
161 return null;
162 }
163 if (oldDevice == null) {
164 // ADD
165 return createDevice(providerId, newDevice);
166 } else {
167 // UPDATE or ignore (no change or stale)
168 return updateDevice(providerId, oldDevice, newDevice);
169 }
170 }
171 }
172
173 // Creates the device and returns the appropriate event if necessary.
174 // Guarded by deviceDescs value (=locking Device)
175 private DeviceEvent createDevice(ProviderId providerId,
176 Device newDevice) {
177
178 // update composed device cache
179 Device oldDevice = devices.putIfAbsent(newDevice.id(), newDevice);
180 verify(oldDevice == null,
181 "Unexpected Device in cache. PID:%s [old=%s, new=%s]",
182 providerId, oldDevice, newDevice);
183
184 if (!providerId.isAncillary()) {
185 availableDevices.add(newDevice.id());
186 }
187
188 return new DeviceEvent(DeviceEvent.Type.DEVICE_ADDED, newDevice, null);
189 }
190
191 // Updates the device and returns the appropriate event if necessary.
192 // Guarded by deviceDescs value (=locking Device)
193 private DeviceEvent updateDevice(ProviderId providerId,
194 Device oldDevice, Device newDevice) {
195
196 // We allow only certain attributes to trigger update
197 if (!Objects.equals(oldDevice.hwVersion(), newDevice.hwVersion()) ||
198 !Objects.equals(oldDevice.swVersion(), newDevice.swVersion()) ||
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700199 !AnnotationsUtil.isEqual(oldDevice.annotations(), newDevice.annotations())) {
Yuta HIGUCHI67a527f2014-10-02 22:23:54 -0700200
201 boolean replaced = devices.replace(newDevice.id(), oldDevice, newDevice);
202 if (!replaced) {
203 verify(replaced,
204 "Replacing devices cache failed. PID:%s [expected:%s, found:%s, new=%s]",
205 providerId, oldDevice, devices.get(newDevice.id())
206 , newDevice);
207 }
208 if (!providerId.isAncillary()) {
209 availableDevices.add(newDevice.id());
210 }
211 return new DeviceEvent(DeviceEvent.Type.DEVICE_UPDATED, newDevice, null);
212 }
213
214 // Otherwise merely attempt to change availability if primary provider
215 if (!providerId.isAncillary()) {
216 boolean added = availableDevices.add(newDevice.id());
217 return !added ? null :
218 new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, newDevice, null);
219 }
220 return null;
221 }
222
223 @Override
224 public DeviceEvent markOffline(DeviceId deviceId) {
225 ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs
Yuta HIGUCHI0d6a5e62014-10-03 15:54:09 -0700226 = getDeviceDescriptions(deviceId);
Yuta HIGUCHI67a527f2014-10-02 22:23:54 -0700227
228 // locking device
229 synchronized (providerDescs) {
230 Device device = devices.get(deviceId);
231 if (device == null) {
232 return null;
233 }
234 boolean removed = availableDevices.remove(deviceId);
235 if (removed) {
236 // TODO: broadcast ... DOWN only?
237 return new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, device, null);
238
239 }
240 return null;
241 }
242 }
243
244 @Override
245 public synchronized List<DeviceEvent> updatePorts(ProviderId providerId, DeviceId deviceId,
246 List<PortDescription> portDescriptions) {
247 Timestamp newTimestamp = clockService.getTimestamp(deviceId);
248
249 List<Timestamped<PortDescription>> deltaDescs = new ArrayList<>(portDescriptions.size());
250 for (PortDescription e : portDescriptions) {
251 deltaDescs.add(new Timestamped<PortDescription>(e, newTimestamp));
252 }
253
254 List<DeviceEvent> events = updatePortsInternal(providerId, deviceId, deltaDescs);
255 if (!events.isEmpty()) {
256 // FIXME: broadcast deltaDesc, UP
257 log.debug("broadcast deltaDesc");
258 }
259 return events;
260
261 }
262
263 private List<DeviceEvent> updatePortsInternal(ProviderId providerId, DeviceId deviceId,
264 List<Timestamped<PortDescription>> deltaDescs) {
265
266 Device device = devices.get(deviceId);
267 checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
268
269 ConcurrentMap<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId);
270 checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId);
271
272 DeviceDescriptions descs = descsMap.get(providerId);
273 // every provider must provide DeviceDescription.
274 checkArgument(descs != null,
275 "Device description for Device ID %s from Provider %s was not found",
276 deviceId, providerId);
277
278 List<DeviceEvent> events = new ArrayList<>();
279 synchronized (descsMap) {
280 Map<PortNumber, Port> ports = getPortMap(deviceId);
281
282 // Add new ports
283 Set<PortNumber> processed = new HashSet<>();
284 for (Timestamped<PortDescription> deltaDesc : deltaDescs) {
285 final PortNumber number = deltaDesc.value().portNumber();
286 final Port oldPort = ports.get(number);
287 final Port newPort;
288
289 final Timestamped<PortDescription> existingPortDesc = descs.getPortDesc(number);
290 if (existingPortDesc == null ||
291 deltaDesc == existingPortDesc ||
292 deltaDesc.isNewer(existingPortDesc)) {
293 // on new port or valid update
294 // update description
295 descs.putPortDesc(deltaDesc);
296 newPort = composePort(device, number, descsMap);
297 } else {
298 // outdated event, ignored.
299 continue;
300 }
301
302 events.add(oldPort == null ?
303 createPort(device, newPort, ports) :
304 updatePort(device, oldPort, newPort, ports));
305 processed.add(number);
306 }
307
308 events.addAll(pruneOldPorts(device, ports, processed));
309 }
310 return FluentIterable.from(events).filter(notNull()).toList();
311 }
312
313 // Creates a new port based on the port description adds it to the map and
314 // Returns corresponding event.
315 // Guarded by deviceDescs value (=locking Device)
316 private DeviceEvent createPort(Device device, Port newPort,
317 Map<PortNumber, Port> ports) {
318 ports.put(newPort.number(), newPort);
319 return new DeviceEvent(PORT_ADDED, device, newPort);
320 }
321
322 // Checks if the specified port requires update and if so, it replaces the
323 // existing entry in the map and returns corresponding event.
324 // Guarded by deviceDescs value (=locking Device)
325 private DeviceEvent updatePort(Device device, Port oldPort,
326 Port newPort,
327 Map<PortNumber, Port> ports) {
328 if (oldPort.isEnabled() != newPort.isEnabled() ||
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700329 !AnnotationsUtil.isEqual(oldPort.annotations(), newPort.annotations())) {
Yuta HIGUCHI67a527f2014-10-02 22:23:54 -0700330
331 ports.put(oldPort.number(), newPort);
332 return new DeviceEvent(PORT_UPDATED, device, newPort);
333 }
334 return null;
335 }
336
337 // Prunes the specified list of ports based on which ports are in the
338 // processed list and returns list of corresponding events.
339 // Guarded by deviceDescs value (=locking Device)
340 private List<DeviceEvent> pruneOldPorts(Device device,
341 Map<PortNumber, Port> ports,
342 Set<PortNumber> processed) {
343 List<DeviceEvent> events = new ArrayList<>();
344 Iterator<PortNumber> iterator = ports.keySet().iterator();
345 while (iterator.hasNext()) {
346 PortNumber portNumber = iterator.next();
347 if (!processed.contains(portNumber)) {
348 events.add(new DeviceEvent(PORT_REMOVED, device,
349 ports.get(portNumber)));
350 iterator.remove();
351 }
352 }
353 return events;
354 }
355
356 // Gets the map of ports for the specified device; if one does not already
357 // exist, it creates and registers a new one.
358 private ConcurrentMap<PortNumber, Port> getPortMap(DeviceId deviceId) {
359 return createIfAbsentUnchecked(devicePorts, deviceId,
Yuta HIGUCHI0d6a5e62014-10-03 15:54:09 -0700360 NewConcurrentHashMap.<PortNumber, Port>ifNeeded());
361 }
362
363 private ConcurrentMap<ProviderId, DeviceDescriptions> getDeviceDescriptions(
364 DeviceId deviceId) {
365 return createIfAbsentUnchecked(deviceDescs, deviceId,
366 NewConcurrentHashMap.<ProviderId, DeviceDescriptions>ifNeeded());
Yuta HIGUCHI67a527f2014-10-02 22:23:54 -0700367 }
368
369 @Override
370 public synchronized DeviceEvent updatePortStatus(ProviderId providerId, DeviceId deviceId,
371 PortDescription portDescription) {
372 Timestamp newTimestamp = clockService.getTimestamp(deviceId);
373 final Timestamped<PortDescription> deltaDesc = new Timestamped<>(portDescription, newTimestamp);
374 DeviceEvent event = updatePortStatusInternal(providerId, deviceId, deltaDesc);
375 if (event != null) {
376 // FIXME: broadcast deltaDesc
377 log.debug("broadcast deltaDesc");
378 }
379 return event;
380 }
381
382 private DeviceEvent updatePortStatusInternal(ProviderId providerId, DeviceId deviceId,
383 Timestamped<PortDescription> deltaDesc) {
384
385 Device device = devices.get(deviceId);
386 checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
387
388 ConcurrentMap<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId);
389 checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId);
390
391 DeviceDescriptions descs = descsMap.get(providerId);
392 // assuming all providers must to give DeviceDescription
393 checkArgument(descs != null,
394 "Device description for Device ID %s from Provider %s was not found",
395 deviceId, providerId);
396
397 synchronized (descsMap) {
398 ConcurrentMap<PortNumber, Port> ports = getPortMap(deviceId);
399 final PortNumber number = deltaDesc.value().portNumber();
400 final Port oldPort = ports.get(number);
401 final Port newPort;
402
403 final Timestamped<PortDescription> existingPortDesc = descs.getPortDesc(number);
404 if (existingPortDesc == null ||
405 deltaDesc == existingPortDesc ||
406 deltaDesc.isNewer(existingPortDesc)) {
407 // on new port or valid update
408 // update description
409 descs.putPortDesc(deltaDesc);
410 newPort = composePort(device, number, descsMap);
411 } else {
412 // outdated event, ignored.
413 return null;
414 }
415
416 if (oldPort == null) {
417 return createPort(device, newPort, ports);
418 } else {
419 return updatePort(device, oldPort, newPort, ports);
420 }
421 }
422 }
423
424 @Override
425 public List<Port> getPorts(DeviceId deviceId) {
426 Map<PortNumber, Port> ports = devicePorts.get(deviceId);
427 if (ports == null) {
428 return Collections.emptyList();
429 }
430 return ImmutableList.copyOf(ports.values());
431 }
432
433 @Override
434 public Port getPort(DeviceId deviceId, PortNumber portNumber) {
435 Map<PortNumber, Port> ports = devicePorts.get(deviceId);
436 return ports == null ? null : ports.get(portNumber);
437 }
438
439 @Override
440 public boolean isAvailable(DeviceId deviceId) {
441 return availableDevices.contains(deviceId);
442 }
443
444 @Override
445 public DeviceEvent removeDevice(DeviceId deviceId) {
Yuta HIGUCHI0d6a5e62014-10-03 15:54:09 -0700446 ConcurrentMap<ProviderId, DeviceDescriptions> descs = getDeviceDescriptions(deviceId);
447 synchronized (descs) {
448 Device device = devices.remove(deviceId);
449 // should DEVICE_REMOVED carry removed ports?
450 devicePorts.get(deviceId).clear();
451 availableDevices.remove(deviceId);
452 descs.clear();
453 return device == null ? null :
454 new DeviceEvent(DEVICE_REMOVED, device, null);
455 }
Yuta HIGUCHI67a527f2014-10-02 22:23:54 -0700456 }
457
458 /**
459 * Returns a Device, merging description given from multiple Providers.
460 *
461 * @param deviceId device identifier
462 * @param providerDescs Collection of Descriptions from multiple providers
463 * @return Device instance
464 */
465 private Device composeDevice(DeviceId deviceId,
466 ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs) {
467
468 checkArgument(!providerDescs.isEmpty(), "No Device descriptions supplied");
469
470 ProviderId primary = pickPrimaryPID(providerDescs);
471
472 DeviceDescriptions desc = providerDescs.get(primary);
473
474 DeviceDescription base = desc.getDeviceDesc().value();
475 Type type = base.type();
476 String manufacturer = base.manufacturer();
477 String hwVersion = base.hwVersion();
478 String swVersion = base.swVersion();
479 String serialNumber = base.serialNumber();
480 DefaultAnnotations annotations = DefaultAnnotations.builder().build();
481 annotations = merge(annotations, base.annotations());
482
483 for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) {
484 if (e.getKey().equals(primary)) {
485 continue;
486 }
487 // TODO: should keep track of Description timestamp
488 // and only merge conflicting keys when timestamp is newer
489 // Currently assuming there will never be a key conflict between
490 // providers
491
492 // annotation merging. not so efficient, should revisit later
493 annotations = merge(annotations, e.getValue().getDeviceDesc().value().annotations());
494 }
495
496 return new DefaultDevice(primary, deviceId , type, manufacturer,
497 hwVersion, swVersion, serialNumber, annotations);
498 }
499
500 /**
501 * Returns a Port, merging description given from multiple Providers.
502 *
503 * @param device device the port is on
504 * @param number port number
505 * @param providerDescs Collection of Descriptions from multiple providers
506 * @return Port instance
507 */
508 private Port composePort(Device device, PortNumber number,
509 ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs) {
510
511 ProviderId primary = pickPrimaryPID(providerDescs);
512 DeviceDescriptions primDescs = providerDescs.get(primary);
513 // if no primary, assume not enabled
514 // TODO: revisit this default port enabled/disabled behavior
515 boolean isEnabled = false;
516 DefaultAnnotations annotations = DefaultAnnotations.builder().build();
517
518 final Timestamped<PortDescription> portDesc = primDescs.getPortDesc(number);
519 if (portDesc != null) {
520 isEnabled = portDesc.value().isEnabled();
521 annotations = merge(annotations, portDesc.value().annotations());
522 }
523
524 for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) {
525 if (e.getKey().equals(primary)) {
526 continue;
527 }
528 // TODO: should keep track of Description timestamp
529 // and only merge conflicting keys when timestamp is newer
530 // Currently assuming there will never be a key conflict between
531 // providers
532
533 // annotation merging. not so efficient, should revisit later
534 final Timestamped<PortDescription> otherPortDesc = e.getValue().getPortDesc(number);
535 if (otherPortDesc != null) {
536 annotations = merge(annotations, otherPortDesc.value().annotations());
537 }
538 }
539
540 return new DefaultPort(device, number, isEnabled, annotations);
541 }
542
543 /**
544 * @return primary ProviderID, or randomly chosen one if none exists
545 */
546 private ProviderId pickPrimaryPID(
547 ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs) {
548 ProviderId fallBackPrimary = null;
549 for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) {
550 if (!e.getKey().isAncillary()) {
551 return e.getKey();
552 } else if (fallBackPrimary == null) {
553 // pick randomly as a fallback in case there is no primary
554 fallBackPrimary = e.getKey();
555 }
556 }
557 return fallBackPrimary;
558 }
559
Yuta HIGUCHI67a527f2014-10-02 22:23:54 -0700560 public static final class InitDeviceDescs
561 implements ConcurrentInitializer<DeviceDescriptions> {
562
563 private final Timestamped<DeviceDescription> deviceDesc;
564
565 public InitDeviceDescs(Timestamped<DeviceDescription> deviceDesc) {
566 this.deviceDesc = checkNotNull(deviceDesc);
567 }
568 @Override
569 public DeviceDescriptions get() throws ConcurrentException {
570 return new DeviceDescriptions(deviceDesc);
571 }
572 }
573
574
575 /**
576 * Collection of Description of a Device and it's Ports given from a Provider.
577 */
578 public static class DeviceDescriptions {
579
580 private final AtomicReference<Timestamped<DeviceDescription>> deviceDesc;
581 private final ConcurrentMap<PortNumber, Timestamped<PortDescription>> portDescs;
582
583 public DeviceDescriptions(Timestamped<DeviceDescription> desc) {
584 this.deviceDesc = new AtomicReference<>(checkNotNull(desc));
585 this.portDescs = new ConcurrentHashMap<>();
586 }
587
588 public Timestamped<DeviceDescription> getDeviceDesc() {
589 return deviceDesc.get();
590 }
591
592 public Timestamped<PortDescription> getPortDesc(PortNumber number) {
593 return portDescs.get(number);
594 }
595
596 /**
597 * Puts DeviceDescription, merging annotations as necessary.
598 *
599 * @param newDesc new DeviceDescription
600 * @return previous DeviceDescription
601 */
602 public synchronized Timestamped<DeviceDescription> putDeviceDesc(Timestamped<DeviceDescription> newDesc) {
603 Timestamped<DeviceDescription> oldOne = deviceDesc.get();
604 Timestamped<DeviceDescription> newOne = newDesc;
605 if (oldOne != null) {
606 SparseAnnotations merged = merge(oldOne.value().annotations(),
607 newDesc.value().annotations());
608 newOne = new Timestamped<DeviceDescription>(
609 new DefaultDeviceDescription(newDesc.value(), merged),
610 newDesc.timestamp());
611 }
612 return deviceDesc.getAndSet(newOne);
613 }
614
615 /**
616 * Puts PortDescription, merging annotations as necessary.
617 *
618 * @param newDesc new PortDescription
619 * @return previous PortDescription
620 */
621 public synchronized Timestamped<PortDescription> putPortDesc(Timestamped<PortDescription> newDesc) {
622 Timestamped<PortDescription> oldOne = portDescs.get(newDesc.value().portNumber());
623 Timestamped<PortDescription> newOne = newDesc;
624 if (oldOne != null) {
625 SparseAnnotations merged = merge(oldOne.value().annotations(),
626 newDesc.value().annotations());
627 newOne = new Timestamped<PortDescription>(
628 new DefaultPortDescription(newDesc.value(), merged),
629 newDesc.timestamp());
630 }
631 return portDescs.put(newOne.value().portNumber(), newOne);
632 }
633 }
634}