blob: df6ed3cb4e84186411d5553342f187ef62137faa [file] [log] [blame]
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07001/*
2 * Copyright 2014 Open Networking Laboratory
3 *
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 */
tomea961ff2014-10-01 12:45:15 -070016package org.onlab.onos.store.trivial.impl;
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;
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070027import org.onlab.onos.net.AnnotationsUtil;
Yuta HIGUCHI55710e72014-10-02 14:58:32 -070028import org.onlab.onos.net.DefaultAnnotations;
tome5ec3fd2014-09-04 15:18:06 -070029import org.onlab.onos.net.DefaultDevice;
tom29df6f42014-09-05 08:14:14 -070030import org.onlab.onos.net.DefaultPort;
tome5ec3fd2014-09-04 15:18:06 -070031import org.onlab.onos.net.Device;
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070032import org.onlab.onos.net.Device.Type;
tome5ec3fd2014-09-04 15:18:06 -070033import org.onlab.onos.net.DeviceId;
tome5ec3fd2014-09-04 15:18:06 -070034import org.onlab.onos.net.Port;
35import org.onlab.onos.net.PortNumber;
Yuta HIGUCHI55710e72014-10-02 14:58:32 -070036import org.onlab.onos.net.SparseAnnotations;
37import org.onlab.onos.net.device.DefaultDeviceDescription;
38import org.onlab.onos.net.device.DefaultPortDescription;
tome5ec3fd2014-09-04 15:18:06 -070039import org.onlab.onos.net.device.DeviceDescription;
40import org.onlab.onos.net.device.DeviceEvent;
tom41a2c5f2014-09-19 09:20:35 -070041import org.onlab.onos.net.device.DeviceStore;
tomf80c9722014-09-24 14:49:18 -070042import org.onlab.onos.net.device.DeviceStoreDelegate;
tome5ec3fd2014-09-04 15:18:06 -070043import org.onlab.onos.net.device.PortDescription;
44import org.onlab.onos.net.provider.ProviderId;
tomf80c9722014-09-24 14:49:18 -070045import org.onlab.onos.store.AbstractStore;
alshabib7911a052014-10-16 17:49:37 -070046import org.onlab.packet.ChassisId;
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070047import org.onlab.util.NewConcurrentHashMap;
tom41a2c5f2014-09-19 09:20:35 -070048import org.slf4j.Logger;
tome5ec3fd2014-09-04 15:18:06 -070049
50import java.util.ArrayList;
51import 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;
59import java.util.Set;
60import java.util.concurrent.ConcurrentHashMap;
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070061import java.util.concurrent.ConcurrentMap;
62import java.util.concurrent.atomic.AtomicReference;
tome5ec3fd2014-09-04 15:18:06 -070063
64import static com.google.common.base.Preconditions.checkArgument;
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070065import static com.google.common.base.Preconditions.checkNotNull;
Yuta HIGUCHIf5479702014-09-25 00:09:24 -070066import static com.google.common.base.Predicates.notNull;
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -070067import static com.google.common.base.Verify.verify;
tom29df6f42014-09-05 08:14:14 -070068import static org.onlab.onos.net.device.DeviceEvent.Type.*;
tom41a2c5f2014-09-19 09:20:35 -070069import static org.slf4j.LoggerFactory.getLogger;
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070070import static org.apache.commons.lang3.concurrent.ConcurrentUtils.createIfAbsentUnchecked;
Yuta HIGUCHI885be1d2014-10-04 21:47:26 -070071import static org.onlab.onos.net.DefaultAnnotations.union;
Yuta HIGUCHI55710e72014-10-02 14:58:32 -070072import static org.onlab.onos.net.DefaultAnnotations.merge;
tome5ec3fd2014-09-04 15:18:06 -070073
74/**
tome4729872014-09-23 00:37:37 -070075 * Manages inventory of infrastructure devices using trivial in-memory
tomcbff9392014-09-10 00:45:23 -070076 * structures implementation.
tome5ec3fd2014-09-04 15:18:06 -070077 */
tom41a2c5f2014-09-19 09:20:35 -070078@Component(immediate = true)
79@Service
tomf80c9722014-09-24 14:49:18 -070080public class SimpleDeviceStore
81 extends AbstractStore<DeviceEvent, DeviceStoreDelegate>
82 implements DeviceStore {
tom41a2c5f2014-09-19 09:20:35 -070083
84 private final Logger log = getLogger(getClass());
tome5ec3fd2014-09-04 15:18:06 -070085
tom29df6f42014-09-05 08:14:14 -070086 public static final String DEVICE_NOT_FOUND = "Device with ID %s not found";
87
Thomas Vachuska56dbeb12014-10-22 16:40:44 -070088 // Collection of Description given from various providers
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -070089 private final ConcurrentMap<DeviceId, Map<ProviderId, DeviceDescriptions>>
Thomas Vachuska56dbeb12014-10-22 16:40:44 -070090 deviceDescs = Maps.newConcurrentMap();
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070091
Thomas Vachuska56dbeb12014-10-22 16:40:44 -070092 // Cache of Device and Ports generated by compositing descriptions from providers
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -070093 private final ConcurrentMap<DeviceId, Device> devices = Maps.newConcurrentMap();
Thomas Vachuska56dbeb12014-10-22 16:40:44 -070094 private final ConcurrentMap<DeviceId, ConcurrentMap<PortNumber, Port>>
95 devicePorts = Maps.newConcurrentMap();
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070096
Thomas Vachuska56dbeb12014-10-22 16:40:44 -070097 // Available (=UP) devices
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -070098 private final Set<DeviceId> availableDevices = Sets.newConcurrentHashSet();
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070099
tome5ec3fd2014-09-04 15:18:06 -0700100
tom41a2c5f2014-09-19 09:20:35 -0700101 @Activate
102 public void activate() {
103 log.info("Started");
104 }
105
106 @Deactivate
107 public void deactivate() {
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700108 deviceDescs.clear();
109 devices.clear();
110 devicePorts.clear();
111 availableDevices.clear();
tom41a2c5f2014-09-19 09:20:35 -0700112 log.info("Stopped");
113 }
tom5bcc9462014-09-19 10:11:31 -0700114
tom41a2c5f2014-09-19 09:20:35 -0700115 @Override
116 public int getDeviceCount() {
tomad2d2092014-09-06 23:24:20 -0700117 return devices.size();
118 }
119
tom41a2c5f2014-09-19 09:20:35 -0700120 @Override
121 public Iterable<Device> getDevices() {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700122 return Collections.unmodifiableCollection(devices.values());
tome5ec3fd2014-09-04 15:18:06 -0700123 }
124
tom41a2c5f2014-09-19 09:20:35 -0700125 @Override
126 public Device getDevice(DeviceId deviceId) {
tome5ec3fd2014-09-04 15:18:06 -0700127 return devices.get(deviceId);
128 }
129
tom41a2c5f2014-09-19 09:20:35 -0700130 @Override
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700131 public DeviceEvent createOrUpdateDevice(ProviderId providerId,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700132 DeviceId deviceId,
133 DeviceDescription deviceDescription) {
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700134 Map<ProviderId, DeviceDescriptions> providerDescs
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700135 = getOrCreateDeviceDescriptions(deviceId);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700136
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700137 synchronized (providerDescs) {
138 // locking per device
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700139 DeviceDescriptions descs
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700140 = getOrCreateProviderDeviceDescriptions(providerDescs,
141 providerId,
142 deviceDescription);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700143
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700144 Device oldDevice = devices.get(deviceId);
145 // update description
146 descs.putDeviceDesc(deviceDescription);
147 Device newDevice = composeDevice(deviceId, providerDescs);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700148
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700149 if (oldDevice == null) {
150 // ADD
151 return createDevice(providerId, newDevice);
152 } else {
153 // UPDATE or ignore (no change or stale)
154 return updateDevice(providerId, oldDevice, newDevice);
155 }
tome5ec3fd2014-09-04 15:18:06 -0700156 }
tome5ec3fd2014-09-04 15:18:06 -0700157 }
158
159 // Creates the device and returns the appropriate event if necessary.
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700160 // Guarded by deviceDescs value (=Device lock)
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700161 private DeviceEvent createDevice(ProviderId providerId, Device newDevice) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700162 // update composed device cache
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700163 Device oldDevice = devices.putIfAbsent(newDevice.id(), newDevice);
164 verify(oldDevice == null,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700165 "Unexpected Device in cache. PID:%s [old=%s, new=%s]",
166 providerId, oldDevice, newDevice);
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700167
168 if (!providerId.isAncillary()) {
169 availableDevices.add(newDevice.id());
tome5ec3fd2014-09-04 15:18:06 -0700170 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700171
172 return new DeviceEvent(DeviceEvent.Type.DEVICE_ADDED, newDevice, null);
tome5ec3fd2014-09-04 15:18:06 -0700173 }
174
175 // Updates 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 updateDevice(ProviderId providerId, Device oldDevice, Device newDevice) {
tome5ec3fd2014-09-04 15:18:06 -0700178 // We allow only certain attributes to trigger update
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700179 boolean propertiesChanged =
180 !Objects.equals(oldDevice.hwVersion(), newDevice.hwVersion()) ||
181 !Objects.equals(oldDevice.swVersion(), newDevice.swVersion());
182 boolean annotationsChanged =
183 !AnnotationsUtil.isEqual(oldDevice.annotations(), newDevice.annotations());
184
185 // Primary providers can respond to all changes, but ancillary ones
186 // should respond only to annotation changes.
187 if ((providerId.isAncillary() && annotationsChanged) ||
188 (!providerId.isAncillary() && (propertiesChanged || annotationsChanged))) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700189
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700190 boolean replaced = devices.replace(newDevice.id(), oldDevice, newDevice);
191 if (!replaced) {
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700192 // FIXME: Is the enclosing if required here?
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700193 verify(replaced,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700194 "Replacing devices cache failed. PID:%s [expected:%s, found:%s, new=%s]",
195 providerId, oldDevice, devices.get(newDevice.id())
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700196 , newDevice);
197 }
198 if (!providerId.isAncillary()) {
199 availableDevices.add(newDevice.id());
tome5ec3fd2014-09-04 15:18:06 -0700200 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700201 return new DeviceEvent(DeviceEvent.Type.DEVICE_UPDATED, newDevice, null);
tome5ec3fd2014-09-04 15:18:06 -0700202 }
203
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700204 // Otherwise merely attempt to change availability if primary provider
205 if (!providerId.isAncillary()) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700206 boolean added = availableDevices.add(newDevice.id());
tom29df6f42014-09-05 08:14:14 -0700207 return !added ? null :
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700208 new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, newDevice, null);
tome5ec3fd2014-09-04 15:18:06 -0700209 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700210 return null;
tome5ec3fd2014-09-04 15:18:06 -0700211 }
212
tom41a2c5f2014-09-19 09:20:35 -0700213 @Override
214 public DeviceEvent markOffline(DeviceId deviceId) {
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700215 Map<ProviderId, DeviceDescriptions> providerDescs
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700216 = getOrCreateDeviceDescriptions(deviceId);
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700217
218 // locking device
219 synchronized (providerDescs) {
tome5ec3fd2014-09-04 15:18:06 -0700220 Device device = devices.get(deviceId);
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700221 if (device == null) {
222 return null;
223 }
224 boolean removed = availableDevices.remove(deviceId);
225 if (removed) {
226 // TODO: broadcast ... DOWN only?
227 return new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, device, null);
228 }
229 return null;
tome5ec3fd2014-09-04 15:18:06 -0700230 }
231 }
232
tom41a2c5f2014-09-19 09:20:35 -0700233 @Override
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700234 public List<DeviceEvent> updatePorts(ProviderId providerId,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700235 DeviceId deviceId,
236 List<PortDescription> portDescriptions) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700237 Device device = devices.get(deviceId);
Ayaka Koshibe78bcbc12014-11-19 14:28:58 -0800238 if (device == null) {
239 log.debug("Device {} doesn't exist or hasn't been initialized yet", deviceId);
240 return Collections.emptyList();
241 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700242
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700243 Map<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700244 checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId);
245
tom29df6f42014-09-05 08:14:14 -0700246 List<DeviceEvent> events = new ArrayList<>();
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700247 synchronized (descsMap) {
248 DeviceDescriptions descs = descsMap.get(providerId);
249 // every provider must provide DeviceDescription.
250 checkArgument(descs != null,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700251 "Device description for Device ID %s from Provider %s was not found",
252 deviceId, providerId);
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700253
254 Map<PortNumber, Port> ports = getPortMap(deviceId);
tom29df6f42014-09-05 08:14:14 -0700255
256 // Add new ports
257 Set<PortNumber> processed = new HashSet<>();
258 for (PortDescription portDescription : portDescriptions) {
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700259 final PortNumber number = portDescription.portNumber();
260 processed.add(portDescription.portNumber());
261
262 final Port oldPort = ports.get(number);
263 final Port newPort;
264
265// event suppression hook?
266
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700267 // update description
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700268 descs.putPortDesc(portDescription);
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700269 newPort = composePort(device, number, descsMap);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700270
271 events.add(oldPort == null ?
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700272 createPort(device, newPort, ports) :
273 updatePort(device, oldPort, newPort, ports));
tom29df6f42014-09-05 08:14:14 -0700274 }
275
276 events.addAll(pruneOldPorts(device, ports, processed));
277 }
Yuta HIGUCHIf5479702014-09-25 00:09:24 -0700278 return FluentIterable.from(events).filter(notNull()).toList();
tom29df6f42014-09-05 08:14:14 -0700279 }
280
281 // Creates a new port based on the port description adds it to the map and
282 // Returns corresponding event.
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700283 // Guarded by deviceDescs value (=Device lock)
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700284 private DeviceEvent createPort(Device device, Port newPort,
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700285 Map<PortNumber, Port> ports) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700286 ports.put(newPort.number(), newPort);
287 return new DeviceEvent(PORT_ADDED, device, newPort);
tom29df6f42014-09-05 08:14:14 -0700288 }
289
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700290 // Checks if the specified port requires update and if so, it replaces the
tom29df6f42014-09-05 08:14:14 -0700291 // existing entry in the map and returns corresponding event.
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700292 // Guarded by deviceDescs value (=Device lock)
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700293 private DeviceEvent updatePort(Device device, Port oldPort,
294 Port newPort,
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700295 Map<PortNumber, Port> ports) {
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700296 if (oldPort.isEnabled() != newPort.isEnabled() ||
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700297 oldPort.type() != newPort.type() ||
298 oldPort.portSpeed() != newPort.portSpeed() ||
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700299 !AnnotationsUtil.isEqual(oldPort.annotations(), newPort.annotations())) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700300 ports.put(oldPort.number(), newPort);
301 return new DeviceEvent(PORT_UPDATED, device, newPort);
tom29df6f42014-09-05 08:14:14 -0700302 }
303 return null;
304 }
305
306 // Prunes the specified list of ports based on which ports are in the
307 // processed list and returns list of corresponding events.
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700308 // Guarded by deviceDescs value (=Device lock)
tom29df6f42014-09-05 08:14:14 -0700309 private List<DeviceEvent> pruneOldPorts(Device device,
310 Map<PortNumber, Port> ports,
311 Set<PortNumber> processed) {
312 List<DeviceEvent> events = new ArrayList<>();
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700313 Iterator<Entry<PortNumber, Port>> iterator = ports.entrySet().iterator();
tom29df6f42014-09-05 08:14:14 -0700314 while (iterator.hasNext()) {
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700315 Entry<PortNumber, Port> e = iterator.next();
316 PortNumber portNumber = e.getKey();
tom24c55cd2014-09-06 10:47:25 -0700317 if (!processed.contains(portNumber)) {
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700318 events.add(new DeviceEvent(PORT_REMOVED, device, e.getValue()));
tom29df6f42014-09-05 08:14:14 -0700319 iterator.remove();
320 }
321 }
322 return events;
323 }
324
325 // Gets the map of ports for the specified device; if one does not already
326 // exist, it creates and registers a new one.
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700327 private ConcurrentMap<PortNumber, Port> getPortMap(DeviceId deviceId) {
328 return createIfAbsentUnchecked(devicePorts, deviceId,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700329 NewConcurrentHashMap.<PortNumber, Port>ifNeeded());
tome5ec3fd2014-09-04 15:18:06 -0700330 }
331
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700332 private Map<ProviderId, DeviceDescriptions> getOrCreateDeviceDescriptions(
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700333 DeviceId deviceId) {
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700334 Map<ProviderId, DeviceDescriptions> r;
335 r = deviceDescs.get(deviceId);
336 if (r != null) {
337 return r;
338 }
339 r = new HashMap<>();
340 final Map<ProviderId, DeviceDescriptions> concurrentlyAdded;
341 concurrentlyAdded = deviceDescs.putIfAbsent(deviceId, r);
342 if (concurrentlyAdded != null) {
343 return concurrentlyAdded;
344 } else {
345 return r;
346 }
347 }
348
349 // Guarded by deviceDescs value (=Device lock)
350 private DeviceDescriptions getOrCreateProviderDeviceDescriptions(
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700351 Map<ProviderId, DeviceDescriptions> device,
352 ProviderId providerId, DeviceDescription deltaDesc) {
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700353 synchronized (device) {
354 DeviceDescriptions r = device.get(providerId);
355 if (r == null) {
356 r = new DeviceDescriptions(deltaDesc);
357 device.put(providerId, r);
358 }
359 return r;
360 }
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700361 }
362
tom41a2c5f2014-09-19 09:20:35 -0700363 @Override
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700364 public DeviceEvent updatePortStatus(ProviderId providerId, DeviceId deviceId,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700365 PortDescription portDescription) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700366 Device device = devices.get(deviceId);
367 checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
368
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700369 Map<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700370 checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId);
371
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700372 synchronized (descsMap) {
373 DeviceDescriptions descs = descsMap.get(providerId);
Yuta HIGUCHIdd841b72014-10-16 13:46:09 -0700374 // assuming all providers must give DeviceDescription first
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700375 checkArgument(descs != null,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700376 "Device description for Device ID %s from Provider %s was not found",
377 deviceId, providerId);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700378
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700379 ConcurrentMap<PortNumber, Port> ports = getPortMap(deviceId);
380 final PortNumber number = portDescription.portNumber();
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700381 final Port oldPort = ports.get(number);
382 final Port newPort;
383
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700384 // update description
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700385 descs.putPortDesc(portDescription);
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700386 newPort = composePort(device, number, descsMap);
387
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700388 if (oldPort == null) {
389 return createPort(device, newPort, ports);
390 } else {
391 return updatePort(device, oldPort, newPort, ports);
392 }
tom46a220d2014-09-05 08:25:56 -0700393 }
tome5ec3fd2014-09-04 15:18:06 -0700394 }
395
tom41a2c5f2014-09-19 09:20:35 -0700396 @Override
397 public List<Port> getPorts(DeviceId deviceId) {
tom46a220d2014-09-05 08:25:56 -0700398 Map<PortNumber, Port> ports = devicePorts.get(deviceId);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700399 if (ports == null) {
400 return Collections.emptyList();
401 }
402 return ImmutableList.copyOf(ports.values());
tome5ec3fd2014-09-04 15:18:06 -0700403 }
404
tom41a2c5f2014-09-19 09:20:35 -0700405 @Override
406 public Port getPort(DeviceId deviceId, PortNumber portNumber) {
tom46a220d2014-09-05 08:25:56 -0700407 Map<PortNumber, Port> ports = devicePorts.get(deviceId);
408 return ports == null ? null : ports.get(portNumber);
tome5ec3fd2014-09-04 15:18:06 -0700409 }
410
tom41a2c5f2014-09-19 09:20:35 -0700411 @Override
412 public boolean isAvailable(DeviceId deviceId) {
tomff7eb7c2014-09-08 12:49:03 -0700413 return availableDevices.contains(deviceId);
414 }
415
tom41a2c5f2014-09-19 09:20:35 -0700416 @Override
tom41a2c5f2014-09-19 09:20:35 -0700417 public DeviceEvent removeDevice(DeviceId deviceId) {
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700418 Map<ProviderId, DeviceDescriptions> descs = getOrCreateDeviceDescriptions(deviceId);
Yuta HIGUCHI0d6a5e62014-10-03 15:54:09 -0700419 synchronized (descs) {
tome5ec3fd2014-09-04 15:18:06 -0700420 Device device = devices.remove(deviceId);
Yuta HIGUCHI0d6a5e62014-10-03 15:54:09 -0700421 // should DEVICE_REMOVED carry removed ports?
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700422 Map<PortNumber, Port> ports = devicePorts.get(deviceId);
Yuta HIGUCHI0d6a5e62014-10-03 15:54:09 -0700423 if (ports != null) {
424 ports.clear();
425 }
426 availableDevices.remove(deviceId);
427 descs.clear();
tom29df6f42014-09-05 08:14:14 -0700428 return device == null ? null :
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700429 new DeviceEvent(DEVICE_REMOVED, device, null);
tome5ec3fd2014-09-04 15:18:06 -0700430 }
431 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700432
433 /**
434 * Returns a Device, merging description given from multiple Providers.
435 *
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700436 * @param deviceId device identifier
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700437 * @param providerDescs Collection of Descriptions from multiple providers
438 * @return Device instance
439 */
440 private Device composeDevice(DeviceId deviceId,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700441 Map<ProviderId, DeviceDescriptions> providerDescs) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700442
443 checkArgument(!providerDescs.isEmpty(), "No Device descriptions supplied");
444
445 ProviderId primary = pickPrimaryPID(providerDescs);
446
447 DeviceDescriptions desc = providerDescs.get(primary);
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700448
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700449 final DeviceDescription base = desc.getDeviceDesc();
450 Type type = base.type();
451 String manufacturer = base.manufacturer();
452 String hwVersion = base.hwVersion();
453 String swVersion = base.swVersion();
454 String serialNumber = base.serialNumber();
alshabib7911a052014-10-16 17:49:37 -0700455 ChassisId chassisId = base.chassisId();
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700456 DefaultAnnotations annotations = DefaultAnnotations.builder().build();
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700457 annotations = merge(annotations, base.annotations());
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700458
459 for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) {
460 if (e.getKey().equals(primary)) {
461 continue;
462 }
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700463 // TODO: should keep track of Description timestamp
464 // and only merge conflicting keys when timestamp is newer
465 // Currently assuming there will never be a key conflict between
466 // providers
467
468 // annotation merging. not so efficient, should revisit later
469 annotations = merge(annotations, e.getValue().getDeviceDesc().annotations());
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700470 }
471
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700472 return new DefaultDevice(primary, deviceId, type, manufacturer,
473 hwVersion, swVersion, serialNumber,
474 chassisId, annotations);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700475 }
476
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700477 /**
478 * Returns a Port, merging description given from multiple Providers.
479 *
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700480 * @param device device the port is on
481 * @param number port number
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700482 * @param descsMap Collection of Descriptions from multiple providers
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700483 * @return Port instance
484 */
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700485 private Port composePort(Device device, PortNumber number,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700486 Map<ProviderId, DeviceDescriptions> descsMap) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700487
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700488 ProviderId primary = pickPrimaryPID(descsMap);
489 DeviceDescriptions primDescs = descsMap.get(primary);
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700490 // if no primary, assume not enabled
491 // TODO: revisit this default port enabled/disabled behavior
492 boolean isEnabled = false;
493 DefaultAnnotations annotations = DefaultAnnotations.builder().build();
494
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700495 final PortDescription portDesc = primDescs.getPortDesc(number);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700496 if (portDesc != null) {
497 isEnabled = portDesc.isEnabled();
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700498 annotations = merge(annotations, portDesc.annotations());
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700499 }
500
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700501 for (Entry<ProviderId, DeviceDescriptions> e : descsMap.entrySet()) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700502 if (e.getKey().equals(primary)) {
503 continue;
504 }
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700505 // TODO: should keep track of Description timestamp
506 // and only merge conflicting keys when timestamp is newer
507 // Currently assuming there will never be a key conflict between
508 // providers
509
510 // annotation merging. not so efficient, should revisit later
511 final PortDescription otherPortDesc = e.getValue().getPortDesc(number);
512 if (otherPortDesc != null) {
513 annotations = merge(annotations, otherPortDesc.annotations());
514 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700515 }
516
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700517 return portDesc == null ?
518 new DefaultPort(device, number, false, annotations) :
519 new DefaultPort(device, number, isEnabled, portDesc.type(),
520 portDesc.portSpeed(), annotations);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700521 }
522
523 /**
524 * @return primary ProviderID, or randomly chosen one if none exists
525 */
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700526 private ProviderId pickPrimaryPID(Map<ProviderId, DeviceDescriptions> descsMap) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700527 ProviderId fallBackPrimary = null;
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700528 for (Entry<ProviderId, DeviceDescriptions> e : descsMap.entrySet()) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700529 if (!e.getKey().isAncillary()) {
530 return e.getKey();
531 } else if (fallBackPrimary == null) {
532 // pick randomly as a fallback in case there is no primary
533 fallBackPrimary = e.getKey();
534 }
535 }
536 return fallBackPrimary;
537 }
538
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700539 /**
540 * Collection of Description of a Device and it's Ports given from a Provider.
541 */
542 private static class DeviceDescriptions {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700543
544 private final AtomicReference<DeviceDescription> deviceDesc;
545 private final ConcurrentMap<PortNumber, PortDescription> portDescs;
546
547 public DeviceDescriptions(DeviceDescription desc) {
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700548 this.deviceDesc = new AtomicReference<>(checkNotNull(desc));
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700549 this.portDescs = new ConcurrentHashMap<>();
550 }
551
552 public DeviceDescription getDeviceDesc() {
553 return deviceDesc.get();
554 }
555
556 public PortDescription getPortDesc(PortNumber number) {
557 return portDescs.get(number);
558 }
559
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700560 /**
561 * Puts DeviceDescription, merging annotations as necessary.
562 *
563 * @param newDesc new DeviceDescription
564 * @return previous DeviceDescription
565 */
566 public synchronized DeviceDescription putDeviceDesc(DeviceDescription newDesc) {
567 DeviceDescription oldOne = deviceDesc.get();
568 DeviceDescription newOne = newDesc;
569 if (oldOne != null) {
Yuta HIGUCHI885be1d2014-10-04 21:47:26 -0700570 SparseAnnotations merged = union(oldOne.annotations(),
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700571 newDesc.annotations());
572 newOne = new DefaultDeviceDescription(newOne, merged);
573 }
574 return deviceDesc.getAndSet(newOne);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700575 }
576
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700577 /**
578 * Puts PortDescription, merging annotations as necessary.
579 *
580 * @param newDesc new PortDescription
581 * @return previous PortDescription
582 */
583 public synchronized PortDescription putPortDesc(PortDescription newDesc) {
584 PortDescription oldOne = portDescs.get(newDesc.portNumber());
585 PortDescription newOne = newDesc;
586 if (oldOne != null) {
Yuta HIGUCHI885be1d2014-10-04 21:47:26 -0700587 SparseAnnotations merged = union(oldOne.annotations(),
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700588 newDesc.annotations());
589 newOne = new DefaultPortDescription(newOne, merged);
590 }
591 return portDescs.put(newOne.portNumber(), newOne);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700592 }
593 }
tome5ec3fd2014-09-04 15:18:06 -0700594}