blob: b538a75e9ad68345b5967a55fdfd2a3781af2d2a [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;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070022import org.onlab.packet.ChassisId;
Brian O'Connorabafb502014-12-02 22:26:20 -080023import org.onosproject.net.AnnotationsUtil;
24import org.onosproject.net.DefaultAnnotations;
25import org.onosproject.net.DefaultDevice;
26import org.onosproject.net.DefaultPort;
27import org.onosproject.net.Device;
28import org.onosproject.net.Device.Type;
29import org.onosproject.net.DeviceId;
30import org.onosproject.net.Port;
31import org.onosproject.net.PortNumber;
32import org.onosproject.net.SparseAnnotations;
33import org.onosproject.net.device.DefaultDeviceDescription;
34import org.onosproject.net.device.DefaultPortDescription;
Dusan Pajin11ff4a82015-08-20 18:03:05 +020035import org.onosproject.net.device.DefaultPortStatistics;
Brian O'Connorabafb502014-12-02 22:26:20 -080036import org.onosproject.net.device.DeviceDescription;
37import org.onosproject.net.device.DeviceEvent;
38import org.onosproject.net.device.DeviceStore;
39import org.onosproject.net.device.DeviceStoreDelegate;
40import org.onosproject.net.device.PortDescription;
sangho538108b2015-04-08 14:29:20 -070041import org.onosproject.net.device.PortStatistics;
Brian O'Connorabafb502014-12-02 22:26:20 -080042import org.onosproject.net.provider.ProviderId;
43import org.onosproject.store.AbstractStore;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070044import org.osgi.service.component.annotations.Activate;
45import org.osgi.service.component.annotations.Component;
46import org.osgi.service.component.annotations.Deactivate;
tom41a2c5f2014-09-19 09:20:35 -070047import org.slf4j.Logger;
tome5ec3fd2014-09-04 15:18:06 -070048
49import java.util.ArrayList;
sangho538108b2015-04-08 14:29:20 -070050import java.util.Collection;
tome5ec3fd2014-09-04 15:18:06 -070051import java.util.Collections;
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -070052import java.util.HashMap;
tome5ec3fd2014-09-04 15:18:06 -070053import java.util.HashSet;
tom29df6f42014-09-05 08:14:14 -070054import java.util.Iterator;
tome5ec3fd2014-09-04 15:18:06 -070055import java.util.List;
56import java.util.Map;
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070057import java.util.Map.Entry;
tome5ec3fd2014-09-04 15:18:06 -070058import java.util.Objects;
Yuta HIGUCHI6eb00cc2016-06-10 11:55:12 -070059import java.util.Optional;
tome5ec3fd2014-09-04 15:18:06 -070060import java.util.Set;
61import java.util.concurrent.ConcurrentHashMap;
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070062import java.util.concurrent.ConcurrentMap;
Dusan Pajin11ff4a82015-08-20 18:03:05 +020063import java.util.concurrent.TimeUnit;
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070064import java.util.concurrent.atomic.AtomicReference;
Yuta HIGUCHI6eb00cc2016-06-10 11:55:12 -070065import java.util.stream.Stream;
tome5ec3fd2014-09-04 15:18:06 -070066
67import static com.google.common.base.Preconditions.checkArgument;
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070068import static com.google.common.base.Preconditions.checkNotNull;
Yuta HIGUCHIf5479702014-09-25 00:09:24 -070069import static com.google.common.base.Predicates.notNull;
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -070070import static com.google.common.base.Verify.verify;
Brian O'Connorabafb502014-12-02 22:26:20 -080071import static org.onosproject.net.DefaultAnnotations.merge;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070072import static org.onosproject.net.DefaultAnnotations.union;
73import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_AVAILABILITY_CHANGED;
74import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_REMOVED;
75import static org.onosproject.net.device.DeviceEvent.Type.PORT_ADDED;
76import static org.onosproject.net.device.DeviceEvent.Type.PORT_REMOVED;
77import static org.onosproject.net.device.DeviceEvent.Type.PORT_STATS_UPDATED;
78import static org.onosproject.net.device.DeviceEvent.Type.PORT_UPDATED;
79import static org.slf4j.LoggerFactory.getLogger;
tome5ec3fd2014-09-04 15:18:06 -070080
81/**
tome4729872014-09-23 00:37:37 -070082 * Manages inventory of infrastructure devices using trivial in-memory
tomcbff9392014-09-10 00:45:23 -070083 * structures implementation.
tome5ec3fd2014-09-04 15:18:06 -070084 */
Ray Milkeyd84f89b2018-08-17 14:54:17 -070085@Component(immediate = true, service = DeviceStore.class)
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
mskala0d0c6832017-07-12 11:21:23 +0200131 public int getAvailableDeviceCount() {
132 return availableDevices.size();
133 }
134
135 @Override
tom41a2c5f2014-09-19 09:20:35 -0700136 public Iterable<Device> getDevices() {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700137 return Collections.unmodifiableCollection(devices.values());
tome5ec3fd2014-09-04 15:18:06 -0700138 }
139
tom41a2c5f2014-09-19 09:20:35 -0700140 @Override
Yuta HIGUCHIf1f2ac02014-11-26 14:02:22 -0800141 public Iterable<Device> getAvailableDevices() {
142 return FluentIterable.from(getDevices())
Sho SHIMIZU74626412015-09-11 11:46:27 -0700143 .filter(input -> isAvailable(input.id()));
Yuta HIGUCHIf1f2ac02014-11-26 14:02:22 -0800144 }
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
pier388ec252020-04-15 20:53:14 +0200170 DeviceEvent event = null;
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700171 if (oldDevice == null) {
172 // ADD
pier388ec252020-04-15 20:53:14 +0200173 event = createDevice(providerId, newDevice);
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700174 } else {
175 // UPDATE or ignore (no change or stale)
pier388ec252020-04-15 20:53:14 +0200176 event = updateDevice(providerId, oldDevice, newDevice);
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700177 }
pier388ec252020-04-15 20:53:14 +0200178 notifyDelegateIfNotNull(event);
179 return event;
tome5ec3fd2014-09-04 15:18:06 -0700180 }
tome5ec3fd2014-09-04 15:18:06 -0700181 }
182
183 // Creates the device and returns the appropriate event if necessary.
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700184 // Guarded by deviceDescs value (=Device lock)
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700185 private DeviceEvent createDevice(ProviderId providerId, Device newDevice) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700186 // update composed device cache
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700187 Device oldDevice = devices.putIfAbsent(newDevice.id(), newDevice);
188 verify(oldDevice == null,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700189 "Unexpected Device in cache. PID:%s [old=%s, new=%s]",
190 providerId, oldDevice, newDevice);
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700191
192 if (!providerId.isAncillary()) {
193 availableDevices.add(newDevice.id());
tome5ec3fd2014-09-04 15:18:06 -0700194 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700195
196 return new DeviceEvent(DeviceEvent.Type.DEVICE_ADDED, newDevice, null);
tome5ec3fd2014-09-04 15:18:06 -0700197 }
198
199 // Updates the device and returns the appropriate event if necessary.
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700200 // Guarded by deviceDescs value (=Device lock)
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700201 private DeviceEvent updateDevice(ProviderId providerId, Device oldDevice, Device newDevice) {
tome5ec3fd2014-09-04 15:18:06 -0700202 // We allow only certain attributes to trigger update
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700203 boolean propertiesChanged =
204 !Objects.equals(oldDevice.hwVersion(), newDevice.hwVersion()) ||
205 !Objects.equals(oldDevice.swVersion(), newDevice.swVersion());
206 boolean annotationsChanged =
207 !AnnotationsUtil.isEqual(oldDevice.annotations(), newDevice.annotations());
208
209 // Primary providers can respond to all changes, but ancillary ones
210 // should respond only to annotation changes.
211 if ((providerId.isAncillary() && annotationsChanged) ||
212 (!providerId.isAncillary() && (propertiesChanged || annotationsChanged))) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700213
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700214 boolean replaced = devices.replace(newDevice.id(), oldDevice, newDevice);
215 if (!replaced) {
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700216 // FIXME: Is the enclosing if required here?
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700217 verify(replaced,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700218 "Replacing devices cache failed. PID:%s [expected:%s, found:%s, new=%s]",
Jian Li68c4fc42016-01-11 16:07:03 -0800219 providerId, oldDevice, devices.get(newDevice.id()), newDevice);
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700220 }
221 if (!providerId.isAncillary()) {
222 availableDevices.add(newDevice.id());
tome5ec3fd2014-09-04 15:18:06 -0700223 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700224 return new DeviceEvent(DeviceEvent.Type.DEVICE_UPDATED, newDevice, null);
tome5ec3fd2014-09-04 15:18:06 -0700225 }
226
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700227 // Otherwise merely attempt to change availability if primary provider
228 if (!providerId.isAncillary()) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700229 boolean added = availableDevices.add(newDevice.id());
tom29df6f42014-09-05 08:14:14 -0700230 return !added ? null :
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700231 new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, newDevice, null);
tome5ec3fd2014-09-04 15:18:06 -0700232 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700233 return null;
tome5ec3fd2014-09-04 15:18:06 -0700234 }
235
tom41a2c5f2014-09-19 09:20:35 -0700236 @Override
237 public DeviceEvent markOffline(DeviceId deviceId) {
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700238 Map<ProviderId, DeviceDescriptions> providerDescs
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700239 = getOrCreateDeviceDescriptions(deviceId);
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700240
241 // locking device
242 synchronized (providerDescs) {
tome5ec3fd2014-09-04 15:18:06 -0700243 Device device = devices.get(deviceId);
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700244 if (device == null) {
245 return null;
246 }
247 boolean removed = availableDevices.remove(deviceId);
248 if (removed) {
249 // TODO: broadcast ... DOWN only?
250 return new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, device, null);
251 }
252 return null;
tome5ec3fd2014-09-04 15:18:06 -0700253 }
254 }
255
helenyrwufd296b62016-06-22 17:43:02 -0700256 // implement differently if desired
257 @Override
Palash Kala6c526062018-04-03 18:25:11 +0900258 public DeviceEvent markOnline(DeviceId deviceId) {
helenyrwufd296b62016-06-22 17:43:02 -0700259 log.warn("Mark online not supported");
Palash Kala6c526062018-04-03 18:25:11 +0900260 return null;
helenyrwufd296b62016-06-22 17:43:02 -0700261 }
262
tom41a2c5f2014-09-19 09:20:35 -0700263 @Override
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700264 public List<DeviceEvent> updatePorts(ProviderId providerId,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700265 DeviceId deviceId,
266 List<PortDescription> portDescriptions) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700267 Device device = devices.get(deviceId);
Ayaka Koshibe78bcbc12014-11-19 14:28:58 -0800268 if (device == null) {
269 log.debug("Device {} doesn't exist or hasn't been initialized yet", deviceId);
270 return Collections.emptyList();
271 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700272
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700273 Map<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700274 checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId);
275
tom29df6f42014-09-05 08:14:14 -0700276 List<DeviceEvent> events = new ArrayList<>();
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700277 synchronized (descsMap) {
278 DeviceDescriptions descs = descsMap.get(providerId);
279 // every provider must provide DeviceDescription.
280 checkArgument(descs != null,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700281 "Device description for Device ID %s from Provider %s was not found",
282 deviceId, providerId);
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700283
284 Map<PortNumber, Port> ports = getPortMap(deviceId);
tom29df6f42014-09-05 08:14:14 -0700285
286 // Add new ports
287 Set<PortNumber> processed = new HashSet<>();
288 for (PortDescription portDescription : portDescriptions) {
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700289 final PortNumber number = portDescription.portNumber();
290 processed.add(portDescription.portNumber());
291
292 final Port oldPort = ports.get(number);
293 final Port newPort;
294
295// event suppression hook?
296
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700297 // update description
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700298 descs.putPortDesc(portDescription);
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700299 newPort = composePort(device, number, descsMap);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700300
301 events.add(oldPort == null ?
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700302 createPort(device, newPort, ports) :
303 updatePort(device, oldPort, newPort, ports));
tom29df6f42014-09-05 08:14:14 -0700304 }
305
306 events.addAll(pruneOldPorts(device, ports, processed));
307 }
Yuta HIGUCHIf5479702014-09-25 00:09:24 -0700308 return FluentIterable.from(events).filter(notNull()).toList();
tom29df6f42014-09-05 08:14:14 -0700309 }
310
311 // Creates a new port based on the port description adds it to the map and
312 // 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 createPort(Device device, Port newPort,
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700315 Map<PortNumber, Port> ports) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700316 ports.put(newPort.number(), newPort);
317 return new DeviceEvent(PORT_ADDED, device, newPort);
tom29df6f42014-09-05 08:14:14 -0700318 }
319
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700320 // Checks if the specified port requires update and if so, it replaces the
tom29df6f42014-09-05 08:14:14 -0700321 // existing entry in the map and returns corresponding event.
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700322 // Guarded by deviceDescs value (=Device lock)
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700323 private DeviceEvent updatePort(Device device, Port oldPort,
324 Port newPort,
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700325 Map<PortNumber, Port> ports) {
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700326 if (oldPort.isEnabled() != newPort.isEnabled() ||
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700327 oldPort.type() != newPort.type() ||
328 oldPort.portSpeed() != newPort.portSpeed() ||
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700329 !AnnotationsUtil.isEqual(oldPort.annotations(), newPort.annotations())) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700330 ports.put(oldPort.number(), newPort);
331 return new DeviceEvent(PORT_UPDATED, device, newPort);
tom29df6f42014-09-05 08:14:14 -0700332 }
333 return null;
334 }
335
336 // Prunes the specified list of ports based on which ports are in the
337 // processed list and returns list of corresponding events.
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700338 // Guarded by deviceDescs value (=Device lock)
tom29df6f42014-09-05 08:14:14 -0700339 private List<DeviceEvent> pruneOldPorts(Device device,
340 Map<PortNumber, Port> ports,
341 Set<PortNumber> processed) {
342 List<DeviceEvent> events = new ArrayList<>();
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700343 Iterator<Entry<PortNumber, Port>> iterator = ports.entrySet().iterator();
tom29df6f42014-09-05 08:14:14 -0700344 while (iterator.hasNext()) {
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700345 Entry<PortNumber, Port> e = iterator.next();
346 PortNumber portNumber = e.getKey();
tom24c55cd2014-09-06 10:47:25 -0700347 if (!processed.contains(portNumber)) {
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700348 events.add(new DeviceEvent(PORT_REMOVED, device, e.getValue()));
tom29df6f42014-09-05 08:14:14 -0700349 iterator.remove();
350 }
351 }
352 return events;
353 }
354
355 // Gets the map of ports for the specified device; if one does not already
356 // exist, it creates and registers a new one.
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700357 private ConcurrentMap<PortNumber, Port> getPortMap(DeviceId deviceId) {
Yuta HIGUCHIc2e68152016-08-16 13:47:36 -0700358 return devicePorts.computeIfAbsent(deviceId, k -> new ConcurrentHashMap<>());
tome5ec3fd2014-09-04 15:18:06 -0700359 }
360
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700361 private Map<ProviderId, DeviceDescriptions> getOrCreateDeviceDescriptions(
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700362 DeviceId deviceId) {
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700363 Map<ProviderId, DeviceDescriptions> r;
364 r = deviceDescs.get(deviceId);
365 if (r != null) {
366 return r;
367 }
368 r = new HashMap<>();
369 final Map<ProviderId, DeviceDescriptions> concurrentlyAdded;
370 concurrentlyAdded = deviceDescs.putIfAbsent(deviceId, r);
371 if (concurrentlyAdded != null) {
372 return concurrentlyAdded;
373 } else {
374 return r;
375 }
376 }
377
378 // Guarded by deviceDescs value (=Device lock)
379 private DeviceDescriptions getOrCreateProviderDeviceDescriptions(
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700380 Map<ProviderId, DeviceDescriptions> device,
381 ProviderId providerId, DeviceDescription deltaDesc) {
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700382 synchronized (device) {
383 DeviceDescriptions r = device.get(providerId);
384 if (r == null) {
385 r = new DeviceDescriptions(deltaDesc);
386 device.put(providerId, r);
387 }
388 return r;
389 }
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700390 }
391
tom41a2c5f2014-09-19 09:20:35 -0700392 @Override
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700393 public DeviceEvent updatePortStatus(ProviderId providerId, DeviceId deviceId,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700394 PortDescription portDescription) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700395 Device device = devices.get(deviceId);
396 checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
397
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700398 Map<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700399 checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId);
400
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700401 synchronized (descsMap) {
402 DeviceDescriptions descs = descsMap.get(providerId);
Yuta HIGUCHIdd841b72014-10-16 13:46:09 -0700403 // assuming all providers must give DeviceDescription first
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700404 checkArgument(descs != null,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700405 "Device description for Device ID %s from Provider %s was not found",
406 deviceId, providerId);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700407
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700408 ConcurrentMap<PortNumber, Port> ports = getPortMap(deviceId);
409 final PortNumber number = portDescription.portNumber();
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700410 final Port oldPort = ports.get(number);
411 final Port newPort;
412
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700413 // update description
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700414 descs.putPortDesc(portDescription);
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700415 newPort = composePort(device, number, descsMap);
416
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700417 if (oldPort == null) {
418 return createPort(device, newPort, ports);
419 } else {
420 return updatePort(device, oldPort, newPort, ports);
421 }
tom46a220d2014-09-05 08:25:56 -0700422 }
tome5ec3fd2014-09-04 15:18:06 -0700423 }
424
tom41a2c5f2014-09-19 09:20:35 -0700425 @Override
426 public List<Port> getPorts(DeviceId deviceId) {
tom46a220d2014-09-05 08:25:56 -0700427 Map<PortNumber, Port> ports = devicePorts.get(deviceId);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700428 if (ports == null) {
429 return Collections.emptyList();
430 }
431 return ImmutableList.copyOf(ports.values());
tome5ec3fd2014-09-04 15:18:06 -0700432 }
433
tom41a2c5f2014-09-19 09:20:35 -0700434 @Override
Yuta HIGUCHI6eb00cc2016-06-10 11:55:12 -0700435 public Stream<PortDescription> getPortDescriptions(ProviderId providerId,
436 DeviceId deviceId) {
437 return Optional.ofNullable(deviceDescs.get(deviceId))
438 .map(m -> m.get(providerId))
439 .map(descs -> descs.portDescs.values().stream())
440 .orElse(Stream.empty());
441 }
442
443 @Override
sangho538108b2015-04-08 14:29:20 -0700444 public DeviceEvent updatePortStatistics(ProviderId providerId, DeviceId deviceId,
Dusan Pajin11ff4a82015-08-20 18:03:05 +0200445 Collection<PortStatistics> newStatsCollection) {
sangho538108b2015-04-08 14:29:20 -0700446
Dusan Pajin11ff4a82015-08-20 18:03:05 +0200447 ConcurrentMap<PortNumber, PortStatistics> prvStatsMap = devicePortStats.get(deviceId);
448 ConcurrentMap<PortNumber, PortStatistics> newStatsMap = Maps.newConcurrentMap();
449 ConcurrentMap<PortNumber, PortStatistics> deltaStatsMap = Maps.newConcurrentMap();
450
451 if (prvStatsMap != null) {
452 for (PortStatistics newStats : newStatsCollection) {
Ray Milkey5ec42082019-02-13 09:56:07 -0800453 PortNumber port = newStats.portNumber();
Dusan Pajin11ff4a82015-08-20 18:03:05 +0200454 PortStatistics prvStats = prvStatsMap.get(port);
455 DefaultPortStatistics.Builder builder = DefaultPortStatistics.builder();
456 PortStatistics deltaStats = builder.build();
457 if (prvStats != null) {
458 deltaStats = calcDeltaStats(deviceId, prvStats, newStats);
459 }
460 deltaStatsMap.put(port, deltaStats);
461 newStatsMap.put(port, newStats);
462 }
463 } else {
464 for (PortStatistics newStats : newStatsCollection) {
Ray Milkey5ec42082019-02-13 09:56:07 -0800465 PortNumber port = newStats.portNumber();
Dusan Pajin11ff4a82015-08-20 18:03:05 +0200466 newStatsMap.put(port, newStats);
467 }
sangho538108b2015-04-08 14:29:20 -0700468 }
Dusan Pajin11ff4a82015-08-20 18:03:05 +0200469 devicePortDeltaStats.put(deviceId, deltaStatsMap);
470 devicePortStats.put(deviceId, newStatsMap);
sangho538108b2015-04-08 14:29:20 -0700471 return new DeviceEvent(PORT_STATS_UPDATED, devices.get(deviceId), null);
472 }
473
Dusan Pajin11ff4a82015-08-20 18:03:05 +0200474 public PortStatistics calcDeltaStats(DeviceId deviceId, PortStatistics prvStats, PortStatistics newStats) {
475 // calculate time difference
476 long deltaStatsSec, deltaStatsNano;
477 if (newStats.durationNano() < prvStats.durationNano()) {
478 deltaStatsNano = newStats.durationNano() - prvStats.durationNano() + TimeUnit.SECONDS.toNanos(1);
479 deltaStatsSec = newStats.durationSec() - prvStats.durationSec() - 1L;
480 } else {
481 deltaStatsNano = newStats.durationNano() - prvStats.durationNano();
482 deltaStatsSec = newStats.durationSec() - prvStats.durationSec();
483 }
484 DefaultPortStatistics.Builder builder = DefaultPortStatistics.builder();
485 DefaultPortStatistics deltaStats = builder.setDeviceId(deviceId)
Ray Milkey5ec42082019-02-13 09:56:07 -0800486 .setPort(newStats.portNumber())
Dusan Pajin11ff4a82015-08-20 18:03:05 +0200487 .setPacketsReceived(newStats.packetsReceived() - prvStats.packetsReceived())
488 .setPacketsSent(newStats.packetsSent() - prvStats.packetsSent())
489 .setBytesReceived(newStats.bytesReceived() - prvStats.bytesReceived())
490 .setBytesSent(newStats.bytesSent() - prvStats.bytesSent())
491 .setPacketsRxDropped(newStats.packetsRxDropped() - prvStats.packetsRxDropped())
492 .setPacketsTxDropped(newStats.packetsTxDropped() - prvStats.packetsTxDropped())
493 .setPacketsRxErrors(newStats.packetsRxErrors() - prvStats.packetsRxErrors())
494 .setPacketsTxErrors(newStats.packetsTxErrors() - prvStats.packetsTxErrors())
495 .setDurationSec(deltaStatsSec)
496 .setDurationNano(deltaStatsNano)
497 .build();
498 return deltaStats;
499 }
500
sangho538108b2015-04-08 14:29:20 -0700501 @Override
tom41a2c5f2014-09-19 09:20:35 -0700502 public Port getPort(DeviceId deviceId, PortNumber portNumber) {
tom46a220d2014-09-05 08:25:56 -0700503 Map<PortNumber, Port> ports = devicePorts.get(deviceId);
504 return ports == null ? null : ports.get(portNumber);
tome5ec3fd2014-09-04 15:18:06 -0700505 }
506
tom41a2c5f2014-09-19 09:20:35 -0700507 @Override
Yuta HIGUCHI6eb00cc2016-06-10 11:55:12 -0700508 public PortDescription getPortDescription(ProviderId providerId,
509 DeviceId deviceId,
510 PortNumber portNumber) {
511 return Optional.ofNullable(deviceDescs.get(deviceId))
512 .map(m -> m.get(providerId))
513 .map(descs -> descs.getPortDesc(portNumber))
514 .orElse(null);
515 }
516
517 @Override
sangho538108b2015-04-08 14:29:20 -0700518 public List<PortStatistics> getPortStatistics(DeviceId deviceId) {
519 Map<PortNumber, PortStatistics> portStats = devicePortStats.get(deviceId);
520 if (portStats == null) {
521 return Collections.emptyList();
522 }
523 return ImmutableList.copyOf(portStats.values());
524 }
525
526 @Override
Viswanath KSP22774cd2016-08-20 20:06:30 +0530527 public PortStatistics getStatisticsForPort(DeviceId deviceId, PortNumber portNumber) {
528 Map<PortNumber, PortStatistics> portStatsMap = devicePortStats.get(deviceId);
529 if (portStatsMap == null) {
530 return null;
531 }
532 PortStatistics portStats = portStatsMap.get(portNumber);
533 return portStats;
534 }
535
536 @Override
Dusan Pajin11ff4a82015-08-20 18:03:05 +0200537 public List<PortStatistics> getPortDeltaStatistics(DeviceId deviceId) {
538 Map<PortNumber, PortStatistics> portStats = devicePortDeltaStats.get(deviceId);
539 if (portStats == null) {
540 return Collections.emptyList();
541 }
542 return ImmutableList.copyOf(portStats.values());
543 }
544
545 @Override
Viswanath KSP22774cd2016-08-20 20:06:30 +0530546 public PortStatistics getDeltaStatisticsForPort(DeviceId deviceId, PortNumber portNumber) {
547 Map<PortNumber, PortStatistics> portStatsMap = devicePortDeltaStats.get(deviceId);
548 if (portStatsMap == null) {
549 return null;
550 }
551 PortStatistics portStats = portStatsMap.get(portNumber);
552 return portStats;
553 }
554
555 @Override
tom41a2c5f2014-09-19 09:20:35 -0700556 public boolean isAvailable(DeviceId deviceId) {
tomff7eb7c2014-09-08 12:49:03 -0700557 return availableDevices.contains(deviceId);
558 }
559
tom41a2c5f2014-09-19 09:20:35 -0700560 @Override
tom41a2c5f2014-09-19 09:20:35 -0700561 public DeviceEvent removeDevice(DeviceId deviceId) {
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700562 Map<ProviderId, DeviceDescriptions> descs = getOrCreateDeviceDescriptions(deviceId);
Yuta HIGUCHI0d6a5e62014-10-03 15:54:09 -0700563 synchronized (descs) {
tome5ec3fd2014-09-04 15:18:06 -0700564 Device device = devices.remove(deviceId);
Yuta HIGUCHI0d6a5e62014-10-03 15:54:09 -0700565 // should DEVICE_REMOVED carry removed ports?
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700566 Map<PortNumber, Port> ports = devicePorts.get(deviceId);
Yuta HIGUCHI0d6a5e62014-10-03 15:54:09 -0700567 if (ports != null) {
568 ports.clear();
569 }
570 availableDevices.remove(deviceId);
571 descs.clear();
tom29df6f42014-09-05 08:14:14 -0700572 return device == null ? null :
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700573 new DeviceEvent(DEVICE_REMOVED, device, null);
tome5ec3fd2014-09-04 15:18:06 -0700574 }
575 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700576
577 /**
578 * Returns a Device, merging description given from multiple Providers.
579 *
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700580 * @param deviceId device identifier
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700581 * @param providerDescs Collection of Descriptions from multiple providers
582 * @return Device instance
583 */
584 private Device composeDevice(DeviceId deviceId,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700585 Map<ProviderId, DeviceDescriptions> providerDescs) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700586
587 checkArgument(!providerDescs.isEmpty(), "No Device descriptions supplied");
588
Jonathan Hartd9df7bd2015-11-10 17:10:25 -0800589 ProviderId primary = pickPrimaryPid(providerDescs);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700590
591 DeviceDescriptions desc = providerDescs.get(primary);
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700592
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700593 final DeviceDescription base = desc.getDeviceDesc();
594 Type type = base.type();
595 String manufacturer = base.manufacturer();
596 String hwVersion = base.hwVersion();
597 String swVersion = base.swVersion();
598 String serialNumber = base.serialNumber();
alshabib7911a052014-10-16 17:49:37 -0700599 ChassisId chassisId = base.chassisId();
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700600 DefaultAnnotations annotations = DefaultAnnotations.builder().build();
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700601 annotations = merge(annotations, base.annotations());
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700602
603 for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) {
604 if (e.getKey().equals(primary)) {
605 continue;
606 }
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700607 // TODO: should keep track of Description timestamp
608 // and only merge conflicting keys when timestamp is newer
609 // Currently assuming there will never be a key conflict between
610 // providers
611
612 // annotation merging. not so efficient, should revisit later
613 annotations = merge(annotations, e.getValue().getDeviceDesc().annotations());
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700614 }
615
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700616 return new DefaultDevice(primary, deviceId, type, manufacturer,
617 hwVersion, swVersion, serialNumber,
618 chassisId, annotations);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700619 }
620
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700621 /**
622 * Returns a Port, merging description given from multiple Providers.
623 *
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700624 * @param device device the port is on
625 * @param number port number
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700626 * @param descsMap Collection of Descriptions from multiple providers
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700627 * @return Port instance
628 */
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700629 private Port composePort(Device device, PortNumber number,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700630 Map<ProviderId, DeviceDescriptions> descsMap) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700631
Jonathan Hartd9df7bd2015-11-10 17:10:25 -0800632 ProviderId primary = pickPrimaryPid(descsMap);
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700633 DeviceDescriptions primDescs = descsMap.get(primary);
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700634 // if no primary, assume not enabled
635 // TODO: revisit this default port enabled/disabled behavior
636 boolean isEnabled = false;
637 DefaultAnnotations annotations = DefaultAnnotations.builder().build();
638
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700639 final PortDescription portDesc = primDescs.getPortDesc(number);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700640 if (portDesc != null) {
641 isEnabled = portDesc.isEnabled();
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700642 annotations = merge(annotations, portDesc.annotations());
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700643 }
644
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().equals(primary)) {
647 continue;
648 }
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700649 // TODO: should keep track of Description timestamp
650 // and only merge conflicting keys when timestamp is newer
651 // Currently assuming there will never be a key conflict between
652 // providers
653
654 // annotation merging. not so efficient, should revisit later
655 final PortDescription otherPortDesc = e.getValue().getPortDesc(number);
656 if (otherPortDesc != null) {
657 annotations = merge(annotations, otherPortDesc.annotations());
658 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700659 }
660
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700661 return portDesc == null ?
662 new DefaultPort(device, number, false, annotations) :
663 new DefaultPort(device, number, isEnabled, portDesc.type(),
664 portDesc.portSpeed(), annotations);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700665 }
666
pier388ec252020-04-15 20:53:14 +0200667 private void notifyDelegateIfNotNull(DeviceEvent event) {
668 if (event != null) {
669 notifyDelegate(event);
670 }
671 }
672
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700673 /**
674 * @return primary ProviderID, or randomly chosen one if none exists
675 */
Jonathan Hartd9df7bd2015-11-10 17:10:25 -0800676 private ProviderId pickPrimaryPid(Map<ProviderId, DeviceDescriptions> descsMap) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700677 ProviderId fallBackPrimary = null;
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700678 for (Entry<ProviderId, DeviceDescriptions> e : descsMap.entrySet()) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700679 if (!e.getKey().isAncillary()) {
680 return e.getKey();
681 } else if (fallBackPrimary == null) {
682 // pick randomly as a fallback in case there is no primary
683 fallBackPrimary = e.getKey();
684 }
685 }
686 return fallBackPrimary;
687 }
688
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700689 /**
690 * Collection of Description of a Device and it's Ports given from a Provider.
691 */
692 private static class DeviceDescriptions {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700693
694 private final AtomicReference<DeviceDescription> deviceDesc;
695 private final ConcurrentMap<PortNumber, PortDescription> portDescs;
696
697 public DeviceDescriptions(DeviceDescription desc) {
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700698 this.deviceDesc = new AtomicReference<>(checkNotNull(desc));
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700699 this.portDescs = new ConcurrentHashMap<>();
700 }
701
702 public DeviceDescription getDeviceDesc() {
703 return deviceDesc.get();
704 }
705
706 public PortDescription getPortDesc(PortNumber number) {
707 return portDescs.get(number);
708 }
709
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700710 /**
711 * Puts DeviceDescription, merging annotations as necessary.
712 *
713 * @param newDesc new DeviceDescription
714 * @return previous DeviceDescription
715 */
716 public synchronized DeviceDescription putDeviceDesc(DeviceDescription newDesc) {
717 DeviceDescription oldOne = deviceDesc.get();
718 DeviceDescription newOne = newDesc;
719 if (oldOne != null) {
Yuta HIGUCHI885be1d2014-10-04 21:47:26 -0700720 SparseAnnotations merged = union(oldOne.annotations(),
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700721 newDesc.annotations());
722 newOne = new DefaultDeviceDescription(newOne, merged);
723 }
724 return deviceDesc.getAndSet(newOne);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700725 }
726
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700727 /**
728 * Puts PortDescription, merging annotations as necessary.
729 *
730 * @param newDesc new PortDescription
731 * @return previous PortDescription
732 */
733 public synchronized PortDescription putPortDesc(PortDescription newDesc) {
734 PortDescription oldOne = portDescs.get(newDesc.portNumber());
735 PortDescription newOne = newDesc;
736 if (oldOne != null) {
Yuta HIGUCHI885be1d2014-10-04 21:47:26 -0700737 SparseAnnotations merged = union(oldOne.annotations(),
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700738 newDesc.annotations());
Yuta HIGUCHI53e47962018-03-01 23:50:48 -0800739 newOne = DefaultPortDescription.builder(newOne)
740 .annotations(merged)
741 .build();
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700742 }
743 return portDescs.put(newOne.portNumber(), newOne);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700744 }
745 }
tome5ec3fd2014-09-04 15:18:06 -0700746}