blob: 3fefbfd3af774be469ea6d20901014bfe25240e1 [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;
alshabib7911a052014-10-16 17:49:37 -070033import org.onlab.packet.ChassisId;
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070034import org.onlab.util.NewConcurrentHashMap;
tom41a2c5f2014-09-19 09:20:35 -070035import org.slf4j.Logger;
tome5ec3fd2014-09-04 15:18:06 -070036
37import java.util.ArrayList;
38import java.util.Collections;
tome5ec3fd2014-09-04 15:18:06 -070039import java.util.HashSet;
tom29df6f42014-09-05 08:14:14 -070040import java.util.Iterator;
tome5ec3fd2014-09-04 15:18:06 -070041import java.util.List;
42import java.util.Map;
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070043import java.util.Map.Entry;
tome5ec3fd2014-09-04 15:18:06 -070044import java.util.Objects;
45import java.util.Set;
46import java.util.concurrent.ConcurrentHashMap;
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070047import java.util.concurrent.ConcurrentMap;
48import java.util.concurrent.atomic.AtomicReference;
tome5ec3fd2014-09-04 15:18:06 -070049
50import static com.google.common.base.Preconditions.checkArgument;
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070051import static com.google.common.base.Preconditions.checkNotNull;
Yuta HIGUCHIf5479702014-09-25 00:09:24 -070052import static com.google.common.base.Predicates.notNull;
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -070053import static com.google.common.base.Verify.verify;
tom29df6f42014-09-05 08:14:14 -070054import static org.onlab.onos.net.device.DeviceEvent.Type.*;
tom41a2c5f2014-09-19 09:20:35 -070055import static org.slf4j.LoggerFactory.getLogger;
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070056import static org.apache.commons.lang3.concurrent.ConcurrentUtils.createIfAbsentUnchecked;
Yuta HIGUCHI885be1d2014-10-04 21:47:26 -070057import static org.onlab.onos.net.DefaultAnnotations.union;
Yuta HIGUCHI55710e72014-10-02 14:58:32 -070058import static org.onlab.onos.net.DefaultAnnotations.merge;
tome5ec3fd2014-09-04 15:18:06 -070059
60/**
tome4729872014-09-23 00:37:37 -070061 * Manages inventory of infrastructure devices using trivial in-memory
tomcbff9392014-09-10 00:45:23 -070062 * structures implementation.
tome5ec3fd2014-09-04 15:18:06 -070063 */
tom41a2c5f2014-09-19 09:20:35 -070064@Component(immediate = true)
65@Service
tomf80c9722014-09-24 14:49:18 -070066public class SimpleDeviceStore
67 extends AbstractStore<DeviceEvent, DeviceStoreDelegate>
68 implements DeviceStore {
tom41a2c5f2014-09-19 09:20:35 -070069
70 private final Logger log = getLogger(getClass());
tome5ec3fd2014-09-04 15:18:06 -070071
tom29df6f42014-09-05 08:14:14 -070072 public static final String DEVICE_NOT_FOUND = "Device with ID %s not found";
73
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070074 // collection of Description given from various providers
75 private final ConcurrentMap<DeviceId,
76 ConcurrentMap<ProviderId, DeviceDescriptions>>
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -070077 deviceDescs = Maps.newConcurrentMap();
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070078
79 // cache of Device and Ports generated by compositing descriptions from providers
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -070080 private final ConcurrentMap<DeviceId, Device> devices = Maps.newConcurrentMap();
81 private final ConcurrentMap<DeviceId, ConcurrentMap<PortNumber, Port>> devicePorts = Maps.newConcurrentMap();
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070082
83 // available(=UP) devices
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -070084 private final Set<DeviceId> availableDevices = Sets.newConcurrentHashSet();
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070085
tome5ec3fd2014-09-04 15:18:06 -070086
tom41a2c5f2014-09-19 09:20:35 -070087 @Activate
88 public void activate() {
89 log.info("Started");
90 }
91
92 @Deactivate
93 public void deactivate() {
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -070094 deviceDescs.clear();
95 devices.clear();
96 devicePorts.clear();
97 availableDevices.clear();
tom41a2c5f2014-09-19 09:20:35 -070098 log.info("Stopped");
99 }
tom5bcc9462014-09-19 10:11:31 -0700100
tom41a2c5f2014-09-19 09:20:35 -0700101 @Override
102 public int getDeviceCount() {
tomad2d2092014-09-06 23:24:20 -0700103 return devices.size();
104 }
105
tom41a2c5f2014-09-19 09:20:35 -0700106 @Override
107 public Iterable<Device> getDevices() {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700108 return Collections.unmodifiableCollection(devices.values());
tome5ec3fd2014-09-04 15:18:06 -0700109 }
110
tom41a2c5f2014-09-19 09:20:35 -0700111 @Override
112 public Device getDevice(DeviceId deviceId) {
tome5ec3fd2014-09-04 15:18:06 -0700113 return devices.get(deviceId);
114 }
115
tom41a2c5f2014-09-19 09:20:35 -0700116 @Override
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700117 public DeviceEvent createOrUpdateDevice(ProviderId providerId,
118 DeviceId deviceId,
tome5ec3fd2014-09-04 15:18:06 -0700119 DeviceDescription deviceDescription) {
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700120
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700121 ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700122 = getDeviceDescriptions(deviceId);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700123
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700124 synchronized (providerDescs) {
125 // locking per device
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700126
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700127 DeviceDescriptions descs
128 = createIfAbsentUnchecked(providerDescs, providerId,
129 new InitDeviceDescs(deviceDescription));
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700130
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700131 Device oldDevice = devices.get(deviceId);
132 // update description
133 descs.putDeviceDesc(deviceDescription);
134 Device newDevice = composeDevice(deviceId, providerDescs);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700135
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700136 if (oldDevice == null) {
137 // ADD
138 return createDevice(providerId, newDevice);
139 } else {
140 // UPDATE or ignore (no change or stale)
141 return updateDevice(providerId, oldDevice, newDevice);
142 }
tome5ec3fd2014-09-04 15:18:06 -0700143 }
tome5ec3fd2014-09-04 15:18:06 -0700144 }
145
146 // Creates the device and returns the appropriate event if necessary.
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700147 // Guarded by deviceDescs value (=Device lock)
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700148 private DeviceEvent createDevice(ProviderId providerId, Device newDevice) {
149
150 // update composed device cache
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700151 Device oldDevice = devices.putIfAbsent(newDevice.id(), newDevice);
152 verify(oldDevice == null,
153 "Unexpected Device in cache. PID:%s [old=%s, new=%s]",
154 providerId, oldDevice, newDevice);
155
156 if (!providerId.isAncillary()) {
157 availableDevices.add(newDevice.id());
tome5ec3fd2014-09-04 15:18:06 -0700158 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700159
160 return new DeviceEvent(DeviceEvent.Type.DEVICE_ADDED, newDevice, null);
tome5ec3fd2014-09-04 15:18:06 -0700161 }
162
163 // Updates the device and returns the appropriate event if necessary.
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700164 // Guarded by deviceDescs value (=Device lock)
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700165 private DeviceEvent updateDevice(ProviderId providerId, Device oldDevice, Device newDevice) {
166
tome5ec3fd2014-09-04 15:18:06 -0700167 // We allow only certain attributes to trigger update
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700168 if (!Objects.equals(oldDevice.hwVersion(), newDevice.hwVersion()) ||
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700169 !Objects.equals(oldDevice.swVersion(), newDevice.swVersion()) ||
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700170 !AnnotationsUtil.isEqual(oldDevice.annotations(), newDevice.annotations())) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700171
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700172 boolean replaced = devices.replace(newDevice.id(), oldDevice, newDevice);
173 if (!replaced) {
174 verify(replaced,
175 "Replacing devices cache failed. PID:%s [expected:%s, found:%s, new=%s]",
176 providerId, oldDevice, devices.get(newDevice.id())
177 , newDevice);
178 }
179 if (!providerId.isAncillary()) {
180 availableDevices.add(newDevice.id());
tome5ec3fd2014-09-04 15:18:06 -0700181 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700182 return new DeviceEvent(DeviceEvent.Type.DEVICE_UPDATED, newDevice, null);
tome5ec3fd2014-09-04 15:18:06 -0700183 }
184
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700185 // Otherwise merely attempt to change availability if primary provider
186 if (!providerId.isAncillary()) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700187 boolean added = availableDevices.add(newDevice.id());
tom29df6f42014-09-05 08:14:14 -0700188 return !added ? null :
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700189 new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, newDevice, null);
tome5ec3fd2014-09-04 15:18:06 -0700190 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700191 return null;
tome5ec3fd2014-09-04 15:18:06 -0700192 }
193
tom41a2c5f2014-09-19 09:20:35 -0700194 @Override
195 public DeviceEvent markOffline(DeviceId deviceId) {
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700196 ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs
197 = getDeviceDescriptions(deviceId);
198
199 // locking device
200 synchronized (providerDescs) {
tome5ec3fd2014-09-04 15:18:06 -0700201 Device device = devices.get(deviceId);
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700202 if (device == null) {
203 return null;
204 }
205 boolean removed = availableDevices.remove(deviceId);
206 if (removed) {
207 // TODO: broadcast ... DOWN only?
208 return new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, device, null);
209 }
210 return null;
tome5ec3fd2014-09-04 15:18:06 -0700211 }
212 }
213
tom41a2c5f2014-09-19 09:20:35 -0700214 @Override
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700215 public List<DeviceEvent> updatePorts(ProviderId providerId,
216 DeviceId deviceId,
217 List<PortDescription> portDescriptions) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700218
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700219 Device device = devices.get(deviceId);
220 checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
221
222 ConcurrentMap<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId);
223 checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId);
224
tom29df6f42014-09-05 08:14:14 -0700225 List<DeviceEvent> events = new ArrayList<>();
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700226 synchronized (descsMap) {
227 DeviceDescriptions descs = descsMap.get(providerId);
228 // every provider must provide DeviceDescription.
229 checkArgument(descs != null,
230 "Device description for Device ID %s from Provider %s was not found",
231 deviceId, providerId);
232
233 Map<PortNumber, Port> ports = getPortMap(deviceId);
tom29df6f42014-09-05 08:14:14 -0700234
235 // Add new ports
236 Set<PortNumber> processed = new HashSet<>();
237 for (PortDescription portDescription : portDescriptions) {
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700238 final PortNumber number = portDescription.portNumber();
239 processed.add(portDescription.portNumber());
240
241 final Port oldPort = ports.get(number);
242 final Port newPort;
243
244// event suppression hook?
245
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700246 // update description
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700247 descs.putPortDesc(portDescription);
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700248 newPort = composePort(device, number, descsMap);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700249
250 events.add(oldPort == null ?
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700251 createPort(device, newPort, ports) :
252 updatePort(device, oldPort, newPort, ports));
tom29df6f42014-09-05 08:14:14 -0700253 }
254
255 events.addAll(pruneOldPorts(device, ports, processed));
256 }
Yuta HIGUCHIf5479702014-09-25 00:09:24 -0700257 return FluentIterable.from(events).filter(notNull()).toList();
tom29df6f42014-09-05 08:14:14 -0700258 }
259
260 // Creates a new port based on the port description adds it to the map and
261 // Returns corresponding event.
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700262 // Guarded by deviceDescs value (=Device lock)
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700263 private DeviceEvent createPort(Device device, Port newPort,
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700264 Map<PortNumber, Port> ports) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700265 ports.put(newPort.number(), newPort);
266 return new DeviceEvent(PORT_ADDED, device, newPort);
tom29df6f42014-09-05 08:14:14 -0700267 }
268
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700269 // Checks if the specified port requires update and if so, it replaces the
tom29df6f42014-09-05 08:14:14 -0700270 // existing entry in the map and returns corresponding event.
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700271 // Guarded by deviceDescs value (=Device lock)
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700272 private DeviceEvent updatePort(Device device, Port oldPort,
273 Port newPort,
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700274 Map<PortNumber, Port> ports) {
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700275 if (oldPort.isEnabled() != newPort.isEnabled() ||
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700276 !AnnotationsUtil.isEqual(oldPort.annotations(), newPort.annotations())) {
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700277
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700278 ports.put(oldPort.number(), newPort);
279 return new DeviceEvent(PORT_UPDATED, device, newPort);
tom29df6f42014-09-05 08:14:14 -0700280 }
281 return null;
282 }
283
284 // Prunes the specified list of ports based on which ports are in the
285 // processed list and returns list of corresponding events.
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700286 // Guarded by deviceDescs value (=Device lock)
tom29df6f42014-09-05 08:14:14 -0700287 private List<DeviceEvent> pruneOldPorts(Device device,
288 Map<PortNumber, Port> ports,
289 Set<PortNumber> processed) {
290 List<DeviceEvent> events = new ArrayList<>();
291 Iterator<PortNumber> iterator = ports.keySet().iterator();
292 while (iterator.hasNext()) {
293 PortNumber portNumber = iterator.next();
tom24c55cd2014-09-06 10:47:25 -0700294 if (!processed.contains(portNumber)) {
tom29df6f42014-09-05 08:14:14 -0700295 events.add(new DeviceEvent(PORT_REMOVED, device,
296 ports.get(portNumber)));
297 iterator.remove();
298 }
299 }
300 return events;
301 }
302
303 // Gets the map of ports for the specified device; if one does not already
304 // exist, it creates and registers a new one.
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700305 private ConcurrentMap<PortNumber, Port> getPortMap(DeviceId deviceId) {
306 return createIfAbsentUnchecked(devicePorts, deviceId,
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -0700307 NewConcurrentHashMap.<PortNumber, Port>ifNeeded());
tome5ec3fd2014-09-04 15:18:06 -0700308 }
309
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700310 private ConcurrentMap<ProviderId, DeviceDescriptions> getDeviceDescriptions(
311 DeviceId deviceId) {
312 return createIfAbsentUnchecked(deviceDescs, deviceId,
313 NewConcurrentHashMap.<ProviderId, DeviceDescriptions>ifNeeded());
314 }
315
tom41a2c5f2014-09-19 09:20:35 -0700316 @Override
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700317 public DeviceEvent updatePortStatus(ProviderId providerId, DeviceId deviceId,
tome5ec3fd2014-09-04 15:18:06 -0700318 PortDescription portDescription) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700319 Device device = devices.get(deviceId);
320 checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
321
322 ConcurrentMap<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId);
323 checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId);
324
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700325 synchronized (descsMap) {
326 DeviceDescriptions descs = descsMap.get(providerId);
327 // assuming all providers must to give DeviceDescription
328 checkArgument(descs != null,
329 "Device description for Device ID %s from Provider %s was not found",
330 deviceId, providerId);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700331
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700332 ConcurrentMap<PortNumber, Port> ports = getPortMap(deviceId);
333 final PortNumber number = portDescription.portNumber();
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700334 final Port oldPort = ports.get(number);
335 final Port newPort;
336
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700337 // update description
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700338 descs.putPortDesc(portDescription);
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700339 newPort = composePort(device, number, descsMap);
340
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700341 if (oldPort == null) {
342 return createPort(device, newPort, ports);
343 } else {
344 return updatePort(device, oldPort, newPort, ports);
345 }
tom46a220d2014-09-05 08:25:56 -0700346 }
tome5ec3fd2014-09-04 15:18:06 -0700347 }
348
tom41a2c5f2014-09-19 09:20:35 -0700349 @Override
350 public List<Port> getPorts(DeviceId deviceId) {
tom46a220d2014-09-05 08:25:56 -0700351 Map<PortNumber, Port> ports = devicePorts.get(deviceId);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700352 if (ports == null) {
353 return Collections.emptyList();
354 }
355 return ImmutableList.copyOf(ports.values());
tome5ec3fd2014-09-04 15:18:06 -0700356 }
357
tom41a2c5f2014-09-19 09:20:35 -0700358 @Override
359 public Port getPort(DeviceId deviceId, PortNumber portNumber) {
tom46a220d2014-09-05 08:25:56 -0700360 Map<PortNumber, Port> ports = devicePorts.get(deviceId);
361 return ports == null ? null : ports.get(portNumber);
tome5ec3fd2014-09-04 15:18:06 -0700362 }
363
tom41a2c5f2014-09-19 09:20:35 -0700364 @Override
365 public boolean isAvailable(DeviceId deviceId) {
tomff7eb7c2014-09-08 12:49:03 -0700366 return availableDevices.contains(deviceId);
367 }
368
tom41a2c5f2014-09-19 09:20:35 -0700369 @Override
tom41a2c5f2014-09-19 09:20:35 -0700370 public DeviceEvent removeDevice(DeviceId deviceId) {
Yuta HIGUCHI0d6a5e62014-10-03 15:54:09 -0700371 ConcurrentMap<ProviderId, DeviceDescriptions> descs = getDeviceDescriptions(deviceId);
372 synchronized (descs) {
tome5ec3fd2014-09-04 15:18:06 -0700373 Device device = devices.remove(deviceId);
Yuta HIGUCHI0d6a5e62014-10-03 15:54:09 -0700374 // should DEVICE_REMOVED carry removed ports?
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700375 Map<PortNumber, Port> ports = devicePorts.get(deviceId);
Yuta HIGUCHI0d6a5e62014-10-03 15:54:09 -0700376 if (ports != null) {
377 ports.clear();
378 }
379 availableDevices.remove(deviceId);
380 descs.clear();
tom29df6f42014-09-05 08:14:14 -0700381 return device == null ? null :
Yuta HIGUCHI0d6a5e62014-10-03 15:54:09 -0700382 new DeviceEvent(DEVICE_REMOVED, device, null);
tome5ec3fd2014-09-04 15:18:06 -0700383 }
384 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700385
386 /**
387 * Returns a Device, merging description given from multiple Providers.
388 *
389 * @param deviceId device identifier
390 * @param providerDescs Collection of Descriptions from multiple providers
391 * @return Device instance
392 */
393 private Device composeDevice(DeviceId deviceId,
394 ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs) {
395
396 checkArgument(!providerDescs.isEmpty(), "No Device descriptions supplied");
397
398 ProviderId primary = pickPrimaryPID(providerDescs);
399
400 DeviceDescriptions desc = providerDescs.get(primary);
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700401
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700402 final DeviceDescription base = desc.getDeviceDesc();
403 Type type = base.type();
404 String manufacturer = base.manufacturer();
405 String hwVersion = base.hwVersion();
406 String swVersion = base.swVersion();
407 String serialNumber = base.serialNumber();
alshabib7911a052014-10-16 17:49:37 -0700408 ChassisId chassisId = base.chassisId();
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700409 DefaultAnnotations annotations = DefaultAnnotations.builder().build();
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700410 annotations = merge(annotations, base.annotations());
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700411
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 annotations = merge(annotations, e.getValue().getDeviceDesc().annotations());
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700423 }
424
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700425 return new DefaultDevice(primary, deviceId , type, manufacturer,
alshabib7911a052014-10-16 17:49:37 -0700426 hwVersion, swVersion, serialNumber,
427 chassisId, annotations);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700428 }
429
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700430 /**
431 * Returns a Port, merging description given from multiple Providers.
432 *
433 * @param device device the port is on
434 * @param number port number
435 * @param providerDescs Collection of Descriptions from multiple providers
436 * @return Port instance
437 */
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700438 private Port composePort(Device device, PortNumber number,
439 ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs) {
440
441 ProviderId primary = pickPrimaryPID(providerDescs);
442 DeviceDescriptions primDescs = providerDescs.get(primary);
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700443 // if no primary, assume not enabled
444 // TODO: revisit this default port enabled/disabled behavior
445 boolean isEnabled = false;
446 DefaultAnnotations annotations = DefaultAnnotations.builder().build();
447
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700448 final PortDescription portDesc = primDescs.getPortDesc(number);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700449 if (portDesc != null) {
450 isEnabled = portDesc.isEnabled();
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700451 annotations = merge(annotations, portDesc.annotations());
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700452 }
453
454 for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) {
455 if (e.getKey().equals(primary)) {
456 continue;
457 }
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700458 // TODO: should keep track of Description timestamp
459 // and only merge conflicting keys when timestamp is newer
460 // Currently assuming there will never be a key conflict between
461 // providers
462
463 // annotation merging. not so efficient, should revisit later
464 final PortDescription otherPortDesc = e.getValue().getPortDesc(number);
465 if (otherPortDesc != null) {
466 annotations = merge(annotations, otherPortDesc.annotations());
467 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700468 }
469
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700470 return new DefaultPort(device, number, isEnabled, annotations);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700471 }
472
473 /**
474 * @return primary ProviderID, or randomly chosen one if none exists
475 */
476 private ProviderId pickPrimaryPID(
477 ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs) {
478 ProviderId fallBackPrimary = null;
479 for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) {
480 if (!e.getKey().isAncillary()) {
481 return e.getKey();
482 } else if (fallBackPrimary == null) {
483 // pick randomly as a fallback in case there is no primary
484 fallBackPrimary = e.getKey();
485 }
486 }
487 return fallBackPrimary;
488 }
489
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700490 public static final class InitDeviceDescs
491 implements ConcurrentInitializer<DeviceDescriptions> {
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700492
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700493 private final DeviceDescription deviceDesc;
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700494
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700495 public InitDeviceDescs(DeviceDescription deviceDesc) {
496 this.deviceDesc = checkNotNull(deviceDesc);
497 }
498 @Override
499 public DeviceDescriptions get() throws ConcurrentException {
500 return new DeviceDescriptions(deviceDesc);
501 }
502 }
503
504
505 /**
506 * Collection of Description of a Device and it's Ports given from a Provider.
507 */
508 private static class DeviceDescriptions {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700509
510 private final AtomicReference<DeviceDescription> deviceDesc;
511 private final ConcurrentMap<PortNumber, PortDescription> portDescs;
512
513 public DeviceDescriptions(DeviceDescription desc) {
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700514 this.deviceDesc = new AtomicReference<>(checkNotNull(desc));
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700515 this.portDescs = new ConcurrentHashMap<>();
516 }
517
518 public DeviceDescription getDeviceDesc() {
519 return deviceDesc.get();
520 }
521
522 public PortDescription getPortDesc(PortNumber number) {
523 return portDescs.get(number);
524 }
525
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700526 /**
527 * Puts DeviceDescription, merging annotations as necessary.
528 *
529 * @param newDesc new DeviceDescription
530 * @return previous DeviceDescription
531 */
532 public synchronized DeviceDescription putDeviceDesc(DeviceDescription newDesc) {
533 DeviceDescription oldOne = deviceDesc.get();
534 DeviceDescription newOne = newDesc;
535 if (oldOne != null) {
Yuta HIGUCHI885be1d2014-10-04 21:47:26 -0700536 SparseAnnotations merged = union(oldOne.annotations(),
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700537 newDesc.annotations());
538 newOne = new DefaultDeviceDescription(newOne, merged);
539 }
540 return deviceDesc.getAndSet(newOne);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700541 }
542
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700543 /**
544 * Puts PortDescription, merging annotations as necessary.
545 *
546 * @param newDesc new PortDescription
547 * @return previous PortDescription
548 */
549 public synchronized PortDescription putPortDesc(PortDescription newDesc) {
550 PortDescription oldOne = portDescs.get(newDesc.portNumber());
551 PortDescription newOne = newDesc;
552 if (oldOne != null) {
Yuta HIGUCHI885be1d2014-10-04 21:47:26 -0700553 SparseAnnotations merged = union(oldOne.annotations(),
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700554 newDesc.annotations());
555 newOne = new DefaultPortDescription(newOne, merged);
556 }
557 return portDescs.put(newOne.portNumber(), newOne);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700558 }
559 }
tome5ec3fd2014-09-04 15:18:06 -0700560}