blob: c1be1f5a504b9492cacbf5b247f191d6c7f342d3 [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;
alshabib7911a052014-10-16 17:49:37 -070031import org.onlab.packet.ChassisId;
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070032import org.onlab.util.NewConcurrentHashMap;
tom41a2c5f2014-09-19 09:20:35 -070033import org.slf4j.Logger;
tome5ec3fd2014-09-04 15:18:06 -070034
35import java.util.ArrayList;
36import java.util.Collections;
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -070037import java.util.HashMap;
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
Thomas Vachuska56dbeb12014-10-22 16:40:44 -070073 // Collection of Description given from various providers
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -070074 private final ConcurrentMap<DeviceId, Map<ProviderId, DeviceDescriptions>>
Thomas Vachuska56dbeb12014-10-22 16:40:44 -070075 deviceDescs = Maps.newConcurrentMap();
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070076
Thomas Vachuska56dbeb12014-10-22 16:40:44 -070077 // Cache of Device and Ports generated by compositing descriptions from providers
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -070078 private final ConcurrentMap<DeviceId, Device> devices = Maps.newConcurrentMap();
Thomas Vachuska56dbeb12014-10-22 16:40:44 -070079 private final ConcurrentMap<DeviceId, ConcurrentMap<PortNumber, Port>>
80 devicePorts = Maps.newConcurrentMap();
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070081
Thomas Vachuska56dbeb12014-10-22 16:40:44 -070082 // 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,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700117 DeviceId deviceId,
118 DeviceDescription deviceDescription) {
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700119 Map<ProviderId, DeviceDescriptions> providerDescs
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700120 = getOrCreateDeviceDescriptions(deviceId);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700121
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700122 synchronized (providerDescs) {
123 // locking per device
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700124 DeviceDescriptions descs
Thomas Vachuska56dbeb12014-10-22 16:40:44 -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) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700147 // update composed device cache
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700148 Device oldDevice = devices.putIfAbsent(newDevice.id(), newDevice);
149 verify(oldDevice == null,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700150 "Unexpected Device in cache. PID:%s [old=%s, new=%s]",
151 providerId, oldDevice, newDevice);
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700152
153 if (!providerId.isAncillary()) {
154 availableDevices.add(newDevice.id());
tome5ec3fd2014-09-04 15:18:06 -0700155 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700156
157 return new DeviceEvent(DeviceEvent.Type.DEVICE_ADDED, newDevice, null);
tome5ec3fd2014-09-04 15:18:06 -0700158 }
159
160 // Updates the device and returns the appropriate event if necessary.
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700161 // Guarded by deviceDescs value (=Device lock)
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700162 private DeviceEvent updateDevice(ProviderId providerId, Device oldDevice, Device newDevice) {
tome5ec3fd2014-09-04 15:18:06 -0700163 // We allow only certain attributes to trigger update
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700164 boolean propertiesChanged =
165 !Objects.equals(oldDevice.hwVersion(), newDevice.hwVersion()) ||
166 !Objects.equals(oldDevice.swVersion(), newDevice.swVersion());
167 boolean annotationsChanged =
168 !AnnotationsUtil.isEqual(oldDevice.annotations(), newDevice.annotations());
169
170 // Primary providers can respond to all changes, but ancillary ones
171 // should respond only to annotation changes.
172 if ((providerId.isAncillary() && annotationsChanged) ||
173 (!providerId.isAncillary() && (propertiesChanged || annotationsChanged))) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700174
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700175 boolean replaced = devices.replace(newDevice.id(), oldDevice, newDevice);
176 if (!replaced) {
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700177 // FIXME: Is the enclosing if required here?
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700178 verify(replaced,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700179 "Replacing devices cache failed. PID:%s [expected:%s, found:%s, new=%s]",
180 providerId, oldDevice, devices.get(newDevice.id())
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700181 , newDevice);
182 }
183 if (!providerId.isAncillary()) {
184 availableDevices.add(newDevice.id());
tome5ec3fd2014-09-04 15:18:06 -0700185 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700186 return new DeviceEvent(DeviceEvent.Type.DEVICE_UPDATED, newDevice, null);
tome5ec3fd2014-09-04 15:18:06 -0700187 }
188
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700189 // Otherwise merely attempt to change availability if primary provider
190 if (!providerId.isAncillary()) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700191 boolean added = availableDevices.add(newDevice.id());
tom29df6f42014-09-05 08:14:14 -0700192 return !added ? null :
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700193 new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, newDevice, null);
tome5ec3fd2014-09-04 15:18:06 -0700194 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700195 return null;
tome5ec3fd2014-09-04 15:18:06 -0700196 }
197
tom41a2c5f2014-09-19 09:20:35 -0700198 @Override
199 public DeviceEvent markOffline(DeviceId deviceId) {
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700200 Map<ProviderId, DeviceDescriptions> providerDescs
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700201 = getOrCreateDeviceDescriptions(deviceId);
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700202
203 // locking device
204 synchronized (providerDescs) {
tome5ec3fd2014-09-04 15:18:06 -0700205 Device device = devices.get(deviceId);
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700206 if (device == null) {
207 return null;
208 }
209 boolean removed = availableDevices.remove(deviceId);
210 if (removed) {
211 // TODO: broadcast ... DOWN only?
212 return new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, device, null);
213 }
214 return null;
tome5ec3fd2014-09-04 15:18:06 -0700215 }
216 }
217
tom41a2c5f2014-09-19 09:20:35 -0700218 @Override
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700219 public List<DeviceEvent> updatePorts(ProviderId providerId,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700220 DeviceId deviceId,
221 List<PortDescription> portDescriptions) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700222 Device device = devices.get(deviceId);
223 checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
224
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700225 Map<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700226 checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId);
227
tom29df6f42014-09-05 08:14:14 -0700228 List<DeviceEvent> events = new ArrayList<>();
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700229 synchronized (descsMap) {
230 DeviceDescriptions descs = descsMap.get(providerId);
231 // every provider must provide DeviceDescription.
232 checkArgument(descs != null,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700233 "Device description for Device ID %s from Provider %s was not found",
234 deviceId, providerId);
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700235
236 Map<PortNumber, Port> ports = getPortMap(deviceId);
tom29df6f42014-09-05 08:14:14 -0700237
238 // Add new ports
239 Set<PortNumber> processed = new HashSet<>();
240 for (PortDescription portDescription : portDescriptions) {
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700241 final PortNumber number = portDescription.portNumber();
242 processed.add(portDescription.portNumber());
243
244 final Port oldPort = ports.get(number);
245 final Port newPort;
246
247// event suppression hook?
248
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700249 // update description
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700250 descs.putPortDesc(portDescription);
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700251 newPort = composePort(device, number, descsMap);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700252
253 events.add(oldPort == null ?
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700254 createPort(device, newPort, ports) :
255 updatePort(device, oldPort, newPort, ports));
tom29df6f42014-09-05 08:14:14 -0700256 }
257
258 events.addAll(pruneOldPorts(device, ports, processed));
259 }
Yuta HIGUCHIf5479702014-09-25 00:09:24 -0700260 return FluentIterable.from(events).filter(notNull()).toList();
tom29df6f42014-09-05 08:14:14 -0700261 }
262
263 // Creates a new port based on the port description adds it to the map and
264 // Returns corresponding event.
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700265 // Guarded by deviceDescs value (=Device lock)
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700266 private DeviceEvent createPort(Device device, Port newPort,
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700267 Map<PortNumber, Port> ports) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700268 ports.put(newPort.number(), newPort);
269 return new DeviceEvent(PORT_ADDED, device, newPort);
tom29df6f42014-09-05 08:14:14 -0700270 }
271
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700272 // Checks if the specified port requires update and if so, it replaces the
tom29df6f42014-09-05 08:14:14 -0700273 // existing entry in the map and returns corresponding event.
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700274 // Guarded by deviceDescs value (=Device lock)
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700275 private DeviceEvent updatePort(Device device, Port oldPort,
276 Port newPort,
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700277 Map<PortNumber, Port> ports) {
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700278 if (oldPort.isEnabled() != newPort.isEnabled() ||
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700279 !AnnotationsUtil.isEqual(oldPort.annotations(), newPort.annotations())) {
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700280
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700281 ports.put(oldPort.number(), newPort);
282 return new DeviceEvent(PORT_UPDATED, device, newPort);
tom29df6f42014-09-05 08:14:14 -0700283 }
284 return null;
285 }
286
287 // Prunes the specified list of ports based on which ports are in the
288 // processed list and returns list of corresponding events.
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700289 // Guarded by deviceDescs value (=Device lock)
tom29df6f42014-09-05 08:14:14 -0700290 private List<DeviceEvent> pruneOldPorts(Device device,
291 Map<PortNumber, Port> ports,
292 Set<PortNumber> processed) {
293 List<DeviceEvent> events = new ArrayList<>();
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700294 Iterator<Entry<PortNumber, Port>> iterator = ports.entrySet().iterator();
tom29df6f42014-09-05 08:14:14 -0700295 while (iterator.hasNext()) {
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700296 Entry<PortNumber, Port> e = iterator.next();
297 PortNumber portNumber = e.getKey();
tom24c55cd2014-09-06 10:47:25 -0700298 if (!processed.contains(portNumber)) {
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700299 events.add(new DeviceEvent(PORT_REMOVED, device, e.getValue()));
tom29df6f42014-09-05 08:14:14 -0700300 iterator.remove();
301 }
302 }
303 return events;
304 }
305
306 // Gets the map of ports for the specified device; if one does not already
307 // exist, it creates and registers a new one.
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700308 private ConcurrentMap<PortNumber, Port> getPortMap(DeviceId deviceId) {
309 return createIfAbsentUnchecked(devicePorts, deviceId,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700310 NewConcurrentHashMap.<PortNumber, Port>ifNeeded());
tome5ec3fd2014-09-04 15:18:06 -0700311 }
312
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700313 private Map<ProviderId, DeviceDescriptions> getOrCreateDeviceDescriptions(
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700314 DeviceId deviceId) {
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700315 Map<ProviderId, DeviceDescriptions> r;
316 r = deviceDescs.get(deviceId);
317 if (r != null) {
318 return r;
319 }
320 r = new HashMap<>();
321 final Map<ProviderId, DeviceDescriptions> concurrentlyAdded;
322 concurrentlyAdded = deviceDescs.putIfAbsent(deviceId, r);
323 if (concurrentlyAdded != null) {
324 return concurrentlyAdded;
325 } else {
326 return r;
327 }
328 }
329
330 // Guarded by deviceDescs value (=Device lock)
331 private DeviceDescriptions getOrCreateProviderDeviceDescriptions(
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700332 Map<ProviderId, DeviceDescriptions> device,
333 ProviderId providerId, DeviceDescription deltaDesc) {
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700334 synchronized (device) {
335 DeviceDescriptions r = device.get(providerId);
336 if (r == null) {
337 r = new DeviceDescriptions(deltaDesc);
338 device.put(providerId, r);
339 }
340 return r;
341 }
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700342 }
343
tom41a2c5f2014-09-19 09:20:35 -0700344 @Override
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700345 public DeviceEvent updatePortStatus(ProviderId providerId, DeviceId deviceId,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700346 PortDescription portDescription) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700347 Device device = devices.get(deviceId);
348 checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
349
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700350 Map<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700351 checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId);
352
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700353 synchronized (descsMap) {
354 DeviceDescriptions descs = descsMap.get(providerId);
Yuta HIGUCHIdd841b72014-10-16 13:46:09 -0700355 // assuming all providers must give DeviceDescription first
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700356 checkArgument(descs != null,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700357 "Device description for Device ID %s from Provider %s was not found",
358 deviceId, providerId);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700359
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700360 ConcurrentMap<PortNumber, Port> ports = getPortMap(deviceId);
361 final PortNumber number = portDescription.portNumber();
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700362 final Port oldPort = ports.get(number);
363 final Port newPort;
364
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700365 // update description
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700366 descs.putPortDesc(portDescription);
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700367 newPort = composePort(device, number, descsMap);
368
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700369 if (oldPort == null) {
370 return createPort(device, newPort, ports);
371 } else {
372 return updatePort(device, oldPort, newPort, ports);
373 }
tom46a220d2014-09-05 08:25:56 -0700374 }
tome5ec3fd2014-09-04 15:18:06 -0700375 }
376
tom41a2c5f2014-09-19 09:20:35 -0700377 @Override
378 public List<Port> getPorts(DeviceId deviceId) {
tom46a220d2014-09-05 08:25:56 -0700379 Map<PortNumber, Port> ports = devicePorts.get(deviceId);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700380 if (ports == null) {
381 return Collections.emptyList();
382 }
383 return ImmutableList.copyOf(ports.values());
tome5ec3fd2014-09-04 15:18:06 -0700384 }
385
tom41a2c5f2014-09-19 09:20:35 -0700386 @Override
387 public Port getPort(DeviceId deviceId, PortNumber portNumber) {
tom46a220d2014-09-05 08:25:56 -0700388 Map<PortNumber, Port> ports = devicePorts.get(deviceId);
389 return ports == null ? null : ports.get(portNumber);
tome5ec3fd2014-09-04 15:18:06 -0700390 }
391
tom41a2c5f2014-09-19 09:20:35 -0700392 @Override
393 public boolean isAvailable(DeviceId deviceId) {
tomff7eb7c2014-09-08 12:49:03 -0700394 return availableDevices.contains(deviceId);
395 }
396
tom41a2c5f2014-09-19 09:20:35 -0700397 @Override
tom41a2c5f2014-09-19 09:20:35 -0700398 public DeviceEvent removeDevice(DeviceId deviceId) {
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700399 Map<ProviderId, DeviceDescriptions> descs = getOrCreateDeviceDescriptions(deviceId);
Yuta HIGUCHI0d6a5e62014-10-03 15:54:09 -0700400 synchronized (descs) {
tome5ec3fd2014-09-04 15:18:06 -0700401 Device device = devices.remove(deviceId);
Yuta HIGUCHI0d6a5e62014-10-03 15:54:09 -0700402 // should DEVICE_REMOVED carry removed ports?
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700403 Map<PortNumber, Port> ports = devicePorts.get(deviceId);
Yuta HIGUCHI0d6a5e62014-10-03 15:54:09 -0700404 if (ports != null) {
405 ports.clear();
406 }
407 availableDevices.remove(deviceId);
408 descs.clear();
tom29df6f42014-09-05 08:14:14 -0700409 return device == null ? null :
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700410 new DeviceEvent(DEVICE_REMOVED, device, null);
tome5ec3fd2014-09-04 15:18:06 -0700411 }
412 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700413
414 /**
415 * Returns a Device, merging description given from multiple Providers.
416 *
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700417 * @param deviceId device identifier
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700418 * @param providerDescs Collection of Descriptions from multiple providers
419 * @return Device instance
420 */
421 private Device composeDevice(DeviceId deviceId,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700422 Map<ProviderId, DeviceDescriptions> providerDescs) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700423
424 checkArgument(!providerDescs.isEmpty(), "No Device descriptions supplied");
425
426 ProviderId primary = pickPrimaryPID(providerDescs);
427
428 DeviceDescriptions desc = providerDescs.get(primary);
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700429
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700430 final DeviceDescription base = desc.getDeviceDesc();
431 Type type = base.type();
432 String manufacturer = base.manufacturer();
433 String hwVersion = base.hwVersion();
434 String swVersion = base.swVersion();
435 String serialNumber = base.serialNumber();
alshabib7911a052014-10-16 17:49:37 -0700436 ChassisId chassisId = base.chassisId();
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700437 DefaultAnnotations annotations = DefaultAnnotations.builder().build();
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700438 annotations = merge(annotations, base.annotations());
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700439
440 for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) {
441 if (e.getKey().equals(primary)) {
442 continue;
443 }
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700444 // TODO: should keep track of Description timestamp
445 // and only merge conflicting keys when timestamp is newer
446 // Currently assuming there will never be a key conflict between
447 // providers
448
449 // annotation merging. not so efficient, should revisit later
450 annotations = merge(annotations, e.getValue().getDeviceDesc().annotations());
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700451 }
452
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700453 return new DefaultDevice(primary, deviceId, type, manufacturer,
454 hwVersion, swVersion, serialNumber,
455 chassisId, annotations);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700456 }
457
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700458 /**
459 * Returns a Port, merging description given from multiple Providers.
460 *
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700461 * @param device device the port is on
462 * @param number port number
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700463 * @param descsMap Collection of Descriptions from multiple providers
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700464 * @return Port instance
465 */
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700466 private Port composePort(Device device, PortNumber number,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700467 Map<ProviderId, DeviceDescriptions> descsMap) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700468
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700469 ProviderId primary = pickPrimaryPID(descsMap);
470 DeviceDescriptions primDescs = descsMap.get(primary);
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700471 // if no primary, assume not enabled
472 // TODO: revisit this default port enabled/disabled behavior
473 boolean isEnabled = false;
474 DefaultAnnotations annotations = DefaultAnnotations.builder().build();
475
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700476 final PortDescription portDesc = primDescs.getPortDesc(number);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700477 if (portDesc != null) {
478 isEnabled = portDesc.isEnabled();
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700479 annotations = merge(annotations, portDesc.annotations());
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700480 }
481
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700482 for (Entry<ProviderId, DeviceDescriptions> e : descsMap.entrySet()) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700483 if (e.getKey().equals(primary)) {
484 continue;
485 }
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700486 // TODO: should keep track of Description timestamp
487 // and only merge conflicting keys when timestamp is newer
488 // Currently assuming there will never be a key conflict between
489 // providers
490
491 // annotation merging. not so efficient, should revisit later
492 final PortDescription otherPortDesc = e.getValue().getPortDesc(number);
493 if (otherPortDesc != null) {
494 annotations = merge(annotations, otherPortDesc.annotations());
495 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700496 }
497
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700498 return new DefaultPort(device, number, isEnabled, annotations);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700499 }
500
501 /**
502 * @return primary ProviderID, or randomly chosen one if none exists
503 */
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700504 private ProviderId pickPrimaryPID(Map<ProviderId, DeviceDescriptions> descsMap) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700505 ProviderId fallBackPrimary = null;
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700506 for (Entry<ProviderId, DeviceDescriptions> e : descsMap.entrySet()) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700507 if (!e.getKey().isAncillary()) {
508 return e.getKey();
509 } else if (fallBackPrimary == null) {
510 // pick randomly as a fallback in case there is no primary
511 fallBackPrimary = e.getKey();
512 }
513 }
514 return fallBackPrimary;
515 }
516
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700517 /**
518 * Collection of Description of a Device and it's Ports given from a Provider.
519 */
520 private static class DeviceDescriptions {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700521
522 private final AtomicReference<DeviceDescription> deviceDesc;
523 private final ConcurrentMap<PortNumber, PortDescription> portDescs;
524
525 public DeviceDescriptions(DeviceDescription desc) {
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700526 this.deviceDesc = new AtomicReference<>(checkNotNull(desc));
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700527 this.portDescs = new ConcurrentHashMap<>();
528 }
529
530 public DeviceDescription getDeviceDesc() {
531 return deviceDesc.get();
532 }
533
534 public PortDescription getPortDesc(PortNumber number) {
535 return portDescs.get(number);
536 }
537
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700538 /**
539 * Puts DeviceDescription, merging annotations as necessary.
540 *
541 * @param newDesc new DeviceDescription
542 * @return previous DeviceDescription
543 */
544 public synchronized DeviceDescription putDeviceDesc(DeviceDescription newDesc) {
545 DeviceDescription oldOne = deviceDesc.get();
546 DeviceDescription newOne = newDesc;
547 if (oldOne != null) {
Yuta HIGUCHI885be1d2014-10-04 21:47:26 -0700548 SparseAnnotations merged = union(oldOne.annotations(),
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700549 newDesc.annotations());
550 newOne = new DefaultDeviceDescription(newOne, merged);
551 }
552 return deviceDesc.getAndSet(newOne);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700553 }
554
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700555 /**
556 * Puts PortDescription, merging annotations as necessary.
557 *
558 * @param newDesc new PortDescription
559 * @return previous PortDescription
560 */
561 public synchronized PortDescription putPortDesc(PortDescription newDesc) {
562 PortDescription oldOne = portDescs.get(newDesc.portNumber());
563 PortDescription newOne = newDesc;
564 if (oldOne != null) {
Yuta HIGUCHI885be1d2014-10-04 21:47:26 -0700565 SparseAnnotations merged = union(oldOne.annotations(),
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700566 newDesc.annotations());
567 newOne = new DefaultPortDescription(newOne, merged);
568 }
569 return portDescs.put(newOne.portNumber(), newOne);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700570 }
571 }
tome5ec3fd2014-09-04 15:18:06 -0700572}