blob: f1a6982b1b48df3f7d03104aa25510c9e09f3c4e [file] [log] [blame]
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07001/*
Ray Milkey34c95902015-04-15 09:47:53 -07002 * Copyright 2014-2015 Open Networking Laboratory
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Thomas Vachuskac97aa612015-06-23 16:00:18 -070016package org.onosproject.store.trivial;
tome5ec3fd2014-09-04 15:18:06 -070017
Yuta HIGUCHIf1f2ac02014-11-26 14:02:22 -080018import com.google.common.base.Predicate;
Yuta HIGUCHIf5479702014-09-25 00:09:24 -070019import com.google.common.collect.FluentIterable;
tom46a220d2014-09-05 08:25:56 -070020import com.google.common.collect.ImmutableList;
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -070021import com.google.common.collect.Maps;
22import com.google.common.collect.Sets;
Yuta HIGUCHIf5479702014-09-25 00:09:24 -070023
tom41a2c5f2014-09-19 09:20:35 -070024import org.apache.felix.scr.annotations.Activate;
25import org.apache.felix.scr.annotations.Component;
26import org.apache.felix.scr.annotations.Deactivate;
27import org.apache.felix.scr.annotations.Service;
Brian O'Connorabafb502014-12-02 22:26:20 -080028import org.onosproject.net.AnnotationsUtil;
29import org.onosproject.net.DefaultAnnotations;
30import org.onosproject.net.DefaultDevice;
31import org.onosproject.net.DefaultPort;
32import org.onosproject.net.Device;
33import org.onosproject.net.Device.Type;
34import org.onosproject.net.DeviceId;
35import org.onosproject.net.Port;
36import org.onosproject.net.PortNumber;
37import org.onosproject.net.SparseAnnotations;
38import org.onosproject.net.device.DefaultDeviceDescription;
39import org.onosproject.net.device.DefaultPortDescription;
40import org.onosproject.net.device.DeviceDescription;
41import org.onosproject.net.device.DeviceEvent;
42import org.onosproject.net.device.DeviceStore;
43import org.onosproject.net.device.DeviceStoreDelegate;
44import org.onosproject.net.device.PortDescription;
sangho538108b2015-04-08 14:29:20 -070045import org.onosproject.net.device.PortStatistics;
Brian O'Connorabafb502014-12-02 22:26:20 -080046import org.onosproject.net.provider.ProviderId;
47import org.onosproject.store.AbstractStore;
alshabib7911a052014-10-16 17:49:37 -070048import org.onlab.packet.ChassisId;
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070049import org.onlab.util.NewConcurrentHashMap;
tom41a2c5f2014-09-19 09:20:35 -070050import org.slf4j.Logger;
tome5ec3fd2014-09-04 15:18:06 -070051
52import java.util.ArrayList;
sangho538108b2015-04-08 14:29:20 -070053import java.util.Collection;
tome5ec3fd2014-09-04 15:18:06 -070054import java.util.Collections;
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -070055import java.util.HashMap;
tome5ec3fd2014-09-04 15:18:06 -070056import java.util.HashSet;
tom29df6f42014-09-05 08:14:14 -070057import java.util.Iterator;
tome5ec3fd2014-09-04 15:18:06 -070058import java.util.List;
59import java.util.Map;
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070060import java.util.Map.Entry;
tome5ec3fd2014-09-04 15:18:06 -070061import java.util.Objects;
62import java.util.Set;
63import java.util.concurrent.ConcurrentHashMap;
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070064import java.util.concurrent.ConcurrentMap;
65import java.util.concurrent.atomic.AtomicReference;
tome5ec3fd2014-09-04 15:18:06 -070066
67import static com.google.common.base.Preconditions.checkArgument;
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070068import static com.google.common.base.Preconditions.checkNotNull;
Yuta HIGUCHIf5479702014-09-25 00:09:24 -070069import static com.google.common.base.Predicates.notNull;
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -070070import static com.google.common.base.Verify.verify;
Brian O'Connorabafb502014-12-02 22:26:20 -080071import static org.onosproject.net.device.DeviceEvent.Type.*;
tom41a2c5f2014-09-19 09:20:35 -070072import static org.slf4j.LoggerFactory.getLogger;
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070073import static org.apache.commons.lang3.concurrent.ConcurrentUtils.createIfAbsentUnchecked;
Brian O'Connorabafb502014-12-02 22:26:20 -080074import static org.onosproject.net.DefaultAnnotations.union;
75import static org.onosproject.net.DefaultAnnotations.merge;
tome5ec3fd2014-09-04 15:18:06 -070076
77/**
tome4729872014-09-23 00:37:37 -070078 * Manages inventory of infrastructure devices using trivial in-memory
tomcbff9392014-09-10 00:45:23 -070079 * structures implementation.
tome5ec3fd2014-09-04 15:18:06 -070080 */
tom41a2c5f2014-09-19 09:20:35 -070081@Component(immediate = true)
82@Service
tomf80c9722014-09-24 14:49:18 -070083public class SimpleDeviceStore
84 extends AbstractStore<DeviceEvent, DeviceStoreDelegate>
85 implements DeviceStore {
tom41a2c5f2014-09-19 09:20:35 -070086
87 private final Logger log = getLogger(getClass());
tome5ec3fd2014-09-04 15:18:06 -070088
tom29df6f42014-09-05 08:14:14 -070089 public static final String DEVICE_NOT_FOUND = "Device with ID %s not found";
90
Thomas Vachuska56dbeb12014-10-22 16:40:44 -070091 // Collection of Description given from various providers
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -070092 private final ConcurrentMap<DeviceId, Map<ProviderId, DeviceDescriptions>>
Thomas Vachuska56dbeb12014-10-22 16:40:44 -070093 deviceDescs = Maps.newConcurrentMap();
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070094
Thomas Vachuska56dbeb12014-10-22 16:40:44 -070095 // Cache of Device and Ports generated by compositing descriptions from providers
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -070096 private final ConcurrentMap<DeviceId, Device> devices = Maps.newConcurrentMap();
Thomas Vachuska56dbeb12014-10-22 16:40:44 -070097 private final ConcurrentMap<DeviceId, ConcurrentMap<PortNumber, Port>>
98 devicePorts = Maps.newConcurrentMap();
sangho538108b2015-04-08 14:29:20 -070099 private final ConcurrentMap<DeviceId, ConcurrentMap<PortNumber, PortStatistics>>
100 devicePortStats = Maps.newConcurrentMap();
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700101
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700102 // Available (=UP) devices
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700103 private final Set<DeviceId> availableDevices = Sets.newConcurrentHashSet();
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700104
tome5ec3fd2014-09-04 15:18:06 -0700105
tom41a2c5f2014-09-19 09:20:35 -0700106 @Activate
107 public void activate() {
108 log.info("Started");
109 }
110
111 @Deactivate
112 public void deactivate() {
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700113 deviceDescs.clear();
114 devices.clear();
115 devicePorts.clear();
116 availableDevices.clear();
tom41a2c5f2014-09-19 09:20:35 -0700117 log.info("Stopped");
118 }
tom5bcc9462014-09-19 10:11:31 -0700119
tom41a2c5f2014-09-19 09:20:35 -0700120 @Override
121 public int getDeviceCount() {
tomad2d2092014-09-06 23:24:20 -0700122 return devices.size();
123 }
124
tom41a2c5f2014-09-19 09:20:35 -0700125 @Override
126 public Iterable<Device> getDevices() {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700127 return Collections.unmodifiableCollection(devices.values());
tome5ec3fd2014-09-04 15:18:06 -0700128 }
129
tom41a2c5f2014-09-19 09:20:35 -0700130 @Override
Yuta HIGUCHIf1f2ac02014-11-26 14:02:22 -0800131 public Iterable<Device> getAvailableDevices() {
132 return FluentIterable.from(getDevices())
133 .filter(new Predicate<Device>() {
134
135 @Override
136 public boolean apply(Device input) {
137 return isAvailable(input.id());
138 }
139 });
140 }
141
142 @Override
tom41a2c5f2014-09-19 09:20:35 -0700143 public Device getDevice(DeviceId deviceId) {
tome5ec3fd2014-09-04 15:18:06 -0700144 return devices.get(deviceId);
145 }
146
tom41a2c5f2014-09-19 09:20:35 -0700147 @Override
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700148 public DeviceEvent createOrUpdateDevice(ProviderId providerId,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700149 DeviceId deviceId,
150 DeviceDescription deviceDescription) {
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700151 Map<ProviderId, DeviceDescriptions> providerDescs
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700152 = getOrCreateDeviceDescriptions(deviceId);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700153
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700154 synchronized (providerDescs) {
155 // locking per device
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700156 DeviceDescriptions descs
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700157 = getOrCreateProviderDeviceDescriptions(providerDescs,
158 providerId,
159 deviceDescription);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700160
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700161 Device oldDevice = devices.get(deviceId);
162 // update description
163 descs.putDeviceDesc(deviceDescription);
164 Device newDevice = composeDevice(deviceId, providerDescs);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700165
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700166 if (oldDevice == null) {
167 // ADD
168 return createDevice(providerId, newDevice);
169 } else {
170 // UPDATE or ignore (no change or stale)
171 return updateDevice(providerId, oldDevice, newDevice);
172 }
tome5ec3fd2014-09-04 15:18:06 -0700173 }
tome5ec3fd2014-09-04 15:18:06 -0700174 }
175
176 // Creates the device and returns the appropriate event if necessary.
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700177 // Guarded by deviceDescs value (=Device lock)
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700178 private DeviceEvent createDevice(ProviderId providerId, Device newDevice) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700179 // update composed device cache
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700180 Device oldDevice = devices.putIfAbsent(newDevice.id(), newDevice);
181 verify(oldDevice == null,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700182 "Unexpected Device in cache. PID:%s [old=%s, new=%s]",
183 providerId, oldDevice, newDevice);
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700184
185 if (!providerId.isAncillary()) {
186 availableDevices.add(newDevice.id());
tome5ec3fd2014-09-04 15:18:06 -0700187 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700188
189 return new DeviceEvent(DeviceEvent.Type.DEVICE_ADDED, newDevice, null);
tome5ec3fd2014-09-04 15:18:06 -0700190 }
191
192 // Updates the device and returns the appropriate event if necessary.
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700193 // Guarded by deviceDescs value (=Device lock)
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700194 private DeviceEvent updateDevice(ProviderId providerId, Device oldDevice, Device newDevice) {
tome5ec3fd2014-09-04 15:18:06 -0700195 // We allow only certain attributes to trigger update
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700196 boolean propertiesChanged =
197 !Objects.equals(oldDevice.hwVersion(), newDevice.hwVersion()) ||
198 !Objects.equals(oldDevice.swVersion(), newDevice.swVersion());
199 boolean annotationsChanged =
200 !AnnotationsUtil.isEqual(oldDevice.annotations(), newDevice.annotations());
201
202 // Primary providers can respond to all changes, but ancillary ones
203 // should respond only to annotation changes.
204 if ((providerId.isAncillary() && annotationsChanged) ||
205 (!providerId.isAncillary() && (propertiesChanged || annotationsChanged))) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700206
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700207 boolean replaced = devices.replace(newDevice.id(), oldDevice, newDevice);
208 if (!replaced) {
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700209 // FIXME: Is the enclosing if required here?
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700210 verify(replaced,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700211 "Replacing devices cache failed. PID:%s [expected:%s, found:%s, new=%s]",
212 providerId, oldDevice, devices.get(newDevice.id())
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700213 , newDevice);
214 }
215 if (!providerId.isAncillary()) {
216 availableDevices.add(newDevice.id());
tome5ec3fd2014-09-04 15:18:06 -0700217 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700218 return new DeviceEvent(DeviceEvent.Type.DEVICE_UPDATED, newDevice, null);
tome5ec3fd2014-09-04 15:18:06 -0700219 }
220
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700221 // Otherwise merely attempt to change availability if primary provider
222 if (!providerId.isAncillary()) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700223 boolean added = availableDevices.add(newDevice.id());
tom29df6f42014-09-05 08:14:14 -0700224 return !added ? null :
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700225 new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, newDevice, null);
tome5ec3fd2014-09-04 15:18:06 -0700226 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700227 return null;
tome5ec3fd2014-09-04 15:18:06 -0700228 }
229
tom41a2c5f2014-09-19 09:20:35 -0700230 @Override
231 public DeviceEvent markOffline(DeviceId deviceId) {
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700232 Map<ProviderId, DeviceDescriptions> providerDescs
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700233 = getOrCreateDeviceDescriptions(deviceId);
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700234
235 // locking device
236 synchronized (providerDescs) {
tome5ec3fd2014-09-04 15:18:06 -0700237 Device device = devices.get(deviceId);
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700238 if (device == null) {
239 return null;
240 }
241 boolean removed = availableDevices.remove(deviceId);
242 if (removed) {
243 // TODO: broadcast ... DOWN only?
244 return new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, device, null);
245 }
246 return null;
tome5ec3fd2014-09-04 15:18:06 -0700247 }
248 }
249
tom41a2c5f2014-09-19 09:20:35 -0700250 @Override
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700251 public List<DeviceEvent> updatePorts(ProviderId providerId,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700252 DeviceId deviceId,
253 List<PortDescription> portDescriptions) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700254 Device device = devices.get(deviceId);
Ayaka Koshibe78bcbc12014-11-19 14:28:58 -0800255 if (device == null) {
256 log.debug("Device {} doesn't exist or hasn't been initialized yet", deviceId);
257 return Collections.emptyList();
258 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700259
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700260 Map<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700261 checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId);
262
tom29df6f42014-09-05 08:14:14 -0700263 List<DeviceEvent> events = new ArrayList<>();
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700264 synchronized (descsMap) {
265 DeviceDescriptions descs = descsMap.get(providerId);
266 // every provider must provide DeviceDescription.
267 checkArgument(descs != null,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700268 "Device description for Device ID %s from Provider %s was not found",
269 deviceId, providerId);
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700270
271 Map<PortNumber, Port> ports = getPortMap(deviceId);
tom29df6f42014-09-05 08:14:14 -0700272
273 // Add new ports
274 Set<PortNumber> processed = new HashSet<>();
275 for (PortDescription portDescription : portDescriptions) {
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700276 final PortNumber number = portDescription.portNumber();
277 processed.add(portDescription.portNumber());
278
279 final Port oldPort = ports.get(number);
280 final Port newPort;
281
282// event suppression hook?
283
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700284 // update description
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700285 descs.putPortDesc(portDescription);
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700286 newPort = composePort(device, number, descsMap);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700287
288 events.add(oldPort == null ?
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700289 createPort(device, newPort, ports) :
290 updatePort(device, oldPort, newPort, ports));
tom29df6f42014-09-05 08:14:14 -0700291 }
292
293 events.addAll(pruneOldPorts(device, ports, processed));
294 }
Yuta HIGUCHIf5479702014-09-25 00:09:24 -0700295 return FluentIterable.from(events).filter(notNull()).toList();
tom29df6f42014-09-05 08:14:14 -0700296 }
297
298 // Creates a new port based on the port description adds it to the map and
299 // Returns corresponding event.
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700300 // Guarded by deviceDescs value (=Device lock)
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700301 private DeviceEvent createPort(Device device, Port newPort,
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700302 Map<PortNumber, Port> ports) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700303 ports.put(newPort.number(), newPort);
304 return new DeviceEvent(PORT_ADDED, device, newPort);
tom29df6f42014-09-05 08:14:14 -0700305 }
306
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700307 // Checks if the specified port requires update and if so, it replaces the
tom29df6f42014-09-05 08:14:14 -0700308 // existing entry in the map and returns corresponding event.
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700309 // Guarded by deviceDescs value (=Device lock)
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700310 private DeviceEvent updatePort(Device device, Port oldPort,
311 Port newPort,
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700312 Map<PortNumber, Port> ports) {
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700313 if (oldPort.isEnabled() != newPort.isEnabled() ||
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700314 oldPort.type() != newPort.type() ||
315 oldPort.portSpeed() != newPort.portSpeed() ||
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700316 !AnnotationsUtil.isEqual(oldPort.annotations(), newPort.annotations())) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700317 ports.put(oldPort.number(), newPort);
318 return new DeviceEvent(PORT_UPDATED, device, newPort);
tom29df6f42014-09-05 08:14:14 -0700319 }
320 return null;
321 }
322
323 // Prunes the specified list of ports based on which ports are in the
324 // processed list and returns list of corresponding events.
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700325 // Guarded by deviceDescs value (=Device lock)
tom29df6f42014-09-05 08:14:14 -0700326 private List<DeviceEvent> pruneOldPorts(Device device,
327 Map<PortNumber, Port> ports,
328 Set<PortNumber> processed) {
329 List<DeviceEvent> events = new ArrayList<>();
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700330 Iterator<Entry<PortNumber, Port>> iterator = ports.entrySet().iterator();
tom29df6f42014-09-05 08:14:14 -0700331 while (iterator.hasNext()) {
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700332 Entry<PortNumber, Port> e = iterator.next();
333 PortNumber portNumber = e.getKey();
tom24c55cd2014-09-06 10:47:25 -0700334 if (!processed.contains(portNumber)) {
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700335 events.add(new DeviceEvent(PORT_REMOVED, device, e.getValue()));
tom29df6f42014-09-05 08:14:14 -0700336 iterator.remove();
337 }
338 }
339 return events;
340 }
341
342 // Gets the map of ports for the specified device; if one does not already
343 // exist, it creates and registers a new one.
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700344 private ConcurrentMap<PortNumber, Port> getPortMap(DeviceId deviceId) {
345 return createIfAbsentUnchecked(devicePorts, deviceId,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700346 NewConcurrentHashMap.<PortNumber, Port>ifNeeded());
tome5ec3fd2014-09-04 15:18:06 -0700347 }
348
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700349 private Map<ProviderId, DeviceDescriptions> getOrCreateDeviceDescriptions(
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700350 DeviceId deviceId) {
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700351 Map<ProviderId, DeviceDescriptions> r;
352 r = deviceDescs.get(deviceId);
353 if (r != null) {
354 return r;
355 }
356 r = new HashMap<>();
357 final Map<ProviderId, DeviceDescriptions> concurrentlyAdded;
358 concurrentlyAdded = deviceDescs.putIfAbsent(deviceId, r);
359 if (concurrentlyAdded != null) {
360 return concurrentlyAdded;
361 } else {
362 return r;
363 }
364 }
365
366 // Guarded by deviceDescs value (=Device lock)
367 private DeviceDescriptions getOrCreateProviderDeviceDescriptions(
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700368 Map<ProviderId, DeviceDescriptions> device,
369 ProviderId providerId, DeviceDescription deltaDesc) {
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700370 synchronized (device) {
371 DeviceDescriptions r = device.get(providerId);
372 if (r == null) {
373 r = new DeviceDescriptions(deltaDesc);
374 device.put(providerId, r);
375 }
376 return r;
377 }
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700378 }
379
tom41a2c5f2014-09-19 09:20:35 -0700380 @Override
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700381 public DeviceEvent updatePortStatus(ProviderId providerId, DeviceId deviceId,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700382 PortDescription portDescription) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700383 Device device = devices.get(deviceId);
384 checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
385
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700386 Map<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700387 checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId);
388
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700389 synchronized (descsMap) {
390 DeviceDescriptions descs = descsMap.get(providerId);
Yuta HIGUCHIdd841b72014-10-16 13:46:09 -0700391 // assuming all providers must give DeviceDescription first
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700392 checkArgument(descs != null,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700393 "Device description for Device ID %s from Provider %s was not found",
394 deviceId, providerId);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700395
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700396 ConcurrentMap<PortNumber, Port> ports = getPortMap(deviceId);
397 final PortNumber number = portDescription.portNumber();
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700398 final Port oldPort = ports.get(number);
399 final Port newPort;
400
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700401 // update description
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700402 descs.putPortDesc(portDescription);
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700403 newPort = composePort(device, number, descsMap);
404
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700405 if (oldPort == null) {
406 return createPort(device, newPort, ports);
407 } else {
408 return updatePort(device, oldPort, newPort, ports);
409 }
tom46a220d2014-09-05 08:25:56 -0700410 }
tome5ec3fd2014-09-04 15:18:06 -0700411 }
412
tom41a2c5f2014-09-19 09:20:35 -0700413 @Override
414 public List<Port> getPorts(DeviceId deviceId) {
tom46a220d2014-09-05 08:25:56 -0700415 Map<PortNumber, Port> ports = devicePorts.get(deviceId);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700416 if (ports == null) {
417 return Collections.emptyList();
418 }
419 return ImmutableList.copyOf(ports.values());
tome5ec3fd2014-09-04 15:18:06 -0700420 }
421
tom41a2c5f2014-09-19 09:20:35 -0700422 @Override
sangho538108b2015-04-08 14:29:20 -0700423 public DeviceEvent updatePortStatistics(ProviderId providerId, DeviceId deviceId,
424 Collection<PortStatistics> portStats) {
425
426 ConcurrentMap<PortNumber, PortStatistics> statsMap = devicePortStats.get(deviceId);
427 if (statsMap == null) {
428 statsMap = Maps.newConcurrentMap();
429 devicePortStats.put(deviceId, statsMap);
430 }
431
432 for (PortStatistics stat: portStats) {
433 PortNumber portNumber = PortNumber.portNumber(stat.port());
434 statsMap.put(portNumber, stat);
435 }
436
437 return new DeviceEvent(PORT_STATS_UPDATED, devices.get(deviceId), null);
438 }
439
440 @Override
tom41a2c5f2014-09-19 09:20:35 -0700441 public Port getPort(DeviceId deviceId, PortNumber portNumber) {
tom46a220d2014-09-05 08:25:56 -0700442 Map<PortNumber, Port> ports = devicePorts.get(deviceId);
443 return ports == null ? null : ports.get(portNumber);
tome5ec3fd2014-09-04 15:18:06 -0700444 }
445
tom41a2c5f2014-09-19 09:20:35 -0700446 @Override
sangho538108b2015-04-08 14:29:20 -0700447 public List<PortStatistics> getPortStatistics(DeviceId deviceId) {
448 Map<PortNumber, PortStatistics> portStats = devicePortStats.get(deviceId);
449 if (portStats == null) {
450 return Collections.emptyList();
451 }
452 return ImmutableList.copyOf(portStats.values());
453 }
454
455 @Override
tom41a2c5f2014-09-19 09:20:35 -0700456 public boolean isAvailable(DeviceId deviceId) {
tomff7eb7c2014-09-08 12:49:03 -0700457 return availableDevices.contains(deviceId);
458 }
459
tom41a2c5f2014-09-19 09:20:35 -0700460 @Override
tom41a2c5f2014-09-19 09:20:35 -0700461 public DeviceEvent removeDevice(DeviceId deviceId) {
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700462 Map<ProviderId, DeviceDescriptions> descs = getOrCreateDeviceDescriptions(deviceId);
Yuta HIGUCHI0d6a5e62014-10-03 15:54:09 -0700463 synchronized (descs) {
tome5ec3fd2014-09-04 15:18:06 -0700464 Device device = devices.remove(deviceId);
Yuta HIGUCHI0d6a5e62014-10-03 15:54:09 -0700465 // should DEVICE_REMOVED carry removed ports?
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700466 Map<PortNumber, Port> ports = devicePorts.get(deviceId);
Yuta HIGUCHI0d6a5e62014-10-03 15:54:09 -0700467 if (ports != null) {
468 ports.clear();
469 }
470 availableDevices.remove(deviceId);
471 descs.clear();
tom29df6f42014-09-05 08:14:14 -0700472 return device == null ? null :
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700473 new DeviceEvent(DEVICE_REMOVED, device, null);
tome5ec3fd2014-09-04 15:18:06 -0700474 }
475 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700476
477 /**
478 * Returns a Device, merging description given from multiple Providers.
479 *
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700480 * @param deviceId device identifier
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700481 * @param providerDescs Collection of Descriptions from multiple providers
482 * @return Device instance
483 */
484 private Device composeDevice(DeviceId deviceId,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700485 Map<ProviderId, DeviceDescriptions> providerDescs) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700486
487 checkArgument(!providerDescs.isEmpty(), "No Device descriptions supplied");
488
489 ProviderId primary = pickPrimaryPID(providerDescs);
490
491 DeviceDescriptions desc = providerDescs.get(primary);
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700492
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700493 final DeviceDescription base = desc.getDeviceDesc();
494 Type type = base.type();
495 String manufacturer = base.manufacturer();
496 String hwVersion = base.hwVersion();
497 String swVersion = base.swVersion();
498 String serialNumber = base.serialNumber();
alshabib7911a052014-10-16 17:49:37 -0700499 ChassisId chassisId = base.chassisId();
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700500 DefaultAnnotations annotations = DefaultAnnotations.builder().build();
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700501 annotations = merge(annotations, base.annotations());
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700502
503 for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) {
504 if (e.getKey().equals(primary)) {
505 continue;
506 }
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700507 // TODO: should keep track of Description timestamp
508 // and only merge conflicting keys when timestamp is newer
509 // Currently assuming there will never be a key conflict between
510 // providers
511
512 // annotation merging. not so efficient, should revisit later
513 annotations = merge(annotations, e.getValue().getDeviceDesc().annotations());
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700514 }
515
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700516 return new DefaultDevice(primary, deviceId, type, manufacturer,
517 hwVersion, swVersion, serialNumber,
518 chassisId, annotations);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700519 }
520
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700521 /**
522 * Returns a Port, merging description given from multiple Providers.
523 *
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700524 * @param device device the port is on
525 * @param number port number
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700526 * @param descsMap Collection of Descriptions from multiple providers
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700527 * @return Port instance
528 */
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700529 private Port composePort(Device device, PortNumber number,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700530 Map<ProviderId, DeviceDescriptions> descsMap) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700531
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700532 ProviderId primary = pickPrimaryPID(descsMap);
533 DeviceDescriptions primDescs = descsMap.get(primary);
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700534 // if no primary, assume not enabled
535 // TODO: revisit this default port enabled/disabled behavior
536 boolean isEnabled = false;
537 DefaultAnnotations annotations = DefaultAnnotations.builder().build();
538
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700539 final PortDescription portDesc = primDescs.getPortDesc(number);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700540 if (portDesc != null) {
541 isEnabled = portDesc.isEnabled();
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700542 annotations = merge(annotations, portDesc.annotations());
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700543 }
544
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700545 for (Entry<ProviderId, DeviceDescriptions> e : descsMap.entrySet()) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700546 if (e.getKey().equals(primary)) {
547 continue;
548 }
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700549 // TODO: should keep track of Description timestamp
550 // and only merge conflicting keys when timestamp is newer
551 // Currently assuming there will never be a key conflict between
552 // providers
553
554 // annotation merging. not so efficient, should revisit later
555 final PortDescription otherPortDesc = e.getValue().getPortDesc(number);
556 if (otherPortDesc != null) {
557 annotations = merge(annotations, otherPortDesc.annotations());
558 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700559 }
560
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700561 return portDesc == null ?
562 new DefaultPort(device, number, false, annotations) :
563 new DefaultPort(device, number, isEnabled, portDesc.type(),
564 portDesc.portSpeed(), annotations);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700565 }
566
567 /**
568 * @return primary ProviderID, or randomly chosen one if none exists
569 */
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700570 private ProviderId pickPrimaryPID(Map<ProviderId, DeviceDescriptions> descsMap) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700571 ProviderId fallBackPrimary = null;
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700572 for (Entry<ProviderId, DeviceDescriptions> e : descsMap.entrySet()) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700573 if (!e.getKey().isAncillary()) {
574 return e.getKey();
575 } else if (fallBackPrimary == null) {
576 // pick randomly as a fallback in case there is no primary
577 fallBackPrimary = e.getKey();
578 }
579 }
580 return fallBackPrimary;
581 }
582
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700583 /**
584 * Collection of Description of a Device and it's Ports given from a Provider.
585 */
586 private static class DeviceDescriptions {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700587
588 private final AtomicReference<DeviceDescription> deviceDesc;
589 private final ConcurrentMap<PortNumber, PortDescription> portDescs;
590
591 public DeviceDescriptions(DeviceDescription desc) {
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700592 this.deviceDesc = new AtomicReference<>(checkNotNull(desc));
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700593 this.portDescs = new ConcurrentHashMap<>();
594 }
595
596 public DeviceDescription getDeviceDesc() {
597 return deviceDesc.get();
598 }
599
600 public PortDescription getPortDesc(PortNumber number) {
601 return portDescs.get(number);
602 }
603
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700604 /**
605 * Puts DeviceDescription, merging annotations as necessary.
606 *
607 * @param newDesc new DeviceDescription
608 * @return previous DeviceDescription
609 */
610 public synchronized DeviceDescription putDeviceDesc(DeviceDescription newDesc) {
611 DeviceDescription oldOne = deviceDesc.get();
612 DeviceDescription newOne = newDesc;
613 if (oldOne != null) {
Yuta HIGUCHI885be1d2014-10-04 21:47:26 -0700614 SparseAnnotations merged = union(oldOne.annotations(),
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700615 newDesc.annotations());
616 newOne = new DefaultDeviceDescription(newOne, merged);
617 }
618 return deviceDesc.getAndSet(newOne);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700619 }
620
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700621 /**
622 * Puts PortDescription, merging annotations as necessary.
623 *
624 * @param newDesc new PortDescription
625 * @return previous PortDescription
626 */
627 public synchronized PortDescription putPortDesc(PortDescription newDesc) {
628 PortDescription oldOne = portDescs.get(newDesc.portNumber());
629 PortDescription newOne = newDesc;
630 if (oldOne != null) {
Yuta HIGUCHI885be1d2014-10-04 21:47:26 -0700631 SparseAnnotations merged = union(oldOne.annotations(),
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700632 newDesc.annotations());
633 newOne = new DefaultPortDescription(newOne, merged);
634 }
635 return portDescs.put(newOne.portNumber(), newOne);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700636 }
637 }
tome5ec3fd2014-09-04 15:18:06 -0700638}