blob: 4eda2fcc577e4873f5f9f1cb483cfdd94a15ab15 [file] [log] [blame]
tomea961ff2014-10-01 12:45:15 -07001package org.onlab.onos.store.trivial.impl;
tome5ec3fd2014-09-04 15:18:06 -07002
Yuta HIGUCHIf5479702014-09-25 00:09:24 -07003import com.google.common.collect.FluentIterable;
tom46a220d2014-09-05 08:25:56 -07004import com.google.common.collect.ImmutableList;
Yuta HIGUCHIf5479702014-09-25 00:09:24 -07005
Yuta HIGUCHI8e493792014-10-01 19:36:32 -07006import org.apache.commons.lang3.concurrent.ConcurrentException;
7import org.apache.commons.lang3.concurrent.ConcurrentInitializer;
tom41a2c5f2014-09-19 09:20:35 -07008import 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.Service;
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070012import org.onlab.onos.net.AnnotationsUtil;
Yuta HIGUCHI55710e72014-10-02 14:58:32 -070013import org.onlab.onos.net.DefaultAnnotations;
tome5ec3fd2014-09-04 15:18:06 -070014import org.onlab.onos.net.DefaultDevice;
tom29df6f42014-09-05 08:14:14 -070015import org.onlab.onos.net.DefaultPort;
tome5ec3fd2014-09-04 15:18:06 -070016import org.onlab.onos.net.Device;
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070017import org.onlab.onos.net.Device.Type;
tome5ec3fd2014-09-04 15:18:06 -070018import org.onlab.onos.net.DeviceId;
tome5ec3fd2014-09-04 15:18:06 -070019import org.onlab.onos.net.Port;
20import org.onlab.onos.net.PortNumber;
Yuta HIGUCHI55710e72014-10-02 14:58:32 -070021import org.onlab.onos.net.SparseAnnotations;
22import org.onlab.onos.net.device.DefaultDeviceDescription;
23import org.onlab.onos.net.device.DefaultPortDescription;
tome5ec3fd2014-09-04 15:18:06 -070024import org.onlab.onos.net.device.DeviceDescription;
25import org.onlab.onos.net.device.DeviceEvent;
tom41a2c5f2014-09-19 09:20:35 -070026import org.onlab.onos.net.device.DeviceStore;
tomf80c9722014-09-24 14:49:18 -070027import org.onlab.onos.net.device.DeviceStoreDelegate;
tome5ec3fd2014-09-04 15:18:06 -070028import org.onlab.onos.net.device.PortDescription;
29import org.onlab.onos.net.provider.ProviderId;
tomf80c9722014-09-24 14:49:18 -070030import org.onlab.onos.store.AbstractStore;
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070031import org.onlab.util.NewConcurrentHashMap;
tom41a2c5f2014-09-19 09:20:35 -070032import org.slf4j.Logger;
tome5ec3fd2014-09-04 15:18:06 -070033
34import java.util.ArrayList;
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070035import java.util.Collection;
tome5ec3fd2014-09-04 15:18:06 -070036import java.util.Collections;
tome5ec3fd2014-09-04 15:18:06 -070037import java.util.HashSet;
tom29df6f42014-09-05 08:14:14 -070038import java.util.Iterator;
tome5ec3fd2014-09-04 15:18:06 -070039import java.util.List;
40import java.util.Map;
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070041import java.util.Map.Entry;
tome5ec3fd2014-09-04 15:18:06 -070042import java.util.Objects;
43import java.util.Set;
44import java.util.concurrent.ConcurrentHashMap;
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070045import java.util.concurrent.ConcurrentMap;
46import java.util.concurrent.atomic.AtomicReference;
tome5ec3fd2014-09-04 15:18:06 -070047
48import static com.google.common.base.Preconditions.checkArgument;
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070049import static com.google.common.base.Preconditions.checkNotNull;
Yuta HIGUCHIf5479702014-09-25 00:09:24 -070050import static com.google.common.base.Predicates.notNull;
tom29df6f42014-09-05 08:14:14 -070051import static org.onlab.onos.net.device.DeviceEvent.Type.*;
tom41a2c5f2014-09-19 09:20:35 -070052import static org.slf4j.LoggerFactory.getLogger;
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070053import static org.apache.commons.lang3.concurrent.ConcurrentUtils.createIfAbsentUnchecked;
Yuta HIGUCHI55710e72014-10-02 14:58:32 -070054import static org.onlab.onos.net.DefaultAnnotations.merge;
tome5ec3fd2014-09-04 15:18:06 -070055
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070056// TODO: synchronization should be done in more fine-grained manner.
tome5ec3fd2014-09-04 15:18:06 -070057/**
tome4729872014-09-23 00:37:37 -070058 * Manages inventory of infrastructure devices using trivial in-memory
tomcbff9392014-09-10 00:45:23 -070059 * structures implementation.
tome5ec3fd2014-09-04 15:18:06 -070060 */
tom41a2c5f2014-09-19 09:20:35 -070061@Component(immediate = true)
62@Service
tomf80c9722014-09-24 14:49:18 -070063public class SimpleDeviceStore
64 extends AbstractStore<DeviceEvent, DeviceStoreDelegate>
65 implements DeviceStore {
tom41a2c5f2014-09-19 09:20:35 -070066
67 private final Logger log = getLogger(getClass());
tome5ec3fd2014-09-04 15:18:06 -070068
tom29df6f42014-09-05 08:14:14 -070069 public static final String DEVICE_NOT_FOUND = "Device with ID %s not found";
70
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070071 // collection of Description given from various providers
72 private final ConcurrentMap<DeviceId,
73 ConcurrentMap<ProviderId, DeviceDescriptions>>
74 deviceDescs = new ConcurrentHashMap<>();
75
76 // cache of Device and Ports generated by compositing descriptions from providers
77 private final ConcurrentMap<DeviceId, Device> devices = new ConcurrentHashMap<>();
78 private final ConcurrentMap<DeviceId, ConcurrentMap<PortNumber, Port>> devicePorts = new ConcurrentHashMap<>();
79
80 // available(=UP) devices
tom249829a2014-09-04 15:28:04 -070081 private final Set<DeviceId> availableDevices = new HashSet<>();
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070082
tome5ec3fd2014-09-04 15:18:06 -070083
tom41a2c5f2014-09-19 09:20:35 -070084 @Activate
85 public void activate() {
86 log.info("Started");
87 }
88
89 @Deactivate
90 public void deactivate() {
91 log.info("Stopped");
92 }
tom5bcc9462014-09-19 10:11:31 -070093
tom41a2c5f2014-09-19 09:20:35 -070094 @Override
95 public int getDeviceCount() {
tomad2d2092014-09-06 23:24:20 -070096 return devices.size();
97 }
98
tom41a2c5f2014-09-19 09:20:35 -070099 @Override
100 public Iterable<Device> getDevices() {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700101 return Collections.unmodifiableCollection(devices.values());
tome5ec3fd2014-09-04 15:18:06 -0700102 }
103
tom41a2c5f2014-09-19 09:20:35 -0700104 @Override
105 public Device getDevice(DeviceId deviceId) {
tome5ec3fd2014-09-04 15:18:06 -0700106 return devices.get(deviceId);
107 }
108
tom41a2c5f2014-09-19 09:20:35 -0700109 @Override
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700110 public synchronized DeviceEvent createOrUpdateDevice(ProviderId providerId, DeviceId deviceId,
tome5ec3fd2014-09-04 15:18:06 -0700111 DeviceDescription deviceDescription) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700112 ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700113 = getDeviceDescriptions(deviceId);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700114
115 Device oldDevice = devices.get(deviceId);
116
117 DeviceDescriptions descs
118 = createIfAbsentUnchecked(providerDescs, providerId,
119 new InitDeviceDescs(deviceDescription));
120
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700121 // update description
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700122 descs.putDeviceDesc(deviceDescription);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700123 Device newDevice = composeDevice(deviceId, providerDescs);
124
125 if (oldDevice == null) {
126 // ADD
127 return createDevice(providerId, newDevice);
128 } else {
129 // UPDATE or ignore (no change or stale)
130 return updateDevice(providerId, oldDevice, newDevice);
tome5ec3fd2014-09-04 15:18:06 -0700131 }
tome5ec3fd2014-09-04 15:18:06 -0700132 }
133
134 // Creates the device and returns the appropriate event if necessary.
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700135 private DeviceEvent createDevice(ProviderId providerId, Device newDevice) {
136
137 // update composed device cache
tome5ec3fd2014-09-04 15:18:06 -0700138 synchronized (this) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700139 devices.putIfAbsent(newDevice.id(), newDevice);
140 if (!providerId.isAncillary()) {
141 availableDevices.add(newDevice.id());
142 }
tome5ec3fd2014-09-04 15:18:06 -0700143 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700144
145 return new DeviceEvent(DeviceEvent.Type.DEVICE_ADDED, newDevice, null);
tome5ec3fd2014-09-04 15:18:06 -0700146 }
147
148 // Updates the device and returns the appropriate event if necessary.
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700149 private DeviceEvent updateDevice(ProviderId providerId, Device oldDevice, Device newDevice) {
150
tome5ec3fd2014-09-04 15:18:06 -0700151 // We allow only certain attributes to trigger update
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700152 if (!Objects.equals(oldDevice.hwVersion(), newDevice.hwVersion()) ||
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700153 !Objects.equals(oldDevice.swVersion(), newDevice.swVersion()) ||
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700154 !AnnotationsUtil.isEqual(oldDevice.annotations(), newDevice.annotations())) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700155
tome5ec3fd2014-09-04 15:18:06 -0700156 synchronized (this) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700157 devices.replace(newDevice.id(), oldDevice, newDevice);
158 if (!providerId.isAncillary()) {
159 availableDevices.add(newDevice.id());
160 }
tome5ec3fd2014-09-04 15:18:06 -0700161 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700162 return new DeviceEvent(DeviceEvent.Type.DEVICE_UPDATED, newDevice, null);
tome5ec3fd2014-09-04 15:18:06 -0700163 }
164
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700165 // Otherwise merely attempt to change availability if primary provider
166 if (!providerId.isAncillary()) {
167 synchronized (this) {
168 boolean added = availableDevices.add(newDevice.id());
tom29df6f42014-09-05 08:14:14 -0700169 return !added ? null :
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700170 new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, newDevice, null);
171 }
tome5ec3fd2014-09-04 15:18:06 -0700172 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700173 return null;
tome5ec3fd2014-09-04 15:18:06 -0700174 }
175
tom41a2c5f2014-09-19 09:20:35 -0700176 @Override
177 public DeviceEvent markOffline(DeviceId deviceId) {
tome5ec3fd2014-09-04 15:18:06 -0700178 synchronized (this) {
179 Device device = devices.get(deviceId);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700180 boolean removed = (device != null) && availableDevices.remove(deviceId);
tom29df6f42014-09-05 08:14:14 -0700181 return !removed ? null :
182 new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, device, null);
tome5ec3fd2014-09-04 15:18:06 -0700183 }
184 }
185
tom41a2c5f2014-09-19 09:20:35 -0700186 @Override
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700187 public synchronized List<DeviceEvent> updatePorts(ProviderId providerId, DeviceId deviceId,
tome5ec3fd2014-09-04 15:18:06 -0700188 List<PortDescription> portDescriptions) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700189
190 // TODO: implement multi-provider
191 Device device = devices.get(deviceId);
192 checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
193
194 ConcurrentMap<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId);
195 checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId);
196
197 DeviceDescriptions descs = descsMap.get(providerId);
198 checkArgument(descs != null,
199 "Device description for Device ID %s from Provider %s was not found",
200 deviceId, providerId);
201
202
tom29df6f42014-09-05 08:14:14 -0700203 List<DeviceEvent> events = new ArrayList<>();
204 synchronized (this) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700205 ConcurrentMap<PortNumber, Port> ports = getPortMap(deviceId);
tom29df6f42014-09-05 08:14:14 -0700206
207 // Add new ports
208 Set<PortNumber> processed = new HashSet<>();
209 for (PortDescription portDescription : portDescriptions) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700210 PortNumber number = portDescription.portNumber();
211 Port oldPort = ports.get(number);
212 // update description
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700213 descs.putPortDesc(portDescription);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700214 Port newPort = composePort(device, number, descsMap);
215
216 events.add(oldPort == null ?
217 createPort(device, newPort, ports) :
218 updatePort(device, oldPort, newPort, ports));
tom29df6f42014-09-05 08:14:14 -0700219 processed.add(portDescription.portNumber());
220 }
221
222 events.addAll(pruneOldPorts(device, ports, processed));
223 }
Yuta HIGUCHIf5479702014-09-25 00:09:24 -0700224 return FluentIterable.from(events).filter(notNull()).toList();
tom29df6f42014-09-05 08:14:14 -0700225 }
226
227 // Creates a new port based on the port description adds it to the map and
228 // Returns corresponding event.
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700229 private DeviceEvent createPort(Device device, Port newPort,
230 ConcurrentMap<PortNumber, Port> ports) {
231 ports.put(newPort.number(), newPort);
232 return new DeviceEvent(PORT_ADDED, device, newPort);
tom29df6f42014-09-05 08:14:14 -0700233 }
234
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700235 // Checks if the specified port requires update and if so, it replaces the
tom29df6f42014-09-05 08:14:14 -0700236 // existing entry in the map and returns corresponding event.
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700237 private DeviceEvent updatePort(Device device, Port oldPort,
238 Port newPort,
239 ConcurrentMap<PortNumber, Port> ports) {
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700240 if (oldPort.isEnabled() != newPort.isEnabled() ||
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700241 !AnnotationsUtil.isEqual(oldPort.annotations(), newPort.annotations())) {
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700242
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700243 ports.put(oldPort.number(), newPort);
244 return new DeviceEvent(PORT_UPDATED, device, newPort);
tom29df6f42014-09-05 08:14:14 -0700245 }
246 return null;
247 }
248
249 // Prunes the specified list of ports based on which ports are in the
250 // processed list and returns list of corresponding events.
251 private List<DeviceEvent> pruneOldPorts(Device device,
252 Map<PortNumber, Port> ports,
253 Set<PortNumber> processed) {
254 List<DeviceEvent> events = new ArrayList<>();
255 Iterator<PortNumber> iterator = ports.keySet().iterator();
256 while (iterator.hasNext()) {
257 PortNumber portNumber = iterator.next();
tom24c55cd2014-09-06 10:47:25 -0700258 if (!processed.contains(portNumber)) {
tom29df6f42014-09-05 08:14:14 -0700259 events.add(new DeviceEvent(PORT_REMOVED, device,
260 ports.get(portNumber)));
261 iterator.remove();
262 }
263 }
264 return events;
265 }
266
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700267 private ConcurrentMap<ProviderId, DeviceDescriptions> getDeviceDescriptions(
268 DeviceId deviceId) {
269 return createIfAbsentUnchecked(deviceDescs, deviceId,
270 NewConcurrentHashMap.<ProviderId, DeviceDescriptions>ifNeeded());
271 }
272
tom29df6f42014-09-05 08:14:14 -0700273 // Gets the map of ports for the specified device; if one does not already
274 // exist, it creates and registers a new one.
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700275 private ConcurrentMap<PortNumber, Port> getPortMap(DeviceId deviceId) {
276 return createIfAbsentUnchecked(devicePorts, deviceId,
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700277 NewConcurrentHashMap.<PortNumber, Port>ifNeeded());
tome5ec3fd2014-09-04 15:18:06 -0700278 }
279
tom41a2c5f2014-09-19 09:20:35 -0700280 @Override
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700281 public synchronized DeviceEvent updatePortStatus(ProviderId providerId, DeviceId deviceId,
tome5ec3fd2014-09-04 15:18:06 -0700282 PortDescription portDescription) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700283 Device device = devices.get(deviceId);
284 checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
285
286 ConcurrentMap<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId);
287 checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId);
288
289 DeviceDescriptions descs = descsMap.get(providerId);
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700290 // assuming all providers must to give DeviceDescription
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700291 checkArgument(descs != null,
292 "Device description for Device ID %s from Provider %s was not found",
293 deviceId, providerId);
294
tom46a220d2014-09-05 08:25:56 -0700295 synchronized (this) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700296 ConcurrentMap<PortNumber, Port> ports = getPortMap(deviceId);
297 final PortNumber number = portDescription.portNumber();
298 Port oldPort = ports.get(number);
299 // update description
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700300 descs.putPortDesc(portDescription);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700301 Port newPort = composePort(device, number, descsMap);
302 if (oldPort == null) {
303 return createPort(device, newPort, ports);
304 } else {
305 return updatePort(device, oldPort, newPort, ports);
306 }
tom46a220d2014-09-05 08:25:56 -0700307 }
tome5ec3fd2014-09-04 15:18:06 -0700308 }
309
tom41a2c5f2014-09-19 09:20:35 -0700310 @Override
311 public List<Port> getPorts(DeviceId deviceId) {
tom46a220d2014-09-05 08:25:56 -0700312 Map<PortNumber, Port> ports = devicePorts.get(deviceId);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700313 if (ports == null) {
314 return Collections.emptyList();
315 }
316 return ImmutableList.copyOf(ports.values());
tome5ec3fd2014-09-04 15:18:06 -0700317 }
318
tom41a2c5f2014-09-19 09:20:35 -0700319 @Override
320 public Port getPort(DeviceId deviceId, PortNumber portNumber) {
tom46a220d2014-09-05 08:25:56 -0700321 Map<PortNumber, Port> ports = devicePorts.get(deviceId);
322 return ports == null ? null : ports.get(portNumber);
tome5ec3fd2014-09-04 15:18:06 -0700323 }
324
tom41a2c5f2014-09-19 09:20:35 -0700325 @Override
326 public boolean isAvailable(DeviceId deviceId) {
tomff7eb7c2014-09-08 12:49:03 -0700327 return availableDevices.contains(deviceId);
328 }
329
tom41a2c5f2014-09-19 09:20:35 -0700330 @Override
tom41a2c5f2014-09-19 09:20:35 -0700331 public DeviceEvent removeDevice(DeviceId deviceId) {
tome5ec3fd2014-09-04 15:18:06 -0700332 synchronized (this) {
tome5ec3fd2014-09-04 15:18:06 -0700333 Device device = devices.remove(deviceId);
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700334 // FIXME: should we be removing deviceDescs also?
tom29df6f42014-09-05 08:14:14 -0700335 return device == null ? null :
336 new DeviceEvent(DEVICE_REMOVED, device, null);
tome5ec3fd2014-09-04 15:18:06 -0700337 }
338 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700339
340 /**
341 * Returns a Device, merging description given from multiple Providers.
342 *
343 * @param deviceId device identifier
344 * @param providerDescs Collection of Descriptions from multiple providers
345 * @return Device instance
346 */
347 private Device composeDevice(DeviceId deviceId,
348 ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs) {
349
350 checkArgument(!providerDescs.isEmpty(), "No Device descriptions supplied");
351
352 ProviderId primary = pickPrimaryPID(providerDescs);
353
354 DeviceDescriptions desc = providerDescs.get(primary);
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700355
356 // base
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700357 Type type = desc.getDeviceDesc().type();
358 String manufacturer = desc.getDeviceDesc().manufacturer();
359 String hwVersion = desc.getDeviceDesc().hwVersion();
360 String swVersion = desc.getDeviceDesc().swVersion();
361 String serialNumber = desc.getDeviceDesc().serialNumber();
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700362 DefaultAnnotations annotations = DefaultAnnotations.builder().build();
363 annotations = merge(annotations, desc.getDeviceDesc().annotations());
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700364
365 for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) {
366 if (e.getKey().equals(primary)) {
367 continue;
368 }
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700369 // TODO: should keep track of Description timestamp
370 // and only merge conflicting keys when timestamp is newer
371 // Currently assuming there will never be a key conflict between
372 // providers
373
374 // annotation merging. not so efficient, should revisit later
375 annotations = merge(annotations, e.getValue().getDeviceDesc().annotations());
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700376 }
377
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700378 return new DefaultDevice(primary, deviceId , type, manufacturer,
379 hwVersion, swVersion, serialNumber, annotations);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700380 }
381
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700382 // probably want composePort"s" also
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700383 private Port composePort(Device device, PortNumber number,
384 ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs) {
385
386 ProviderId primary = pickPrimaryPID(providerDescs);
387 DeviceDescriptions primDescs = providerDescs.get(primary);
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700388 // if no primary, assume not enabled
389 // TODO: revisit this default port enabled/disabled behavior
390 boolean isEnabled = false;
391 DefaultAnnotations annotations = DefaultAnnotations.builder().build();
392
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700393 final PortDescription portDesc = primDescs.getPortDesc(number);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700394 if (portDesc != null) {
395 isEnabled = portDesc.isEnabled();
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700396 annotations = merge(annotations, portDesc.annotations());
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700397 }
398
399 for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) {
400 if (e.getKey().equals(primary)) {
401 continue;
402 }
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700403 // TODO: should keep track of Description timestamp
404 // and only merge conflicting keys when timestamp is newer
405 // Currently assuming there will never be a key conflict between
406 // providers
407
408 // annotation merging. not so efficient, should revisit later
409 final PortDescription otherPortDesc = e.getValue().getPortDesc(number);
410 if (otherPortDesc != null) {
411 annotations = merge(annotations, otherPortDesc.annotations());
412 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700413 }
414
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700415 return new DefaultPort(device, number, isEnabled, annotations);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700416 }
417
418 /**
419 * @return primary ProviderID, or randomly chosen one if none exists
420 */
421 private ProviderId pickPrimaryPID(
422 ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs) {
423 ProviderId fallBackPrimary = null;
424 for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) {
425 if (!e.getKey().isAncillary()) {
426 return e.getKey();
427 } else if (fallBackPrimary == null) {
428 // pick randomly as a fallback in case there is no primary
429 fallBackPrimary = e.getKey();
430 }
431 }
432 return fallBackPrimary;
433 }
434
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700435 public static final class InitDeviceDescs
436 implements ConcurrentInitializer<DeviceDescriptions> {
437 private final DeviceDescription deviceDesc;
438 public InitDeviceDescs(DeviceDescription deviceDesc) {
439 this.deviceDesc = checkNotNull(deviceDesc);
440 }
441 @Override
442 public DeviceDescriptions get() throws ConcurrentException {
443 return new DeviceDescriptions(deviceDesc);
444 }
445 }
446
447
448 /**
449 * Collection of Description of a Device and it's Ports given from a Provider.
450 */
451 private static class DeviceDescriptions {
452 // private final DeviceId id;
453 // private final ProviderId pid;
454
455 private final AtomicReference<DeviceDescription> deviceDesc;
456 private final ConcurrentMap<PortNumber, PortDescription> portDescs;
457
458 public DeviceDescriptions(DeviceDescription desc) {
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700459 this.deviceDesc = new AtomicReference<>(checkNotNull(desc));
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700460 this.portDescs = new ConcurrentHashMap<>();
461 }
462
463 public DeviceDescription getDeviceDesc() {
464 return deviceDesc.get();
465 }
466
467 public PortDescription getPortDesc(PortNumber number) {
468 return portDescs.get(number);
469 }
470
471 public Collection<PortDescription> getPortDescs() {
472 return Collections.unmodifiableCollection(portDescs.values());
473 }
474
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700475 /**
476 * Puts DeviceDescription, merging annotations as necessary.
477 *
478 * @param newDesc new DeviceDescription
479 * @return previous DeviceDescription
480 */
481 public synchronized DeviceDescription putDeviceDesc(DeviceDescription newDesc) {
482 DeviceDescription oldOne = deviceDesc.get();
483 DeviceDescription newOne = newDesc;
484 if (oldOne != null) {
485 SparseAnnotations merged = merge(oldOne.annotations(),
486 newDesc.annotations());
487 newOne = new DefaultDeviceDescription(newOne, merged);
488 }
489 return deviceDesc.getAndSet(newOne);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700490 }
491
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700492 /**
493 * Puts PortDescription, merging annotations as necessary.
494 *
495 * @param newDesc new PortDescription
496 * @return previous PortDescription
497 */
498 public synchronized PortDescription putPortDesc(PortDescription newDesc) {
499 PortDescription oldOne = portDescs.get(newDesc.portNumber());
500 PortDescription newOne = newDesc;
501 if (oldOne != null) {
502 SparseAnnotations merged = merge(oldOne.annotations(),
503 newDesc.annotations());
504 newOne = new DefaultPortDescription(newOne, merged);
505 }
506 return portDescs.put(newOne.portNumber(), newOne);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700507 }
508 }
tome5ec3fd2014-09-04 15:18:06 -0700509}