blob: 30904ba4fd396eb47e95f4e974626e2c90d8e1f5 [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 HIGUCHIc35efac2014-10-06 14:43:53 -07005import com.google.common.collect.Maps;
6import com.google.common.collect.Sets;
Yuta HIGUCHIf5479702014-09-25 00:09:24 -07007
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;
35import java.util.Collections;
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -070036import java.util.HashMap;
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;
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -070051import static com.google.common.base.Verify.verify;
tom29df6f42014-09-05 08:14:14 -070052import static org.onlab.onos.net.device.DeviceEvent.Type.*;
tom41a2c5f2014-09-19 09:20:35 -070053import static org.slf4j.LoggerFactory.getLogger;
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070054import static org.apache.commons.lang3.concurrent.ConcurrentUtils.createIfAbsentUnchecked;
Yuta HIGUCHI885be1d2014-10-04 21:47:26 -070055import static org.onlab.onos.net.DefaultAnnotations.union;
Yuta HIGUCHI55710e72014-10-02 14:58:32 -070056import static org.onlab.onos.net.DefaultAnnotations.merge;
tome5ec3fd2014-09-04 15:18:06 -070057
58/**
tome4729872014-09-23 00:37:37 -070059 * Manages inventory of infrastructure devices using trivial in-memory
tomcbff9392014-09-10 00:45:23 -070060 * structures implementation.
tome5ec3fd2014-09-04 15:18:06 -070061 */
tom41a2c5f2014-09-19 09:20:35 -070062@Component(immediate = true)
63@Service
tomf80c9722014-09-24 14:49:18 -070064public class SimpleDeviceStore
65 extends AbstractStore<DeviceEvent, DeviceStoreDelegate>
66 implements DeviceStore {
tom41a2c5f2014-09-19 09:20:35 -070067
68 private final Logger log = getLogger(getClass());
tome5ec3fd2014-09-04 15:18:06 -070069
tom29df6f42014-09-05 08:14:14 -070070 public static final String DEVICE_NOT_FOUND = "Device with ID %s not found";
71
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070072 // collection of Description given from various providers
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -070073 private final ConcurrentMap<DeviceId, Map<ProviderId, DeviceDescriptions>>
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -070074 deviceDescs = Maps.newConcurrentMap();
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070075
76 // cache of Device and Ports generated by compositing descriptions from providers
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -070077 private final ConcurrentMap<DeviceId, Device> devices = Maps.newConcurrentMap();
78 private final ConcurrentMap<DeviceId, ConcurrentMap<PortNumber, Port>> devicePorts = Maps.newConcurrentMap();
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070079
80 // available(=UP) devices
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -070081 private final Set<DeviceId> availableDevices = Sets.newConcurrentHashSet();
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() {
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -070091 deviceDescs.clear();
92 devices.clear();
93 devicePorts.clear();
94 availableDevices.clear();
tom41a2c5f2014-09-19 09:20:35 -070095 log.info("Stopped");
96 }
tom5bcc9462014-09-19 10:11:31 -070097
tom41a2c5f2014-09-19 09:20:35 -070098 @Override
99 public int getDeviceCount() {
tomad2d2092014-09-06 23:24:20 -0700100 return devices.size();
101 }
102
tom41a2c5f2014-09-19 09:20:35 -0700103 @Override
104 public Iterable<Device> getDevices() {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700105 return Collections.unmodifiableCollection(devices.values());
tome5ec3fd2014-09-04 15:18:06 -0700106 }
107
tom41a2c5f2014-09-19 09:20:35 -0700108 @Override
109 public Device getDevice(DeviceId deviceId) {
tome5ec3fd2014-09-04 15:18:06 -0700110 return devices.get(deviceId);
111 }
112
tom41a2c5f2014-09-19 09:20:35 -0700113 @Override
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700114 public DeviceEvent createOrUpdateDevice(ProviderId providerId,
115 DeviceId deviceId,
tome5ec3fd2014-09-04 15:18:06 -0700116 DeviceDescription deviceDescription) {
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700117
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700118 Map<ProviderId, DeviceDescriptions> providerDescs
119 = getOrCreateDeviceDescriptions(deviceId);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700120
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700121 synchronized (providerDescs) {
122 // locking per device
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700123
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700124 DeviceDescriptions descs
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700125 = getOrCreateProviderDeviceDescriptions(providerDescs,
126 providerId,
127 deviceDescription);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700128
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700129 Device oldDevice = devices.get(deviceId);
130 // update description
131 descs.putDeviceDesc(deviceDescription);
132 Device newDevice = composeDevice(deviceId, providerDescs);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700133
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700134 if (oldDevice == null) {
135 // ADD
136 return createDevice(providerId, newDevice);
137 } else {
138 // UPDATE or ignore (no change or stale)
139 return updateDevice(providerId, oldDevice, newDevice);
140 }
tome5ec3fd2014-09-04 15:18:06 -0700141 }
tome5ec3fd2014-09-04 15:18:06 -0700142 }
143
144 // Creates the device and returns the appropriate event if necessary.
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700145 // Guarded by deviceDescs value (=Device lock)
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700146 private DeviceEvent createDevice(ProviderId providerId, Device newDevice) {
147
148 // update composed device cache
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700149 Device oldDevice = devices.putIfAbsent(newDevice.id(), newDevice);
150 verify(oldDevice == null,
151 "Unexpected Device in cache. PID:%s [old=%s, new=%s]",
152 providerId, oldDevice, newDevice);
153
154 if (!providerId.isAncillary()) {
155 availableDevices.add(newDevice.id());
tome5ec3fd2014-09-04 15:18:06 -0700156 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700157
158 return new DeviceEvent(DeviceEvent.Type.DEVICE_ADDED, newDevice, null);
tome5ec3fd2014-09-04 15:18:06 -0700159 }
160
161 // Updates the device and returns the appropriate event if necessary.
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700162 // Guarded by deviceDescs value (=Device lock)
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700163 private DeviceEvent updateDevice(ProviderId providerId, Device oldDevice, Device newDevice) {
164
tome5ec3fd2014-09-04 15:18:06 -0700165 // We allow only certain attributes to trigger update
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700166 if (!Objects.equals(oldDevice.hwVersion(), newDevice.hwVersion()) ||
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700167 !Objects.equals(oldDevice.swVersion(), newDevice.swVersion()) ||
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700168 !AnnotationsUtil.isEqual(oldDevice.annotations(), newDevice.annotations())) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700169
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700170 boolean replaced = devices.replace(newDevice.id(), oldDevice, newDevice);
171 if (!replaced) {
172 verify(replaced,
173 "Replacing devices cache failed. PID:%s [expected:%s, found:%s, new=%s]",
174 providerId, oldDevice, devices.get(newDevice.id())
175 , newDevice);
176 }
177 if (!providerId.isAncillary()) {
178 availableDevices.add(newDevice.id());
tome5ec3fd2014-09-04 15:18:06 -0700179 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700180 return new DeviceEvent(DeviceEvent.Type.DEVICE_UPDATED, newDevice, null);
tome5ec3fd2014-09-04 15:18:06 -0700181 }
182
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700183 // Otherwise merely attempt to change availability if primary provider
184 if (!providerId.isAncillary()) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700185 boolean added = availableDevices.add(newDevice.id());
tom29df6f42014-09-05 08:14:14 -0700186 return !added ? null :
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700187 new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, newDevice, null);
tome5ec3fd2014-09-04 15:18:06 -0700188 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700189 return null;
tome5ec3fd2014-09-04 15:18:06 -0700190 }
191
tom41a2c5f2014-09-19 09:20:35 -0700192 @Override
193 public DeviceEvent markOffline(DeviceId deviceId) {
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700194 Map<ProviderId, DeviceDescriptions> providerDescs
195 = getOrCreateDeviceDescriptions(deviceId);
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700196
197 // locking device
198 synchronized (providerDescs) {
tome5ec3fd2014-09-04 15:18:06 -0700199 Device device = devices.get(deviceId);
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700200 if (device == null) {
201 return null;
202 }
203 boolean removed = availableDevices.remove(deviceId);
204 if (removed) {
205 // TODO: broadcast ... DOWN only?
206 return new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, device, null);
207 }
208 return null;
tome5ec3fd2014-09-04 15:18:06 -0700209 }
210 }
211
tom41a2c5f2014-09-19 09:20:35 -0700212 @Override
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700213 public List<DeviceEvent> updatePorts(ProviderId providerId,
214 DeviceId deviceId,
215 List<PortDescription> portDescriptions) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700216
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700217 Device device = devices.get(deviceId);
218 checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
219
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700220 Map<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700221 checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId);
222
tom29df6f42014-09-05 08:14:14 -0700223 List<DeviceEvent> events = new ArrayList<>();
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700224 synchronized (descsMap) {
225 DeviceDescriptions descs = descsMap.get(providerId);
226 // every provider must provide DeviceDescription.
227 checkArgument(descs != null,
228 "Device description for Device ID %s from Provider %s was not found",
229 deviceId, providerId);
230
231 Map<PortNumber, Port> ports = getPortMap(deviceId);
tom29df6f42014-09-05 08:14:14 -0700232
233 // Add new ports
234 Set<PortNumber> processed = new HashSet<>();
235 for (PortDescription portDescription : portDescriptions) {
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700236 final PortNumber number = portDescription.portNumber();
237 processed.add(portDescription.portNumber());
238
239 final Port oldPort = ports.get(number);
240 final Port newPort;
241
242// event suppression hook?
243
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700244 // update description
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700245 descs.putPortDesc(portDescription);
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700246 newPort = composePort(device, number, descsMap);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700247
248 events.add(oldPort == null ?
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700249 createPort(device, newPort, ports) :
250 updatePort(device, oldPort, newPort, ports));
tom29df6f42014-09-05 08:14:14 -0700251 }
252
253 events.addAll(pruneOldPorts(device, ports, processed));
254 }
Yuta HIGUCHIf5479702014-09-25 00:09:24 -0700255 return FluentIterable.from(events).filter(notNull()).toList();
tom29df6f42014-09-05 08:14:14 -0700256 }
257
258 // Creates a new port based on the port description adds it to the map and
259 // Returns corresponding event.
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700260 // Guarded by deviceDescs value (=Device lock)
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700261 private DeviceEvent createPort(Device device, Port newPort,
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700262 Map<PortNumber, Port> ports) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700263 ports.put(newPort.number(), newPort);
264 return new DeviceEvent(PORT_ADDED, device, newPort);
tom29df6f42014-09-05 08:14:14 -0700265 }
266
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700267 // Checks if the specified port requires update and if so, it replaces the
tom29df6f42014-09-05 08:14:14 -0700268 // existing entry in the map and returns corresponding event.
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700269 // Guarded by deviceDescs value (=Device lock)
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700270 private DeviceEvent updatePort(Device device, Port oldPort,
271 Port newPort,
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700272 Map<PortNumber, Port> ports) {
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700273 if (oldPort.isEnabled() != newPort.isEnabled() ||
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700274 !AnnotationsUtil.isEqual(oldPort.annotations(), newPort.annotations())) {
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700275
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700276 ports.put(oldPort.number(), newPort);
277 return new DeviceEvent(PORT_UPDATED, device, newPort);
tom29df6f42014-09-05 08:14:14 -0700278 }
279 return null;
280 }
281
282 // Prunes the specified list of ports based on which ports are in the
283 // processed list and returns list of corresponding events.
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700284 // Guarded by deviceDescs value (=Device lock)
tom29df6f42014-09-05 08:14:14 -0700285 private List<DeviceEvent> pruneOldPorts(Device device,
286 Map<PortNumber, Port> ports,
287 Set<PortNumber> processed) {
288 List<DeviceEvent> events = new ArrayList<>();
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700289 Iterator<Entry<PortNumber, Port>> iterator = ports.entrySet().iterator();
tom29df6f42014-09-05 08:14:14 -0700290 while (iterator.hasNext()) {
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700291 Entry<PortNumber, Port> e = iterator.next();
292 PortNumber portNumber = e.getKey();
tom24c55cd2014-09-06 10:47:25 -0700293 if (!processed.contains(portNumber)) {
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700294 events.add(new DeviceEvent(PORT_REMOVED, device, e.getValue()));
tom29df6f42014-09-05 08:14:14 -0700295 iterator.remove();
296 }
297 }
298 return events;
299 }
300
301 // Gets the map of ports for the specified device; if one does not already
302 // exist, it creates and registers a new one.
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700303 private ConcurrentMap<PortNumber, Port> getPortMap(DeviceId deviceId) {
304 return createIfAbsentUnchecked(devicePorts, deviceId,
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700305 NewConcurrentHashMap.<PortNumber, Port>ifNeeded());
tome5ec3fd2014-09-04 15:18:06 -0700306 }
307
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700308 private Map<ProviderId, DeviceDescriptions> getOrCreateDeviceDescriptions(
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700309 DeviceId deviceId) {
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700310 Map<ProviderId, DeviceDescriptions> r;
311 r = deviceDescs.get(deviceId);
312 if (r != null) {
313 return r;
314 }
315 r = new HashMap<>();
316 final Map<ProviderId, DeviceDescriptions> concurrentlyAdded;
317 concurrentlyAdded = deviceDescs.putIfAbsent(deviceId, r);
318 if (concurrentlyAdded != null) {
319 return concurrentlyAdded;
320 } else {
321 return r;
322 }
323 }
324
325 // Guarded by deviceDescs value (=Device lock)
326 private DeviceDescriptions getOrCreateProviderDeviceDescriptions(
327 Map<ProviderId, DeviceDescriptions> device,
328 ProviderId providerId, DeviceDescription deltaDesc) {
329
330 synchronized (device) {
331 DeviceDescriptions r = device.get(providerId);
332 if (r == null) {
333 r = new DeviceDescriptions(deltaDesc);
334 device.put(providerId, r);
335 }
336 return r;
337 }
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700338 }
339
tom41a2c5f2014-09-19 09:20:35 -0700340 @Override
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700341 public DeviceEvent updatePortStatus(ProviderId providerId, DeviceId deviceId,
tome5ec3fd2014-09-04 15:18:06 -0700342 PortDescription portDescription) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700343 Device device = devices.get(deviceId);
344 checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
345
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700346 Map<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700347 checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId);
348
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700349 synchronized (descsMap) {
350 DeviceDescriptions descs = descsMap.get(providerId);
Yuta HIGUCHIdd841b72014-10-16 13:46:09 -0700351 // assuming all providers must give DeviceDescription first
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700352 checkArgument(descs != null,
353 "Device description for Device ID %s from Provider %s was not found",
354 deviceId, providerId);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700355
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700356 ConcurrentMap<PortNumber, Port> ports = getPortMap(deviceId);
357 final PortNumber number = portDescription.portNumber();
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700358 final Port oldPort = ports.get(number);
359 final Port newPort;
360
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700361 // update description
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700362 descs.putPortDesc(portDescription);
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700363 newPort = composePort(device, number, descsMap);
364
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700365 if (oldPort == null) {
366 return createPort(device, newPort, ports);
367 } else {
368 return updatePort(device, oldPort, newPort, ports);
369 }
tom46a220d2014-09-05 08:25:56 -0700370 }
tome5ec3fd2014-09-04 15:18:06 -0700371 }
372
tom41a2c5f2014-09-19 09:20:35 -0700373 @Override
374 public List<Port> getPorts(DeviceId deviceId) {
tom46a220d2014-09-05 08:25:56 -0700375 Map<PortNumber, Port> ports = devicePorts.get(deviceId);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700376 if (ports == null) {
377 return Collections.emptyList();
378 }
379 return ImmutableList.copyOf(ports.values());
tome5ec3fd2014-09-04 15:18:06 -0700380 }
381
tom41a2c5f2014-09-19 09:20:35 -0700382 @Override
383 public Port getPort(DeviceId deviceId, PortNumber portNumber) {
tom46a220d2014-09-05 08:25:56 -0700384 Map<PortNumber, Port> ports = devicePorts.get(deviceId);
385 return ports == null ? null : ports.get(portNumber);
tome5ec3fd2014-09-04 15:18:06 -0700386 }
387
tom41a2c5f2014-09-19 09:20:35 -0700388 @Override
389 public boolean isAvailable(DeviceId deviceId) {
tomff7eb7c2014-09-08 12:49:03 -0700390 return availableDevices.contains(deviceId);
391 }
392
tom41a2c5f2014-09-19 09:20:35 -0700393 @Override
tom41a2c5f2014-09-19 09:20:35 -0700394 public DeviceEvent removeDevice(DeviceId deviceId) {
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700395 Map<ProviderId, DeviceDescriptions> descs = getOrCreateDeviceDescriptions(deviceId);
Yuta HIGUCHI0d6a5e62014-10-03 15:54:09 -0700396 synchronized (descs) {
tome5ec3fd2014-09-04 15:18:06 -0700397 Device device = devices.remove(deviceId);
Yuta HIGUCHI0d6a5e62014-10-03 15:54:09 -0700398 // should DEVICE_REMOVED carry removed ports?
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700399 Map<PortNumber, Port> ports = devicePorts.get(deviceId);
Yuta HIGUCHI0d6a5e62014-10-03 15:54:09 -0700400 if (ports != null) {
401 ports.clear();
402 }
403 availableDevices.remove(deviceId);
404 descs.clear();
tom29df6f42014-09-05 08:14:14 -0700405 return device == null ? null :
Yuta HIGUCHI0d6a5e62014-10-03 15:54:09 -0700406 new DeviceEvent(DEVICE_REMOVED, device, null);
tome5ec3fd2014-09-04 15:18:06 -0700407 }
408 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700409
410 /**
411 * Returns a Device, merging description given from multiple Providers.
412 *
413 * @param deviceId device identifier
414 * @param providerDescs Collection of Descriptions from multiple providers
415 * @return Device instance
416 */
417 private Device composeDevice(DeviceId deviceId,
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700418 Map<ProviderId, DeviceDescriptions> providerDescs) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700419
420 checkArgument(!providerDescs.isEmpty(), "No Device descriptions supplied");
421
422 ProviderId primary = pickPrimaryPID(providerDescs);
423
424 DeviceDescriptions desc = providerDescs.get(primary);
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700425
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700426 final DeviceDescription base = desc.getDeviceDesc();
427 Type type = base.type();
428 String manufacturer = base.manufacturer();
429 String hwVersion = base.hwVersion();
430 String swVersion = base.swVersion();
431 String serialNumber = base.serialNumber();
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700432 DefaultAnnotations annotations = DefaultAnnotations.builder().build();
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700433 annotations = merge(annotations, base.annotations());
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700434
435 for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) {
436 if (e.getKey().equals(primary)) {
437 continue;
438 }
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700439 // TODO: should keep track of Description timestamp
440 // and only merge conflicting keys when timestamp is newer
441 // Currently assuming there will never be a key conflict between
442 // providers
443
444 // annotation merging. not so efficient, should revisit later
445 annotations = merge(annotations, e.getValue().getDeviceDesc().annotations());
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700446 }
447
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700448 return new DefaultDevice(primary, deviceId , type, manufacturer,
449 hwVersion, swVersion, serialNumber, annotations);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700450 }
451
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700452 /**
453 * Returns a Port, merging description given from multiple Providers.
454 *
455 * @param device device the port is on
456 * @param number port number
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700457 * @param descsMap Collection of Descriptions from multiple providers
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700458 * @return Port instance
459 */
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700460 private Port composePort(Device device, PortNumber number,
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700461 Map<ProviderId, DeviceDescriptions> descsMap) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700462
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700463 ProviderId primary = pickPrimaryPID(descsMap);
464 DeviceDescriptions primDescs = descsMap.get(primary);
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700465 // if no primary, assume not enabled
466 // TODO: revisit this default port enabled/disabled behavior
467 boolean isEnabled = false;
468 DefaultAnnotations annotations = DefaultAnnotations.builder().build();
469
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700470 final PortDescription portDesc = primDescs.getPortDesc(number);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700471 if (portDesc != null) {
472 isEnabled = portDesc.isEnabled();
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700473 annotations = merge(annotations, portDesc.annotations());
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700474 }
475
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700476 for (Entry<ProviderId, DeviceDescriptions> e : descsMap.entrySet()) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700477 if (e.getKey().equals(primary)) {
478 continue;
479 }
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700480 // TODO: should keep track of Description timestamp
481 // and only merge conflicting keys when timestamp is newer
482 // Currently assuming there will never be a key conflict between
483 // providers
484
485 // annotation merging. not so efficient, should revisit later
486 final PortDescription otherPortDesc = e.getValue().getPortDesc(number);
487 if (otherPortDesc != null) {
488 annotations = merge(annotations, otherPortDesc.annotations());
489 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700490 }
491
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700492 return new DefaultPort(device, number, isEnabled, annotations);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700493 }
494
495 /**
496 * @return primary ProviderID, or randomly chosen one if none exists
497 */
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700498 private ProviderId pickPrimaryPID(Map<ProviderId, DeviceDescriptions> descsMap) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700499 ProviderId fallBackPrimary = null;
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700500 for (Entry<ProviderId, DeviceDescriptions> e : descsMap.entrySet()) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700501 if (!e.getKey().isAncillary()) {
502 return e.getKey();
503 } else if (fallBackPrimary == null) {
504 // pick randomly as a fallback in case there is no primary
505 fallBackPrimary = e.getKey();
506 }
507 }
508 return fallBackPrimary;
509 }
510
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700511 /**
512 * Collection of Description of a Device and it's Ports given from a Provider.
513 */
514 private static class DeviceDescriptions {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700515
516 private final AtomicReference<DeviceDescription> deviceDesc;
517 private final ConcurrentMap<PortNumber, PortDescription> portDescs;
518
519 public DeviceDescriptions(DeviceDescription desc) {
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700520 this.deviceDesc = new AtomicReference<>(checkNotNull(desc));
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700521 this.portDescs = new ConcurrentHashMap<>();
522 }
523
524 public DeviceDescription getDeviceDesc() {
525 return deviceDesc.get();
526 }
527
528 public PortDescription getPortDesc(PortNumber number) {
529 return portDescs.get(number);
530 }
531
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700532 /**
533 * Puts DeviceDescription, merging annotations as necessary.
534 *
535 * @param newDesc new DeviceDescription
536 * @return previous DeviceDescription
537 */
538 public synchronized DeviceDescription putDeviceDesc(DeviceDescription newDesc) {
539 DeviceDescription oldOne = deviceDesc.get();
540 DeviceDescription newOne = newDesc;
541 if (oldOne != null) {
Yuta HIGUCHI885be1d2014-10-04 21:47:26 -0700542 SparseAnnotations merged = union(oldOne.annotations(),
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700543 newDesc.annotations());
544 newOne = new DefaultDeviceDescription(newOne, merged);
545 }
546 return deviceDesc.getAndSet(newOne);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700547 }
548
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700549 /**
550 * Puts PortDescription, merging annotations as necessary.
551 *
552 * @param newDesc new PortDescription
553 * @return previous PortDescription
554 */
555 public synchronized PortDescription putPortDesc(PortDescription newDesc) {
556 PortDescription oldOne = portDescs.get(newDesc.portNumber());
557 PortDescription newOne = newDesc;
558 if (oldOne != null) {
Yuta HIGUCHI885be1d2014-10-04 21:47:26 -0700559 SparseAnnotations merged = union(oldOne.annotations(),
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700560 newDesc.annotations());
561 newOne = new DefaultPortDescription(newOne, merged);
562 }
563 return portDescs.put(newOne.portNumber(), newOne);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700564 }
565 }
tome5ec3fd2014-09-04 15:18:06 -0700566}