blob: 0880ac9eaa4325412f7f16ddbe2a512ab9e9974b [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 HIGUCHI885be1d2014-10-04 21:47:26 -070054import static org.onlab.onos.net.DefaultAnnotations.union;
Yuta HIGUCHI55710e72014-10-02 14:58:32 -070055import static org.onlab.onos.net.DefaultAnnotations.merge;
tome5ec3fd2014-09-04 15:18:06 -070056
57/**
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) {
Yuta HIGUCHI0d6a5e62014-10-03 15:54:09 -0700332 ConcurrentMap<ProviderId, DeviceDescriptions> descs = getDeviceDescriptions(deviceId);
333 synchronized (descs) {
tome5ec3fd2014-09-04 15:18:06 -0700334 Device device = devices.remove(deviceId);
Yuta HIGUCHI0d6a5e62014-10-03 15:54:09 -0700335 // should DEVICE_REMOVED carry removed ports?
336 ConcurrentMap<PortNumber, Port> ports = devicePorts.get(deviceId);
337 if (ports != null) {
338 ports.clear();
339 }
340 availableDevices.remove(deviceId);
341 descs.clear();
tom29df6f42014-09-05 08:14:14 -0700342 return device == null ? null :
Yuta HIGUCHI0d6a5e62014-10-03 15:54:09 -0700343 new DeviceEvent(DEVICE_REMOVED, device, null);
tome5ec3fd2014-09-04 15:18:06 -0700344 }
345 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700346
347 /**
348 * Returns a Device, merging description given from multiple Providers.
349 *
350 * @param deviceId device identifier
351 * @param providerDescs Collection of Descriptions from multiple providers
352 * @return Device instance
353 */
354 private Device composeDevice(DeviceId deviceId,
355 ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs) {
356
357 checkArgument(!providerDescs.isEmpty(), "No Device descriptions supplied");
358
359 ProviderId primary = pickPrimaryPID(providerDescs);
360
361 DeviceDescriptions desc = providerDescs.get(primary);
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700362
363 // base
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700364 Type type = desc.getDeviceDesc().type();
365 String manufacturer = desc.getDeviceDesc().manufacturer();
366 String hwVersion = desc.getDeviceDesc().hwVersion();
367 String swVersion = desc.getDeviceDesc().swVersion();
368 String serialNumber = desc.getDeviceDesc().serialNumber();
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700369 DefaultAnnotations annotations = DefaultAnnotations.builder().build();
370 annotations = merge(annotations, desc.getDeviceDesc().annotations());
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700371
372 for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) {
373 if (e.getKey().equals(primary)) {
374 continue;
375 }
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700376 // TODO: should keep track of Description timestamp
377 // and only merge conflicting keys when timestamp is newer
378 // Currently assuming there will never be a key conflict between
379 // providers
380
381 // annotation merging. not so efficient, should revisit later
382 annotations = merge(annotations, e.getValue().getDeviceDesc().annotations());
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700383 }
384
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700385 return new DefaultDevice(primary, deviceId , type, manufacturer,
386 hwVersion, swVersion, serialNumber, annotations);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700387 }
388
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700389 // probably want composePort"s" also
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700390 private Port composePort(Device device, PortNumber number,
391 ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs) {
392
393 ProviderId primary = pickPrimaryPID(providerDescs);
394 DeviceDescriptions primDescs = providerDescs.get(primary);
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700395 // if no primary, assume not enabled
396 // TODO: revisit this default port enabled/disabled behavior
397 boolean isEnabled = false;
398 DefaultAnnotations annotations = DefaultAnnotations.builder().build();
399
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700400 final PortDescription portDesc = primDescs.getPortDesc(number);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700401 if (portDesc != null) {
402 isEnabled = portDesc.isEnabled();
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700403 annotations = merge(annotations, portDesc.annotations());
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700404 }
405
406 for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) {
407 if (e.getKey().equals(primary)) {
408 continue;
409 }
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700410 // TODO: should keep track of Description timestamp
411 // and only merge conflicting keys when timestamp is newer
412 // Currently assuming there will never be a key conflict between
413 // providers
414
415 // annotation merging. not so efficient, should revisit later
416 final PortDescription otherPortDesc = e.getValue().getPortDesc(number);
417 if (otherPortDesc != null) {
418 annotations = merge(annotations, otherPortDesc.annotations());
419 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700420 }
421
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700422 return new DefaultPort(device, number, isEnabled, annotations);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700423 }
424
425 /**
426 * @return primary ProviderID, or randomly chosen one if none exists
427 */
428 private ProviderId pickPrimaryPID(
429 ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs) {
430 ProviderId fallBackPrimary = null;
431 for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) {
432 if (!e.getKey().isAncillary()) {
433 return e.getKey();
434 } else if (fallBackPrimary == null) {
435 // pick randomly as a fallback in case there is no primary
436 fallBackPrimary = e.getKey();
437 }
438 }
439 return fallBackPrimary;
440 }
441
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700442 public static final class InitDeviceDescs
443 implements ConcurrentInitializer<DeviceDescriptions> {
444 private final DeviceDescription deviceDesc;
445 public InitDeviceDescs(DeviceDescription deviceDesc) {
446 this.deviceDesc = checkNotNull(deviceDesc);
447 }
448 @Override
449 public DeviceDescriptions get() throws ConcurrentException {
450 return new DeviceDescriptions(deviceDesc);
451 }
452 }
453
454
455 /**
456 * Collection of Description of a Device and it's Ports given from a Provider.
457 */
458 private static class DeviceDescriptions {
459 // private final DeviceId id;
460 // private final ProviderId pid;
461
462 private final AtomicReference<DeviceDescription> deviceDesc;
463 private final ConcurrentMap<PortNumber, PortDescription> portDescs;
464
465 public DeviceDescriptions(DeviceDescription desc) {
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700466 this.deviceDesc = new AtomicReference<>(checkNotNull(desc));
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700467 this.portDescs = new ConcurrentHashMap<>();
468 }
469
470 public DeviceDescription getDeviceDesc() {
471 return deviceDesc.get();
472 }
473
474 public PortDescription getPortDesc(PortNumber number) {
475 return portDescs.get(number);
476 }
477
478 public Collection<PortDescription> getPortDescs() {
479 return Collections.unmodifiableCollection(portDescs.values());
480 }
481
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700482 /**
483 * Puts DeviceDescription, merging annotations as necessary.
484 *
485 * @param newDesc new DeviceDescription
486 * @return previous DeviceDescription
487 */
488 public synchronized DeviceDescription putDeviceDesc(DeviceDescription newDesc) {
489 DeviceDescription oldOne = deviceDesc.get();
490 DeviceDescription newOne = newDesc;
491 if (oldOne != null) {
Yuta HIGUCHI885be1d2014-10-04 21:47:26 -0700492 SparseAnnotations merged = union(oldOne.annotations(),
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700493 newDesc.annotations());
494 newOne = new DefaultDeviceDescription(newOne, merged);
495 }
496 return deviceDesc.getAndSet(newOne);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700497 }
498
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700499 /**
500 * Puts PortDescription, merging annotations as necessary.
501 *
502 * @param newDesc new PortDescription
503 * @return previous PortDescription
504 */
505 public synchronized PortDescription putPortDesc(PortDescription newDesc) {
506 PortDescription oldOne = portDescs.get(newDesc.portNumber());
507 PortDescription newOne = newDesc;
508 if (oldOne != null) {
Yuta HIGUCHI885be1d2014-10-04 21:47:26 -0700509 SparseAnnotations merged = union(oldOne.annotations(),
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700510 newDesc.annotations());
511 newOne = new DefaultPortDescription(newOne, merged);
512 }
513 return portDescs.put(newOne.portNumber(), newOne);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700514 }
515 }
tome5ec3fd2014-09-04 15:18:06 -0700516}