blob: 514a22e89c3e2574ab729e922a64facf60a2ee3d [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
Yuta HIGUCHI8e493792014-10-01 19:36:32 -07008import org.apache.commons.lang3.concurrent.ConcurrentException;
9import org.apache.commons.lang3.concurrent.ConcurrentInitializer;
tom41a2c5f2014-09-19 09:20:35 -070010import org.apache.felix.scr.annotations.Activate;
11import org.apache.felix.scr.annotations.Component;
12import org.apache.felix.scr.annotations.Deactivate;
13import org.apache.felix.scr.annotations.Service;
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070014import org.onlab.onos.net.AnnotationsUtil;
Yuta HIGUCHI55710e72014-10-02 14:58:32 -070015import org.onlab.onos.net.DefaultAnnotations;
tome5ec3fd2014-09-04 15:18:06 -070016import org.onlab.onos.net.DefaultDevice;
tom29df6f42014-09-05 08:14:14 -070017import org.onlab.onos.net.DefaultPort;
tome5ec3fd2014-09-04 15:18:06 -070018import org.onlab.onos.net.Device;
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070019import org.onlab.onos.net.Device.Type;
tome5ec3fd2014-09-04 15:18:06 -070020import org.onlab.onos.net.DeviceId;
tome5ec3fd2014-09-04 15:18:06 -070021import org.onlab.onos.net.Port;
22import org.onlab.onos.net.PortNumber;
Yuta HIGUCHI55710e72014-10-02 14:58:32 -070023import org.onlab.onos.net.SparseAnnotations;
24import org.onlab.onos.net.device.DefaultDeviceDescription;
25import org.onlab.onos.net.device.DefaultPortDescription;
tome5ec3fd2014-09-04 15:18:06 -070026import org.onlab.onos.net.device.DeviceDescription;
27import org.onlab.onos.net.device.DeviceEvent;
tom41a2c5f2014-09-19 09:20:35 -070028import org.onlab.onos.net.device.DeviceStore;
tomf80c9722014-09-24 14:49:18 -070029import org.onlab.onos.net.device.DeviceStoreDelegate;
tome5ec3fd2014-09-04 15:18:06 -070030import org.onlab.onos.net.device.PortDescription;
31import org.onlab.onos.net.provider.ProviderId;
tomf80c9722014-09-24 14:49:18 -070032import org.onlab.onos.store.AbstractStore;
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070033import org.onlab.util.NewConcurrentHashMap;
tom41a2c5f2014-09-19 09:20:35 -070034import org.slf4j.Logger;
tome5ec3fd2014-09-04 15:18:06 -070035
36import java.util.ArrayList;
37import java.util.Collections;
tome5ec3fd2014-09-04 15:18:06 -070038import java.util.HashSet;
tom29df6f42014-09-05 08:14:14 -070039import java.util.Iterator;
tome5ec3fd2014-09-04 15:18:06 -070040import java.util.List;
41import java.util.Map;
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070042import java.util.Map.Entry;
tome5ec3fd2014-09-04 15:18:06 -070043import java.util.Objects;
44import java.util.Set;
45import java.util.concurrent.ConcurrentHashMap;
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070046import java.util.concurrent.ConcurrentMap;
47import java.util.concurrent.atomic.AtomicReference;
tome5ec3fd2014-09-04 15:18:06 -070048
49import static com.google.common.base.Preconditions.checkArgument;
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070050import static com.google.common.base.Preconditions.checkNotNull;
Yuta HIGUCHIf5479702014-09-25 00:09:24 -070051import static com.google.common.base.Predicates.notNull;
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -070052import static com.google.common.base.Verify.verify;
tom29df6f42014-09-05 08:14:14 -070053import static org.onlab.onos.net.device.DeviceEvent.Type.*;
tom41a2c5f2014-09-19 09:20:35 -070054import static org.slf4j.LoggerFactory.getLogger;
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070055import static org.apache.commons.lang3.concurrent.ConcurrentUtils.createIfAbsentUnchecked;
Yuta HIGUCHI885be1d2014-10-04 21:47:26 -070056import static org.onlab.onos.net.DefaultAnnotations.union;
Yuta HIGUCHI55710e72014-10-02 14:58:32 -070057import static org.onlab.onos.net.DefaultAnnotations.merge;
tome5ec3fd2014-09-04 15:18:06 -070058
59/**
tome4729872014-09-23 00:37:37 -070060 * Manages inventory of infrastructure devices using trivial in-memory
tomcbff9392014-09-10 00:45:23 -070061 * structures implementation.
tome5ec3fd2014-09-04 15:18:06 -070062 */
tom41a2c5f2014-09-19 09:20:35 -070063@Component(immediate = true)
64@Service
tomf80c9722014-09-24 14:49:18 -070065public class SimpleDeviceStore
66 extends AbstractStore<DeviceEvent, DeviceStoreDelegate>
67 implements DeviceStore {
tom41a2c5f2014-09-19 09:20:35 -070068
69 private final Logger log = getLogger(getClass());
tome5ec3fd2014-09-04 15:18:06 -070070
tom29df6f42014-09-05 08:14:14 -070071 public static final String DEVICE_NOT_FOUND = "Device with ID %s not found";
72
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070073 // collection of Description given from various providers
74 private final ConcurrentMap<DeviceId,
75 ConcurrentMap<ProviderId, DeviceDescriptions>>
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -070076 deviceDescs = Maps.newConcurrentMap();
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070077
78 // cache of Device and Ports generated by compositing descriptions from providers
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -070079 private final ConcurrentMap<DeviceId, Device> devices = Maps.newConcurrentMap();
80 private final ConcurrentMap<DeviceId, ConcurrentMap<PortNumber, Port>> devicePorts = Maps.newConcurrentMap();
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070081
82 // available(=UP) devices
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -070083 private final Set<DeviceId> availableDevices = Sets.newConcurrentHashSet();
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070084
tome5ec3fd2014-09-04 15:18:06 -070085
tom41a2c5f2014-09-19 09:20:35 -070086 @Activate
87 public void activate() {
88 log.info("Started");
89 }
90
91 @Deactivate
92 public void deactivate() {
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -070093 deviceDescs.clear();
94 devices.clear();
95 devicePorts.clear();
96 availableDevices.clear();
tom41a2c5f2014-09-19 09:20:35 -070097 log.info("Stopped");
98 }
tom5bcc9462014-09-19 10:11:31 -070099
tom41a2c5f2014-09-19 09:20:35 -0700100 @Override
101 public int getDeviceCount() {
tomad2d2092014-09-06 23:24:20 -0700102 return devices.size();
103 }
104
tom41a2c5f2014-09-19 09:20:35 -0700105 @Override
106 public Iterable<Device> getDevices() {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700107 return Collections.unmodifiableCollection(devices.values());
tome5ec3fd2014-09-04 15:18:06 -0700108 }
109
tom41a2c5f2014-09-19 09:20:35 -0700110 @Override
111 public Device getDevice(DeviceId deviceId) {
tome5ec3fd2014-09-04 15:18:06 -0700112 return devices.get(deviceId);
113 }
114
tom41a2c5f2014-09-19 09:20:35 -0700115 @Override
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700116 public DeviceEvent createOrUpdateDevice(ProviderId providerId,
117 DeviceId deviceId,
tome5ec3fd2014-09-04 15:18:06 -0700118 DeviceDescription deviceDescription) {
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700119
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700120 ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700121 = getDeviceDescriptions(deviceId);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700122
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700123 synchronized (providerDescs) {
124 // locking per device
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700125
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700126 DeviceDescriptions descs
127 = createIfAbsentUnchecked(providerDescs, providerId,
128 new InitDeviceDescs(deviceDescription));
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700129
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700130 Device oldDevice = devices.get(deviceId);
131 // update description
132 descs.putDeviceDesc(deviceDescription);
133 Device newDevice = composeDevice(deviceId, providerDescs);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700134
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700135 if (oldDevice == null) {
136 // ADD
137 return createDevice(providerId, newDevice);
138 } else {
139 // UPDATE or ignore (no change or stale)
140 return updateDevice(providerId, oldDevice, newDevice);
141 }
tome5ec3fd2014-09-04 15:18:06 -0700142 }
tome5ec3fd2014-09-04 15:18:06 -0700143 }
144
145 // Creates the device and returns the appropriate event if necessary.
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700146 // Guarded by deviceDescs value (=Device lock)
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700147 private DeviceEvent createDevice(ProviderId providerId, Device newDevice) {
148
149 // update composed device cache
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700150 Device oldDevice = devices.putIfAbsent(newDevice.id(), newDevice);
151 verify(oldDevice == null,
152 "Unexpected Device in cache. PID:%s [old=%s, new=%s]",
153 providerId, oldDevice, newDevice);
154
155 if (!providerId.isAncillary()) {
156 availableDevices.add(newDevice.id());
tome5ec3fd2014-09-04 15:18:06 -0700157 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700158
159 return new DeviceEvent(DeviceEvent.Type.DEVICE_ADDED, newDevice, null);
tome5ec3fd2014-09-04 15:18:06 -0700160 }
161
162 // Updates the device and returns the appropriate event if necessary.
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700163 // Guarded by deviceDescs value (=Device lock)
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700164 private DeviceEvent updateDevice(ProviderId providerId, Device oldDevice, Device newDevice) {
165
tome5ec3fd2014-09-04 15:18:06 -0700166 // We allow only certain attributes to trigger update
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700167 if (!Objects.equals(oldDevice.hwVersion(), newDevice.hwVersion()) ||
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700168 !Objects.equals(oldDevice.swVersion(), newDevice.swVersion()) ||
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700169 !AnnotationsUtil.isEqual(oldDevice.annotations(), newDevice.annotations())) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700170
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700171 boolean replaced = devices.replace(newDevice.id(), oldDevice, newDevice);
172 if (!replaced) {
173 verify(replaced,
174 "Replacing devices cache failed. PID:%s [expected:%s, found:%s, new=%s]",
175 providerId, oldDevice, devices.get(newDevice.id())
176 , newDevice);
177 }
178 if (!providerId.isAncillary()) {
179 availableDevices.add(newDevice.id());
tome5ec3fd2014-09-04 15:18:06 -0700180 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700181 return new DeviceEvent(DeviceEvent.Type.DEVICE_UPDATED, newDevice, null);
tome5ec3fd2014-09-04 15:18:06 -0700182 }
183
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700184 // Otherwise merely attempt to change availability if primary provider
185 if (!providerId.isAncillary()) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700186 boolean added = availableDevices.add(newDevice.id());
tom29df6f42014-09-05 08:14:14 -0700187 return !added ? null :
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700188 new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, newDevice, null);
tome5ec3fd2014-09-04 15:18:06 -0700189 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700190 return null;
tome5ec3fd2014-09-04 15:18:06 -0700191 }
192
tom41a2c5f2014-09-19 09:20:35 -0700193 @Override
194 public DeviceEvent markOffline(DeviceId deviceId) {
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700195 ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs
196 = getDeviceDescriptions(deviceId);
197
198 // locking device
199 synchronized (providerDescs) {
tome5ec3fd2014-09-04 15:18:06 -0700200 Device device = devices.get(deviceId);
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700201 if (device == null) {
202 return null;
203 }
204 boolean removed = availableDevices.remove(deviceId);
205 if (removed) {
206 // TODO: broadcast ... DOWN only?
207 return new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, device, null);
208 }
209 return null;
tome5ec3fd2014-09-04 15:18:06 -0700210 }
211 }
212
tom41a2c5f2014-09-19 09:20:35 -0700213 @Override
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700214 public List<DeviceEvent> updatePorts(ProviderId providerId,
215 DeviceId deviceId,
216 List<PortDescription> portDescriptions) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700217
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700218 Device device = devices.get(deviceId);
219 checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
220
221 ConcurrentMap<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId);
222 checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId);
223
tom29df6f42014-09-05 08:14:14 -0700224 List<DeviceEvent> events = new ArrayList<>();
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700225 synchronized (descsMap) {
226 DeviceDescriptions descs = descsMap.get(providerId);
227 // every provider must provide DeviceDescription.
228 checkArgument(descs != null,
229 "Device description for Device ID %s from Provider %s was not found",
230 deviceId, providerId);
231
232 Map<PortNumber, Port> ports = getPortMap(deviceId);
tom29df6f42014-09-05 08:14:14 -0700233
234 // Add new ports
235 Set<PortNumber> processed = new HashSet<>();
236 for (PortDescription portDescription : portDescriptions) {
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700237 final PortNumber number = portDescription.portNumber();
238 processed.add(portDescription.portNumber());
239
240 final Port oldPort = ports.get(number);
241 final Port newPort;
242
243// event suppression hook?
244
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700245 // update description
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700246 descs.putPortDesc(portDescription);
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700247 newPort = composePort(device, number, descsMap);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700248
249 events.add(oldPort == null ?
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700250 createPort(device, newPort, ports) :
251 updatePort(device, oldPort, newPort, ports));
tom29df6f42014-09-05 08:14:14 -0700252 }
253
254 events.addAll(pruneOldPorts(device, ports, processed));
255 }
Yuta HIGUCHIf5479702014-09-25 00:09:24 -0700256 return FluentIterable.from(events).filter(notNull()).toList();
tom29df6f42014-09-05 08:14:14 -0700257 }
258
259 // Creates a new port based on the port description adds it to the map and
260 // Returns corresponding event.
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700261 // Guarded by deviceDescs value (=Device lock)
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700262 private DeviceEvent createPort(Device device, Port newPort,
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700263 Map<PortNumber, Port> ports) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700264 ports.put(newPort.number(), newPort);
265 return new DeviceEvent(PORT_ADDED, device, newPort);
tom29df6f42014-09-05 08:14:14 -0700266 }
267
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700268 // Checks if the specified port requires update and if so, it replaces the
tom29df6f42014-09-05 08:14:14 -0700269 // existing entry in the map and returns corresponding event.
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700270 // Guarded by deviceDescs value (=Device lock)
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700271 private DeviceEvent updatePort(Device device, Port oldPort,
272 Port newPort,
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700273 Map<PortNumber, Port> ports) {
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700274 if (oldPort.isEnabled() != newPort.isEnabled() ||
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700275 !AnnotationsUtil.isEqual(oldPort.annotations(), newPort.annotations())) {
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700276
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700277 ports.put(oldPort.number(), newPort);
278 return new DeviceEvent(PORT_UPDATED, device, newPort);
tom29df6f42014-09-05 08:14:14 -0700279 }
280 return null;
281 }
282
283 // Prunes the specified list of ports based on which ports are in the
284 // processed list and returns list of corresponding events.
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700285 // Guarded by deviceDescs value (=Device lock)
tom29df6f42014-09-05 08:14:14 -0700286 private List<DeviceEvent> pruneOldPorts(Device device,
287 Map<PortNumber, Port> ports,
288 Set<PortNumber> processed) {
289 List<DeviceEvent> events = new ArrayList<>();
290 Iterator<PortNumber> iterator = ports.keySet().iterator();
291 while (iterator.hasNext()) {
292 PortNumber portNumber = iterator.next();
tom24c55cd2014-09-06 10:47:25 -0700293 if (!processed.contains(portNumber)) {
tom29df6f42014-09-05 08:14:14 -0700294 events.add(new DeviceEvent(PORT_REMOVED, device,
295 ports.get(portNumber)));
296 iterator.remove();
297 }
298 }
299 return events;
300 }
301
302 // Gets the map of ports for the specified device; if one does not already
303 // exist, it creates and registers a new one.
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700304 private ConcurrentMap<PortNumber, Port> getPortMap(DeviceId deviceId) {
305 return createIfAbsentUnchecked(devicePorts, deviceId,
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700306 NewConcurrentHashMap.<PortNumber, Port>ifNeeded());
tome5ec3fd2014-09-04 15:18:06 -0700307 }
308
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700309 private ConcurrentMap<ProviderId, DeviceDescriptions> getDeviceDescriptions(
310 DeviceId deviceId) {
311 return createIfAbsentUnchecked(deviceDescs, deviceId,
312 NewConcurrentHashMap.<ProviderId, DeviceDescriptions>ifNeeded());
313 }
314
tom41a2c5f2014-09-19 09:20:35 -0700315 @Override
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700316 public DeviceEvent updatePortStatus(ProviderId providerId, DeviceId deviceId,
tome5ec3fd2014-09-04 15:18:06 -0700317 PortDescription portDescription) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700318 Device device = devices.get(deviceId);
319 checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
320
321 ConcurrentMap<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId);
322 checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId);
323
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700324 synchronized (descsMap) {
325 DeviceDescriptions descs = descsMap.get(providerId);
326 // assuming all providers must to give DeviceDescription
327 checkArgument(descs != null,
328 "Device description for Device ID %s from Provider %s was not found",
329 deviceId, providerId);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700330
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700331 ConcurrentMap<PortNumber, Port> ports = getPortMap(deviceId);
332 final PortNumber number = portDescription.portNumber();
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700333 final Port oldPort = ports.get(number);
334 final Port newPort;
335
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700336 // update description
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700337 descs.putPortDesc(portDescription);
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700338 newPort = composePort(device, number, descsMap);
339
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700340 if (oldPort == null) {
341 return createPort(device, newPort, ports);
342 } else {
343 return updatePort(device, oldPort, newPort, ports);
344 }
tom46a220d2014-09-05 08:25:56 -0700345 }
tome5ec3fd2014-09-04 15:18:06 -0700346 }
347
tom41a2c5f2014-09-19 09:20:35 -0700348 @Override
349 public List<Port> getPorts(DeviceId deviceId) {
tom46a220d2014-09-05 08:25:56 -0700350 Map<PortNumber, Port> ports = devicePorts.get(deviceId);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700351 if (ports == null) {
352 return Collections.emptyList();
353 }
354 return ImmutableList.copyOf(ports.values());
tome5ec3fd2014-09-04 15:18:06 -0700355 }
356
tom41a2c5f2014-09-19 09:20:35 -0700357 @Override
358 public Port getPort(DeviceId deviceId, PortNumber portNumber) {
tom46a220d2014-09-05 08:25:56 -0700359 Map<PortNumber, Port> ports = devicePorts.get(deviceId);
360 return ports == null ? null : ports.get(portNumber);
tome5ec3fd2014-09-04 15:18:06 -0700361 }
362
tom41a2c5f2014-09-19 09:20:35 -0700363 @Override
364 public boolean isAvailable(DeviceId deviceId) {
tomff7eb7c2014-09-08 12:49:03 -0700365 return availableDevices.contains(deviceId);
366 }
367
tom41a2c5f2014-09-19 09:20:35 -0700368 @Override
tom41a2c5f2014-09-19 09:20:35 -0700369 public DeviceEvent removeDevice(DeviceId deviceId) {
Yuta HIGUCHI0d6a5e62014-10-03 15:54:09 -0700370 ConcurrentMap<ProviderId, DeviceDescriptions> descs = getDeviceDescriptions(deviceId);
371 synchronized (descs) {
tome5ec3fd2014-09-04 15:18:06 -0700372 Device device = devices.remove(deviceId);
Yuta HIGUCHI0d6a5e62014-10-03 15:54:09 -0700373 // should DEVICE_REMOVED carry removed ports?
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700374 Map<PortNumber, Port> ports = devicePorts.get(deviceId);
Yuta HIGUCHI0d6a5e62014-10-03 15:54:09 -0700375 if (ports != null) {
376 ports.clear();
377 }
378 availableDevices.remove(deviceId);
379 descs.clear();
tom29df6f42014-09-05 08:14:14 -0700380 return device == null ? null :
Yuta HIGUCHI0d6a5e62014-10-03 15:54:09 -0700381 new DeviceEvent(DEVICE_REMOVED, device, null);
tome5ec3fd2014-09-04 15:18:06 -0700382 }
383 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700384
385 /**
386 * Returns a Device, merging description given from multiple Providers.
387 *
388 * @param deviceId device identifier
389 * @param providerDescs Collection of Descriptions from multiple providers
390 * @return Device instance
391 */
392 private Device composeDevice(DeviceId deviceId,
393 ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs) {
394
395 checkArgument(!providerDescs.isEmpty(), "No Device descriptions supplied");
396
397 ProviderId primary = pickPrimaryPID(providerDescs);
398
399 DeviceDescriptions desc = providerDescs.get(primary);
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700400
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700401 final DeviceDescription base = desc.getDeviceDesc();
402 Type type = base.type();
403 String manufacturer = base.manufacturer();
404 String hwVersion = base.hwVersion();
405 String swVersion = base.swVersion();
406 String serialNumber = base.serialNumber();
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700407 DefaultAnnotations annotations = DefaultAnnotations.builder().build();
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700408 annotations = merge(annotations, base.annotations());
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700409
410 for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) {
411 if (e.getKey().equals(primary)) {
412 continue;
413 }
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700414 // TODO: should keep track of Description timestamp
415 // and only merge conflicting keys when timestamp is newer
416 // Currently assuming there will never be a key conflict between
417 // providers
418
419 // annotation merging. not so efficient, should revisit later
420 annotations = merge(annotations, e.getValue().getDeviceDesc().annotations());
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700421 }
422
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700423 return new DefaultDevice(primary, deviceId , type, manufacturer,
424 hwVersion, swVersion, serialNumber, annotations);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700425 }
426
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700427 /**
428 * Returns a Port, merging description given from multiple Providers.
429 *
430 * @param device device the port is on
431 * @param number port number
432 * @param providerDescs Collection of Descriptions from multiple providers
433 * @return Port instance
434 */
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700435 private Port composePort(Device device, PortNumber number,
436 ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs) {
437
438 ProviderId primary = pickPrimaryPID(providerDescs);
439 DeviceDescriptions primDescs = providerDescs.get(primary);
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700440 // if no primary, assume not enabled
441 // TODO: revisit this default port enabled/disabled behavior
442 boolean isEnabled = false;
443 DefaultAnnotations annotations = DefaultAnnotations.builder().build();
444
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700445 final PortDescription portDesc = primDescs.getPortDesc(number);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700446 if (portDesc != null) {
447 isEnabled = portDesc.isEnabled();
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700448 annotations = merge(annotations, portDesc.annotations());
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700449 }
450
451 for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) {
452 if (e.getKey().equals(primary)) {
453 continue;
454 }
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700455 // TODO: should keep track of Description timestamp
456 // and only merge conflicting keys when timestamp is newer
457 // Currently assuming there will never be a key conflict between
458 // providers
459
460 // annotation merging. not so efficient, should revisit later
461 final PortDescription otherPortDesc = e.getValue().getPortDesc(number);
462 if (otherPortDesc != null) {
463 annotations = merge(annotations, otherPortDesc.annotations());
464 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700465 }
466
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700467 return new DefaultPort(device, number, isEnabled, annotations);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700468 }
469
470 /**
471 * @return primary ProviderID, or randomly chosen one if none exists
472 */
473 private ProviderId pickPrimaryPID(
474 ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs) {
475 ProviderId fallBackPrimary = null;
476 for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) {
477 if (!e.getKey().isAncillary()) {
478 return e.getKey();
479 } else if (fallBackPrimary == null) {
480 // pick randomly as a fallback in case there is no primary
481 fallBackPrimary = e.getKey();
482 }
483 }
484 return fallBackPrimary;
485 }
486
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700487 public static final class InitDeviceDescs
488 implements ConcurrentInitializer<DeviceDescriptions> {
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700489
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700490 private final DeviceDescription deviceDesc;
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700491
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700492 public InitDeviceDescs(DeviceDescription deviceDesc) {
493 this.deviceDesc = checkNotNull(deviceDesc);
494 }
495 @Override
496 public DeviceDescriptions get() throws ConcurrentException {
497 return new DeviceDescriptions(deviceDesc);
498 }
499 }
500
501
502 /**
503 * Collection of Description of a Device and it's Ports given from a Provider.
504 */
505 private static class DeviceDescriptions {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700506
507 private final AtomicReference<DeviceDescription> deviceDesc;
508 private final ConcurrentMap<PortNumber, PortDescription> portDescs;
509
510 public DeviceDescriptions(DeviceDescription desc) {
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700511 this.deviceDesc = new AtomicReference<>(checkNotNull(desc));
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700512 this.portDescs = new ConcurrentHashMap<>();
513 }
514
515 public DeviceDescription getDeviceDesc() {
516 return deviceDesc.get();
517 }
518
519 public PortDescription getPortDesc(PortNumber number) {
520 return portDescs.get(number);
521 }
522
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700523 /**
524 * Puts DeviceDescription, merging annotations as necessary.
525 *
526 * @param newDesc new DeviceDescription
527 * @return previous DeviceDescription
528 */
529 public synchronized DeviceDescription putDeviceDesc(DeviceDescription newDesc) {
530 DeviceDescription oldOne = deviceDesc.get();
531 DeviceDescription newOne = newDesc;
532 if (oldOne != null) {
Yuta HIGUCHI885be1d2014-10-04 21:47:26 -0700533 SparseAnnotations merged = union(oldOne.annotations(),
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700534 newDesc.annotations());
535 newOne = new DefaultDeviceDescription(newOne, merged);
536 }
537 return deviceDesc.getAndSet(newOne);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700538 }
539
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700540 /**
541 * Puts PortDescription, merging annotations as necessary.
542 *
543 * @param newDesc new PortDescription
544 * @return previous PortDescription
545 */
546 public synchronized PortDescription putPortDesc(PortDescription newDesc) {
547 PortDescription oldOne = portDescs.get(newDesc.portNumber());
548 PortDescription newOne = newDesc;
549 if (oldOne != null) {
Yuta HIGUCHI885be1d2014-10-04 21:47:26 -0700550 SparseAnnotations merged = union(oldOne.annotations(),
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700551 newDesc.annotations());
552 newOne = new DefaultPortDescription(newOne, merged);
553 }
554 return portDescs.put(newOne.portNumber(), newOne);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700555 }
556 }
tome5ec3fd2014-09-04 15:18:06 -0700557}