blob: fc90dfad5864cff4b8b0071eb2e3db593a8ef6cb [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;
Dusan Pajin11ff4a82015-08-20 18:03:05 +020040import org.onosproject.net.device.DefaultPortStatistics;
Brian O'Connorabafb502014-12-02 22:26:20 -080041import org.onosproject.net.device.DeviceDescription;
42import org.onosproject.net.device.DeviceEvent;
43import org.onosproject.net.device.DeviceStore;
44import org.onosproject.net.device.DeviceStoreDelegate;
45import org.onosproject.net.device.PortDescription;
sangho538108b2015-04-08 14:29:20 -070046import org.onosproject.net.device.PortStatistics;
Brian O'Connorabafb502014-12-02 22:26:20 -080047import org.onosproject.net.provider.ProviderId;
48import org.onosproject.store.AbstractStore;
alshabib7911a052014-10-16 17:49:37 -070049import org.onlab.packet.ChassisId;
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070050import org.onlab.util.NewConcurrentHashMap;
tom41a2c5f2014-09-19 09:20:35 -070051import org.slf4j.Logger;
tome5ec3fd2014-09-04 15:18:06 -070052
53import java.util.ArrayList;
sangho538108b2015-04-08 14:29:20 -070054import java.util.Collection;
tome5ec3fd2014-09-04 15:18:06 -070055import java.util.Collections;
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -070056import java.util.HashMap;
tome5ec3fd2014-09-04 15:18:06 -070057import java.util.HashSet;
tom29df6f42014-09-05 08:14:14 -070058import java.util.Iterator;
tome5ec3fd2014-09-04 15:18:06 -070059import java.util.List;
60import java.util.Map;
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070061import java.util.Map.Entry;
tome5ec3fd2014-09-04 15:18:06 -070062import java.util.Objects;
63import java.util.Set;
64import java.util.concurrent.ConcurrentHashMap;
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070065import java.util.concurrent.ConcurrentMap;
Dusan Pajin11ff4a82015-08-20 18:03:05 +020066import java.util.concurrent.TimeUnit;
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070067import java.util.concurrent.atomic.AtomicReference;
tome5ec3fd2014-09-04 15:18:06 -070068
69import static com.google.common.base.Preconditions.checkArgument;
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070070import static com.google.common.base.Preconditions.checkNotNull;
Yuta HIGUCHIf5479702014-09-25 00:09:24 -070071import static com.google.common.base.Predicates.notNull;
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -070072import static com.google.common.base.Verify.verify;
Brian O'Connorabafb502014-12-02 22:26:20 -080073import static org.onosproject.net.device.DeviceEvent.Type.*;
tom41a2c5f2014-09-19 09:20:35 -070074import static org.slf4j.LoggerFactory.getLogger;
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070075import static org.apache.commons.lang3.concurrent.ConcurrentUtils.createIfAbsentUnchecked;
Brian O'Connorabafb502014-12-02 22:26:20 -080076import static org.onosproject.net.DefaultAnnotations.union;
77import static org.onosproject.net.DefaultAnnotations.merge;
tome5ec3fd2014-09-04 15:18:06 -070078
79/**
tome4729872014-09-23 00:37:37 -070080 * Manages inventory of infrastructure devices using trivial in-memory
tomcbff9392014-09-10 00:45:23 -070081 * structures implementation.
tome5ec3fd2014-09-04 15:18:06 -070082 */
tom41a2c5f2014-09-19 09:20:35 -070083@Component(immediate = true)
84@Service
tomf80c9722014-09-24 14:49:18 -070085public class SimpleDeviceStore
86 extends AbstractStore<DeviceEvent, DeviceStoreDelegate>
87 implements DeviceStore {
tom41a2c5f2014-09-19 09:20:35 -070088
89 private final Logger log = getLogger(getClass());
tome5ec3fd2014-09-04 15:18:06 -070090
tom29df6f42014-09-05 08:14:14 -070091 public static final String DEVICE_NOT_FOUND = "Device with ID %s not found";
92
Thomas Vachuska56dbeb12014-10-22 16:40:44 -070093 // Collection of Description given from various providers
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -070094 private final ConcurrentMap<DeviceId, Map<ProviderId, DeviceDescriptions>>
Thomas Vachuska56dbeb12014-10-22 16:40:44 -070095 deviceDescs = Maps.newConcurrentMap();
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070096
Thomas Vachuska56dbeb12014-10-22 16:40:44 -070097 // Cache of Device and Ports generated by compositing descriptions from providers
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -070098 private final ConcurrentMap<DeviceId, Device> devices = Maps.newConcurrentMap();
Thomas Vachuska56dbeb12014-10-22 16:40:44 -070099 private final ConcurrentMap<DeviceId, ConcurrentMap<PortNumber, Port>>
100 devicePorts = Maps.newConcurrentMap();
sangho538108b2015-04-08 14:29:20 -0700101 private final ConcurrentMap<DeviceId, ConcurrentMap<PortNumber, PortStatistics>>
102 devicePortStats = Maps.newConcurrentMap();
Dusan Pajin11ff4a82015-08-20 18:03:05 +0200103 private final ConcurrentMap<DeviceId, ConcurrentMap<PortNumber, PortStatistics>>
104 devicePortDeltaStats = Maps.newConcurrentMap();
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700105
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700106 // Available (=UP) devices
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700107 private final Set<DeviceId> availableDevices = Sets.newConcurrentHashSet();
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700108
tome5ec3fd2014-09-04 15:18:06 -0700109
tom41a2c5f2014-09-19 09:20:35 -0700110 @Activate
111 public void activate() {
112 log.info("Started");
113 }
114
115 @Deactivate
116 public void deactivate() {
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700117 deviceDescs.clear();
118 devices.clear();
119 devicePorts.clear();
120 availableDevices.clear();
tom41a2c5f2014-09-19 09:20:35 -0700121 log.info("Stopped");
122 }
tom5bcc9462014-09-19 10:11:31 -0700123
tom41a2c5f2014-09-19 09:20:35 -0700124 @Override
125 public int getDeviceCount() {
tomad2d2092014-09-06 23:24:20 -0700126 return devices.size();
127 }
128
tom41a2c5f2014-09-19 09:20:35 -0700129 @Override
130 public Iterable<Device> getDevices() {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700131 return Collections.unmodifiableCollection(devices.values());
tome5ec3fd2014-09-04 15:18:06 -0700132 }
133
tom41a2c5f2014-09-19 09:20:35 -0700134 @Override
Yuta HIGUCHIf1f2ac02014-11-26 14:02:22 -0800135 public Iterable<Device> getAvailableDevices() {
136 return FluentIterable.from(getDevices())
137 .filter(new Predicate<Device>() {
138
139 @Override
140 public boolean apply(Device input) {
141 return isAvailable(input.id());
142 }
143 });
144 }
145
146 @Override
tom41a2c5f2014-09-19 09:20:35 -0700147 public Device getDevice(DeviceId deviceId) {
tome5ec3fd2014-09-04 15:18:06 -0700148 return devices.get(deviceId);
149 }
150
tom41a2c5f2014-09-19 09:20:35 -0700151 @Override
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700152 public DeviceEvent createOrUpdateDevice(ProviderId providerId,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700153 DeviceId deviceId,
154 DeviceDescription deviceDescription) {
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700155 Map<ProviderId, DeviceDescriptions> providerDescs
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700156 = getOrCreateDeviceDescriptions(deviceId);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700157
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700158 synchronized (providerDescs) {
159 // locking per device
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700160 DeviceDescriptions descs
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700161 = getOrCreateProviderDeviceDescriptions(providerDescs,
162 providerId,
163 deviceDescription);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700164
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700165 Device oldDevice = devices.get(deviceId);
166 // update description
167 descs.putDeviceDesc(deviceDescription);
168 Device newDevice = composeDevice(deviceId, providerDescs);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700169
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700170 if (oldDevice == null) {
171 // ADD
172 return createDevice(providerId, newDevice);
173 } else {
174 // UPDATE or ignore (no change or stale)
175 return updateDevice(providerId, oldDevice, newDevice);
176 }
tome5ec3fd2014-09-04 15:18:06 -0700177 }
tome5ec3fd2014-09-04 15:18:06 -0700178 }
179
180 // Creates the device and returns the appropriate event if necessary.
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700181 // Guarded by deviceDescs value (=Device lock)
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700182 private DeviceEvent createDevice(ProviderId providerId, Device newDevice) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700183 // update composed device cache
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700184 Device oldDevice = devices.putIfAbsent(newDevice.id(), newDevice);
185 verify(oldDevice == null,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700186 "Unexpected Device in cache. PID:%s [old=%s, new=%s]",
187 providerId, oldDevice, newDevice);
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700188
189 if (!providerId.isAncillary()) {
190 availableDevices.add(newDevice.id());
tome5ec3fd2014-09-04 15:18:06 -0700191 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700192
193 return new DeviceEvent(DeviceEvent.Type.DEVICE_ADDED, newDevice, null);
tome5ec3fd2014-09-04 15:18:06 -0700194 }
195
196 // Updates the device and returns the appropriate event if necessary.
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700197 // Guarded by deviceDescs value (=Device lock)
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700198 private DeviceEvent updateDevice(ProviderId providerId, Device oldDevice, Device newDevice) {
tome5ec3fd2014-09-04 15:18:06 -0700199 // We allow only certain attributes to trigger update
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700200 boolean propertiesChanged =
201 !Objects.equals(oldDevice.hwVersion(), newDevice.hwVersion()) ||
202 !Objects.equals(oldDevice.swVersion(), newDevice.swVersion());
203 boolean annotationsChanged =
204 !AnnotationsUtil.isEqual(oldDevice.annotations(), newDevice.annotations());
205
206 // Primary providers can respond to all changes, but ancillary ones
207 // should respond only to annotation changes.
208 if ((providerId.isAncillary() && annotationsChanged) ||
209 (!providerId.isAncillary() && (propertiesChanged || annotationsChanged))) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700210
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700211 boolean replaced = devices.replace(newDevice.id(), oldDevice, newDevice);
212 if (!replaced) {
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700213 // FIXME: Is the enclosing if required here?
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700214 verify(replaced,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700215 "Replacing devices cache failed. PID:%s [expected:%s, found:%s, new=%s]",
216 providerId, oldDevice, devices.get(newDevice.id())
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700217 , newDevice);
218 }
219 if (!providerId.isAncillary()) {
220 availableDevices.add(newDevice.id());
tome5ec3fd2014-09-04 15:18:06 -0700221 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700222 return new DeviceEvent(DeviceEvent.Type.DEVICE_UPDATED, newDevice, null);
tome5ec3fd2014-09-04 15:18:06 -0700223 }
224
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700225 // Otherwise merely attempt to change availability if primary provider
226 if (!providerId.isAncillary()) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700227 boolean added = availableDevices.add(newDevice.id());
tom29df6f42014-09-05 08:14:14 -0700228 return !added ? null :
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700229 new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, newDevice, null);
tome5ec3fd2014-09-04 15:18:06 -0700230 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700231 return null;
tome5ec3fd2014-09-04 15:18:06 -0700232 }
233
tom41a2c5f2014-09-19 09:20:35 -0700234 @Override
235 public DeviceEvent markOffline(DeviceId deviceId) {
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700236 Map<ProviderId, DeviceDescriptions> providerDescs
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700237 = getOrCreateDeviceDescriptions(deviceId);
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700238
239 // locking device
240 synchronized (providerDescs) {
tome5ec3fd2014-09-04 15:18:06 -0700241 Device device = devices.get(deviceId);
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700242 if (device == null) {
243 return null;
244 }
245 boolean removed = availableDevices.remove(deviceId);
246 if (removed) {
247 // TODO: broadcast ... DOWN only?
248 return new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, device, null);
249 }
250 return null;
tome5ec3fd2014-09-04 15:18:06 -0700251 }
252 }
253
tom41a2c5f2014-09-19 09:20:35 -0700254 @Override
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700255 public List<DeviceEvent> updatePorts(ProviderId providerId,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700256 DeviceId deviceId,
257 List<PortDescription> portDescriptions) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700258 Device device = devices.get(deviceId);
Ayaka Koshibe78bcbc12014-11-19 14:28:58 -0800259 if (device == null) {
260 log.debug("Device {} doesn't exist or hasn't been initialized yet", deviceId);
261 return Collections.emptyList();
262 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700263
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700264 Map<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700265 checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId);
266
tom29df6f42014-09-05 08:14:14 -0700267 List<DeviceEvent> events = new ArrayList<>();
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700268 synchronized (descsMap) {
269 DeviceDescriptions descs = descsMap.get(providerId);
270 // every provider must provide DeviceDescription.
271 checkArgument(descs != null,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700272 "Device description for Device ID %s from Provider %s was not found",
273 deviceId, providerId);
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700274
275 Map<PortNumber, Port> ports = getPortMap(deviceId);
tom29df6f42014-09-05 08:14:14 -0700276
277 // Add new ports
278 Set<PortNumber> processed = new HashSet<>();
279 for (PortDescription portDescription : portDescriptions) {
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700280 final PortNumber number = portDescription.portNumber();
281 processed.add(portDescription.portNumber());
282
283 final Port oldPort = ports.get(number);
284 final Port newPort;
285
286// event suppression hook?
287
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700288 // update description
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700289 descs.putPortDesc(portDescription);
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700290 newPort = composePort(device, number, descsMap);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700291
292 events.add(oldPort == null ?
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700293 createPort(device, newPort, ports) :
294 updatePort(device, oldPort, newPort, ports));
tom29df6f42014-09-05 08:14:14 -0700295 }
296
297 events.addAll(pruneOldPorts(device, ports, processed));
298 }
Yuta HIGUCHIf5479702014-09-25 00:09:24 -0700299 return FluentIterable.from(events).filter(notNull()).toList();
tom29df6f42014-09-05 08:14:14 -0700300 }
301
302 // Creates a new port based on the port description adds it to the map and
303 // Returns corresponding event.
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700304 // Guarded by deviceDescs value (=Device lock)
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700305 private DeviceEvent createPort(Device device, Port newPort,
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700306 Map<PortNumber, Port> ports) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700307 ports.put(newPort.number(), newPort);
308 return new DeviceEvent(PORT_ADDED, device, newPort);
tom29df6f42014-09-05 08:14:14 -0700309 }
310
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700311 // Checks if the specified port requires update and if so, it replaces the
tom29df6f42014-09-05 08:14:14 -0700312 // existing entry in the map and returns corresponding event.
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700313 // Guarded by deviceDescs value (=Device lock)
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700314 private DeviceEvent updatePort(Device device, Port oldPort,
315 Port newPort,
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700316 Map<PortNumber, Port> ports) {
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700317 if (oldPort.isEnabled() != newPort.isEnabled() ||
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700318 oldPort.type() != newPort.type() ||
319 oldPort.portSpeed() != newPort.portSpeed() ||
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700320 !AnnotationsUtil.isEqual(oldPort.annotations(), newPort.annotations())) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700321 ports.put(oldPort.number(), newPort);
322 return new DeviceEvent(PORT_UPDATED, device, newPort);
tom29df6f42014-09-05 08:14:14 -0700323 }
324 return null;
325 }
326
327 // Prunes the specified list of ports based on which ports are in the
328 // processed list and returns list of corresponding events.
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700329 // Guarded by deviceDescs value (=Device lock)
tom29df6f42014-09-05 08:14:14 -0700330 private List<DeviceEvent> pruneOldPorts(Device device,
331 Map<PortNumber, Port> ports,
332 Set<PortNumber> processed) {
333 List<DeviceEvent> events = new ArrayList<>();
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700334 Iterator<Entry<PortNumber, Port>> iterator = ports.entrySet().iterator();
tom29df6f42014-09-05 08:14:14 -0700335 while (iterator.hasNext()) {
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700336 Entry<PortNumber, Port> e = iterator.next();
337 PortNumber portNumber = e.getKey();
tom24c55cd2014-09-06 10:47:25 -0700338 if (!processed.contains(portNumber)) {
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700339 events.add(new DeviceEvent(PORT_REMOVED, device, e.getValue()));
tom29df6f42014-09-05 08:14:14 -0700340 iterator.remove();
341 }
342 }
343 return events;
344 }
345
346 // Gets the map of ports for the specified device; if one does not already
347 // exist, it creates and registers a new one.
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700348 private ConcurrentMap<PortNumber, Port> getPortMap(DeviceId deviceId) {
349 return createIfAbsentUnchecked(devicePorts, deviceId,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700350 NewConcurrentHashMap.<PortNumber, Port>ifNeeded());
tome5ec3fd2014-09-04 15:18:06 -0700351 }
352
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700353 private Map<ProviderId, DeviceDescriptions> getOrCreateDeviceDescriptions(
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700354 DeviceId deviceId) {
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700355 Map<ProviderId, DeviceDescriptions> r;
356 r = deviceDescs.get(deviceId);
357 if (r != null) {
358 return r;
359 }
360 r = new HashMap<>();
361 final Map<ProviderId, DeviceDescriptions> concurrentlyAdded;
362 concurrentlyAdded = deviceDescs.putIfAbsent(deviceId, r);
363 if (concurrentlyAdded != null) {
364 return concurrentlyAdded;
365 } else {
366 return r;
367 }
368 }
369
370 // Guarded by deviceDescs value (=Device lock)
371 private DeviceDescriptions getOrCreateProviderDeviceDescriptions(
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700372 Map<ProviderId, DeviceDescriptions> device,
373 ProviderId providerId, DeviceDescription deltaDesc) {
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700374 synchronized (device) {
375 DeviceDescriptions r = device.get(providerId);
376 if (r == null) {
377 r = new DeviceDescriptions(deltaDesc);
378 device.put(providerId, r);
379 }
380 return r;
381 }
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700382 }
383
tom41a2c5f2014-09-19 09:20:35 -0700384 @Override
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700385 public DeviceEvent updatePortStatus(ProviderId providerId, DeviceId deviceId,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700386 PortDescription portDescription) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700387 Device device = devices.get(deviceId);
388 checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
389
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700390 Map<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700391 checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId);
392
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700393 synchronized (descsMap) {
394 DeviceDescriptions descs = descsMap.get(providerId);
Yuta HIGUCHIdd841b72014-10-16 13:46:09 -0700395 // assuming all providers must give DeviceDescription first
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700396 checkArgument(descs != null,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700397 "Device description for Device ID %s from Provider %s was not found",
398 deviceId, providerId);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700399
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700400 ConcurrentMap<PortNumber, Port> ports = getPortMap(deviceId);
401 final PortNumber number = portDescription.portNumber();
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700402 final Port oldPort = ports.get(number);
403 final Port newPort;
404
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700405 // update description
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700406 descs.putPortDesc(portDescription);
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700407 newPort = composePort(device, number, descsMap);
408
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700409 if (oldPort == null) {
410 return createPort(device, newPort, ports);
411 } else {
412 return updatePort(device, oldPort, newPort, ports);
413 }
tom46a220d2014-09-05 08:25:56 -0700414 }
tome5ec3fd2014-09-04 15:18:06 -0700415 }
416
tom41a2c5f2014-09-19 09:20:35 -0700417 @Override
418 public List<Port> getPorts(DeviceId deviceId) {
tom46a220d2014-09-05 08:25:56 -0700419 Map<PortNumber, Port> ports = devicePorts.get(deviceId);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700420 if (ports == null) {
421 return Collections.emptyList();
422 }
423 return ImmutableList.copyOf(ports.values());
tome5ec3fd2014-09-04 15:18:06 -0700424 }
425
tom41a2c5f2014-09-19 09:20:35 -0700426 @Override
sangho538108b2015-04-08 14:29:20 -0700427 public DeviceEvent updatePortStatistics(ProviderId providerId, DeviceId deviceId,
Dusan Pajin11ff4a82015-08-20 18:03:05 +0200428 Collection<PortStatistics> newStatsCollection) {
sangho538108b2015-04-08 14:29:20 -0700429
Dusan Pajin11ff4a82015-08-20 18:03:05 +0200430 ConcurrentMap<PortNumber, PortStatistics> prvStatsMap = devicePortStats.get(deviceId);
431 ConcurrentMap<PortNumber, PortStatistics> newStatsMap = Maps.newConcurrentMap();
432 ConcurrentMap<PortNumber, PortStatistics> deltaStatsMap = Maps.newConcurrentMap();
433
434 if (prvStatsMap != null) {
435 for (PortStatistics newStats : newStatsCollection) {
436 PortNumber port = PortNumber.portNumber(newStats.port());
437 PortStatistics prvStats = prvStatsMap.get(port);
438 DefaultPortStatistics.Builder builder = DefaultPortStatistics.builder();
439 PortStatistics deltaStats = builder.build();
440 if (prvStats != null) {
441 deltaStats = calcDeltaStats(deviceId, prvStats, newStats);
442 }
443 deltaStatsMap.put(port, deltaStats);
444 newStatsMap.put(port, newStats);
445 }
446 } else {
447 for (PortStatistics newStats : newStatsCollection) {
448 PortNumber port = PortNumber.portNumber(newStats.port());
449 newStatsMap.put(port, newStats);
450 }
sangho538108b2015-04-08 14:29:20 -0700451 }
Dusan Pajin11ff4a82015-08-20 18:03:05 +0200452 devicePortDeltaStats.put(deviceId, deltaStatsMap);
453 devicePortStats.put(deviceId, newStatsMap);
sangho538108b2015-04-08 14:29:20 -0700454 return new DeviceEvent(PORT_STATS_UPDATED, devices.get(deviceId), null);
455 }
456
Dusan Pajin11ff4a82015-08-20 18:03:05 +0200457 public PortStatistics calcDeltaStats(DeviceId deviceId, PortStatistics prvStats, PortStatistics newStats) {
458 // calculate time difference
459 long deltaStatsSec, deltaStatsNano;
460 if (newStats.durationNano() < prvStats.durationNano()) {
461 deltaStatsNano = newStats.durationNano() - prvStats.durationNano() + TimeUnit.SECONDS.toNanos(1);
462 deltaStatsSec = newStats.durationSec() - prvStats.durationSec() - 1L;
463 } else {
464 deltaStatsNano = newStats.durationNano() - prvStats.durationNano();
465 deltaStatsSec = newStats.durationSec() - prvStats.durationSec();
466 }
467 DefaultPortStatistics.Builder builder = DefaultPortStatistics.builder();
468 DefaultPortStatistics deltaStats = builder.setDeviceId(deviceId)
469 .setPort(newStats.port())
470 .setPacketsReceived(newStats.packetsReceived() - prvStats.packetsReceived())
471 .setPacketsSent(newStats.packetsSent() - prvStats.packetsSent())
472 .setBytesReceived(newStats.bytesReceived() - prvStats.bytesReceived())
473 .setBytesSent(newStats.bytesSent() - prvStats.bytesSent())
474 .setPacketsRxDropped(newStats.packetsRxDropped() - prvStats.packetsRxDropped())
475 .setPacketsTxDropped(newStats.packetsTxDropped() - prvStats.packetsTxDropped())
476 .setPacketsRxErrors(newStats.packetsRxErrors() - prvStats.packetsRxErrors())
477 .setPacketsTxErrors(newStats.packetsTxErrors() - prvStats.packetsTxErrors())
478 .setDurationSec(deltaStatsSec)
479 .setDurationNano(deltaStatsNano)
480 .build();
481 return deltaStats;
482 }
483
sangho538108b2015-04-08 14:29:20 -0700484 @Override
tom41a2c5f2014-09-19 09:20:35 -0700485 public Port getPort(DeviceId deviceId, PortNumber portNumber) {
tom46a220d2014-09-05 08:25:56 -0700486 Map<PortNumber, Port> ports = devicePorts.get(deviceId);
487 return ports == null ? null : ports.get(portNumber);
tome5ec3fd2014-09-04 15:18:06 -0700488 }
489
tom41a2c5f2014-09-19 09:20:35 -0700490 @Override
sangho538108b2015-04-08 14:29:20 -0700491 public List<PortStatistics> getPortStatistics(DeviceId deviceId) {
492 Map<PortNumber, PortStatistics> portStats = devicePortStats.get(deviceId);
493 if (portStats == null) {
494 return Collections.emptyList();
495 }
496 return ImmutableList.copyOf(portStats.values());
497 }
498
499 @Override
Dusan Pajin11ff4a82015-08-20 18:03:05 +0200500 public List<PortStatistics> getPortDeltaStatistics(DeviceId deviceId) {
501 Map<PortNumber, PortStatistics> portStats = devicePortDeltaStats.get(deviceId);
502 if (portStats == null) {
503 return Collections.emptyList();
504 }
505 return ImmutableList.copyOf(portStats.values());
506 }
507
508 @Override
tom41a2c5f2014-09-19 09:20:35 -0700509 public boolean isAvailable(DeviceId deviceId) {
tomff7eb7c2014-09-08 12:49:03 -0700510 return availableDevices.contains(deviceId);
511 }
512
tom41a2c5f2014-09-19 09:20:35 -0700513 @Override
tom41a2c5f2014-09-19 09:20:35 -0700514 public DeviceEvent removeDevice(DeviceId deviceId) {
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700515 Map<ProviderId, DeviceDescriptions> descs = getOrCreateDeviceDescriptions(deviceId);
Yuta HIGUCHI0d6a5e62014-10-03 15:54:09 -0700516 synchronized (descs) {
tome5ec3fd2014-09-04 15:18:06 -0700517 Device device = devices.remove(deviceId);
Yuta HIGUCHI0d6a5e62014-10-03 15:54:09 -0700518 // should DEVICE_REMOVED carry removed ports?
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700519 Map<PortNumber, Port> ports = devicePorts.get(deviceId);
Yuta HIGUCHI0d6a5e62014-10-03 15:54:09 -0700520 if (ports != null) {
521 ports.clear();
522 }
523 availableDevices.remove(deviceId);
524 descs.clear();
tom29df6f42014-09-05 08:14:14 -0700525 return device == null ? null :
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700526 new DeviceEvent(DEVICE_REMOVED, device, null);
tome5ec3fd2014-09-04 15:18:06 -0700527 }
528 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700529
530 /**
531 * Returns a Device, merging description given from multiple Providers.
532 *
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700533 * @param deviceId device identifier
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700534 * @param providerDescs Collection of Descriptions from multiple providers
535 * @return Device instance
536 */
537 private Device composeDevice(DeviceId deviceId,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700538 Map<ProviderId, DeviceDescriptions> providerDescs) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700539
540 checkArgument(!providerDescs.isEmpty(), "No Device descriptions supplied");
541
542 ProviderId primary = pickPrimaryPID(providerDescs);
543
544 DeviceDescriptions desc = providerDescs.get(primary);
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700545
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700546 final DeviceDescription base = desc.getDeviceDesc();
547 Type type = base.type();
548 String manufacturer = base.manufacturer();
549 String hwVersion = base.hwVersion();
550 String swVersion = base.swVersion();
551 String serialNumber = base.serialNumber();
alshabib7911a052014-10-16 17:49:37 -0700552 ChassisId chassisId = base.chassisId();
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700553 DefaultAnnotations annotations = DefaultAnnotations.builder().build();
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700554 annotations = merge(annotations, base.annotations());
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700555
556 for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) {
557 if (e.getKey().equals(primary)) {
558 continue;
559 }
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700560 // TODO: should keep track of Description timestamp
561 // and only merge conflicting keys when timestamp is newer
562 // Currently assuming there will never be a key conflict between
563 // providers
564
565 // annotation merging. not so efficient, should revisit later
566 annotations = merge(annotations, e.getValue().getDeviceDesc().annotations());
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700567 }
568
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700569 return new DefaultDevice(primary, deviceId, type, manufacturer,
570 hwVersion, swVersion, serialNumber,
571 chassisId, annotations);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700572 }
573
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700574 /**
575 * Returns a Port, merging description given from multiple Providers.
576 *
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700577 * @param device device the port is on
578 * @param number port number
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700579 * @param descsMap Collection of Descriptions from multiple providers
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700580 * @return Port instance
581 */
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700582 private Port composePort(Device device, PortNumber number,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700583 Map<ProviderId, DeviceDescriptions> descsMap) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700584
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700585 ProviderId primary = pickPrimaryPID(descsMap);
586 DeviceDescriptions primDescs = descsMap.get(primary);
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700587 // if no primary, assume not enabled
588 // TODO: revisit this default port enabled/disabled behavior
589 boolean isEnabled = false;
590 DefaultAnnotations annotations = DefaultAnnotations.builder().build();
591
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700592 final PortDescription portDesc = primDescs.getPortDesc(number);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700593 if (portDesc != null) {
594 isEnabled = portDesc.isEnabled();
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700595 annotations = merge(annotations, portDesc.annotations());
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700596 }
597
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700598 for (Entry<ProviderId, DeviceDescriptions> e : descsMap.entrySet()) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700599 if (e.getKey().equals(primary)) {
600 continue;
601 }
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700602 // TODO: should keep track of Description timestamp
603 // and only merge conflicting keys when timestamp is newer
604 // Currently assuming there will never be a key conflict between
605 // providers
606
607 // annotation merging. not so efficient, should revisit later
608 final PortDescription otherPortDesc = e.getValue().getPortDesc(number);
609 if (otherPortDesc != null) {
610 annotations = merge(annotations, otherPortDesc.annotations());
611 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700612 }
613
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700614 return portDesc == null ?
615 new DefaultPort(device, number, false, annotations) :
616 new DefaultPort(device, number, isEnabled, portDesc.type(),
617 portDesc.portSpeed(), annotations);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700618 }
619
620 /**
621 * @return primary ProviderID, or randomly chosen one if none exists
622 */
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700623 private ProviderId pickPrimaryPID(Map<ProviderId, DeviceDescriptions> descsMap) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700624 ProviderId fallBackPrimary = null;
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700625 for (Entry<ProviderId, DeviceDescriptions> e : descsMap.entrySet()) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700626 if (!e.getKey().isAncillary()) {
627 return e.getKey();
628 } else if (fallBackPrimary == null) {
629 // pick randomly as a fallback in case there is no primary
630 fallBackPrimary = e.getKey();
631 }
632 }
633 return fallBackPrimary;
634 }
635
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700636 /**
637 * Collection of Description of a Device and it's Ports given from a Provider.
638 */
639 private static class DeviceDescriptions {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700640
641 private final AtomicReference<DeviceDescription> deviceDesc;
642 private final ConcurrentMap<PortNumber, PortDescription> portDescs;
643
644 public DeviceDescriptions(DeviceDescription desc) {
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700645 this.deviceDesc = new AtomicReference<>(checkNotNull(desc));
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700646 this.portDescs = new ConcurrentHashMap<>();
647 }
648
649 public DeviceDescription getDeviceDesc() {
650 return deviceDesc.get();
651 }
652
653 public PortDescription getPortDesc(PortNumber number) {
654 return portDescs.get(number);
655 }
656
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700657 /**
658 * Puts DeviceDescription, merging annotations as necessary.
659 *
660 * @param newDesc new DeviceDescription
661 * @return previous DeviceDescription
662 */
663 public synchronized DeviceDescription putDeviceDesc(DeviceDescription newDesc) {
664 DeviceDescription oldOne = deviceDesc.get();
665 DeviceDescription newOne = newDesc;
666 if (oldOne != null) {
Yuta HIGUCHI885be1d2014-10-04 21:47:26 -0700667 SparseAnnotations merged = union(oldOne.annotations(),
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700668 newDesc.annotations());
669 newOne = new DefaultDeviceDescription(newOne, merged);
670 }
671 return deviceDesc.getAndSet(newOne);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700672 }
673
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700674 /**
675 * Puts PortDescription, merging annotations as necessary.
676 *
677 * @param newDesc new PortDescription
678 * @return previous PortDescription
679 */
680 public synchronized PortDescription putPortDesc(PortDescription newDesc) {
681 PortDescription oldOne = portDescs.get(newDesc.portNumber());
682 PortDescription newOne = newDesc;
683 if (oldOne != null) {
Yuta HIGUCHI885be1d2014-10-04 21:47:26 -0700684 SparseAnnotations merged = union(oldOne.annotations(),
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700685 newDesc.annotations());
686 newOne = new DefaultPortDescription(newOne, merged);
687 }
688 return portDescs.put(newOne.portNumber(), newOne);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700689 }
690 }
tome5ec3fd2014-09-04 15:18:06 -0700691}