blob: 926c8a7275884650a3e6c9231b4e7709da431ff6 [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
56/**
tome4729872014-09-23 00:37:37 -070057 * Manages inventory of infrastructure devices using trivial in-memory
tomcbff9392014-09-10 00:45:23 -070058 * structures implementation.
tome5ec3fd2014-09-04 15:18:06 -070059 */
tom41a2c5f2014-09-19 09:20:35 -070060@Component(immediate = true)
61@Service
tomf80c9722014-09-24 14:49:18 -070062public class SimpleDeviceStore
63 extends AbstractStore<DeviceEvent, DeviceStoreDelegate>
64 implements DeviceStore {
tom41a2c5f2014-09-19 09:20:35 -070065
66 private final Logger log = getLogger(getClass());
tome5ec3fd2014-09-04 15:18:06 -070067
tom29df6f42014-09-05 08:14:14 -070068 public static final String DEVICE_NOT_FOUND = "Device with ID %s not found";
69
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070070 // collection of Description given from various providers
71 private final ConcurrentMap<DeviceId,
72 ConcurrentMap<ProviderId, DeviceDescriptions>>
73 deviceDescs = new ConcurrentHashMap<>();
74
75 // cache of Device and Ports generated by compositing descriptions from providers
76 private final ConcurrentMap<DeviceId, Device> devices = new ConcurrentHashMap<>();
77 private final ConcurrentMap<DeviceId, ConcurrentMap<PortNumber, Port>> devicePorts = new ConcurrentHashMap<>();
78
79 // available(=UP) devices
tom249829a2014-09-04 15:28:04 -070080 private final Set<DeviceId> availableDevices = new HashSet<>();
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070081
tome5ec3fd2014-09-04 15:18:06 -070082
tom41a2c5f2014-09-19 09:20:35 -070083 @Activate
84 public void activate() {
85 log.info("Started");
86 }
87
88 @Deactivate
89 public void deactivate() {
90 log.info("Stopped");
91 }
tom5bcc9462014-09-19 10:11:31 -070092
tom41a2c5f2014-09-19 09:20:35 -070093 @Override
94 public int getDeviceCount() {
tomad2d2092014-09-06 23:24:20 -070095 return devices.size();
96 }
97
tom41a2c5f2014-09-19 09:20:35 -070098 @Override
99 public Iterable<Device> getDevices() {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700100 return Collections.unmodifiableCollection(devices.values());
tome5ec3fd2014-09-04 15:18:06 -0700101 }
102
tom41a2c5f2014-09-19 09:20:35 -0700103 @Override
104 public Device getDevice(DeviceId deviceId) {
tome5ec3fd2014-09-04 15:18:06 -0700105 return devices.get(deviceId);
106 }
107
tom41a2c5f2014-09-19 09:20:35 -0700108 @Override
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700109 public synchronized DeviceEvent createOrUpdateDevice(ProviderId providerId, DeviceId deviceId,
tome5ec3fd2014-09-04 15:18:06 -0700110 DeviceDescription deviceDescription) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700111 ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700112 = getDeviceDescriptions(deviceId);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700113
114 Device oldDevice = devices.get(deviceId);
115
116 DeviceDescriptions descs
117 = createIfAbsentUnchecked(providerDescs, providerId,
118 new InitDeviceDescs(deviceDescription));
119
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700120 // update description
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700121 descs.putDeviceDesc(deviceDescription);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700122 Device newDevice = composeDevice(deviceId, providerDescs);
123
124 if (oldDevice == null) {
125 // ADD
126 return createDevice(providerId, newDevice);
127 } else {
128 // UPDATE or ignore (no change or stale)
129 return updateDevice(providerId, oldDevice, newDevice);
tome5ec3fd2014-09-04 15:18:06 -0700130 }
tome5ec3fd2014-09-04 15:18:06 -0700131 }
132
133 // Creates the device and returns the appropriate event if necessary.
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700134 private DeviceEvent createDevice(ProviderId providerId, Device newDevice) {
135
136 // update composed device cache
tome5ec3fd2014-09-04 15:18:06 -0700137 synchronized (this) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700138 devices.putIfAbsent(newDevice.id(), newDevice);
139 if (!providerId.isAncillary()) {
140 availableDevices.add(newDevice.id());
141 }
tome5ec3fd2014-09-04 15:18:06 -0700142 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700143
144 return new DeviceEvent(DeviceEvent.Type.DEVICE_ADDED, newDevice, null);
tome5ec3fd2014-09-04 15:18:06 -0700145 }
146
147 // Updates the device and returns the appropriate event if necessary.
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700148 private DeviceEvent updateDevice(ProviderId providerId, Device oldDevice, Device newDevice) {
149
tome5ec3fd2014-09-04 15:18:06 -0700150 // We allow only certain attributes to trigger update
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700151 if (!Objects.equals(oldDevice.hwVersion(), newDevice.hwVersion()) ||
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700152 !Objects.equals(oldDevice.swVersion(), newDevice.swVersion()) ||
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700153 !AnnotationsUtil.isEqual(oldDevice.annotations(), newDevice.annotations())) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700154
tome5ec3fd2014-09-04 15:18:06 -0700155 synchronized (this) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700156 devices.replace(newDevice.id(), oldDevice, newDevice);
157 if (!providerId.isAncillary()) {
158 availableDevices.add(newDevice.id());
159 }
tome5ec3fd2014-09-04 15:18:06 -0700160 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700161 return new DeviceEvent(DeviceEvent.Type.DEVICE_UPDATED, newDevice, null);
tome5ec3fd2014-09-04 15:18:06 -0700162 }
163
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700164 // Otherwise merely attempt to change availability if primary provider
165 if (!providerId.isAncillary()) {
166 synchronized (this) {
167 boolean added = availableDevices.add(newDevice.id());
tom29df6f42014-09-05 08:14:14 -0700168 return !added ? null :
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700169 new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, newDevice, null);
170 }
tome5ec3fd2014-09-04 15:18:06 -0700171 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700172 return null;
tome5ec3fd2014-09-04 15:18:06 -0700173 }
174
tom41a2c5f2014-09-19 09:20:35 -0700175 @Override
176 public DeviceEvent markOffline(DeviceId deviceId) {
tome5ec3fd2014-09-04 15:18:06 -0700177 synchronized (this) {
178 Device device = devices.get(deviceId);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700179 boolean removed = (device != null) && availableDevices.remove(deviceId);
tom29df6f42014-09-05 08:14:14 -0700180 return !removed ? null :
181 new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, device, null);
tome5ec3fd2014-09-04 15:18:06 -0700182 }
183 }
184
tom41a2c5f2014-09-19 09:20:35 -0700185 @Override
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700186 public synchronized List<DeviceEvent> updatePorts(ProviderId providerId, DeviceId deviceId,
tome5ec3fd2014-09-04 15:18:06 -0700187 List<PortDescription> portDescriptions) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700188
189 // TODO: implement multi-provider
190 Device device = devices.get(deviceId);
191 checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
192
193 ConcurrentMap<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId);
194 checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId);
195
196 DeviceDescriptions descs = descsMap.get(providerId);
197 checkArgument(descs != null,
198 "Device description for Device ID %s from Provider %s was not found",
199 deviceId, providerId);
200
201
tom29df6f42014-09-05 08:14:14 -0700202 List<DeviceEvent> events = new ArrayList<>();
203 synchronized (this) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700204 ConcurrentMap<PortNumber, Port> ports = getPortMap(deviceId);
tom29df6f42014-09-05 08:14:14 -0700205
206 // Add new ports
207 Set<PortNumber> processed = new HashSet<>();
208 for (PortDescription portDescription : portDescriptions) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700209 PortNumber number = portDescription.portNumber();
210 Port oldPort = ports.get(number);
211 // update description
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700212 descs.putPortDesc(portDescription);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700213 Port newPort = composePort(device, number, descsMap);
214
215 events.add(oldPort == null ?
216 createPort(device, newPort, ports) :
217 updatePort(device, oldPort, newPort, ports));
tom29df6f42014-09-05 08:14:14 -0700218 processed.add(portDescription.portNumber());
219 }
220
221 events.addAll(pruneOldPorts(device, ports, processed));
222 }
Yuta HIGUCHIf5479702014-09-25 00:09:24 -0700223 return FluentIterable.from(events).filter(notNull()).toList();
tom29df6f42014-09-05 08:14:14 -0700224 }
225
226 // Creates a new port based on the port description adds it to the map and
227 // Returns corresponding event.
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700228 private DeviceEvent createPort(Device device, Port newPort,
229 ConcurrentMap<PortNumber, Port> ports) {
230 ports.put(newPort.number(), newPort);
231 return new DeviceEvent(PORT_ADDED, device, newPort);
tom29df6f42014-09-05 08:14:14 -0700232 }
233
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700234 // Checks if the specified port requires update and if so, it replaces the
tom29df6f42014-09-05 08:14:14 -0700235 // existing entry in the map and returns corresponding event.
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700236 private DeviceEvent updatePort(Device device, Port oldPort,
237 Port newPort,
238 ConcurrentMap<PortNumber, Port> ports) {
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700239 if (oldPort.isEnabled() != newPort.isEnabled() ||
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700240 !AnnotationsUtil.isEqual(oldPort.annotations(), newPort.annotations())) {
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700241
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700242 ports.put(oldPort.number(), newPort);
243 return new DeviceEvent(PORT_UPDATED, device, newPort);
tom29df6f42014-09-05 08:14:14 -0700244 }
245 return null;
246 }
247
248 // Prunes the specified list of ports based on which ports are in the
249 // processed list and returns list of corresponding events.
250 private List<DeviceEvent> pruneOldPorts(Device device,
251 Map<PortNumber, Port> ports,
252 Set<PortNumber> processed) {
253 List<DeviceEvent> events = new ArrayList<>();
254 Iterator<PortNumber> iterator = ports.keySet().iterator();
255 while (iterator.hasNext()) {
256 PortNumber portNumber = iterator.next();
tom24c55cd2014-09-06 10:47:25 -0700257 if (!processed.contains(portNumber)) {
tom29df6f42014-09-05 08:14:14 -0700258 events.add(new DeviceEvent(PORT_REMOVED, device,
259 ports.get(portNumber)));
260 iterator.remove();
261 }
262 }
263 return events;
264 }
265
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700266 private ConcurrentMap<ProviderId, DeviceDescriptions> getDeviceDescriptions(
267 DeviceId deviceId) {
268 return createIfAbsentUnchecked(deviceDescs, deviceId,
269 NewConcurrentHashMap.<ProviderId, DeviceDescriptions>ifNeeded());
270 }
271
tom29df6f42014-09-05 08:14:14 -0700272 // Gets the map of ports for the specified device; if one does not already
273 // exist, it creates and registers a new one.
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700274 private ConcurrentMap<PortNumber, Port> getPortMap(DeviceId deviceId) {
275 return createIfAbsentUnchecked(devicePorts, deviceId,
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700276 NewConcurrentHashMap.<PortNumber, Port>ifNeeded());
tome5ec3fd2014-09-04 15:18:06 -0700277 }
278
tom41a2c5f2014-09-19 09:20:35 -0700279 @Override
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700280 public synchronized DeviceEvent updatePortStatus(ProviderId providerId, DeviceId deviceId,
tome5ec3fd2014-09-04 15:18:06 -0700281 PortDescription portDescription) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700282 Device device = devices.get(deviceId);
283 checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
284
285 ConcurrentMap<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId);
286 checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId);
287
288 DeviceDescriptions descs = descsMap.get(providerId);
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700289 // assuming all providers must to give DeviceDescription
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700290 checkArgument(descs != null,
291 "Device description for Device ID %s from Provider %s was not found",
292 deviceId, providerId);
293
tom46a220d2014-09-05 08:25:56 -0700294 synchronized (this) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700295 ConcurrentMap<PortNumber, Port> ports = getPortMap(deviceId);
296 final PortNumber number = portDescription.portNumber();
297 Port oldPort = ports.get(number);
298 // update description
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700299 descs.putPortDesc(portDescription);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700300 Port newPort = composePort(device, number, descsMap);
301 if (oldPort == null) {
302 return createPort(device, newPort, ports);
303 } else {
304 return updatePort(device, oldPort, newPort, ports);
305 }
tom46a220d2014-09-05 08:25:56 -0700306 }
tome5ec3fd2014-09-04 15:18:06 -0700307 }
308
tom41a2c5f2014-09-19 09:20:35 -0700309 @Override
310 public List<Port> getPorts(DeviceId deviceId) {
tom46a220d2014-09-05 08:25:56 -0700311 Map<PortNumber, Port> ports = devicePorts.get(deviceId);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700312 if (ports == null) {
313 return Collections.emptyList();
314 }
315 return ImmutableList.copyOf(ports.values());
tome5ec3fd2014-09-04 15:18:06 -0700316 }
317
tom41a2c5f2014-09-19 09:20:35 -0700318 @Override
319 public Port getPort(DeviceId deviceId, PortNumber portNumber) {
tom46a220d2014-09-05 08:25:56 -0700320 Map<PortNumber, Port> ports = devicePorts.get(deviceId);
321 return ports == null ? null : ports.get(portNumber);
tome5ec3fd2014-09-04 15:18:06 -0700322 }
323
tom41a2c5f2014-09-19 09:20:35 -0700324 @Override
325 public boolean isAvailable(DeviceId deviceId) {
tomff7eb7c2014-09-08 12:49:03 -0700326 return availableDevices.contains(deviceId);
327 }
328
tom41a2c5f2014-09-19 09:20:35 -0700329 @Override
tom41a2c5f2014-09-19 09:20:35 -0700330 public DeviceEvent removeDevice(DeviceId deviceId) {
Yuta HIGUCHI0d6a5e62014-10-03 15:54:09 -0700331 ConcurrentMap<ProviderId, DeviceDescriptions> descs = getDeviceDescriptions(deviceId);
332 synchronized (descs) {
tome5ec3fd2014-09-04 15:18:06 -0700333 Device device = devices.remove(deviceId);
Yuta HIGUCHI0d6a5e62014-10-03 15:54:09 -0700334 // should DEVICE_REMOVED carry removed ports?
335 ConcurrentMap<PortNumber, Port> ports = devicePorts.get(deviceId);
336 if (ports != null) {
337 ports.clear();
338 }
339 availableDevices.remove(deviceId);
340 descs.clear();
tom29df6f42014-09-05 08:14:14 -0700341 return device == null ? null :
Yuta HIGUCHI0d6a5e62014-10-03 15:54:09 -0700342 new DeviceEvent(DEVICE_REMOVED, device, null);
tome5ec3fd2014-09-04 15:18:06 -0700343 }
344 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700345
346 /**
347 * Returns a Device, merging description given from multiple Providers.
348 *
349 * @param deviceId device identifier
350 * @param providerDescs Collection of Descriptions from multiple providers
351 * @return Device instance
352 */
353 private Device composeDevice(DeviceId deviceId,
354 ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs) {
355
356 checkArgument(!providerDescs.isEmpty(), "No Device descriptions supplied");
357
358 ProviderId primary = pickPrimaryPID(providerDescs);
359
360 DeviceDescriptions desc = providerDescs.get(primary);
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700361
362 // base
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700363 Type type = desc.getDeviceDesc().type();
364 String manufacturer = desc.getDeviceDesc().manufacturer();
365 String hwVersion = desc.getDeviceDesc().hwVersion();
366 String swVersion = desc.getDeviceDesc().swVersion();
367 String serialNumber = desc.getDeviceDesc().serialNumber();
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700368 DefaultAnnotations annotations = DefaultAnnotations.builder().build();
369 annotations = merge(annotations, desc.getDeviceDesc().annotations());
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700370
371 for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) {
372 if (e.getKey().equals(primary)) {
373 continue;
374 }
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700375 // TODO: should keep track of Description timestamp
376 // and only merge conflicting keys when timestamp is newer
377 // Currently assuming there will never be a key conflict between
378 // providers
379
380 // annotation merging. not so efficient, should revisit later
381 annotations = merge(annotations, e.getValue().getDeviceDesc().annotations());
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700382 }
383
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700384 return new DefaultDevice(primary, deviceId , type, manufacturer,
385 hwVersion, swVersion, serialNumber, annotations);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700386 }
387
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700388 // probably want composePort"s" also
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700389 private Port composePort(Device device, PortNumber number,
390 ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs) {
391
392 ProviderId primary = pickPrimaryPID(providerDescs);
393 DeviceDescriptions primDescs = providerDescs.get(primary);
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700394 // if no primary, assume not enabled
395 // TODO: revisit this default port enabled/disabled behavior
396 boolean isEnabled = false;
397 DefaultAnnotations annotations = DefaultAnnotations.builder().build();
398
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700399 final PortDescription portDesc = primDescs.getPortDesc(number);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700400 if (portDesc != null) {
401 isEnabled = portDesc.isEnabled();
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700402 annotations = merge(annotations, portDesc.annotations());
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700403 }
404
405 for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) {
406 if (e.getKey().equals(primary)) {
407 continue;
408 }
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700409 // TODO: should keep track of Description timestamp
410 // and only merge conflicting keys when timestamp is newer
411 // Currently assuming there will never be a key conflict between
412 // providers
413
414 // annotation merging. not so efficient, should revisit later
415 final PortDescription otherPortDesc = e.getValue().getPortDesc(number);
416 if (otherPortDesc != null) {
417 annotations = merge(annotations, otherPortDesc.annotations());
418 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700419 }
420
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700421 return new DefaultPort(device, number, isEnabled, annotations);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700422 }
423
424 /**
425 * @return primary ProviderID, or randomly chosen one if none exists
426 */
427 private ProviderId pickPrimaryPID(
428 ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs) {
429 ProviderId fallBackPrimary = null;
430 for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) {
431 if (!e.getKey().isAncillary()) {
432 return e.getKey();
433 } else if (fallBackPrimary == null) {
434 // pick randomly as a fallback in case there is no primary
435 fallBackPrimary = e.getKey();
436 }
437 }
438 return fallBackPrimary;
439 }
440
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700441 public static final class InitDeviceDescs
442 implements ConcurrentInitializer<DeviceDescriptions> {
443 private final DeviceDescription deviceDesc;
444 public InitDeviceDescs(DeviceDescription deviceDesc) {
445 this.deviceDesc = checkNotNull(deviceDesc);
446 }
447 @Override
448 public DeviceDescriptions get() throws ConcurrentException {
449 return new DeviceDescriptions(deviceDesc);
450 }
451 }
452
453
454 /**
455 * Collection of Description of a Device and it's Ports given from a Provider.
456 */
457 private static class DeviceDescriptions {
458 // private final DeviceId id;
459 // private final ProviderId pid;
460
461 private final AtomicReference<DeviceDescription> deviceDesc;
462 private final ConcurrentMap<PortNumber, PortDescription> portDescs;
463
464 public DeviceDescriptions(DeviceDescription desc) {
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700465 this.deviceDesc = new AtomicReference<>(checkNotNull(desc));
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700466 this.portDescs = new ConcurrentHashMap<>();
467 }
468
469 public DeviceDescription getDeviceDesc() {
470 return deviceDesc.get();
471 }
472
473 public PortDescription getPortDesc(PortNumber number) {
474 return portDescs.get(number);
475 }
476
477 public Collection<PortDescription> getPortDescs() {
478 return Collections.unmodifiableCollection(portDescs.values());
479 }
480
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700481 /**
482 * Puts DeviceDescription, merging annotations as necessary.
483 *
484 * @param newDesc new DeviceDescription
485 * @return previous DeviceDescription
486 */
487 public synchronized DeviceDescription putDeviceDesc(DeviceDescription newDesc) {
488 DeviceDescription oldOne = deviceDesc.get();
489 DeviceDescription newOne = newDesc;
490 if (oldOne != null) {
491 SparseAnnotations merged = merge(oldOne.annotations(),
492 newDesc.annotations());
493 newOne = new DefaultDeviceDescription(newOne, merged);
494 }
495 return deviceDesc.getAndSet(newOne);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700496 }
497
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700498 /**
499 * Puts PortDescription, merging annotations as necessary.
500 *
501 * @param newDesc new PortDescription
502 * @return previous PortDescription
503 */
504 public synchronized PortDescription putPortDesc(PortDescription newDesc) {
505 PortDescription oldOne = portDescs.get(newDesc.portNumber());
506 PortDescription newOne = newDesc;
507 if (oldOne != null) {
508 SparseAnnotations merged = merge(oldOne.annotations(),
509 newDesc.annotations());
510 newOne = new DefaultPortDescription(newOne, merged);
511 }
512 return portDescs.put(newOne.portNumber(), newOne);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700513 }
514 }
tome5ec3fd2014-09-04 15:18:06 -0700515}