blob: 0b0ae37a2a87a91d0cd7b5cc260e67ad95b7ab0b [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 HIGUCHI55710e72014-10-02 14:58:32 -070012import org.onlab.onos.net.Annotations;
13import 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;
tom41a2c5f2014-09-19 09:20:35 -070031import org.slf4j.Logger;
tome5ec3fd2014-09-04 15:18:06 -070032
33import java.util.ArrayList;
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070034import java.util.Collection;
tome5ec3fd2014-09-04 15:18:06 -070035import java.util.Collections;
tome5ec3fd2014-09-04 15:18:06 -070036import java.util.HashSet;
tom29df6f42014-09-05 08:14:14 -070037import java.util.Iterator;
tome5ec3fd2014-09-04 15:18:06 -070038import java.util.List;
39import java.util.Map;
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070040import java.util.Map.Entry;
tome5ec3fd2014-09-04 15:18:06 -070041import java.util.Objects;
42import java.util.Set;
43import java.util.concurrent.ConcurrentHashMap;
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070044import java.util.concurrent.ConcurrentMap;
45import java.util.concurrent.atomic.AtomicReference;
tome5ec3fd2014-09-04 15:18:06 -070046
47import static com.google.common.base.Preconditions.checkArgument;
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070048import static com.google.common.base.Preconditions.checkNotNull;
Yuta HIGUCHIf5479702014-09-25 00:09:24 -070049import static com.google.common.base.Predicates.notNull;
tom29df6f42014-09-05 08:14:14 -070050import static org.onlab.onos.net.device.DeviceEvent.Type.*;
tom41a2c5f2014-09-19 09:20:35 -070051import static org.slf4j.LoggerFactory.getLogger;
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070052import static org.apache.commons.lang3.concurrent.ConcurrentUtils.createIfAbsentUnchecked;
Yuta HIGUCHI55710e72014-10-02 14:58:32 -070053import static org.onlab.onos.net.DefaultAnnotations.merge;
tome5ec3fd2014-09-04 15:18:06 -070054
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070055// TODO: synchronization should be done in more fine-grained manner.
tome5ec3fd2014-09-04 15:18:06 -070056/**
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
112 = createIfAbsentUnchecked(deviceDescs, deviceId,
113 new InitConcurrentHashMap<ProviderId, DeviceDescriptions>());
114
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()) ||
154 !isAnnotationsEqual(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() ||
241 !isAnnotationsEqual(oldPort.annotations(), newPort.annotations())) {
242
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
267 // Gets the map of ports for the specified device; if one does not already
268 // exist, it creates and registers a new one.
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700269 private ConcurrentMap<PortNumber, Port> getPortMap(DeviceId deviceId) {
270 return createIfAbsentUnchecked(devicePorts, deviceId,
271 new InitConcurrentHashMap<PortNumber, Port>());
tome5ec3fd2014-09-04 15:18:06 -0700272 }
273
tom41a2c5f2014-09-19 09:20:35 -0700274 @Override
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700275 public synchronized DeviceEvent updatePortStatus(ProviderId providerId, DeviceId deviceId,
tome5ec3fd2014-09-04 15:18:06 -0700276 PortDescription portDescription) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700277 Device device = devices.get(deviceId);
278 checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
279
280 ConcurrentMap<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId);
281 checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId);
282
283 DeviceDescriptions descs = descsMap.get(providerId);
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700284 // assuming all providers must to give DeviceDescription
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700285 checkArgument(descs != null,
286 "Device description for Device ID %s from Provider %s was not found",
287 deviceId, providerId);
288
tom46a220d2014-09-05 08:25:56 -0700289 synchronized (this) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700290 ConcurrentMap<PortNumber, Port> ports = getPortMap(deviceId);
291 final PortNumber number = portDescription.portNumber();
292 Port oldPort = ports.get(number);
293 // update description
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700294 descs.putPortDesc(portDescription);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700295 Port newPort = composePort(device, number, descsMap);
296 if (oldPort == null) {
297 return createPort(device, newPort, ports);
298 } else {
299 return updatePort(device, oldPort, newPort, ports);
300 }
tom46a220d2014-09-05 08:25:56 -0700301 }
tome5ec3fd2014-09-04 15:18:06 -0700302 }
303
tom41a2c5f2014-09-19 09:20:35 -0700304 @Override
305 public List<Port> getPorts(DeviceId deviceId) {
tom46a220d2014-09-05 08:25:56 -0700306 Map<PortNumber, Port> ports = devicePorts.get(deviceId);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700307 if (ports == null) {
308 return Collections.emptyList();
309 }
310 return ImmutableList.copyOf(ports.values());
tome5ec3fd2014-09-04 15:18:06 -0700311 }
312
tom41a2c5f2014-09-19 09:20:35 -0700313 @Override
314 public Port getPort(DeviceId deviceId, PortNumber portNumber) {
tom46a220d2014-09-05 08:25:56 -0700315 Map<PortNumber, Port> ports = devicePorts.get(deviceId);
316 return ports == null ? null : ports.get(portNumber);
tome5ec3fd2014-09-04 15:18:06 -0700317 }
318
tom41a2c5f2014-09-19 09:20:35 -0700319 @Override
320 public boolean isAvailable(DeviceId deviceId) {
tomff7eb7c2014-09-08 12:49:03 -0700321 return availableDevices.contains(deviceId);
322 }
323
tom41a2c5f2014-09-19 09:20:35 -0700324 @Override
tom41a2c5f2014-09-19 09:20:35 -0700325 public DeviceEvent removeDevice(DeviceId deviceId) {
tome5ec3fd2014-09-04 15:18:06 -0700326 synchronized (this) {
tome5ec3fd2014-09-04 15:18:06 -0700327 Device device = devices.remove(deviceId);
tom29df6f42014-09-05 08:14:14 -0700328 return device == null ? null :
329 new DeviceEvent(DEVICE_REMOVED, device, null);
tome5ec3fd2014-09-04 15:18:06 -0700330 }
331 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700332
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700333 private static boolean isAnnotationsEqual(Annotations lhs, Annotations rhs) {
334 if (lhs == rhs) {
335 return true;
336 }
337 if (lhs == null || rhs == null) {
338 return false;
339 }
340
341 if (!lhs.keys().equals(rhs.keys())) {
342 return false;
343 }
344
345 for (String key : lhs.keys()) {
346 if (!lhs.value(key).equals(rhs.value(key))) {
347 return false;
348 }
349 }
350 return true;
351 }
352
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700353 /**
354 * Returns a Device, merging description given from multiple Providers.
355 *
356 * @param deviceId device identifier
357 * @param providerDescs Collection of Descriptions from multiple providers
358 * @return Device instance
359 */
360 private Device composeDevice(DeviceId deviceId,
361 ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs) {
362
363 checkArgument(!providerDescs.isEmpty(), "No Device descriptions supplied");
364
365 ProviderId primary = pickPrimaryPID(providerDescs);
366
367 DeviceDescriptions desc = providerDescs.get(primary);
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700368
369 // base
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700370 Type type = desc.getDeviceDesc().type();
371 String manufacturer = desc.getDeviceDesc().manufacturer();
372 String hwVersion = desc.getDeviceDesc().hwVersion();
373 String swVersion = desc.getDeviceDesc().swVersion();
374 String serialNumber = desc.getDeviceDesc().serialNumber();
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700375 DefaultAnnotations annotations = DefaultAnnotations.builder().build();
376 annotations = merge(annotations, desc.getDeviceDesc().annotations());
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700377
378 for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) {
379 if (e.getKey().equals(primary)) {
380 continue;
381 }
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700382 // TODO: should keep track of Description timestamp
383 // and only merge conflicting keys when timestamp is newer
384 // Currently assuming there will never be a key conflict between
385 // providers
386
387 // annotation merging. not so efficient, should revisit later
388 annotations = merge(annotations, e.getValue().getDeviceDesc().annotations());
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700389 }
390
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700391 return new DefaultDevice(primary, deviceId , type, manufacturer,
392 hwVersion, swVersion, serialNumber, annotations);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700393 }
394
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700395 // probably want composePort"s" also
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700396 private Port composePort(Device device, PortNumber number,
397 ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs) {
398
399 ProviderId primary = pickPrimaryPID(providerDescs);
400 DeviceDescriptions primDescs = providerDescs.get(primary);
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700401 // if no primary, assume not enabled
402 // TODO: revisit this default port enabled/disabled behavior
403 boolean isEnabled = false;
404 DefaultAnnotations annotations = DefaultAnnotations.builder().build();
405
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700406 final PortDescription portDesc = primDescs.getPortDesc(number);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700407 if (portDesc != null) {
408 isEnabled = portDesc.isEnabled();
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700409 annotations = merge(annotations, portDesc.annotations());
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700410 }
411
412 for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) {
413 if (e.getKey().equals(primary)) {
414 continue;
415 }
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700416 // TODO: should keep track of Description timestamp
417 // and only merge conflicting keys when timestamp is newer
418 // Currently assuming there will never be a key conflict between
419 // providers
420
421 // annotation merging. not so efficient, should revisit later
422 final PortDescription otherPortDesc = e.getValue().getPortDesc(number);
423 if (otherPortDesc != null) {
424 annotations = merge(annotations, otherPortDesc.annotations());
425 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700426 }
427
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700428 return new DefaultPort(device, number, isEnabled, annotations);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700429 }
430
431 /**
432 * @return primary ProviderID, or randomly chosen one if none exists
433 */
434 private ProviderId pickPrimaryPID(
435 ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs) {
436 ProviderId fallBackPrimary = null;
437 for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) {
438 if (!e.getKey().isAncillary()) {
439 return e.getKey();
440 } else if (fallBackPrimary == null) {
441 // pick randomly as a fallback in case there is no primary
442 fallBackPrimary = e.getKey();
443 }
444 }
445 return fallBackPrimary;
446 }
447
448 // TODO: can be made generic
449 private static final class InitConcurrentHashMap<K, V> implements
450 ConcurrentInitializer<ConcurrentMap<K, V>> {
451 @Override
452 public ConcurrentMap<K, V> get() throws ConcurrentException {
453 return new ConcurrentHashMap<>();
454 }
455 }
456
457 public static final class InitDeviceDescs
458 implements ConcurrentInitializer<DeviceDescriptions> {
459 private final DeviceDescription deviceDesc;
460 public InitDeviceDescs(DeviceDescription deviceDesc) {
461 this.deviceDesc = checkNotNull(deviceDesc);
462 }
463 @Override
464 public DeviceDescriptions get() throws ConcurrentException {
465 return new DeviceDescriptions(deviceDesc);
466 }
467 }
468
469
470 /**
471 * Collection of Description of a Device and it's Ports given from a Provider.
472 */
473 private static class DeviceDescriptions {
474 // private final DeviceId id;
475 // private final ProviderId pid;
476
477 private final AtomicReference<DeviceDescription> deviceDesc;
478 private final ConcurrentMap<PortNumber, PortDescription> portDescs;
479
480 public DeviceDescriptions(DeviceDescription desc) {
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700481 this.deviceDesc = new AtomicReference<>(checkNotNull(desc));
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700482 this.portDescs = new ConcurrentHashMap<>();
483 }
484
485 public DeviceDescription getDeviceDesc() {
486 return deviceDesc.get();
487 }
488
489 public PortDescription getPortDesc(PortNumber number) {
490 return portDescs.get(number);
491 }
492
493 public Collection<PortDescription> getPortDescs() {
494 return Collections.unmodifiableCollection(portDescs.values());
495 }
496
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700497 /**
498 * Puts DeviceDescription, merging annotations as necessary.
499 *
500 * @param newDesc new DeviceDescription
501 * @return previous DeviceDescription
502 */
503 public synchronized DeviceDescription putDeviceDesc(DeviceDescription newDesc) {
504 DeviceDescription oldOne = deviceDesc.get();
505 DeviceDescription newOne = newDesc;
506 if (oldOne != null) {
507 SparseAnnotations merged = merge(oldOne.annotations(),
508 newDesc.annotations());
509 newOne = new DefaultDeviceDescription(newOne, merged);
510 }
511 return deviceDesc.getAndSet(newOne);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700512 }
513
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700514 /**
515 * Puts PortDescription, merging annotations as necessary.
516 *
517 * @param newDesc new PortDescription
518 * @return previous PortDescription
519 */
520 public synchronized PortDescription putPortDesc(PortDescription newDesc) {
521 PortDescription oldOne = portDescs.get(newDesc.portNumber());
522 PortDescription newOne = newDesc;
523 if (oldOne != null) {
524 SparseAnnotations merged = merge(oldOne.annotations(),
525 newDesc.annotations());
526 newOne = new DefaultPortDescription(newOne, merged);
527 }
528 return portDescs.put(newOne.portNumber(), newOne);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700529 }
530 }
tome5ec3fd2014-09-04 15:18:06 -0700531}