blob: 14e37b8cd1a0df6a60eb36703155ebedd6fb2091 [file] [log] [blame]
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present 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 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;
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;
Yuta HIGUCHI6eb00cc2016-06-10 11:55:12 -070062import java.util.Optional;
tome5ec3fd2014-09-04 15:18:06 -070063import 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;
Yuta HIGUCHI6eb00cc2016-06-10 11:55:12 -070068import java.util.stream.Stream;
tome5ec3fd2014-09-04 15:18:06 -070069
70import static com.google.common.base.Preconditions.checkArgument;
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070071import static com.google.common.base.Preconditions.checkNotNull;
Yuta HIGUCHIf5479702014-09-25 00:09:24 -070072import static com.google.common.base.Predicates.notNull;
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -070073import static com.google.common.base.Verify.verify;
Brian O'Connorabafb502014-12-02 22:26:20 -080074import static org.onosproject.net.device.DeviceEvent.Type.*;
tom41a2c5f2014-09-19 09:20:35 -070075import static org.slf4j.LoggerFactory.getLogger;
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070076import static org.apache.commons.lang3.concurrent.ConcurrentUtils.createIfAbsentUnchecked;
Brian O'Connorabafb502014-12-02 22:26:20 -080077import static org.onosproject.net.DefaultAnnotations.union;
78import static org.onosproject.net.DefaultAnnotations.merge;
tome5ec3fd2014-09-04 15:18:06 -070079
80/**
tome4729872014-09-23 00:37:37 -070081 * Manages inventory of infrastructure devices using trivial in-memory
tomcbff9392014-09-10 00:45:23 -070082 * structures implementation.
tome5ec3fd2014-09-04 15:18:06 -070083 */
tom41a2c5f2014-09-19 09:20:35 -070084@Component(immediate = true)
85@Service
tomf80c9722014-09-24 14:49:18 -070086public class SimpleDeviceStore
87 extends AbstractStore<DeviceEvent, DeviceStoreDelegate>
88 implements DeviceStore {
tom41a2c5f2014-09-19 09:20:35 -070089
90 private final Logger log = getLogger(getClass());
tome5ec3fd2014-09-04 15:18:06 -070091
tom29df6f42014-09-05 08:14:14 -070092 public static final String DEVICE_NOT_FOUND = "Device with ID %s not found";
93
Thomas Vachuska56dbeb12014-10-22 16:40:44 -070094 // Collection of Description given from various providers
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -070095 private final ConcurrentMap<DeviceId, Map<ProviderId, DeviceDescriptions>>
Thomas Vachuska56dbeb12014-10-22 16:40:44 -070096 deviceDescs = Maps.newConcurrentMap();
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070097
Thomas Vachuska56dbeb12014-10-22 16:40:44 -070098 // Cache of Device and Ports generated by compositing descriptions from providers
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -070099 private final ConcurrentMap<DeviceId, Device> devices = Maps.newConcurrentMap();
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700100 private final ConcurrentMap<DeviceId, ConcurrentMap<PortNumber, Port>>
101 devicePorts = Maps.newConcurrentMap();
sangho538108b2015-04-08 14:29:20 -0700102 private final ConcurrentMap<DeviceId, ConcurrentMap<PortNumber, PortStatistics>>
103 devicePortStats = Maps.newConcurrentMap();
Dusan Pajin11ff4a82015-08-20 18:03:05 +0200104 private final ConcurrentMap<DeviceId, ConcurrentMap<PortNumber, PortStatistics>>
105 devicePortDeltaStats = Maps.newConcurrentMap();
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700106
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700107 // Available (=UP) devices
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700108 private final Set<DeviceId> availableDevices = Sets.newConcurrentHashSet();
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700109
tome5ec3fd2014-09-04 15:18:06 -0700110
tom41a2c5f2014-09-19 09:20:35 -0700111 @Activate
112 public void activate() {
113 log.info("Started");
114 }
115
116 @Deactivate
117 public void deactivate() {
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700118 deviceDescs.clear();
119 devices.clear();
120 devicePorts.clear();
121 availableDevices.clear();
tom41a2c5f2014-09-19 09:20:35 -0700122 log.info("Stopped");
123 }
tom5bcc9462014-09-19 10:11:31 -0700124
tom41a2c5f2014-09-19 09:20:35 -0700125 @Override
126 public int getDeviceCount() {
tomad2d2092014-09-06 23:24:20 -0700127 return devices.size();
128 }
129
tom41a2c5f2014-09-19 09:20:35 -0700130 @Override
131 public Iterable<Device> getDevices() {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700132 return Collections.unmodifiableCollection(devices.values());
tome5ec3fd2014-09-04 15:18:06 -0700133 }
134
tom41a2c5f2014-09-19 09:20:35 -0700135 @Override
Yuta HIGUCHIf1f2ac02014-11-26 14:02:22 -0800136 public Iterable<Device> getAvailableDevices() {
137 return FluentIterable.from(getDevices())
Sho SHIMIZU74626412015-09-11 11:46:27 -0700138 .filter(input -> isAvailable(input.id()));
Yuta HIGUCHIf1f2ac02014-11-26 14:02:22 -0800139 }
140
141 @Override
tom41a2c5f2014-09-19 09:20:35 -0700142 public Device getDevice(DeviceId deviceId) {
tome5ec3fd2014-09-04 15:18:06 -0700143 return devices.get(deviceId);
144 }
145
tom41a2c5f2014-09-19 09:20:35 -0700146 @Override
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700147 public DeviceEvent createOrUpdateDevice(ProviderId providerId,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700148 DeviceId deviceId,
149 DeviceDescription deviceDescription) {
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700150 Map<ProviderId, DeviceDescriptions> providerDescs
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700151 = getOrCreateDeviceDescriptions(deviceId);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700152
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700153 synchronized (providerDescs) {
154 // locking per device
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700155 DeviceDescriptions descs
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700156 = getOrCreateProviderDeviceDescriptions(providerDescs,
157 providerId,
158 deviceDescription);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700159
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700160 Device oldDevice = devices.get(deviceId);
161 // update description
162 descs.putDeviceDesc(deviceDescription);
163 Device newDevice = composeDevice(deviceId, providerDescs);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700164
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700165 if (oldDevice == null) {
166 // ADD
167 return createDevice(providerId, newDevice);
168 } else {
169 // UPDATE or ignore (no change or stale)
170 return updateDevice(providerId, oldDevice, newDevice);
171 }
tome5ec3fd2014-09-04 15:18:06 -0700172 }
tome5ec3fd2014-09-04 15:18:06 -0700173 }
174
175 // Creates the device and returns the appropriate event if necessary.
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700176 // Guarded by deviceDescs value (=Device lock)
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700177 private DeviceEvent createDevice(ProviderId providerId, Device newDevice) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700178 // update composed device cache
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700179 Device oldDevice = devices.putIfAbsent(newDevice.id(), newDevice);
180 verify(oldDevice == null,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700181 "Unexpected Device in cache. PID:%s [old=%s, new=%s]",
182 providerId, oldDevice, newDevice);
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700183
184 if (!providerId.isAncillary()) {
185 availableDevices.add(newDevice.id());
tome5ec3fd2014-09-04 15:18:06 -0700186 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700187
188 return new DeviceEvent(DeviceEvent.Type.DEVICE_ADDED, newDevice, null);
tome5ec3fd2014-09-04 15:18:06 -0700189 }
190
191 // Updates the device and returns the appropriate event if necessary.
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700192 // Guarded by deviceDescs value (=Device lock)
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700193 private DeviceEvent updateDevice(ProviderId providerId, Device oldDevice, Device newDevice) {
tome5ec3fd2014-09-04 15:18:06 -0700194 // We allow only certain attributes to trigger update
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700195 boolean propertiesChanged =
196 !Objects.equals(oldDevice.hwVersion(), newDevice.hwVersion()) ||
197 !Objects.equals(oldDevice.swVersion(), newDevice.swVersion());
198 boolean annotationsChanged =
199 !AnnotationsUtil.isEqual(oldDevice.annotations(), newDevice.annotations());
200
201 // Primary providers can respond to all changes, but ancillary ones
202 // should respond only to annotation changes.
203 if ((providerId.isAncillary() && annotationsChanged) ||
204 (!providerId.isAncillary() && (propertiesChanged || annotationsChanged))) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700205
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700206 boolean replaced = devices.replace(newDevice.id(), oldDevice, newDevice);
207 if (!replaced) {
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700208 // FIXME: Is the enclosing if required here?
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700209 verify(replaced,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700210 "Replacing devices cache failed. PID:%s [expected:%s, found:%s, new=%s]",
Jian Li68c4fc42016-01-11 16:07:03 -0800211 providerId, oldDevice, devices.get(newDevice.id()), newDevice);
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700212 }
213 if (!providerId.isAncillary()) {
214 availableDevices.add(newDevice.id());
tome5ec3fd2014-09-04 15:18:06 -0700215 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700216 return new DeviceEvent(DeviceEvent.Type.DEVICE_UPDATED, newDevice, null);
tome5ec3fd2014-09-04 15:18:06 -0700217 }
218
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700219 // Otherwise merely attempt to change availability if primary provider
220 if (!providerId.isAncillary()) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700221 boolean added = availableDevices.add(newDevice.id());
tom29df6f42014-09-05 08:14:14 -0700222 return !added ? null :
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700223 new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, newDevice, null);
tome5ec3fd2014-09-04 15:18:06 -0700224 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700225 return null;
tome5ec3fd2014-09-04 15:18:06 -0700226 }
227
tom41a2c5f2014-09-19 09:20:35 -0700228 @Override
229 public DeviceEvent markOffline(DeviceId deviceId) {
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700230 Map<ProviderId, DeviceDescriptions> providerDescs
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700231 = getOrCreateDeviceDescriptions(deviceId);
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700232
233 // locking device
234 synchronized (providerDescs) {
tome5ec3fd2014-09-04 15:18:06 -0700235 Device device = devices.get(deviceId);
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700236 if (device == null) {
237 return null;
238 }
239 boolean removed = availableDevices.remove(deviceId);
240 if (removed) {
241 // TODO: broadcast ... DOWN only?
242 return new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, device, null);
243 }
244 return null;
tome5ec3fd2014-09-04 15:18:06 -0700245 }
246 }
247
helenyrwufd296b62016-06-22 17:43:02 -0700248 // implement differently if desired
249 @Override
250 public boolean markOnline(DeviceId deviceId) {
251 log.warn("Mark online not supported");
252 return false;
253 }
254
tom41a2c5f2014-09-19 09:20:35 -0700255 @Override
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700256 public List<DeviceEvent> updatePorts(ProviderId providerId,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700257 DeviceId deviceId,
258 List<PortDescription> portDescriptions) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700259 Device device = devices.get(deviceId);
Ayaka Koshibe78bcbc12014-11-19 14:28:58 -0800260 if (device == null) {
261 log.debug("Device {} doesn't exist or hasn't been initialized yet", deviceId);
262 return Collections.emptyList();
263 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700264
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700265 Map<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700266 checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId);
267
tom29df6f42014-09-05 08:14:14 -0700268 List<DeviceEvent> events = new ArrayList<>();
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700269 synchronized (descsMap) {
270 DeviceDescriptions descs = descsMap.get(providerId);
271 // every provider must provide DeviceDescription.
272 checkArgument(descs != null,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700273 "Device description for Device ID %s from Provider %s was not found",
274 deviceId, providerId);
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700275
276 Map<PortNumber, Port> ports = getPortMap(deviceId);
tom29df6f42014-09-05 08:14:14 -0700277
278 // Add new ports
279 Set<PortNumber> processed = new HashSet<>();
280 for (PortDescription portDescription : portDescriptions) {
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700281 final PortNumber number = portDescription.portNumber();
282 processed.add(portDescription.portNumber());
283
284 final Port oldPort = ports.get(number);
285 final Port newPort;
286
287// event suppression hook?
288
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700289 // update description
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700290 descs.putPortDesc(portDescription);
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700291 newPort = composePort(device, number, descsMap);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700292
293 events.add(oldPort == null ?
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700294 createPort(device, newPort, ports) :
295 updatePort(device, oldPort, newPort, ports));
tom29df6f42014-09-05 08:14:14 -0700296 }
297
298 events.addAll(pruneOldPorts(device, ports, processed));
299 }
Yuta HIGUCHIf5479702014-09-25 00:09:24 -0700300 return FluentIterable.from(events).filter(notNull()).toList();
tom29df6f42014-09-05 08:14:14 -0700301 }
302
303 // Creates a new port based on the port description adds it to the map and
304 // Returns corresponding event.
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700305 // Guarded by deviceDescs value (=Device lock)
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700306 private DeviceEvent createPort(Device device, Port newPort,
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700307 Map<PortNumber, Port> ports) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700308 ports.put(newPort.number(), newPort);
309 return new DeviceEvent(PORT_ADDED, device, newPort);
tom29df6f42014-09-05 08:14:14 -0700310 }
311
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700312 // Checks if the specified port requires update and if so, it replaces the
tom29df6f42014-09-05 08:14:14 -0700313 // existing entry in the map and returns corresponding event.
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700314 // Guarded by deviceDescs value (=Device lock)
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700315 private DeviceEvent updatePort(Device device, Port oldPort,
316 Port newPort,
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700317 Map<PortNumber, Port> ports) {
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700318 if (oldPort.isEnabled() != newPort.isEnabled() ||
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700319 oldPort.type() != newPort.type() ||
320 oldPort.portSpeed() != newPort.portSpeed() ||
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700321 !AnnotationsUtil.isEqual(oldPort.annotations(), newPort.annotations())) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700322 ports.put(oldPort.number(), newPort);
323 return new DeviceEvent(PORT_UPDATED, device, newPort);
tom29df6f42014-09-05 08:14:14 -0700324 }
325 return null;
326 }
327
328 // Prunes the specified list of ports based on which ports are in the
329 // processed list and returns list of corresponding events.
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700330 // Guarded by deviceDescs value (=Device lock)
tom29df6f42014-09-05 08:14:14 -0700331 private List<DeviceEvent> pruneOldPorts(Device device,
332 Map<PortNumber, Port> ports,
333 Set<PortNumber> processed) {
334 List<DeviceEvent> events = new ArrayList<>();
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700335 Iterator<Entry<PortNumber, Port>> iterator = ports.entrySet().iterator();
tom29df6f42014-09-05 08:14:14 -0700336 while (iterator.hasNext()) {
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700337 Entry<PortNumber, Port> e = iterator.next();
338 PortNumber portNumber = e.getKey();
tom24c55cd2014-09-06 10:47:25 -0700339 if (!processed.contains(portNumber)) {
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700340 events.add(new DeviceEvent(PORT_REMOVED, device, e.getValue()));
tom29df6f42014-09-05 08:14:14 -0700341 iterator.remove();
342 }
343 }
344 return events;
345 }
346
347 // Gets the map of ports for the specified device; if one does not already
348 // exist, it creates and registers a new one.
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700349 private ConcurrentMap<PortNumber, Port> getPortMap(DeviceId deviceId) {
350 return createIfAbsentUnchecked(devicePorts, deviceId,
Sho SHIMIZU21d00692016-08-15 11:15:28 -0700351 NewConcurrentHashMap.ifNeeded());
tome5ec3fd2014-09-04 15:18:06 -0700352 }
353
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700354 private Map<ProviderId, DeviceDescriptions> getOrCreateDeviceDescriptions(
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700355 DeviceId deviceId) {
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700356 Map<ProviderId, DeviceDescriptions> r;
357 r = deviceDescs.get(deviceId);
358 if (r != null) {
359 return r;
360 }
361 r = new HashMap<>();
362 final Map<ProviderId, DeviceDescriptions> concurrentlyAdded;
363 concurrentlyAdded = deviceDescs.putIfAbsent(deviceId, r);
364 if (concurrentlyAdded != null) {
365 return concurrentlyAdded;
366 } else {
367 return r;
368 }
369 }
370
371 // Guarded by deviceDescs value (=Device lock)
372 private DeviceDescriptions getOrCreateProviderDeviceDescriptions(
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700373 Map<ProviderId, DeviceDescriptions> device,
374 ProviderId providerId, DeviceDescription deltaDesc) {
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700375 synchronized (device) {
376 DeviceDescriptions r = device.get(providerId);
377 if (r == null) {
378 r = new DeviceDescriptions(deltaDesc);
379 device.put(providerId, r);
380 }
381 return r;
382 }
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700383 }
384
tom41a2c5f2014-09-19 09:20:35 -0700385 @Override
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700386 public DeviceEvent updatePortStatus(ProviderId providerId, DeviceId deviceId,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700387 PortDescription portDescription) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700388 Device device = devices.get(deviceId);
389 checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
390
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700391 Map<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700392 checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId);
393
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700394 synchronized (descsMap) {
395 DeviceDescriptions descs = descsMap.get(providerId);
Yuta HIGUCHIdd841b72014-10-16 13:46:09 -0700396 // assuming all providers must give DeviceDescription first
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700397 checkArgument(descs != null,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700398 "Device description for Device ID %s from Provider %s was not found",
399 deviceId, providerId);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700400
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700401 ConcurrentMap<PortNumber, Port> ports = getPortMap(deviceId);
402 final PortNumber number = portDescription.portNumber();
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700403 final Port oldPort = ports.get(number);
404 final Port newPort;
405
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700406 // update description
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700407 descs.putPortDesc(portDescription);
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700408 newPort = composePort(device, number, descsMap);
409
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700410 if (oldPort == null) {
411 return createPort(device, newPort, ports);
412 } else {
413 return updatePort(device, oldPort, newPort, ports);
414 }
tom46a220d2014-09-05 08:25:56 -0700415 }
tome5ec3fd2014-09-04 15:18:06 -0700416 }
417
tom41a2c5f2014-09-19 09:20:35 -0700418 @Override
419 public List<Port> getPorts(DeviceId deviceId) {
tom46a220d2014-09-05 08:25:56 -0700420 Map<PortNumber, Port> ports = devicePorts.get(deviceId);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700421 if (ports == null) {
422 return Collections.emptyList();
423 }
424 return ImmutableList.copyOf(ports.values());
tome5ec3fd2014-09-04 15:18:06 -0700425 }
426
tom41a2c5f2014-09-19 09:20:35 -0700427 @Override
Yuta HIGUCHI6eb00cc2016-06-10 11:55:12 -0700428 public Stream<PortDescription> getPortDescriptions(ProviderId providerId,
429 DeviceId deviceId) {
430 return Optional.ofNullable(deviceDescs.get(deviceId))
431 .map(m -> m.get(providerId))
432 .map(descs -> descs.portDescs.values().stream())
433 .orElse(Stream.empty());
434 }
435
436 @Override
sangho538108b2015-04-08 14:29:20 -0700437 public DeviceEvent updatePortStatistics(ProviderId providerId, DeviceId deviceId,
Dusan Pajin11ff4a82015-08-20 18:03:05 +0200438 Collection<PortStatistics> newStatsCollection) {
sangho538108b2015-04-08 14:29:20 -0700439
Dusan Pajin11ff4a82015-08-20 18:03:05 +0200440 ConcurrentMap<PortNumber, PortStatistics> prvStatsMap = devicePortStats.get(deviceId);
441 ConcurrentMap<PortNumber, PortStatistics> newStatsMap = Maps.newConcurrentMap();
442 ConcurrentMap<PortNumber, PortStatistics> deltaStatsMap = Maps.newConcurrentMap();
443
444 if (prvStatsMap != null) {
445 for (PortStatistics newStats : newStatsCollection) {
446 PortNumber port = PortNumber.portNumber(newStats.port());
447 PortStatistics prvStats = prvStatsMap.get(port);
448 DefaultPortStatistics.Builder builder = DefaultPortStatistics.builder();
449 PortStatistics deltaStats = builder.build();
450 if (prvStats != null) {
451 deltaStats = calcDeltaStats(deviceId, prvStats, newStats);
452 }
453 deltaStatsMap.put(port, deltaStats);
454 newStatsMap.put(port, newStats);
455 }
456 } else {
457 for (PortStatistics newStats : newStatsCollection) {
458 PortNumber port = PortNumber.portNumber(newStats.port());
459 newStatsMap.put(port, newStats);
460 }
sangho538108b2015-04-08 14:29:20 -0700461 }
Dusan Pajin11ff4a82015-08-20 18:03:05 +0200462 devicePortDeltaStats.put(deviceId, deltaStatsMap);
463 devicePortStats.put(deviceId, newStatsMap);
sangho538108b2015-04-08 14:29:20 -0700464 return new DeviceEvent(PORT_STATS_UPDATED, devices.get(deviceId), null);
465 }
466
Dusan Pajin11ff4a82015-08-20 18:03:05 +0200467 public PortStatistics calcDeltaStats(DeviceId deviceId, PortStatistics prvStats, PortStatistics newStats) {
468 // calculate time difference
469 long deltaStatsSec, deltaStatsNano;
470 if (newStats.durationNano() < prvStats.durationNano()) {
471 deltaStatsNano = newStats.durationNano() - prvStats.durationNano() + TimeUnit.SECONDS.toNanos(1);
472 deltaStatsSec = newStats.durationSec() - prvStats.durationSec() - 1L;
473 } else {
474 deltaStatsNano = newStats.durationNano() - prvStats.durationNano();
475 deltaStatsSec = newStats.durationSec() - prvStats.durationSec();
476 }
477 DefaultPortStatistics.Builder builder = DefaultPortStatistics.builder();
478 DefaultPortStatistics deltaStats = builder.setDeviceId(deviceId)
479 .setPort(newStats.port())
480 .setPacketsReceived(newStats.packetsReceived() - prvStats.packetsReceived())
481 .setPacketsSent(newStats.packetsSent() - prvStats.packetsSent())
482 .setBytesReceived(newStats.bytesReceived() - prvStats.bytesReceived())
483 .setBytesSent(newStats.bytesSent() - prvStats.bytesSent())
484 .setPacketsRxDropped(newStats.packetsRxDropped() - prvStats.packetsRxDropped())
485 .setPacketsTxDropped(newStats.packetsTxDropped() - prvStats.packetsTxDropped())
486 .setPacketsRxErrors(newStats.packetsRxErrors() - prvStats.packetsRxErrors())
487 .setPacketsTxErrors(newStats.packetsTxErrors() - prvStats.packetsTxErrors())
488 .setDurationSec(deltaStatsSec)
489 .setDurationNano(deltaStatsNano)
490 .build();
491 return deltaStats;
492 }
493
sangho538108b2015-04-08 14:29:20 -0700494 @Override
tom41a2c5f2014-09-19 09:20:35 -0700495 public Port getPort(DeviceId deviceId, PortNumber portNumber) {
tom46a220d2014-09-05 08:25:56 -0700496 Map<PortNumber, Port> ports = devicePorts.get(deviceId);
497 return ports == null ? null : ports.get(portNumber);
tome5ec3fd2014-09-04 15:18:06 -0700498 }
499
tom41a2c5f2014-09-19 09:20:35 -0700500 @Override
Yuta HIGUCHI6eb00cc2016-06-10 11:55:12 -0700501 public PortDescription getPortDescription(ProviderId providerId,
502 DeviceId deviceId,
503 PortNumber portNumber) {
504 return Optional.ofNullable(deviceDescs.get(deviceId))
505 .map(m -> m.get(providerId))
506 .map(descs -> descs.getPortDesc(portNumber))
507 .orElse(null);
508 }
509
510 @Override
sangho538108b2015-04-08 14:29:20 -0700511 public List<PortStatistics> getPortStatistics(DeviceId deviceId) {
512 Map<PortNumber, PortStatistics> portStats = devicePortStats.get(deviceId);
513 if (portStats == null) {
514 return Collections.emptyList();
515 }
516 return ImmutableList.copyOf(portStats.values());
517 }
518
519 @Override
Dusan Pajin11ff4a82015-08-20 18:03:05 +0200520 public List<PortStatistics> getPortDeltaStatistics(DeviceId deviceId) {
521 Map<PortNumber, PortStatistics> portStats = devicePortDeltaStats.get(deviceId);
522 if (portStats == null) {
523 return Collections.emptyList();
524 }
525 return ImmutableList.copyOf(portStats.values());
526 }
527
528 @Override
tom41a2c5f2014-09-19 09:20:35 -0700529 public boolean isAvailable(DeviceId deviceId) {
tomff7eb7c2014-09-08 12:49:03 -0700530 return availableDevices.contains(deviceId);
531 }
532
tom41a2c5f2014-09-19 09:20:35 -0700533 @Override
tom41a2c5f2014-09-19 09:20:35 -0700534 public DeviceEvent removeDevice(DeviceId deviceId) {
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700535 Map<ProviderId, DeviceDescriptions> descs = getOrCreateDeviceDescriptions(deviceId);
Yuta HIGUCHI0d6a5e62014-10-03 15:54:09 -0700536 synchronized (descs) {
tome5ec3fd2014-09-04 15:18:06 -0700537 Device device = devices.remove(deviceId);
Yuta HIGUCHI0d6a5e62014-10-03 15:54:09 -0700538 // should DEVICE_REMOVED carry removed ports?
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700539 Map<PortNumber, Port> ports = devicePorts.get(deviceId);
Yuta HIGUCHI0d6a5e62014-10-03 15:54:09 -0700540 if (ports != null) {
541 ports.clear();
542 }
543 availableDevices.remove(deviceId);
544 descs.clear();
tom29df6f42014-09-05 08:14:14 -0700545 return device == null ? null :
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700546 new DeviceEvent(DEVICE_REMOVED, device, null);
tome5ec3fd2014-09-04 15:18:06 -0700547 }
548 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700549
550 /**
551 * Returns a Device, merging description given from multiple Providers.
552 *
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700553 * @param deviceId device identifier
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700554 * @param providerDescs Collection of Descriptions from multiple providers
555 * @return Device instance
556 */
557 private Device composeDevice(DeviceId deviceId,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700558 Map<ProviderId, DeviceDescriptions> providerDescs) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700559
560 checkArgument(!providerDescs.isEmpty(), "No Device descriptions supplied");
561
Jonathan Hartd9df7bd2015-11-10 17:10:25 -0800562 ProviderId primary = pickPrimaryPid(providerDescs);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700563
564 DeviceDescriptions desc = providerDescs.get(primary);
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700565
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700566 final DeviceDescription base = desc.getDeviceDesc();
567 Type type = base.type();
568 String manufacturer = base.manufacturer();
569 String hwVersion = base.hwVersion();
570 String swVersion = base.swVersion();
571 String serialNumber = base.serialNumber();
alshabib7911a052014-10-16 17:49:37 -0700572 ChassisId chassisId = base.chassisId();
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700573 DefaultAnnotations annotations = DefaultAnnotations.builder().build();
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700574 annotations = merge(annotations, base.annotations());
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700575
576 for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) {
577 if (e.getKey().equals(primary)) {
578 continue;
579 }
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700580 // TODO: should keep track of Description timestamp
581 // and only merge conflicting keys when timestamp is newer
582 // Currently assuming there will never be a key conflict between
583 // providers
584
585 // annotation merging. not so efficient, should revisit later
586 annotations = merge(annotations, e.getValue().getDeviceDesc().annotations());
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700587 }
588
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700589 return new DefaultDevice(primary, deviceId, type, manufacturer,
590 hwVersion, swVersion, serialNumber,
591 chassisId, annotations);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700592 }
593
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700594 /**
595 * Returns a Port, merging description given from multiple Providers.
596 *
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700597 * @param device device the port is on
598 * @param number port number
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700599 * @param descsMap Collection of Descriptions from multiple providers
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700600 * @return Port instance
601 */
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700602 private Port composePort(Device device, PortNumber number,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700603 Map<ProviderId, DeviceDescriptions> descsMap) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700604
Jonathan Hartd9df7bd2015-11-10 17:10:25 -0800605 ProviderId primary = pickPrimaryPid(descsMap);
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700606 DeviceDescriptions primDescs = descsMap.get(primary);
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700607 // if no primary, assume not enabled
608 // TODO: revisit this default port enabled/disabled behavior
609 boolean isEnabled = false;
610 DefaultAnnotations annotations = DefaultAnnotations.builder().build();
611
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700612 final PortDescription portDesc = primDescs.getPortDesc(number);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700613 if (portDesc != null) {
614 isEnabled = portDesc.isEnabled();
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700615 annotations = merge(annotations, portDesc.annotations());
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700616 }
617
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700618 for (Entry<ProviderId, DeviceDescriptions> e : descsMap.entrySet()) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700619 if (e.getKey().equals(primary)) {
620 continue;
621 }
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700622 // TODO: should keep track of Description timestamp
623 // and only merge conflicting keys when timestamp is newer
624 // Currently assuming there will never be a key conflict between
625 // providers
626
627 // annotation merging. not so efficient, should revisit later
628 final PortDescription otherPortDesc = e.getValue().getPortDesc(number);
629 if (otherPortDesc != null) {
630 annotations = merge(annotations, otherPortDesc.annotations());
631 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700632 }
633
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700634 return portDesc == null ?
635 new DefaultPort(device, number, false, annotations) :
636 new DefaultPort(device, number, isEnabled, portDesc.type(),
637 portDesc.portSpeed(), annotations);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700638 }
639
640 /**
641 * @return primary ProviderID, or randomly chosen one if none exists
642 */
Jonathan Hartd9df7bd2015-11-10 17:10:25 -0800643 private ProviderId pickPrimaryPid(Map<ProviderId, DeviceDescriptions> descsMap) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700644 ProviderId fallBackPrimary = null;
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700645 for (Entry<ProviderId, DeviceDescriptions> e : descsMap.entrySet()) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700646 if (!e.getKey().isAncillary()) {
647 return e.getKey();
648 } else if (fallBackPrimary == null) {
649 // pick randomly as a fallback in case there is no primary
650 fallBackPrimary = e.getKey();
651 }
652 }
653 return fallBackPrimary;
654 }
655
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700656 /**
657 * Collection of Description of a Device and it's Ports given from a Provider.
658 */
659 private static class DeviceDescriptions {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700660
661 private final AtomicReference<DeviceDescription> deviceDesc;
662 private final ConcurrentMap<PortNumber, PortDescription> portDescs;
663
664 public DeviceDescriptions(DeviceDescription desc) {
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700665 this.deviceDesc = new AtomicReference<>(checkNotNull(desc));
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700666 this.portDescs = new ConcurrentHashMap<>();
667 }
668
669 public DeviceDescription getDeviceDesc() {
670 return deviceDesc.get();
671 }
672
673 public PortDescription getPortDesc(PortNumber number) {
674 return portDescs.get(number);
675 }
676
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700677 /**
678 * Puts DeviceDescription, merging annotations as necessary.
679 *
680 * @param newDesc new DeviceDescription
681 * @return previous DeviceDescription
682 */
683 public synchronized DeviceDescription putDeviceDesc(DeviceDescription newDesc) {
684 DeviceDescription oldOne = deviceDesc.get();
685 DeviceDescription newOne = newDesc;
686 if (oldOne != null) {
Yuta HIGUCHI885be1d2014-10-04 21:47:26 -0700687 SparseAnnotations merged = union(oldOne.annotations(),
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700688 newDesc.annotations());
689 newOne = new DefaultDeviceDescription(newOne, merged);
690 }
691 return deviceDesc.getAndSet(newOne);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700692 }
693
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700694 /**
695 * Puts PortDescription, merging annotations as necessary.
696 *
697 * @param newDesc new PortDescription
698 * @return previous PortDescription
699 */
700 public synchronized PortDescription putPortDesc(PortDescription newDesc) {
701 PortDescription oldOne = portDescs.get(newDesc.portNumber());
702 PortDescription newOne = newDesc;
703 if (oldOne != null) {
Yuta HIGUCHI885be1d2014-10-04 21:47:26 -0700704 SparseAnnotations merged = union(oldOne.annotations(),
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700705 newDesc.annotations());
706 newOne = new DefaultPortDescription(newOne, merged);
707 }
708 return portDescs.put(newOne.portNumber(), newOne);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700709 }
710 }
tome5ec3fd2014-09-04 15:18:06 -0700711}