blob: 647b657b8d200f10de6b0a73fb42bdad7fae1282 [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 HIGUCHIf1f2ac02014-11-26 14:02:22 -080018import com.google.common.base.Predicate;
Yuta HIGUCHIf5479702014-09-25 00:09:24 -070019import com.google.common.collect.FluentIterable;
tom46a220d2014-09-05 08:25:56 -070020import com.google.common.collect.ImmutableList;
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -070021import com.google.common.collect.Maps;
22import com.google.common.collect.Sets;
Yuta HIGUCHIf5479702014-09-25 00:09:24 -070023
tom41a2c5f2014-09-19 09:20:35 -070024import org.apache.felix.scr.annotations.Activate;
25import org.apache.felix.scr.annotations.Component;
26import org.apache.felix.scr.annotations.Deactivate;
27import org.apache.felix.scr.annotations.Service;
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070028import org.onlab.onos.net.AnnotationsUtil;
Yuta HIGUCHI55710e72014-10-02 14:58:32 -070029import org.onlab.onos.net.DefaultAnnotations;
tome5ec3fd2014-09-04 15:18:06 -070030import org.onlab.onos.net.DefaultDevice;
tom29df6f42014-09-05 08:14:14 -070031import org.onlab.onos.net.DefaultPort;
tome5ec3fd2014-09-04 15:18:06 -070032import org.onlab.onos.net.Device;
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070033import org.onlab.onos.net.Device.Type;
tome5ec3fd2014-09-04 15:18:06 -070034import org.onlab.onos.net.DeviceId;
tome5ec3fd2014-09-04 15:18:06 -070035import org.onlab.onos.net.Port;
36import org.onlab.onos.net.PortNumber;
Yuta HIGUCHI55710e72014-10-02 14:58:32 -070037import org.onlab.onos.net.SparseAnnotations;
38import org.onlab.onos.net.device.DefaultDeviceDescription;
39import org.onlab.onos.net.device.DefaultPortDescription;
tome5ec3fd2014-09-04 15:18:06 -070040import org.onlab.onos.net.device.DeviceDescription;
41import org.onlab.onos.net.device.DeviceEvent;
tom41a2c5f2014-09-19 09:20:35 -070042import org.onlab.onos.net.device.DeviceStore;
tomf80c9722014-09-24 14:49:18 -070043import org.onlab.onos.net.device.DeviceStoreDelegate;
tome5ec3fd2014-09-04 15:18:06 -070044import org.onlab.onos.net.device.PortDescription;
45import org.onlab.onos.net.provider.ProviderId;
tomf80c9722014-09-24 14:49:18 -070046import org.onlab.onos.store.AbstractStore;
alshabib7911a052014-10-16 17:49:37 -070047import org.onlab.packet.ChassisId;
Yuta HIGUCHI39ede6a2014-10-03 15:23:33 -070048import org.onlab.util.NewConcurrentHashMap;
tom41a2c5f2014-09-19 09:20:35 -070049import org.slf4j.Logger;
tome5ec3fd2014-09-04 15:18:06 -070050
51import java.util.ArrayList;
52import java.util.Collections;
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -070053import java.util.HashMap;
tome5ec3fd2014-09-04 15:18:06 -070054import java.util.HashSet;
tom29df6f42014-09-05 08:14:14 -070055import java.util.Iterator;
tome5ec3fd2014-09-04 15:18:06 -070056import java.util.List;
57import java.util.Map;
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070058import java.util.Map.Entry;
tome5ec3fd2014-09-04 15:18:06 -070059import java.util.Objects;
60import java.util.Set;
61import java.util.concurrent.ConcurrentHashMap;
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070062import java.util.concurrent.ConcurrentMap;
63import java.util.concurrent.atomic.AtomicReference;
tome5ec3fd2014-09-04 15:18:06 -070064
65import static com.google.common.base.Preconditions.checkArgument;
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070066import static com.google.common.base.Preconditions.checkNotNull;
Yuta HIGUCHIf5479702014-09-25 00:09:24 -070067import static com.google.common.base.Predicates.notNull;
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -070068import static com.google.common.base.Verify.verify;
tom29df6f42014-09-05 08:14:14 -070069import static org.onlab.onos.net.device.DeviceEvent.Type.*;
tom41a2c5f2014-09-19 09:20:35 -070070import static org.slf4j.LoggerFactory.getLogger;
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070071import static org.apache.commons.lang3.concurrent.ConcurrentUtils.createIfAbsentUnchecked;
Yuta HIGUCHI885be1d2014-10-04 21:47:26 -070072import static org.onlab.onos.net.DefaultAnnotations.union;
Yuta HIGUCHI55710e72014-10-02 14:58:32 -070073import static org.onlab.onos.net.DefaultAnnotations.merge;
tome5ec3fd2014-09-04 15:18:06 -070074
75/**
tome4729872014-09-23 00:37:37 -070076 * Manages inventory of infrastructure devices using trivial in-memory
tomcbff9392014-09-10 00:45:23 -070077 * structures implementation.
tome5ec3fd2014-09-04 15:18:06 -070078 */
tom41a2c5f2014-09-19 09:20:35 -070079@Component(immediate = true)
80@Service
tomf80c9722014-09-24 14:49:18 -070081public class SimpleDeviceStore
82 extends AbstractStore<DeviceEvent, DeviceStoreDelegate>
83 implements DeviceStore {
tom41a2c5f2014-09-19 09:20:35 -070084
85 private final Logger log = getLogger(getClass());
tome5ec3fd2014-09-04 15:18:06 -070086
tom29df6f42014-09-05 08:14:14 -070087 public static final String DEVICE_NOT_FOUND = "Device with ID %s not found";
88
Thomas Vachuska56dbeb12014-10-22 16:40:44 -070089 // Collection of Description given from various providers
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -070090 private final ConcurrentMap<DeviceId, Map<ProviderId, DeviceDescriptions>>
Thomas Vachuska56dbeb12014-10-22 16:40:44 -070091 deviceDescs = Maps.newConcurrentMap();
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070092
Thomas Vachuska56dbeb12014-10-22 16:40:44 -070093 // Cache of Device and Ports generated by compositing descriptions from providers
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -070094 private final ConcurrentMap<DeviceId, Device> devices = Maps.newConcurrentMap();
Thomas Vachuska56dbeb12014-10-22 16:40:44 -070095 private final ConcurrentMap<DeviceId, ConcurrentMap<PortNumber, Port>>
96 devicePorts = Maps.newConcurrentMap();
Yuta HIGUCHI8e493792014-10-01 19:36:32 -070097
Thomas Vachuska56dbeb12014-10-22 16:40:44 -070098 // Available (=UP) devices
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -070099 private final Set<DeviceId> availableDevices = Sets.newConcurrentHashSet();
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700100
tome5ec3fd2014-09-04 15:18:06 -0700101
tom41a2c5f2014-09-19 09:20:35 -0700102 @Activate
103 public void activate() {
104 log.info("Started");
105 }
106
107 @Deactivate
108 public void deactivate() {
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700109 deviceDescs.clear();
110 devices.clear();
111 devicePorts.clear();
112 availableDevices.clear();
tom41a2c5f2014-09-19 09:20:35 -0700113 log.info("Stopped");
114 }
tom5bcc9462014-09-19 10:11:31 -0700115
tom41a2c5f2014-09-19 09:20:35 -0700116 @Override
117 public int getDeviceCount() {
tomad2d2092014-09-06 23:24:20 -0700118 return devices.size();
119 }
120
tom41a2c5f2014-09-19 09:20:35 -0700121 @Override
122 public Iterable<Device> getDevices() {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700123 return Collections.unmodifiableCollection(devices.values());
tome5ec3fd2014-09-04 15:18:06 -0700124 }
125
tom41a2c5f2014-09-19 09:20:35 -0700126 @Override
Yuta HIGUCHIf1f2ac02014-11-26 14:02:22 -0800127 public Iterable<Device> getAvailableDevices() {
128 return FluentIterable.from(getDevices())
129 .filter(new Predicate<Device>() {
130
131 @Override
132 public boolean apply(Device input) {
133 return isAvailable(input.id());
134 }
135 });
136 }
137
138 @Override
tom41a2c5f2014-09-19 09:20:35 -0700139 public Device getDevice(DeviceId deviceId) {
tome5ec3fd2014-09-04 15:18:06 -0700140 return devices.get(deviceId);
141 }
142
tom41a2c5f2014-09-19 09:20:35 -0700143 @Override
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700144 public DeviceEvent createOrUpdateDevice(ProviderId providerId,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700145 DeviceId deviceId,
146 DeviceDescription deviceDescription) {
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700147 Map<ProviderId, DeviceDescriptions> providerDescs
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700148 = getOrCreateDeviceDescriptions(deviceId);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700149
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700150 synchronized (providerDescs) {
151 // locking per device
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700152 DeviceDescriptions descs
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700153 = getOrCreateProviderDeviceDescriptions(providerDescs,
154 providerId,
155 deviceDescription);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700156
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700157 Device oldDevice = devices.get(deviceId);
158 // update description
159 descs.putDeviceDesc(deviceDescription);
160 Device newDevice = composeDevice(deviceId, providerDescs);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700161
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700162 if (oldDevice == null) {
163 // ADD
164 return createDevice(providerId, newDevice);
165 } else {
166 // UPDATE or ignore (no change or stale)
167 return updateDevice(providerId, oldDevice, newDevice);
168 }
tome5ec3fd2014-09-04 15:18:06 -0700169 }
tome5ec3fd2014-09-04 15:18:06 -0700170 }
171
172 // Creates the device and returns the appropriate event if necessary.
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700173 // Guarded by deviceDescs value (=Device lock)
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700174 private DeviceEvent createDevice(ProviderId providerId, Device newDevice) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700175 // update composed device cache
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700176 Device oldDevice = devices.putIfAbsent(newDevice.id(), newDevice);
177 verify(oldDevice == null,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700178 "Unexpected Device in cache. PID:%s [old=%s, new=%s]",
179 providerId, oldDevice, newDevice);
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700180
181 if (!providerId.isAncillary()) {
182 availableDevices.add(newDevice.id());
tome5ec3fd2014-09-04 15:18:06 -0700183 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700184
185 return new DeviceEvent(DeviceEvent.Type.DEVICE_ADDED, newDevice, null);
tome5ec3fd2014-09-04 15:18:06 -0700186 }
187
188 // Updates the device and returns the appropriate event if necessary.
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700189 // Guarded by deviceDescs value (=Device lock)
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700190 private DeviceEvent updateDevice(ProviderId providerId, Device oldDevice, Device newDevice) {
tome5ec3fd2014-09-04 15:18:06 -0700191 // We allow only certain attributes to trigger update
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700192 boolean propertiesChanged =
193 !Objects.equals(oldDevice.hwVersion(), newDevice.hwVersion()) ||
194 !Objects.equals(oldDevice.swVersion(), newDevice.swVersion());
195 boolean annotationsChanged =
196 !AnnotationsUtil.isEqual(oldDevice.annotations(), newDevice.annotations());
197
198 // Primary providers can respond to all changes, but ancillary ones
199 // should respond only to annotation changes.
200 if ((providerId.isAncillary() && annotationsChanged) ||
201 (!providerId.isAncillary() && (propertiesChanged || annotationsChanged))) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700202
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700203 boolean replaced = devices.replace(newDevice.id(), oldDevice, newDevice);
204 if (!replaced) {
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700205 // FIXME: Is the enclosing if required here?
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700206 verify(replaced,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700207 "Replacing devices cache failed. PID:%s [expected:%s, found:%s, new=%s]",
208 providerId, oldDevice, devices.get(newDevice.id())
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700209 , newDevice);
210 }
211 if (!providerId.isAncillary()) {
212 availableDevices.add(newDevice.id());
tome5ec3fd2014-09-04 15:18:06 -0700213 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700214 return new DeviceEvent(DeviceEvent.Type.DEVICE_UPDATED, newDevice, null);
tome5ec3fd2014-09-04 15:18:06 -0700215 }
216
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700217 // Otherwise merely attempt to change availability if primary provider
218 if (!providerId.isAncillary()) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700219 boolean added = availableDevices.add(newDevice.id());
tom29df6f42014-09-05 08:14:14 -0700220 return !added ? null :
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700221 new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, newDevice, null);
tome5ec3fd2014-09-04 15:18:06 -0700222 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700223 return null;
tome5ec3fd2014-09-04 15:18:06 -0700224 }
225
tom41a2c5f2014-09-19 09:20:35 -0700226 @Override
227 public DeviceEvent markOffline(DeviceId deviceId) {
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700228 Map<ProviderId, DeviceDescriptions> providerDescs
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700229 = getOrCreateDeviceDescriptions(deviceId);
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700230
231 // locking device
232 synchronized (providerDescs) {
tome5ec3fd2014-09-04 15:18:06 -0700233 Device device = devices.get(deviceId);
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700234 if (device == null) {
235 return null;
236 }
237 boolean removed = availableDevices.remove(deviceId);
238 if (removed) {
239 // TODO: broadcast ... DOWN only?
240 return new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, device, null);
241 }
242 return null;
tome5ec3fd2014-09-04 15:18:06 -0700243 }
244 }
245
tom41a2c5f2014-09-19 09:20:35 -0700246 @Override
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700247 public List<DeviceEvent> updatePorts(ProviderId providerId,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700248 DeviceId deviceId,
249 List<PortDescription> portDescriptions) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700250 Device device = devices.get(deviceId);
Ayaka Koshibe78bcbc12014-11-19 14:28:58 -0800251 if (device == null) {
252 log.debug("Device {} doesn't exist or hasn't been initialized yet", deviceId);
253 return Collections.emptyList();
254 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700255
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700256 Map<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700257 checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId);
258
tom29df6f42014-09-05 08:14:14 -0700259 List<DeviceEvent> events = new ArrayList<>();
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700260 synchronized (descsMap) {
261 DeviceDescriptions descs = descsMap.get(providerId);
262 // every provider must provide DeviceDescription.
263 checkArgument(descs != null,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700264 "Device description for Device ID %s from Provider %s was not found",
265 deviceId, providerId);
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700266
267 Map<PortNumber, Port> ports = getPortMap(deviceId);
tom29df6f42014-09-05 08:14:14 -0700268
269 // Add new ports
270 Set<PortNumber> processed = new HashSet<>();
271 for (PortDescription portDescription : portDescriptions) {
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700272 final PortNumber number = portDescription.portNumber();
273 processed.add(portDescription.portNumber());
274
275 final Port oldPort = ports.get(number);
276 final Port newPort;
277
278// event suppression hook?
279
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700280 // update description
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700281 descs.putPortDesc(portDescription);
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700282 newPort = composePort(device, number, descsMap);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700283
284 events.add(oldPort == null ?
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700285 createPort(device, newPort, ports) :
286 updatePort(device, oldPort, newPort, ports));
tom29df6f42014-09-05 08:14:14 -0700287 }
288
289 events.addAll(pruneOldPorts(device, ports, processed));
290 }
Yuta HIGUCHIf5479702014-09-25 00:09:24 -0700291 return FluentIterable.from(events).filter(notNull()).toList();
tom29df6f42014-09-05 08:14:14 -0700292 }
293
294 // Creates a new port based on the port description adds it to the map and
295 // Returns corresponding event.
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700296 // Guarded by deviceDescs value (=Device lock)
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700297 private DeviceEvent createPort(Device device, Port newPort,
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700298 Map<PortNumber, Port> ports) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700299 ports.put(newPort.number(), newPort);
300 return new DeviceEvent(PORT_ADDED, device, newPort);
tom29df6f42014-09-05 08:14:14 -0700301 }
302
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700303 // Checks if the specified port requires update and if so, it replaces the
tom29df6f42014-09-05 08:14:14 -0700304 // existing entry in the map and 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 updatePort(Device device, Port oldPort,
307 Port newPort,
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700308 Map<PortNumber, Port> ports) {
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700309 if (oldPort.isEnabled() != newPort.isEnabled() ||
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700310 oldPort.type() != newPort.type() ||
311 oldPort.portSpeed() != newPort.portSpeed() ||
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700312 !AnnotationsUtil.isEqual(oldPort.annotations(), newPort.annotations())) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700313 ports.put(oldPort.number(), newPort);
314 return new DeviceEvent(PORT_UPDATED, device, newPort);
tom29df6f42014-09-05 08:14:14 -0700315 }
316 return null;
317 }
318
319 // Prunes the specified list of ports based on which ports are in the
320 // processed list and returns list of corresponding events.
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700321 // Guarded by deviceDescs value (=Device lock)
tom29df6f42014-09-05 08:14:14 -0700322 private List<DeviceEvent> pruneOldPorts(Device device,
323 Map<PortNumber, Port> ports,
324 Set<PortNumber> processed) {
325 List<DeviceEvent> events = new ArrayList<>();
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700326 Iterator<Entry<PortNumber, Port>> iterator = ports.entrySet().iterator();
tom29df6f42014-09-05 08:14:14 -0700327 while (iterator.hasNext()) {
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700328 Entry<PortNumber, Port> e = iterator.next();
329 PortNumber portNumber = e.getKey();
tom24c55cd2014-09-06 10:47:25 -0700330 if (!processed.contains(portNumber)) {
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700331 events.add(new DeviceEvent(PORT_REMOVED, device, e.getValue()));
tom29df6f42014-09-05 08:14:14 -0700332 iterator.remove();
333 }
334 }
335 return events;
336 }
337
338 // Gets the map of ports for the specified device; if one does not already
339 // exist, it creates and registers a new one.
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700340 private ConcurrentMap<PortNumber, Port> getPortMap(DeviceId deviceId) {
341 return createIfAbsentUnchecked(devicePorts, deviceId,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700342 NewConcurrentHashMap.<PortNumber, Port>ifNeeded());
tome5ec3fd2014-09-04 15:18:06 -0700343 }
344
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700345 private Map<ProviderId, DeviceDescriptions> getOrCreateDeviceDescriptions(
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700346 DeviceId deviceId) {
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700347 Map<ProviderId, DeviceDescriptions> r;
348 r = deviceDescs.get(deviceId);
349 if (r != null) {
350 return r;
351 }
352 r = new HashMap<>();
353 final Map<ProviderId, DeviceDescriptions> concurrentlyAdded;
354 concurrentlyAdded = deviceDescs.putIfAbsent(deviceId, r);
355 if (concurrentlyAdded != null) {
356 return concurrentlyAdded;
357 } else {
358 return r;
359 }
360 }
361
362 // Guarded by deviceDescs value (=Device lock)
363 private DeviceDescriptions getOrCreateProviderDeviceDescriptions(
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700364 Map<ProviderId, DeviceDescriptions> device,
365 ProviderId providerId, DeviceDescription deltaDesc) {
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700366 synchronized (device) {
367 DeviceDescriptions r = device.get(providerId);
368 if (r == null) {
369 r = new DeviceDescriptions(deltaDesc);
370 device.put(providerId, r);
371 }
372 return r;
373 }
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700374 }
375
tom41a2c5f2014-09-19 09:20:35 -0700376 @Override
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700377 public DeviceEvent updatePortStatus(ProviderId providerId, DeviceId deviceId,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700378 PortDescription portDescription) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700379 Device device = devices.get(deviceId);
380 checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
381
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700382 Map<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700383 checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId);
384
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700385 synchronized (descsMap) {
386 DeviceDescriptions descs = descsMap.get(providerId);
Yuta HIGUCHIdd841b72014-10-16 13:46:09 -0700387 // assuming all providers must give DeviceDescription first
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700388 checkArgument(descs != null,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700389 "Device description for Device ID %s from Provider %s was not found",
390 deviceId, providerId);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700391
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700392 ConcurrentMap<PortNumber, Port> ports = getPortMap(deviceId);
393 final PortNumber number = portDescription.portNumber();
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700394 final Port oldPort = ports.get(number);
395 final Port newPort;
396
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700397 // update description
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700398 descs.putPortDesc(portDescription);
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700399 newPort = composePort(device, number, descsMap);
400
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700401 if (oldPort == null) {
402 return createPort(device, newPort, ports);
403 } else {
404 return updatePort(device, oldPort, newPort, ports);
405 }
tom46a220d2014-09-05 08:25:56 -0700406 }
tome5ec3fd2014-09-04 15:18:06 -0700407 }
408
tom41a2c5f2014-09-19 09:20:35 -0700409 @Override
410 public List<Port> getPorts(DeviceId deviceId) {
tom46a220d2014-09-05 08:25:56 -0700411 Map<PortNumber, Port> ports = devicePorts.get(deviceId);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700412 if (ports == null) {
413 return Collections.emptyList();
414 }
415 return ImmutableList.copyOf(ports.values());
tome5ec3fd2014-09-04 15:18:06 -0700416 }
417
tom41a2c5f2014-09-19 09:20:35 -0700418 @Override
419 public Port getPort(DeviceId deviceId, PortNumber portNumber) {
tom46a220d2014-09-05 08:25:56 -0700420 Map<PortNumber, Port> ports = devicePorts.get(deviceId);
421 return ports == null ? null : ports.get(portNumber);
tome5ec3fd2014-09-04 15:18:06 -0700422 }
423
tom41a2c5f2014-09-19 09:20:35 -0700424 @Override
425 public boolean isAvailable(DeviceId deviceId) {
tomff7eb7c2014-09-08 12:49:03 -0700426 return availableDevices.contains(deviceId);
427 }
428
tom41a2c5f2014-09-19 09:20:35 -0700429 @Override
tom41a2c5f2014-09-19 09:20:35 -0700430 public DeviceEvent removeDevice(DeviceId deviceId) {
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700431 Map<ProviderId, DeviceDescriptions> descs = getOrCreateDeviceDescriptions(deviceId);
Yuta HIGUCHI0d6a5e62014-10-03 15:54:09 -0700432 synchronized (descs) {
tome5ec3fd2014-09-04 15:18:06 -0700433 Device device = devices.remove(deviceId);
Yuta HIGUCHI0d6a5e62014-10-03 15:54:09 -0700434 // should DEVICE_REMOVED carry removed ports?
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700435 Map<PortNumber, Port> ports = devicePorts.get(deviceId);
Yuta HIGUCHI0d6a5e62014-10-03 15:54:09 -0700436 if (ports != null) {
437 ports.clear();
438 }
439 availableDevices.remove(deviceId);
440 descs.clear();
tom29df6f42014-09-05 08:14:14 -0700441 return device == null ? null :
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700442 new DeviceEvent(DEVICE_REMOVED, device, null);
tome5ec3fd2014-09-04 15:18:06 -0700443 }
444 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700445
446 /**
447 * Returns a Device, merging description given from multiple Providers.
448 *
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700449 * @param deviceId device identifier
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700450 * @param providerDescs Collection of Descriptions from multiple providers
451 * @return Device instance
452 */
453 private Device composeDevice(DeviceId deviceId,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700454 Map<ProviderId, DeviceDescriptions> providerDescs) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700455
456 checkArgument(!providerDescs.isEmpty(), "No Device descriptions supplied");
457
458 ProviderId primary = pickPrimaryPID(providerDescs);
459
460 DeviceDescriptions desc = providerDescs.get(primary);
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700461
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700462 final DeviceDescription base = desc.getDeviceDesc();
463 Type type = base.type();
464 String manufacturer = base.manufacturer();
465 String hwVersion = base.hwVersion();
466 String swVersion = base.swVersion();
467 String serialNumber = base.serialNumber();
alshabib7911a052014-10-16 17:49:37 -0700468 ChassisId chassisId = base.chassisId();
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700469 DefaultAnnotations annotations = DefaultAnnotations.builder().build();
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700470 annotations = merge(annotations, base.annotations());
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700471
472 for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) {
473 if (e.getKey().equals(primary)) {
474 continue;
475 }
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700476 // TODO: should keep track of Description timestamp
477 // and only merge conflicting keys when timestamp is newer
478 // Currently assuming there will never be a key conflict between
479 // providers
480
481 // annotation merging. not so efficient, should revisit later
482 annotations = merge(annotations, e.getValue().getDeviceDesc().annotations());
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700483 }
484
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700485 return new DefaultDevice(primary, deviceId, type, manufacturer,
486 hwVersion, swVersion, serialNumber,
487 chassisId, annotations);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700488 }
489
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700490 /**
491 * Returns a Port, merging description given from multiple Providers.
492 *
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700493 * @param device device the port is on
494 * @param number port number
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700495 * @param descsMap Collection of Descriptions from multiple providers
Yuta HIGUCHIc35efac2014-10-06 14:43:53 -0700496 * @return Port instance
497 */
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700498 private Port composePort(Device device, PortNumber number,
Thomas Vachuska56dbeb12014-10-22 16:40:44 -0700499 Map<ProviderId, DeviceDescriptions> descsMap) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700500
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700501 ProviderId primary = pickPrimaryPID(descsMap);
502 DeviceDescriptions primDescs = descsMap.get(primary);
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700503 // if no primary, assume not enabled
504 // TODO: revisit this default port enabled/disabled behavior
505 boolean isEnabled = false;
506 DefaultAnnotations annotations = DefaultAnnotations.builder().build();
507
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700508 final PortDescription portDesc = primDescs.getPortDesc(number);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700509 if (portDesc != null) {
510 isEnabled = portDesc.isEnabled();
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700511 annotations = merge(annotations, portDesc.annotations());
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700512 }
513
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700514 for (Entry<ProviderId, DeviceDescriptions> e : descsMap.entrySet()) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700515 if (e.getKey().equals(primary)) {
516 continue;
517 }
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700518 // TODO: should keep track of Description timestamp
519 // and only merge conflicting keys when timestamp is newer
520 // Currently assuming there will never be a key conflict between
521 // providers
522
523 // annotation merging. not so efficient, should revisit later
524 final PortDescription otherPortDesc = e.getValue().getPortDesc(number);
525 if (otherPortDesc != null) {
526 annotations = merge(annotations, otherPortDesc.annotations());
527 }
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700528 }
529
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700530 return portDesc == null ?
531 new DefaultPort(device, number, false, annotations) :
532 new DefaultPort(device, number, isEnabled, portDesc.type(),
533 portDesc.portSpeed(), annotations);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700534 }
535
536 /**
537 * @return primary ProviderID, or randomly chosen one if none exists
538 */
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700539 private ProviderId pickPrimaryPID(Map<ProviderId, DeviceDescriptions> descsMap) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700540 ProviderId fallBackPrimary = null;
Yuta HIGUCHI2c546a12014-10-16 11:02:00 -0700541 for (Entry<ProviderId, DeviceDescriptions> e : descsMap.entrySet()) {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700542 if (!e.getKey().isAncillary()) {
543 return e.getKey();
544 } else if (fallBackPrimary == null) {
545 // pick randomly as a fallback in case there is no primary
546 fallBackPrimary = e.getKey();
547 }
548 }
549 return fallBackPrimary;
550 }
551
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700552 /**
553 * Collection of Description of a Device and it's Ports given from a Provider.
554 */
555 private static class DeviceDescriptions {
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700556
557 private final AtomicReference<DeviceDescription> deviceDesc;
558 private final ConcurrentMap<PortNumber, PortDescription> portDescs;
559
560 public DeviceDescriptions(DeviceDescription desc) {
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700561 this.deviceDesc = new AtomicReference<>(checkNotNull(desc));
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700562 this.portDescs = new ConcurrentHashMap<>();
563 }
564
565 public DeviceDescription getDeviceDesc() {
566 return deviceDesc.get();
567 }
568
569 public PortDescription getPortDesc(PortNumber number) {
570 return portDescs.get(number);
571 }
572
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700573 /**
574 * Puts DeviceDescription, merging annotations as necessary.
575 *
576 * @param newDesc new DeviceDescription
577 * @return previous DeviceDescription
578 */
579 public synchronized DeviceDescription putDeviceDesc(DeviceDescription newDesc) {
580 DeviceDescription oldOne = deviceDesc.get();
581 DeviceDescription newOne = newDesc;
582 if (oldOne != null) {
Yuta HIGUCHI885be1d2014-10-04 21:47:26 -0700583 SparseAnnotations merged = union(oldOne.annotations(),
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700584 newDesc.annotations());
585 newOne = new DefaultDeviceDescription(newOne, merged);
586 }
587 return deviceDesc.getAndSet(newOne);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700588 }
589
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700590 /**
591 * Puts PortDescription, merging annotations as necessary.
592 *
593 * @param newDesc new PortDescription
594 * @return previous PortDescription
595 */
596 public synchronized PortDescription putPortDesc(PortDescription newDesc) {
597 PortDescription oldOne = portDescs.get(newDesc.portNumber());
598 PortDescription newOne = newDesc;
599 if (oldOne != null) {
Yuta HIGUCHI885be1d2014-10-04 21:47:26 -0700600 SparseAnnotations merged = union(oldOne.annotations(),
Yuta HIGUCHI55710e72014-10-02 14:58:32 -0700601 newDesc.annotations());
602 newOne = new DefaultPortDescription(newOne, merged);
603 }
604 return portDescs.put(newOne.portNumber(), newOne);
Yuta HIGUCHI8e493792014-10-01 19:36:32 -0700605 }
606 }
tome5ec3fd2014-09-04 15:18:06 -0700607}