blob: 5a4105740005af2e1807368fa389cd699d7b4b93 [file] [log] [blame]
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
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 HIGUCHIf5479702014-09-25 00:09:24 -070018import com.google.common.collect.FluentIterable;
tom46a220d2014-09-05 08:25:56 -070019import com.google.common.collect.ImmutableList;
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -070020import com.google.common.collect.Maps;
21import com.google.common.collect.Sets;
Yuta HIGUCHIf5479702014-09-25 00:09:24 -070022
tom41a2c5f2014-09-19 09:20:35 -070023import org.apache.felix.scr.annotations.Activate;
24import org.apache.felix.scr.annotations.Component;
25import org.apache.felix.scr.annotations.Deactivate;
26import org.apache.felix.scr.annotations.Service;
Brian O'Connorabafb502014-12-02 22:26:20 -080027import org.onosproject.net.AnnotationsUtil;
28import org.onosproject.net.DefaultAnnotations;
29import org.onosproject.net.DefaultDevice;
30import org.onosproject.net.DefaultPort;
31import org.onosproject.net.Device;
32import org.onosproject.net.Device.Type;
33import org.onosproject.net.DeviceId;
34import org.onosproject.net.Port;
35import org.onosproject.net.PortNumber;
36import org.onosproject.net.SparseAnnotations;
37import org.onosproject.net.device.DefaultDeviceDescription;
38import org.onosproject.net.device.DefaultPortDescription;
Dusan Pajin11ff4a82015-08-20 18:03:05 +020039import org.onosproject.net.device.DefaultPortStatistics;
Brian O'Connorabafb502014-12-02 22:26:20 -080040import 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;
tom41a2c5f2014-09-19 09:20:35 -070049import org.slf4j.Logger;
tome5ec3fd2014-09-04 15:18:06 -070050
51import java.util.ArrayList;
sangho538108b2015-04-08 14:29:20 -070052import java.util.Collection;
tome5ec3fd2014-09-04 15:18:06 -070053import java.util.Collections;
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -070054import java.util.HashMap;
tome5ec3fd2014-09-04 15:18:06 -070055import java.util.HashSet;
tom29df6f42014-09-05 08:14:14 -070056import java.util.Iterator;
tome5ec3fd2014-09-04 15:18:06 -070057import java.util.List;
58import java.util.Map;
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070059import java.util.Map.Entry;
tome5ec3fd2014-09-04 15:18:06 -070060import java.util.Objects;
Yuta HIGUCHI6eb00cc2016-06-10 11:55:12 -070061import java.util.Optional;
tome5ec3fd2014-09-04 15:18:06 -070062import java.util.Set;
63import java.util.concurrent.ConcurrentHashMap;
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070064import java.util.concurrent.ConcurrentMap;
Dusan Pajin11ff4a82015-08-20 18:03:05 +020065import java.util.concurrent.TimeUnit;
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070066import java.util.concurrent.atomic.AtomicReference;
Yuta HIGUCHI6eb00cc2016-06-10 11:55:12 -070067import java.util.stream.Stream;
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;
Brian O'Connorabafb502014-12-02 22:26:20 -080075import static org.onosproject.net.DefaultAnnotations.union;
76import static org.onosproject.net.DefaultAnnotations.merge;
tome5ec3fd2014-09-04 15:18:06 -070077
78/**
tome4729872014-09-23 00:37:37 -070079 * Manages inventory of infrastructure devices using trivial in-memory
tomcbff9392014-09-10 00:45:23 -070080 * structures implementation.
tome5ec3fd2014-09-04 15:18:06 -070081 */
tom41a2c5f2014-09-19 09:20:35 -070082@Component(immediate = true)
83@Service
tomf80c9722014-09-24 14:49:18 -070084public class SimpleDeviceStore
85 extends AbstractStore<DeviceEvent, DeviceStoreDelegate>
86 implements DeviceStore {
tom41a2c5f2014-09-19 09:20:35 -070087
88 private final Logger log = getLogger(getClass());
tome5ec3fd2014-09-04 15:18:06 -070089
tom29df6f42014-09-05 08:14:14 -070090 public static final String DEVICE_NOT_FOUND = "Device with ID %s not found";
91
Thomas Vachuska56dbeb12014-10-22 16:40:44 -070092 // Collection of Description given from various providers
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -070093 private final ConcurrentMap<DeviceId, Map<ProviderId, DeviceDescriptions>>
Thomas Vachuska56dbeb12014-10-22 16:40:44 -070094 deviceDescs = Maps.newConcurrentMap();
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070095
Thomas Vachuska56dbeb12014-10-22 16:40:44 -070096 // Cache of Device and Ports generated by compositing descriptions from providers
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -070097 private final ConcurrentMap<DeviceId, Device> devices = Maps.newConcurrentMap();
Thomas Vachuska56dbeb12014-10-22 16:40:44 -070098 private final ConcurrentMap<DeviceId, ConcurrentMap<PortNumber, Port>>
99 devicePorts = Maps.newConcurrentMap();
sangho538108b2015-04-08 14:29:20 -0700100 private final ConcurrentMap<DeviceId, ConcurrentMap<PortNumber, PortStatistics>>
101 devicePortStats = Maps.newConcurrentMap();
Dusan Pajin11ff4a82015-08-20 18:03:05 +0200102 private final ConcurrentMap<DeviceId, ConcurrentMap<PortNumber, PortStatistics>>
103 devicePortDeltaStats = Maps.newConcurrentMap();
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700104
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700105 // Available (=UP) devices
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700106 private final Set<DeviceId> availableDevices = Sets.newConcurrentHashSet();
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700107
tome5ec3fd2014-09-04 15:18:06 -0700108
tom41a2c5f2014-09-19 09:20:35 -0700109 @Activate
110 public void activate() {
111 log.info("Started");
112 }
113
114 @Deactivate
115 public void deactivate() {
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700116 deviceDescs.clear();
117 devices.clear();
118 devicePorts.clear();
119 availableDevices.clear();
tom41a2c5f2014-09-19 09:20:35 -0700120 log.info("Stopped");
121 }
tom5bcc9462014-09-19 10:11:31 -0700122
tom41a2c5f2014-09-19 09:20:35 -0700123 @Override
124 public int getDeviceCount() {
tomad2d2092014-09-06 23:24:20 -0700125 return devices.size();
126 }
127
tom41a2c5f2014-09-19 09:20:35 -0700128 @Override
mskala0d0c6832017-07-12 11:21:23 +0200129 public int getAvailableDeviceCount() {
130 return availableDevices.size();
131 }
132
133 @Override
tom41a2c5f2014-09-19 09:20:35 -0700134 public Iterable<Device> getDevices() {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700135 return Collections.unmodifiableCollection(devices.values());
tome5ec3fd2014-09-04 15:18:06 -0700136 }
137
tom41a2c5f2014-09-19 09:20:35 -0700138 @Override
Yuta HIGUCHIf1f2ac02014-11-26 14:02:22 -0800139 public Iterable<Device> getAvailableDevices() {
140 return FluentIterable.from(getDevices())
Sho SHIMIZU74626412015-09-11 11:46:27 -0700141 .filter(input -> isAvailable(input.id()));
Yuta HIGUCHIf1f2ac02014-11-26 14:02:22 -0800142 }
143
144 @Override
tom41a2c5f2014-09-19 09:20:35 -0700145 public Device getDevice(DeviceId deviceId) {
tome5ec3fd2014-09-04 15:18:06 -0700146 return devices.get(deviceId);
147 }
148
tom41a2c5f2014-09-19 09:20:35 -0700149 @Override
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700150 public DeviceEvent createOrUpdateDevice(ProviderId providerId,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700151 DeviceId deviceId,
152 DeviceDescription deviceDescription) {
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700153 Map<ProviderId, DeviceDescriptions> providerDescs
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700154 = getOrCreateDeviceDescriptions(deviceId);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700155
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700156 synchronized (providerDescs) {
157 // locking per device
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700158 DeviceDescriptions descs
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700159 = getOrCreateProviderDeviceDescriptions(providerDescs,
160 providerId,
161 deviceDescription);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700162
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700163 Device oldDevice = devices.get(deviceId);
164 // update description
165 descs.putDeviceDesc(deviceDescription);
166 Device newDevice = composeDevice(deviceId, providerDescs);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700167
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700168 if (oldDevice == null) {
169 // ADD
170 return createDevice(providerId, newDevice);
171 } else {
172 // UPDATE or ignore (no change or stale)
173 return updateDevice(providerId, oldDevice, newDevice);
174 }
tome5ec3fd2014-09-04 15:18:06 -0700175 }
tome5ec3fd2014-09-04 15:18:06 -0700176 }
177
178 // Creates the device and returns the appropriate event if necessary.
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700179 // Guarded by deviceDescs value (=Device lock)
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700180 private DeviceEvent createDevice(ProviderId providerId, Device newDevice) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700181 // update composed device cache
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700182 Device oldDevice = devices.putIfAbsent(newDevice.id(), newDevice);
183 verify(oldDevice == null,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700184 "Unexpected Device in cache. PID:%s [old=%s, new=%s]",
185 providerId, oldDevice, newDevice);
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700186
187 if (!providerId.isAncillary()) {
188 availableDevices.add(newDevice.id());
tome5ec3fd2014-09-04 15:18:06 -0700189 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700190
191 return new DeviceEvent(DeviceEvent.Type.DEVICE_ADDED, newDevice, null);
tome5ec3fd2014-09-04 15:18:06 -0700192 }
193
194 // Updates the device and returns the appropriate event if necessary.
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700195 // Guarded by deviceDescs value (=Device lock)
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700196 private DeviceEvent updateDevice(ProviderId providerId, Device oldDevice, Device newDevice) {
tome5ec3fd2014-09-04 15:18:06 -0700197 // We allow only certain attributes to trigger update
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700198 boolean propertiesChanged =
199 !Objects.equals(oldDevice.hwVersion(), newDevice.hwVersion()) ||
200 !Objects.equals(oldDevice.swVersion(), newDevice.swVersion());
201 boolean annotationsChanged =
202 !AnnotationsUtil.isEqual(oldDevice.annotations(), newDevice.annotations());
203
204 // Primary providers can respond to all changes, but ancillary ones
205 // should respond only to annotation changes.
206 if ((providerId.isAncillary() && annotationsChanged) ||
207 (!providerId.isAncillary() && (propertiesChanged || annotationsChanged))) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700208
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700209 boolean replaced = devices.replace(newDevice.id(), oldDevice, newDevice);
210 if (!replaced) {
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700211 // FIXME: Is the enclosing if required here?
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700212 verify(replaced,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700213 "Replacing devices cache failed. PID:%s [expected:%s, found:%s, new=%s]",
Jian Li68c4fc42016-01-11 16:07:03 -0800214 providerId, oldDevice, devices.get(newDevice.id()), newDevice);
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700215 }
216 if (!providerId.isAncillary()) {
217 availableDevices.add(newDevice.id());
tome5ec3fd2014-09-04 15:18:06 -0700218 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700219 return new DeviceEvent(DeviceEvent.Type.DEVICE_UPDATED, newDevice, null);
tome5ec3fd2014-09-04 15:18:06 -0700220 }
221
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700222 // Otherwise merely attempt to change availability if primary provider
223 if (!providerId.isAncillary()) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700224 boolean added = availableDevices.add(newDevice.id());
tom29df6f42014-09-05 08:14:14 -0700225 return !added ? null :
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700226 new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, newDevice, null);
tome5ec3fd2014-09-04 15:18:06 -0700227 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700228 return null;
tome5ec3fd2014-09-04 15:18:06 -0700229 }
230
tom41a2c5f2014-09-19 09:20:35 -0700231 @Override
232 public DeviceEvent markOffline(DeviceId deviceId) {
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700233 Map<ProviderId, DeviceDescriptions> providerDescs
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700234 = getOrCreateDeviceDescriptions(deviceId);
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700235
236 // locking device
237 synchronized (providerDescs) {
tome5ec3fd2014-09-04 15:18:06 -0700238 Device device = devices.get(deviceId);
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700239 if (device == null) {
240 return null;
241 }
242 boolean removed = availableDevices.remove(deviceId);
243 if (removed) {
244 // TODO: broadcast ... DOWN only?
245 return new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, device, null);
246 }
247 return null;
tome5ec3fd2014-09-04 15:18:06 -0700248 }
249 }
250
helenyrwufd296b62016-06-22 17:43:02 -0700251 // implement differently if desired
252 @Override
253 public boolean markOnline(DeviceId deviceId) {
254 log.warn("Mark online not supported");
255 return false;
256 }
257
tom41a2c5f2014-09-19 09:20:35 -0700258 @Override
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700259 public List<DeviceEvent> updatePorts(ProviderId providerId,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700260 DeviceId deviceId,
261 List<PortDescription> portDescriptions) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700262 Device device = devices.get(deviceId);
Ayaka Koshibe78bcbc12014-11-19 14:28:58 -0800263 if (device == null) {
264 log.debug("Device {} doesn't exist or hasn't been initialized yet", deviceId);
265 return Collections.emptyList();
266 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700267
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700268 Map<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700269 checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId);
270
tom29df6f42014-09-05 08:14:14 -0700271 List<DeviceEvent> events = new ArrayList<>();
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700272 synchronized (descsMap) {
273 DeviceDescriptions descs = descsMap.get(providerId);
274 // every provider must provide DeviceDescription.
275 checkArgument(descs != null,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700276 "Device description for Device ID %s from Provider %s was not found",
277 deviceId, providerId);
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700278
279 Map<PortNumber, Port> ports = getPortMap(deviceId);
tom29df6f42014-09-05 08:14:14 -0700280
281 // Add new ports
282 Set<PortNumber> processed = new HashSet<>();
283 for (PortDescription portDescription : portDescriptions) {
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700284 final PortNumber number = portDescription.portNumber();
285 processed.add(portDescription.portNumber());
286
287 final Port oldPort = ports.get(number);
288 final Port newPort;
289
290// event suppression hook?
291
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700292 // update description
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700293 descs.putPortDesc(portDescription);
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700294 newPort = composePort(device, number, descsMap);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700295
296 events.add(oldPort == null ?
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700297 createPort(device, newPort, ports) :
298 updatePort(device, oldPort, newPort, ports));
tom29df6f42014-09-05 08:14:14 -0700299 }
300
301 events.addAll(pruneOldPorts(device, ports, processed));
302 }
Yuta HIGUCHIf5479702014-09-25 00:09:24 -0700303 return FluentIterable.from(events).filter(notNull()).toList();
tom29df6f42014-09-05 08:14:14 -0700304 }
305
306 // Creates a new port based on the port description adds it to the map and
307 // Returns corresponding event.
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700308 // Guarded by deviceDescs value (=Device lock)
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700309 private DeviceEvent createPort(Device device, Port newPort,
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700310 Map<PortNumber, Port> ports) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700311 ports.put(newPort.number(), newPort);
312 return new DeviceEvent(PORT_ADDED, device, newPort);
tom29df6f42014-09-05 08:14:14 -0700313 }
314
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700315 // Checks if the specified port requires update and if so, it replaces the
tom29df6f42014-09-05 08:14:14 -0700316 // existing entry in the map and returns corresponding event.
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700317 // Guarded by deviceDescs value (=Device lock)
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700318 private DeviceEvent updatePort(Device device, Port oldPort,
319 Port newPort,
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700320 Map<PortNumber, Port> ports) {
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700321 if (oldPort.isEnabled() != newPort.isEnabled() ||
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700322 oldPort.type() != newPort.type() ||
323 oldPort.portSpeed() != newPort.portSpeed() ||
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700324 !AnnotationsUtil.isEqual(oldPort.annotations(), newPort.annotations())) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700325 ports.put(oldPort.number(), newPort);
326 return new DeviceEvent(PORT_UPDATED, device, newPort);
tom29df6f42014-09-05 08:14:14 -0700327 }
328 return null;
329 }
330
331 // Prunes the specified list of ports based on which ports are in the
332 // processed list and returns list of corresponding events.
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700333 // Guarded by deviceDescs value (=Device lock)
tom29df6f42014-09-05 08:14:14 -0700334 private List<DeviceEvent> pruneOldPorts(Device device,
335 Map<PortNumber, Port> ports,
336 Set<PortNumber> processed) {
337 List<DeviceEvent> events = new ArrayList<>();
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700338 Iterator<Entry<PortNumber, Port>> iterator = ports.entrySet().iterator();
tom29df6f42014-09-05 08:14:14 -0700339 while (iterator.hasNext()) {
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700340 Entry<PortNumber, Port> e = iterator.next();
341 PortNumber portNumber = e.getKey();
tom24c55cd2014-09-06 10:47:25 -0700342 if (!processed.contains(portNumber)) {
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700343 events.add(new DeviceEvent(PORT_REMOVED, device, e.getValue()));
tom29df6f42014-09-05 08:14:14 -0700344 iterator.remove();
345 }
346 }
347 return events;
348 }
349
350 // Gets the map of ports for the specified device; if one does not already
351 // exist, it creates and registers a new one.
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700352 private ConcurrentMap<PortNumber, Port> getPortMap(DeviceId deviceId) {
Yuta HIGUCHIc2e68152016-08-16 13:47:36 -0700353 return devicePorts.computeIfAbsent(deviceId, k -> new ConcurrentHashMap<>());
tome5ec3fd2014-09-04 15:18:06 -0700354 }
355
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700356 private Map<ProviderId, DeviceDescriptions> getOrCreateDeviceDescriptions(
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700357 DeviceId deviceId) {
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700358 Map<ProviderId, DeviceDescriptions> r;
359 r = deviceDescs.get(deviceId);
360 if (r != null) {
361 return r;
362 }
363 r = new HashMap<>();
364 final Map<ProviderId, DeviceDescriptions> concurrentlyAdded;
365 concurrentlyAdded = deviceDescs.putIfAbsent(deviceId, r);
366 if (concurrentlyAdded != null) {
367 return concurrentlyAdded;
368 } else {
369 return r;
370 }
371 }
372
373 // Guarded by deviceDescs value (=Device lock)
374 private DeviceDescriptions getOrCreateProviderDeviceDescriptions(
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700375 Map<ProviderId, DeviceDescriptions> device,
376 ProviderId providerId, DeviceDescription deltaDesc) {
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700377 synchronized (device) {
378 DeviceDescriptions r = device.get(providerId);
379 if (r == null) {
380 r = new DeviceDescriptions(deltaDesc);
381 device.put(providerId, r);
382 }
383 return r;
384 }
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700385 }
386
tom41a2c5f2014-09-19 09:20:35 -0700387 @Override
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700388 public DeviceEvent updatePortStatus(ProviderId providerId, DeviceId deviceId,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700389 PortDescription portDescription) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700390 Device device = devices.get(deviceId);
391 checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
392
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700393 Map<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700394 checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId);
395
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700396 synchronized (descsMap) {
397 DeviceDescriptions descs = descsMap.get(providerId);
Yuta HIGUCHIdd841b72014-10-16 13:46:09 -0700398 // assuming all providers must give DeviceDescription first
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700399 checkArgument(descs != null,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700400 "Device description for Device ID %s from Provider %s was not found",
401 deviceId, providerId);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700402
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700403 ConcurrentMap<PortNumber, Port> ports = getPortMap(deviceId);
404 final PortNumber number = portDescription.portNumber();
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700405 final Port oldPort = ports.get(number);
406 final Port newPort;
407
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700408 // update description
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700409 descs.putPortDesc(portDescription);
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700410 newPort = composePort(device, number, descsMap);
411
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700412 if (oldPort == null) {
413 return createPort(device, newPort, ports);
414 } else {
415 return updatePort(device, oldPort, newPort, ports);
416 }
tom46a220d2014-09-05 08:25:56 -0700417 }
tome5ec3fd2014-09-04 15:18:06 -0700418 }
419
tom41a2c5f2014-09-19 09:20:35 -0700420 @Override
421 public List<Port> getPorts(DeviceId deviceId) {
tom46a220d2014-09-05 08:25:56 -0700422 Map<PortNumber, Port> ports = devicePorts.get(deviceId);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700423 if (ports == null) {
424 return Collections.emptyList();
425 }
426 return ImmutableList.copyOf(ports.values());
tome5ec3fd2014-09-04 15:18:06 -0700427 }
428
tom41a2c5f2014-09-19 09:20:35 -0700429 @Override
Yuta HIGUCHI6eb00cc2016-06-10 11:55:12 -0700430 public Stream<PortDescription> getPortDescriptions(ProviderId providerId,
431 DeviceId deviceId) {
432 return Optional.ofNullable(deviceDescs.get(deviceId))
433 .map(m -> m.get(providerId))
434 .map(descs -> descs.portDescs.values().stream())
435 .orElse(Stream.empty());
436 }
437
438 @Override
sangho538108b2015-04-08 14:29:20 -0700439 public DeviceEvent updatePortStatistics(ProviderId providerId, DeviceId deviceId,
Dusan Pajin11ff4a82015-08-20 18:03:05 +0200440 Collection<PortStatistics> newStatsCollection) {
sangho538108b2015-04-08 14:29:20 -0700441
Dusan Pajin11ff4a82015-08-20 18:03:05 +0200442 ConcurrentMap<PortNumber, PortStatistics> prvStatsMap = devicePortStats.get(deviceId);
443 ConcurrentMap<PortNumber, PortStatistics> newStatsMap = Maps.newConcurrentMap();
444 ConcurrentMap<PortNumber, PortStatistics> deltaStatsMap = Maps.newConcurrentMap();
445
446 if (prvStatsMap != null) {
447 for (PortStatistics newStats : newStatsCollection) {
448 PortNumber port = PortNumber.portNumber(newStats.port());
449 PortStatistics prvStats = prvStatsMap.get(port);
450 DefaultPortStatistics.Builder builder = DefaultPortStatistics.builder();
451 PortStatistics deltaStats = builder.build();
452 if (prvStats != null) {
453 deltaStats = calcDeltaStats(deviceId, prvStats, newStats);
454 }
455 deltaStatsMap.put(port, deltaStats);
456 newStatsMap.put(port, newStats);
457 }
458 } else {
459 for (PortStatistics newStats : newStatsCollection) {
460 PortNumber port = PortNumber.portNumber(newStats.port());
461 newStatsMap.put(port, newStats);
462 }
sangho538108b2015-04-08 14:29:20 -0700463 }
Dusan Pajin11ff4a82015-08-20 18:03:05 +0200464 devicePortDeltaStats.put(deviceId, deltaStatsMap);
465 devicePortStats.put(deviceId, newStatsMap);
sangho538108b2015-04-08 14:29:20 -0700466 return new DeviceEvent(PORT_STATS_UPDATED, devices.get(deviceId), null);
467 }
468
Dusan Pajin11ff4a82015-08-20 18:03:05 +0200469 public PortStatistics calcDeltaStats(DeviceId deviceId, PortStatistics prvStats, PortStatistics newStats) {
470 // calculate time difference
471 long deltaStatsSec, deltaStatsNano;
472 if (newStats.durationNano() < prvStats.durationNano()) {
473 deltaStatsNano = newStats.durationNano() - prvStats.durationNano() + TimeUnit.SECONDS.toNanos(1);
474 deltaStatsSec = newStats.durationSec() - prvStats.durationSec() - 1L;
475 } else {
476 deltaStatsNano = newStats.durationNano() - prvStats.durationNano();
477 deltaStatsSec = newStats.durationSec() - prvStats.durationSec();
478 }
479 DefaultPortStatistics.Builder builder = DefaultPortStatistics.builder();
480 DefaultPortStatistics deltaStats = builder.setDeviceId(deviceId)
481 .setPort(newStats.port())
482 .setPacketsReceived(newStats.packetsReceived() - prvStats.packetsReceived())
483 .setPacketsSent(newStats.packetsSent() - prvStats.packetsSent())
484 .setBytesReceived(newStats.bytesReceived() - prvStats.bytesReceived())
485 .setBytesSent(newStats.bytesSent() - prvStats.bytesSent())
486 .setPacketsRxDropped(newStats.packetsRxDropped() - prvStats.packetsRxDropped())
487 .setPacketsTxDropped(newStats.packetsTxDropped() - prvStats.packetsTxDropped())
488 .setPacketsRxErrors(newStats.packetsRxErrors() - prvStats.packetsRxErrors())
489 .setPacketsTxErrors(newStats.packetsTxErrors() - prvStats.packetsTxErrors())
490 .setDurationSec(deltaStatsSec)
491 .setDurationNano(deltaStatsNano)
492 .build();
493 return deltaStats;
494 }
495
sangho538108b2015-04-08 14:29:20 -0700496 @Override
tom41a2c5f2014-09-19 09:20:35 -0700497 public Port getPort(DeviceId deviceId, PortNumber portNumber) {
tom46a220d2014-09-05 08:25:56 -0700498 Map<PortNumber, Port> ports = devicePorts.get(deviceId);
499 return ports == null ? null : ports.get(portNumber);
tome5ec3fd2014-09-04 15:18:06 -0700500 }
501
tom41a2c5f2014-09-19 09:20:35 -0700502 @Override
Yuta HIGUCHI6eb00cc2016-06-10 11:55:12 -0700503 public PortDescription getPortDescription(ProviderId providerId,
504 DeviceId deviceId,
505 PortNumber portNumber) {
506 return Optional.ofNullable(deviceDescs.get(deviceId))
507 .map(m -> m.get(providerId))
508 .map(descs -> descs.getPortDesc(portNumber))
509 .orElse(null);
510 }
511
512 @Override
sangho538108b2015-04-08 14:29:20 -0700513 public List<PortStatistics> getPortStatistics(DeviceId deviceId) {
514 Map<PortNumber, PortStatistics> portStats = devicePortStats.get(deviceId);
515 if (portStats == null) {
516 return Collections.emptyList();
517 }
518 return ImmutableList.copyOf(portStats.values());
519 }
520
521 @Override
Viswanath KSP22774cd2016-08-20 20:06:30 +0530522 public PortStatistics getStatisticsForPort(DeviceId deviceId, PortNumber portNumber) {
523 Map<PortNumber, PortStatistics> portStatsMap = devicePortStats.get(deviceId);
524 if (portStatsMap == null) {
525 return null;
526 }
527 PortStatistics portStats = portStatsMap.get(portNumber);
528 return portStats;
529 }
530
531 @Override
Dusan Pajin11ff4a82015-08-20 18:03:05 +0200532 public List<PortStatistics> getPortDeltaStatistics(DeviceId deviceId) {
533 Map<PortNumber, PortStatistics> portStats = devicePortDeltaStats.get(deviceId);
534 if (portStats == null) {
535 return Collections.emptyList();
536 }
537 return ImmutableList.copyOf(portStats.values());
538 }
539
540 @Override
Viswanath KSP22774cd2016-08-20 20:06:30 +0530541 public PortStatistics getDeltaStatisticsForPort(DeviceId deviceId, PortNumber portNumber) {
542 Map<PortNumber, PortStatistics> portStatsMap = devicePortDeltaStats.get(deviceId);
543 if (portStatsMap == null) {
544 return null;
545 }
546 PortStatistics portStats = portStatsMap.get(portNumber);
547 return portStats;
548 }
549
550 @Override
tom41a2c5f2014-09-19 09:20:35 -0700551 public boolean isAvailable(DeviceId deviceId) {
tomff7eb7c2014-09-08 12:49:03 -0700552 return availableDevices.contains(deviceId);
553 }
554
tom41a2c5f2014-09-19 09:20:35 -0700555 @Override
tom41a2c5f2014-09-19 09:20:35 -0700556 public DeviceEvent removeDevice(DeviceId deviceId) {
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700557 Map<ProviderId, DeviceDescriptions> descs = getOrCreateDeviceDescriptions(deviceId);
Yuta HIGUCHI0d6a5e62014-10-03 15:54:09 -0700558 synchronized (descs) {
tome5ec3fd2014-09-04 15:18:06 -0700559 Device device = devices.remove(deviceId);
Yuta HIGUCHI0d6a5e62014-10-03 15:54:09 -0700560 // should DEVICE_REMOVED carry removed ports?
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700561 Map<PortNumber, Port> ports = devicePorts.get(deviceId);
Yuta HIGUCHI0d6a5e62014-10-03 15:54:09 -0700562 if (ports != null) {
563 ports.clear();
564 }
565 availableDevices.remove(deviceId);
566 descs.clear();
tom29df6f42014-09-05 08:14:14 -0700567 return device == null ? null :
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700568 new DeviceEvent(DEVICE_REMOVED, device, null);
tome5ec3fd2014-09-04 15:18:06 -0700569 }
570 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700571
572 /**
573 * Returns a Device, merging description given from multiple Providers.
574 *
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700575 * @param deviceId device identifier
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700576 * @param providerDescs Collection of Descriptions from multiple providers
577 * @return Device instance
578 */
579 private Device composeDevice(DeviceId deviceId,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700580 Map<ProviderId, DeviceDescriptions> providerDescs) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700581
582 checkArgument(!providerDescs.isEmpty(), "No Device descriptions supplied");
583
Jonathan Hartd9df7bd2015-11-10 17:10:25 -0800584 ProviderId primary = pickPrimaryPid(providerDescs);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700585
586 DeviceDescriptions desc = providerDescs.get(primary);
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700587
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700588 final DeviceDescription base = desc.getDeviceDesc();
589 Type type = base.type();
590 String manufacturer = base.manufacturer();
591 String hwVersion = base.hwVersion();
592 String swVersion = base.swVersion();
593 String serialNumber = base.serialNumber();
alshabib7911a052014-10-16 17:49:37 -0700594 ChassisId chassisId = base.chassisId();
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700595 DefaultAnnotations annotations = DefaultAnnotations.builder().build();
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700596 annotations = merge(annotations, base.annotations());
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700597
598 for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) {
599 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 annotations = merge(annotations, e.getValue().getDeviceDesc().annotations());
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700609 }
610
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700611 return new DefaultDevice(primary, deviceId, type, manufacturer,
612 hwVersion, swVersion, serialNumber,
613 chassisId, annotations);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700614 }
615
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700616 /**
617 * Returns a Port, merging description given from multiple Providers.
618 *
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700619 * @param device device the port is on
620 * @param number port number
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700621 * @param descsMap Collection of Descriptions from multiple providers
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700622 * @return Port instance
623 */
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700624 private Port composePort(Device device, PortNumber number,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700625 Map<ProviderId, DeviceDescriptions> descsMap) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700626
Jonathan Hartd9df7bd2015-11-10 17:10:25 -0800627 ProviderId primary = pickPrimaryPid(descsMap);
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700628 DeviceDescriptions primDescs = descsMap.get(primary);
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700629 // if no primary, assume not enabled
630 // TODO: revisit this default port enabled/disabled behavior
631 boolean isEnabled = false;
632 DefaultAnnotations annotations = DefaultAnnotations.builder().build();
633
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700634 final PortDescription portDesc = primDescs.getPortDesc(number);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700635 if (portDesc != null) {
636 isEnabled = portDesc.isEnabled();
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700637 annotations = merge(annotations, portDesc.annotations());
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700638 }
639
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700640 for (Entry<ProviderId, DeviceDescriptions> e : descsMap.entrySet()) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700641 if (e.getKey().equals(primary)) {
642 continue;
643 }
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700644 // TODO: should keep track of Description timestamp
645 // and only merge conflicting keys when timestamp is newer
646 // Currently assuming there will never be a key conflict between
647 // providers
648
649 // annotation merging. not so efficient, should revisit later
650 final PortDescription otherPortDesc = e.getValue().getPortDesc(number);
651 if (otherPortDesc != null) {
652 annotations = merge(annotations, otherPortDesc.annotations());
653 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700654 }
655
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700656 return portDesc == null ?
657 new DefaultPort(device, number, false, annotations) :
658 new DefaultPort(device, number, isEnabled, portDesc.type(),
659 portDesc.portSpeed(), annotations);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700660 }
661
662 /**
663 * @return primary ProviderID, or randomly chosen one if none exists
664 */
Jonathan Hartd9df7bd2015-11-10 17:10:25 -0800665 private ProviderId pickPrimaryPid(Map<ProviderId, DeviceDescriptions> descsMap) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700666 ProviderId fallBackPrimary = null;
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700667 for (Entry<ProviderId, DeviceDescriptions> e : descsMap.entrySet()) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700668 if (!e.getKey().isAncillary()) {
669 return e.getKey();
670 } else if (fallBackPrimary == null) {
671 // pick randomly as a fallback in case there is no primary
672 fallBackPrimary = e.getKey();
673 }
674 }
675 return fallBackPrimary;
676 }
677
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700678 /**
679 * Collection of Description of a Device and it's Ports given from a Provider.
680 */
681 private static class DeviceDescriptions {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700682
683 private final AtomicReference<DeviceDescription> deviceDesc;
684 private final ConcurrentMap<PortNumber, PortDescription> portDescs;
685
686 public DeviceDescriptions(DeviceDescription desc) {
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700687 this.deviceDesc = new AtomicReference<>(checkNotNull(desc));
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700688 this.portDescs = new ConcurrentHashMap<>();
689 }
690
691 public DeviceDescription getDeviceDesc() {
692 return deviceDesc.get();
693 }
694
695 public PortDescription getPortDesc(PortNumber number) {
696 return portDescs.get(number);
697 }
698
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700699 /**
700 * Puts DeviceDescription, merging annotations as necessary.
701 *
702 * @param newDesc new DeviceDescription
703 * @return previous DeviceDescription
704 */
705 public synchronized DeviceDescription putDeviceDesc(DeviceDescription newDesc) {
706 DeviceDescription oldOne = deviceDesc.get();
707 DeviceDescription newOne = newDesc;
708 if (oldOne != null) {
Yuta HIGUCHI885be1d2014-10-04 21:47:26 -0700709 SparseAnnotations merged = union(oldOne.annotations(),
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700710 newDesc.annotations());
711 newOne = new DefaultDeviceDescription(newOne, merged);
712 }
713 return deviceDesc.getAndSet(newOne);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700714 }
715
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700716 /**
717 * Puts PortDescription, merging annotations as necessary.
718 *
719 * @param newDesc new PortDescription
720 * @return previous PortDescription
721 */
722 public synchronized PortDescription putPortDesc(PortDescription newDesc) {
723 PortDescription oldOne = portDescs.get(newDesc.portNumber());
724 PortDescription newOne = newDesc;
725 if (oldOne != null) {
Yuta HIGUCHI885be1d2014-10-04 21:47:26 -0700726 SparseAnnotations merged = union(oldOne.annotations(),
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700727 newDesc.annotations());
728 newOne = new DefaultPortDescription(newOne, merged);
729 }
730 return portDescs.put(newOne.portNumber(), newOne);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700731 }
732 }
tome5ec3fd2014-09-04 15:18:06 -0700733}