blob: 481a3104d02acee49ae866301c07335ce88fc181 [file] [log] [blame]
Andrea Campanella241896c2017-05-10 13:11:04 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2017-present Open Networking Foundation
Andrea Campanella241896c2017-05-10 13:11:04 -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 */
16
17package org.onosproject.provider.general.device.impl;
18
Andrea Campanellabc112a92017-06-26 19:06:43 +020019import com.google.common.collect.Maps;
Andrea Campanella241896c2017-05-10 13:11:04 -070020import org.onlab.packet.ChassisId;
21import org.onlab.util.ItemNotFoundException;
Andrea Campanella19090322017-08-22 10:31:37 +020022import org.onlab.util.Tools;
Andrea Campanella4929a812017-10-09 18:38:23 +020023import org.onosproject.cfg.ComponentConfigService;
Carmelo Cascone3977ea42019-02-28 13:43:42 -080024import org.onosproject.cluster.ClusterService;
25import org.onosproject.cluster.NodeId;
Andrea Campanella241896c2017-05-10 13:11:04 -070026import org.onosproject.core.CoreService;
Yi Tsenge616d752018-11-27 10:53:27 -080027import org.onosproject.gnmi.api.GnmiController;
Carmelo Cascone3977ea42019-02-28 13:43:42 -080028import org.onosproject.mastership.MastershipInfo;
Andrea Campanella14e196d2017-07-24 18:11:36 +020029import org.onosproject.mastership.MastershipService;
Andrea Campanella241896c2017-05-10 13:11:04 -070030import org.onosproject.net.DefaultAnnotations;
31import org.onosproject.net.Device;
32import org.onosproject.net.DeviceId;
33import org.onosproject.net.MastershipRole;
34import org.onosproject.net.PortNumber;
Carmelo Cascone87892e22017-11-13 16:01:29 -080035import org.onosproject.net.behaviour.PiPipelineProgrammable;
Andrea Campanella241896c2017-05-10 13:11:04 -070036import org.onosproject.net.behaviour.PortAdmin;
Andrea Campanella241896c2017-05-10 13:11:04 -070037import org.onosproject.net.config.NetworkConfigEvent;
38import org.onosproject.net.config.NetworkConfigListener;
39import org.onosproject.net.config.NetworkConfigRegistry;
40import org.onosproject.net.config.basics.BasicDeviceConfig;
Andrea Campanella241896c2017-05-10 13:11:04 -070041import org.onosproject.net.device.DefaultDeviceDescription;
Carmelo Casconee5b28722018-06-22 17:28:28 +020042import org.onosproject.net.device.DeviceAgentEvent;
43import org.onosproject.net.device.DeviceAgentListener;
Andrea Campanella241896c2017-05-10 13:11:04 -070044import org.onosproject.net.device.DeviceDescription;
45import org.onosproject.net.device.DeviceDescriptionDiscovery;
Andrea Campanella241896c2017-05-10 13:11:04 -070046import org.onosproject.net.device.DeviceHandshaker;
Andrea Campanella241896c2017-05-10 13:11:04 -070047import org.onosproject.net.device.DeviceProvider;
48import org.onosproject.net.device.DeviceProviderRegistry;
49import org.onosproject.net.device.DeviceProviderService;
50import org.onosproject.net.device.DeviceService;
51import org.onosproject.net.device.PortDescription;
Andrea Campanella241896c2017-05-10 13:11:04 -070052import org.onosproject.net.driver.Behaviour;
53import org.onosproject.net.driver.DefaultDriverData;
54import org.onosproject.net.driver.DefaultDriverHandler;
55import org.onosproject.net.driver.Driver;
56import org.onosproject.net.driver.DriverData;
57import org.onosproject.net.driver.DriverService;
Carmelo Cascone59f57de2017-07-11 19:55:09 -040058import org.onosproject.net.pi.model.PiPipeconf;
Andrea Campanellabc112a92017-06-26 19:06:43 +020059import org.onosproject.net.pi.model.PiPipeconfId;
Carmelo Cascone39c28ca2017-11-15 13:03:57 -080060import org.onosproject.net.pi.service.PiPipeconfService;
Carmelo Cascone9e4972c2018-08-30 00:29:16 -070061import org.onosproject.net.pi.service.PiPipeconfWatchdogEvent;
62import org.onosproject.net.pi.service.PiPipeconfWatchdogListener;
63import org.onosproject.net.pi.service.PiPipeconfWatchdogService;
Andrea Campanella241896c2017-05-10 13:11:04 -070064import org.onosproject.net.provider.AbstractProvider;
65import org.onosproject.net.provider.ProviderId;
Carmelo Cascone3977ea42019-02-28 13:43:42 -080066import org.onosproject.provider.general.device.impl.DeviceTaskExecutor.DeviceTaskException;
Andrea Campanella19090322017-08-22 10:31:37 +020067import org.osgi.service.component.ComponentContext;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070068import org.osgi.service.component.annotations.Activate;
69import org.osgi.service.component.annotations.Component;
70import org.osgi.service.component.annotations.Deactivate;
71import org.osgi.service.component.annotations.Modified;
72import org.osgi.service.component.annotations.Reference;
73import org.osgi.service.component.annotations.ReferenceCardinality;
Andrea Campanella241896c2017-05-10 13:11:04 -070074import org.slf4j.Logger;
75
Andrea Campanellabc112a92017-06-26 19:06:43 +020076import java.util.Collections;
Andrea Campanella19090322017-08-22 10:31:37 +020077import java.util.Dictionary;
Andrea Campanella241896c2017-05-10 13:11:04 -070078import java.util.List;
Thomas Vachuska5b38dc02018-05-10 15:24:40 -070079import java.util.Map;
Carmelo Cascone3977ea42019-02-28 13:43:42 -080080import java.util.Objects;
Andrea Campanella241896c2017-05-10 13:11:04 -070081import java.util.concurrent.CompletableFuture;
82import java.util.concurrent.ExecutionException;
Carmelo Casconee5b28722018-06-22 17:28:28 +020083import java.util.concurrent.ExecutorService;
Andrea Campanella241896c2017-05-10 13:11:04 -070084import java.util.concurrent.ScheduledExecutorService;
Andrea Campanella19090322017-08-22 10:31:37 +020085import java.util.concurrent.ScheduledFuture;
Andrea Campanella241896c2017-05-10 13:11:04 -070086import java.util.concurrent.TimeUnit;
87import java.util.concurrent.TimeoutException;
88
Carmelo Cascone3977ea42019-02-28 13:43:42 -080089import static com.google.common.base.Preconditions.checkNotNull;
90import static com.google.common.base.Strings.isNullOrEmpty;
91import static java.lang.System.currentTimeMillis;
Carmelo Casconee5b28722018-06-22 17:28:28 +020092import static java.util.concurrent.Executors.newFixedThreadPool;
Carmelo Cascone158b8c42018-07-04 19:42:37 +020093import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
Andrea Campanella241896c2017-05-10 13:11:04 -070094import static org.onlab.util.Tools.groupedThreads;
Yi Tsengd7716482018-10-31 15:34:30 -070095import static org.onosproject.provider.general.device.impl.OsgiPropertyConstants.OP_TIMEOUT_SHORT;
96import static org.onosproject.provider.general.device.impl.OsgiPropertyConstants.OP_TIMEOUT_SHORT_DEFAULT;
Carmelo Cascone3977ea42019-02-28 13:43:42 -080097import static org.onosproject.provider.general.device.impl.OsgiPropertyConstants.PROBE_INTERVAL;
98import static org.onosproject.provider.general.device.impl.OsgiPropertyConstants.PROBE_INTERVAL_DEFAULT;
99import static org.onosproject.provider.general.device.impl.OsgiPropertyConstants.STATS_POLL_INTERVAL;
100import static org.onosproject.provider.general.device.impl.OsgiPropertyConstants.STATS_POLL_INTERVAL_DEFAULT;
Andrea Campanella241896c2017-05-10 13:11:04 -0700101import static org.slf4j.LoggerFactory.getLogger;
102
103/**
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800104 * Provider which uses drivers to discover devices, perform initial handshake,
105 * and notify the core of disconnection events. The implementation listens for
106 * events from netcfg or the drivers (via {@link DeviceAgentListener}) andP
107 * schedules task for each event.
Andrea Campanella241896c2017-05-10 13:11:04 -0700108 */
Thomas Vachuska4167c3f2018-10-16 07:16:31 -0700109@Component(immediate = true,
110 property = {
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800111 PROBE_INTERVAL + ":Integer=" + PROBE_INTERVAL_DEFAULT,
112 STATS_POLL_INTERVAL + ":Integer=" + STATS_POLL_INTERVAL_DEFAULT,
Thomas Vachuska00b5d4f2018-10-30 15:13:20 -0700113 OP_TIMEOUT_SHORT + ":Integer=" + OP_TIMEOUT_SHORT_DEFAULT,
Thomas Vachuska4167c3f2018-10-16 07:16:31 -0700114 })
Andrea Campanella241896c2017-05-10 13:11:04 -0700115public class GeneralDeviceProvider extends AbstractProvider
116 implements DeviceProvider {
Carmelo Casconee5b28722018-06-22 17:28:28 +0200117
Andrea Campanella241896c2017-05-10 13:11:04 -0700118 private final Logger log = getLogger(getClass());
119
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800120 private static final String APP_NAME = "org.onosproject.generaldeviceprovider";
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700121 private static final String URI_SCHEME = "device";
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700122 private static final String DEVICE_PROVIDER_PACKAGE =
123 "org.onosproject.general.provider.device";
124 private static final int CORE_POOL_SIZE = 10;
125 private static final String UNKNOWN = "unknown";
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700126
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700127 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Carmelo Casconee5b28722018-06-22 17:28:28 +0200128 private DeviceProviderRegistry providerRegistry;
Andrea Campanella241896c2017-05-10 13:11:04 -0700129
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700130 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Carmelo Casconee5b28722018-06-22 17:28:28 +0200131 private ComponentConfigService componentConfigService;
Andrea Campanella4929a812017-10-09 18:38:23 +0200132
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700133 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Carmelo Casconee5b28722018-06-22 17:28:28 +0200134 private NetworkConfigRegistry cfgService;
Andrea Campanella241896c2017-05-10 13:11:04 -0700135
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700136 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Carmelo Casconee5b28722018-06-22 17:28:28 +0200137 private CoreService coreService;
Andrea Campanella241896c2017-05-10 13:11:04 -0700138
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700139 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Carmelo Casconee5b28722018-06-22 17:28:28 +0200140 private DeviceService deviceService;
Andrea Campanella241896c2017-05-10 13:11:04 -0700141
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700142 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Carmelo Casconee5b28722018-06-22 17:28:28 +0200143 private DriverService driverService;
Andrea Campanella241896c2017-05-10 13:11:04 -0700144
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700145 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Carmelo Casconee5b28722018-06-22 17:28:28 +0200146 private MastershipService mastershipService;
Andrea Campanella14e196d2017-07-24 18:11:36 +0200147
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700148 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800149 private ClusterService clusterService;
150
151 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700152 private PiPipeconfService pipeconfService;
Andrea Campanella241896c2017-05-10 13:11:04 -0700153
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700154 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700155 private PiPipeconfWatchdogService pipeconfWatchdogService;
Andrea Campanella14e196d2017-07-24 18:11:36 +0200156
Yi Tsenge616d752018-11-27 10:53:27 -0800157 // FIXME: no longer general if we add a dependency to a protocol-specific
158 // service. Possible solutions are: rename this provider to
159 // StratumDeviceProvider, find a way to allow this provider to register for
160 // protocol specific events (e.g. port events) via drivers (similar to
161 // DeviceAgentListener).
162 @Reference(cardinality = ReferenceCardinality.MANDATORY)
163 private GnmiController gnmiController;
164
165 private GnmiDeviceStateSubscriber gnmiDeviceStateSubscriber;
166
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800167 /** Configure interval for checking device availability; default is 10 sec. */
168 private int probeInterval = PROBE_INTERVAL_DEFAULT;
Andrea Campanella19090322017-08-22 10:31:37 +0200169
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800170 /** Configure poll frequency for port status and stats; default is 10 sec. */
171 private int statsPollInterval = STATS_POLL_INTERVAL_DEFAULT;
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200172
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800173 /** Configure timeout in seconds for device operations; default is 10 sec. */
Thomas Vachuska4167c3f2018-10-16 07:16:31 -0700174 private int opTimeoutShort = OP_TIMEOUT_SHORT_DEFAULT;
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200175
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700176 private final Map<DeviceId, DeviceHandshaker> handshakersWithListeners = Maps.newConcurrentMap();
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800177 private final Map<DeviceId, Long> lastProbedAvailability = Maps.newConcurrentMap();
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700178 private final InternalPipeconfWatchdogListener pipeconfWatchdogListener = new InternalPipeconfWatchdogListener();
Carmelo Casconee5b28722018-06-22 17:28:28 +0200179 private final NetworkConfigListener cfgListener = new InternalNetworkConfigListener();
180 private final DeviceAgentListener deviceAgentListener = new InternalDeviceAgentListener();
Andrea Campanella241896c2017-05-10 13:11:04 -0700181
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800182 private ExecutorService mainExecutor;
183 private DeviceTaskExecutor<TaskType> taskExecutor;
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700184 private ScheduledExecutorService probeExecutor;
185 private ScheduledFuture<?> probeTask;
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800186 private StatsPoller statsPoller;
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700187 private DeviceProviderService providerService;
188
189 public GeneralDeviceProvider() {
190 super(new ProviderId(URI_SCHEME, DEVICE_PROVIDER_PACKAGE));
191 }
Andrea Campanella241896c2017-05-10 13:11:04 -0700192
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800193 protected DeviceProviderService providerService() {
194 return providerService;
195 }
196
Andrea Campanella241896c2017-05-10 13:11:04 -0700197 @Activate
Andrea Campanella1e573442018-05-17 17:07:13 +0200198 public void activate(ComponentContext context) {
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800199 mainExecutor = newFixedThreadPool(CORE_POOL_SIZE, groupedThreads(
200 "onos/gdp-task", "%d", log));
201 taskExecutor = new DeviceTaskExecutor<>(mainExecutor);
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700202 probeExecutor = newSingleThreadScheduledExecutor(groupedThreads(
203 "onos/gdp-probe", "%d", log));
Andrea Campanella241896c2017-05-10 13:11:04 -0700204 providerService = providerRegistry.register(this);
Andrea Campanella4929a812017-10-09 18:38:23 +0200205 componentConfigService.registerProperties(getClass());
Andrea Campanella241896c2017-05-10 13:11:04 -0700206 coreService.registerApplication(APP_NAME);
Andrea Campanella241896c2017-05-10 13:11:04 -0700207 cfgService.addListener(cfgListener);
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700208 pipeconfWatchdogService.addListener(pipeconfWatchdogListener);
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800209 gnmiDeviceStateSubscriber = new GnmiDeviceStateSubscriber(
210 gnmiController, deviceService, mastershipService, providerService);
Yi Tsenge616d752018-11-27 10:53:27 -0800211 gnmiDeviceStateSubscriber.activate();
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800212 startOrRescheduleProbeTask();
213 statsPoller = new StatsPoller(deviceService, mastershipService, providerService);
214 statsPoller.activate(statsPollInterval);
215 modified(context);
Andrea Campanella241896c2017-05-10 13:11:04 -0700216 log.info("Started");
217 }
218
Andrea Campanella19090322017-08-22 10:31:37 +0200219 @Modified
220 public void modified(ComponentContext context) {
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200221 if (context == null) {
222 return;
Andrea Campanella19090322017-08-22 10:31:37 +0200223 }
224
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200225 Dictionary<?, ?> properties = context.getProperties();
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800226 final int oldProbeFrequency = probeInterval;
227 probeInterval = Tools.getIntegerProperty(
228 properties, PROBE_INTERVAL, PROBE_INTERVAL_DEFAULT);
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200229 log.info("Configured. {} is configured to {} seconds",
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800230 PROBE_INTERVAL, probeInterval);
231 final int oldStatsPollFrequency = statsPollInterval;
232 statsPollInterval = Tools.getIntegerProperty(
233 properties, STATS_POLL_INTERVAL, STATS_POLL_INTERVAL_DEFAULT);
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200234 log.info("Configured. {} is configured to {} seconds",
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800235 STATS_POLL_INTERVAL, statsPollInterval);
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200236 opTimeoutShort = Tools.getIntegerProperty(
Thomas Vachuska4167c3f2018-10-16 07:16:31 -0700237 properties, OP_TIMEOUT_SHORT, OP_TIMEOUT_SHORT_DEFAULT);
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200238 log.info("Configured. {} is configured to {} seconds",
239 OP_TIMEOUT_SHORT, opTimeoutShort);
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200240
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800241 if (oldProbeFrequency != probeInterval) {
242 startOrRescheduleProbeTask();
Andrea Campanella19090322017-08-22 10:31:37 +0200243 }
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200244
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800245 if (oldStatsPollFrequency != statsPollInterval) {
246 statsPoller.reschedule(statsPollInterval);
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200247 }
Andrea Campanella19090322017-08-22 10:31:37 +0200248 }
249
Andrea Campanella241896c2017-05-10 13:11:04 -0700250 @Deactivate
251 public void deactivate() {
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800252 // Shutdown stats poller.
253 statsPoller.deactivate();
254 statsPoller = null;
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700255 // Shutdown probe executor.
256 probeTask.cancel(true);
257 probeTask = null;
258 probeExecutor.shutdownNow();
259 try {
260 probeExecutor.awaitTermination(5, TimeUnit.SECONDS);
261 } catch (InterruptedException e) {
262 log.warn("probeExecutor not terminated properly");
263 }
264 probeExecutor = null;
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800265 // Shutdown main and task executor.
266 taskExecutor.cancel();
267 taskExecutor = null;
268 mainExecutor.shutdownNow();
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700269 try {
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800270 mainExecutor.awaitTermination(5, TimeUnit.SECONDS);
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700271 } catch (InterruptedException e) {
272 log.warn("connectionExecutor not terminated properly");
273 }
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800274 mainExecutor = null;
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700275 // Remove all device agent listeners
276 handshakersWithListeners.values().forEach(h -> h.removeDeviceAgentListener(id()));
277 handshakersWithListeners.clear();
278 // Other cleanup.
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800279 lastProbedAvailability.clear();
Andrea Campanella4929a812017-10-09 18:38:23 +0200280 componentConfigService.unregisterProperties(getClass(), false);
Andrea Campanella241896c2017-05-10 13:11:04 -0700281 cfgService.removeListener(cfgListener);
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700282 pipeconfWatchdogService.removeListener(pipeconfWatchdogListener);
Andrea Campanella241896c2017-05-10 13:11:04 -0700283 providerRegistry.unregister(this);
284 providerService = null;
Yi Tsenge616d752018-11-27 10:53:27 -0800285 gnmiDeviceStateSubscriber.deactivate();
286 gnmiDeviceStateSubscriber = null;
Andrea Campanella241896c2017-05-10 13:11:04 -0700287 log.info("Stopped");
288 }
289
Andrea Campanella241896c2017-05-10 13:11:04 -0700290 @Override
291 public void triggerProbe(DeviceId deviceId) {
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800292 checkNotNull(deviceId);
293 submitTask(deviceId, TaskType.PROBE_AVAILABILITY);
Andrea Campanella241896c2017-05-10 13:11:04 -0700294 }
295
296 @Override
297 public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
Carmelo Casconee5b28722018-06-22 17:28:28 +0200298
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800299 final MastershipInfo mastershipInfo = mastershipService.getMastershipFor(deviceId);
300 final NodeId localNodeId = clusterService.getLocalNode().id();
301
302 if (!mastershipInfo.getRole(localNodeId).equals(newRole)) {
303 log.warn("Inconsistent mastership info for {}! Requested {}, but " +
304 "mastership service reports {}, will apply the latter...",
305 deviceId, newRole, mastershipInfo.getRole(localNodeId));
306 newRole = mastershipInfo.getRole(localNodeId);
307 }
308
309 // Derive preference value.
310 final int preference;
311 switch (newRole) {
312 case MASTER:
313 preference = 0;
314 break;
315 case STANDBY:
316 preference = mastershipInfo.backups().indexOf(localNodeId) + 1;
317 if (preference == 0) {
318 // Not found in list.
319 log.error("Unable to derive mastership preference for {}, " +
320 "requested role {} but local node ID was " +
321 "not found among list of backup nodes " +
322 "reported by mastership service");
323 return;
324 }
325 break;
326 case NONE:
327 // No preference for NONE, apply as is.
328 log.info("Notifying role {} to {}", newRole, deviceId);
329 roleChanged(deviceId, newRole);
330 return;
331 default:
332 log.error("Unrecognized mastership role {}", newRole);
333 return;
334 }
335
336 log.info("Notifying role {} (preference {}) for term {} to {}",
337 newRole, preference, mastershipInfo.term(), deviceId);
338
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700339 final DeviceHandshaker handshaker = getBehaviour(
340 deviceId, DeviceHandshaker.class);
Carmelo Casconee5b28722018-06-22 17:28:28 +0200341 if (handshaker == null) {
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800342 log.error("Null handshaker. Unable to notify role {} to {}",
Carmelo Cascone96beb6f2018-06-27 18:07:12 +0200343 newRole, deviceId);
Carmelo Casconee5b28722018-06-22 17:28:28 +0200344 return;
345 }
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800346
347 try {
348 handshaker.roleChanged(preference, mastershipInfo.term());
349 } catch (UnsupportedOperationException e) {
350 // Preference-based method not supported.
351 handshaker.roleChanged(newRole);
352 }
Andrea Campanella241896c2017-05-10 13:11:04 -0700353 }
354
355 @Override
356 public boolean isReachable(DeviceId deviceId) {
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700357 final DeviceHandshaker handshaker = getBehaviour(
358 deviceId, DeviceHandshaker.class);
Andrea Campanellac1ecdd02018-01-12 12:48:24 +0100359 if (handshaker == null) {
360 return false;
361 }
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800362 return handshaker.isReachable();
Andrea Campanella241896c2017-05-10 13:11:04 -0700363 }
364
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800365 @Override
366 public boolean isAvailable(DeviceId deviceId) {
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700367 final DeviceHandshaker handshaker = getBehaviour(
368 deviceId, DeviceHandshaker.class);
369 if (handshaker == null) {
370 return false;
371 }
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800372 try {
373 // Try without probing the device...
374 return handshaker.isAvailable();
375 } catch (UnsupportedOperationException e) {
376 // Driver does not support that.
377 return probeAvailability(handshaker);
378 }
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700379 }
380
Andrea Campanella241896c2017-05-10 13:11:04 -0700381 @Override
382 public void changePortState(DeviceId deviceId, PortNumber portNumber,
383 boolean enable) {
Carmelo Cascone96beb6f2018-06-27 18:07:12 +0200384 if (!deviceService.getDevice(deviceId).is(PortAdmin.class)) {
385 log.warn("Missing PortAdmin behaviour on {}, aborting port state change",
386 deviceId);
387 return;
Andrea Campanella241896c2017-05-10 13:11:04 -0700388 }
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200389 final PortAdmin portAdmin = deviceService.getDevice(deviceId)
390 .as(PortAdmin.class);
391 final CompletableFuture<Boolean> modifyTask = enable
Carmelo Cascone96beb6f2018-06-27 18:07:12 +0200392 ? portAdmin.enable(portNumber)
393 : portAdmin.disable(portNumber);
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200394 final String descr = (enable ? "enabling" : "disabling") + " port " + portNumber;
395 getFutureWithDeadline(
396 modifyTask, descr, deviceId, null, opTimeoutShort);
Andrea Campanella241896c2017-05-10 13:11:04 -0700397 }
398
Thomas Vachuska5b38dc02018-05-10 15:24:40 -0700399 @Override
400 public void triggerDisconnect(DeviceId deviceId) {
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800401 checkNotNull(deviceId);
402 log.info("Triggering disconnection of device {}", deviceId);
403 submitTask(deviceId, TaskType.CONNECTION_TEARDOWN);
404 }
405
406 /**
407 * Listener for configuration events.
408 */
409 private class InternalNetworkConfigListener implements NetworkConfigListener {
410 @Override
411 public void event(NetworkConfigEvent event) {
412 DeviceId deviceId = (DeviceId) event.subject();
413 switch (event.type()) {
414 case CONFIG_ADDED:
415 if (configIsComplete(deviceId)) {
416 submitTask(deviceId, TaskType.CONNECTION_SETUP);
417 }
418 break;
419 case CONFIG_UPDATED:
420 if (configIsComplete(deviceId) && mgmtAddrUpdated(event)) {
421 submitTask(deviceId, TaskType.CONNECTION_UPDATE);
422 }
423 break;
424 case CONFIG_REMOVED:
425 if (event.configClass().equals(BasicDeviceConfig.class)) {
426 submitTask(deviceId, TaskType.CONNECTION_TEARDOWN);
427 }
428 break;
429 default:
430 // Ignore
431 break;
432 }
433 }
434
435 private boolean mgmtAddrUpdated(NetworkConfigEvent event) {
436 if (!event.prevConfig().isPresent() || !event.config().isPresent()) {
437 return false;
438 }
439 final BasicDeviceConfig prev = (BasicDeviceConfig) event.prevConfig().get();
440 final BasicDeviceConfig current = (BasicDeviceConfig) event.config().get();
441 return !Objects.equals(prev.managementAddress(), current.managementAddress());
442 }
443
444 @Override
445 public boolean isRelevant(NetworkConfigEvent event) {
446 return event.configClass().equals(BasicDeviceConfig.class) &&
447 (event.subject() instanceof DeviceId) &&
448 myScheme((DeviceId) event.subject());
449 }
450 }
451
452 /**
453 * Listener for device agent events.
454 */
455 private class InternalDeviceAgentListener implements DeviceAgentListener {
456 @Override
457 public void event(DeviceAgentEvent event) {
458 DeviceId deviceId = event.subject();
459 switch (event.type()) {
460 case CHANNEL_OPEN:
461 submitTask(deviceId, TaskType.CHANNEL_OPEN);
462 break;
463 case CHANNEL_CLOSED:
464 case CHANNEL_ERROR:
465 submitTask(deviceId, TaskType.CHANNEL_CLOSED);
466 break;
467 case ROLE_MASTER:
468 submitTask(deviceId, TaskType.ROLE_MASTER);
469 break;
470 case ROLE_STANDBY:
471 submitTask(deviceId, TaskType.ROLE_STANDBY);
472 break;
473 case ROLE_NONE:
474 submitTask(deviceId, TaskType.ROLE_NONE);
475 break;
476 case NOT_MASTER:
477 submitTask(deviceId, TaskType.NOT_MASTER);
478 break;
479 default:
480 log.warn("Unrecognized device agent event {}", event.type());
481 }
482 }
483 }
484
485 /**
486 * Pipeline event listener.
487 */
488 private class InternalPipeconfWatchdogListener implements PiPipeconfWatchdogListener {
489 @Override
490 public void event(PiPipeconfWatchdogEvent event) {
491 final DeviceId deviceId = event.subject();
492 switch (event.type()) {
493 case PIPELINE_READY:
494 submitTask(deviceId, TaskType.PIPELINE_READY);
495 break;
496 case PIPELINE_UNKNOWN:
497 submitTask(deviceId, TaskType.PIPELINE_NOT_READY);
498 break;
499 default:
500 break;
501 }
502 }
503
504 @Override
505 public boolean isRelevant(PiPipeconfWatchdogEvent event) {
506 return myScheme(event.subject());
507 }
508 }
509
510 private void startOrRescheduleProbeTask() {
511 synchronized (this) {
512 if (probeTask != null) {
513 probeTask.cancel(false);
514 }
515 probeTask = probeExecutor.scheduleAtFixedRate(
516 this::submitProbeTasks,
517 0,
518 probeInterval,
519 TimeUnit.SECONDS);
520 }
521 }
522
523 private void submitProbeTasks() {
524 // Async trigger a task for all devices in the cfg.
525 log.debug("Starting probing for all devices");
526 cfgService.getSubjects(DeviceId.class).stream()
527 .filter(GeneralDeviceProvider::myScheme)
528 .forEach(this::submitProbeTask);
529 }
530
531 private void submitProbeTask(DeviceId deviceId) {
532 final DeviceHandshaker handshaker = handshakersWithListeners.get(deviceId);
533
534 if (handshaker == null) {
535 if (configIsComplete(deviceId)) {
536 // Device in config but we have not initiated a connection.
537 // Perhaps we missed the config event?
538 submitTask(deviceId, TaskType.CONNECTION_SETUP);
539 }
540 return;
541 }
542
543 if (!handshaker.isConnected()) {
544 // Device is in the core, but driver reports there is NOT a
545 // connection to it. Perhaps the netcfg changed and we didn't
546 // pick the event?
547 log.warn("Re-establishing lost connection to {}", deviceId);
548 submitTask(deviceId, TaskType.CONNECTION_TEARDOWN);
549 submitTask(deviceId, TaskType.CONNECTION_SETUP);
550 return;
551 }
552
553 // On probing offline devices, while we expect them to signal
554 // availability via CHANNEL_OPEN or similar events, periodic probing
555 // might be needed to stimulate some channel activity. We might consider
556 // requiring active probing of closed channels in the protocol layer.
557
558 final Long lastProbe = lastProbedAvailability.get(deviceId);
559 if (lastProbe != null &&
560 (currentTimeMillis() - lastProbe) < (probeInterval * 1000 / 3)) {
561 // This avoids overload of probe tasks which might involve sending
562 // messages over the network. We require a minimum interval of 1/3
563 // of the configured probeInterval between consecutive probe tasks.
564 if (log.isDebugEnabled()) {
565 log.debug("Dropping probe task for {} as it happened recently",
566 deviceId);
567 }
568 return;
569 }
570
571 submitTask(deviceId, TaskType.PROBE_AVAILABILITY);
572 }
573
574 /**
575 * Type of tasks performed by this provider.
576 */
577 enum TaskType {
578 CONNECTION_SETUP,
579 CONNECTION_UPDATE,
580 CONNECTION_TEARDOWN,
581 PIPELINE_READY,
582 CHANNEL_OPEN,
583 CHANNEL_CLOSED,
584 PIPELINE_NOT_READY,
585 PROBE_AVAILABILITY,
586 ROLE_MASTER,
587 ROLE_NONE,
588 ROLE_STANDBY,
589 NOT_MASTER,
590 }
591
592 private void submitTask(DeviceId deviceId, TaskType taskType) {
593 taskExecutor.submit(deviceId, taskType, taskRunnable(deviceId, taskType));
594 }
595
596 private Runnable taskRunnable(DeviceId deviceId, TaskType taskType) {
597 switch (taskType) {
598 case CONNECTION_SETUP:
599 return () -> handleConnectionSetup(deviceId);
600 case CONNECTION_UPDATE:
601 return () -> handleConnectionUpdate(deviceId);
602 case CONNECTION_TEARDOWN:
603 return () -> handleConnectionTeardown(deviceId);
604 case CHANNEL_OPEN:
605 return () -> handleProbeAvailability(deviceId);
606 case CHANNEL_CLOSED:
607 return () -> markOfflineIfNeeded(deviceId);
608 case PIPELINE_NOT_READY:
609 return () -> markOfflineIfNeeded(deviceId);
610 case PIPELINE_READY:
611 return () -> handleProbeAvailability(deviceId);
612 case PROBE_AVAILABILITY:
613 return () -> handleProbeAvailability(deviceId);
614 case ROLE_MASTER:
615 return () -> handleMastershipResponse(deviceId, MastershipRole.MASTER);
616 case ROLE_STANDBY:
617 return () -> handleMastershipResponse(deviceId, MastershipRole.STANDBY);
618 case ROLE_NONE:
619 return () -> handleMastershipResponse(deviceId, MastershipRole.NONE);
620 case NOT_MASTER:
621 return () -> handleNotMaster(deviceId);
622 default:
623 throw new IllegalArgumentException("Unrecognized task type " + taskType);
624 }
625 }
626
627 private void handleConnectionSetup(DeviceId deviceId) {
628 assertConfig(deviceId);
629 // Bind pipeconf (if any and if device is capable).
630 bindPipeconfIfRequired(deviceId);
631 // Get handshaker.
632 final DeviceHandshaker handshaker = handshakerOrFail(deviceId);
633 if (handshaker.isConnected() || handshakersWithListeners.containsKey(deviceId)) {
634 throw new DeviceTaskException("connection already exists");
635 }
636 // Add device agent listener.
637 handshaker.addDeviceAgentListener(id(), deviceAgentListener);
638 handshakersWithListeners.put(deviceId, handshaker);
639 // Start connection via handshaker.
640 final Boolean connectSuccess = getFutureWithDeadline(
641 handshaker.connect(), "initiating connection",
642 deviceId, false, opTimeoutShort);
643 if (!connectSuccess) {
644 // Failed! Remove listeners.
645 handshaker.removeDeviceAgentListener(id());
646 handshakersWithListeners.remove(deviceId);
647 throw new DeviceTaskException("connection failed");
648 }
649 createOrUpdateDevice(deviceId, false);
650 final List<PortDescription> ports = getPortDetails(deviceId);
651 providerService.updatePorts(deviceId, ports);
652 // From here we expect a CHANNEL_OPEN event to update availability.
653 }
654
655 private void handleConnectionUpdate(DeviceId deviceId) {
656 assertConfig(deviceId);
657 final DeviceHandshaker handshaker = handshakerOrFail(deviceId);
658 if (!handshaker.isConnected()) {
659 // If driver reports that a connection still exists, perhaps the
660 // part of the netcfg that changed does not affect the connection.
661 // Otherwise, remove any previous connection state from the old
662 // netcfg and create a new one.
663 log.warn("Detected change of connection endpoints for {}, will " +
664 "tear down existing connection and set up a new one...",
665 deviceId);
666 handleConnectionTeardown(deviceId);
667 handleConnectionSetup(deviceId);
668 }
669 }
670
671 private void createOrUpdateDevice(DeviceId deviceId, boolean available) {
672 if (deviceService.getDevice(deviceId) != null
673 && deviceService.isAvailable(deviceId) == available) {
674 // Other nodes might have advertised this device before us.
675 return;
676 }
677 assertConfig(deviceId);
678 providerService.deviceConnected(deviceId, getDeviceDescription(
679 deviceId, available));
680 }
681
682 private boolean probeAvailability(DeviceHandshaker handshaker) {
683 lastProbedAvailability.put(handshaker.data().deviceId(), currentTimeMillis());
684 return getFutureWithDeadline(
685 handshaker.probeAvailability(), "probing availability",
686 handshaker.data().deviceId(), false, opTimeoutShort);
687 }
688
689 private boolean probeReachability(DeviceHandshaker handshaker) {
690 lastProbedAvailability.put(handshaker.data().deviceId(), currentTimeMillis());
691 return getFutureWithDeadline(
692 handshaker.probeReachability(), "probing reachability",
693 handshaker.data().deviceId(), false, opTimeoutShort);
694 }
695
696 private void markOfflineIfNeeded(DeviceId deviceId) {
697 assertDeviceRegistered(deviceId);
698 if (deviceService.isAvailable(deviceId)) {
699 providerService.deviceDisconnected(deviceId);
700 }
701 }
702
703 private void handleProbeAvailability(DeviceId deviceId) {
704 assertDeviceRegistered(deviceId);
705
706 // Make device has a valid mastership role.
707 final DeviceHandshaker handshaker = handshakerOrFail(deviceId);
708 final MastershipRole deviceRole = handshaker.getRole();
709 final MastershipRole expectedRole = mastershipService.getLocalRole(deviceId);
710 if (expectedRole == MastershipRole.NONE || expectedRole != deviceRole) {
711 // Device does NOT have a valid role...
712 if (!handshaker.isReachable() && !probeReachability(handshaker)) {
713 // ...but is not reachable. There isn't much we can do.
714 markOfflineIfNeeded(deviceId);
715 return;
716 }
717 // ...and is reachable, re-assert role.
718 roleChanged(deviceId, expectedRole == MastershipRole.NONE
719 ? mastershipService.requestRoleForSync(deviceId)
720 : expectedRole);
721 try {
722 // Wait for role to be notified and reachability state to be
723 // updated. This should be roughly equivalent to one RTT.
724 Thread.sleep(500);
725 } catch (InterruptedException e) {
726 Thread.currentThread().interrupt();
727 return;
728 }
729 }
730
731 // Check and update availability.
732 if (probeAvailability(handshakerOrFail(deviceId))) {
733 // Device ready to do its job.
734 createOrUpdateDevice(deviceId, true);
735 } else {
736 markOfflineIfNeeded(deviceId);
737 if (handshaker.isReachable() && isPipelineProgrammable(deviceId)) {
738 // If reachable, but not available, and pipeline programmable, there
739 // is a high chance it's because the pipeline is not READY
740 // (independently from what the pipeconf watchdog reports, as the
741 // status there might be outdated). Encourage pipeconf watchdog to
742 // perform a pipeline probe ASAP.
743 pipeconfWatchdogService.triggerProbe(deviceId);
744 }
745 }
746 }
747
748 private void handleMastershipResponse(DeviceId deviceId, MastershipRole response) {
749 assertDeviceRegistered(deviceId);
750 log.debug("Device {} asserted role {}", deviceId, response);
751 providerService.receivedRoleReply(deviceId, response);
752 }
753
754 private void handleNotMaster(DeviceId deviceId) {
755 assertDeviceRegistered(deviceId);
756 if (mastershipService.isLocalMaster(deviceId)) {
757 log.warn("Device {} notified that this node is not master, " +
758 "relinquishing mastership...", deviceId);
759 mastershipService.relinquishMastership(deviceId);
760 }
761 }
762
763 private void assertDeviceRegistered(DeviceId deviceId) {
764 if (deviceService.getDevice(deviceId) == null) {
765 throw new DeviceTaskException("device not registered in the core");
766 }
767 }
768
769 private void handleConnectionTeardown(DeviceId deviceId) {
770 if (deviceService.getDevice(deviceId) != null
771 && deviceService.isAvailable(deviceId)) {
772 providerService.deviceDisconnected(deviceId);
773 }
774 final DeviceHandshaker handshaker = handshakerOrFail(deviceId);
775 handshaker.removeDeviceAgentListener(id());
776 handshakersWithListeners.remove(deviceId);
777 handshaker.disconnect();
778 lastProbedAvailability.remove(deviceId);
779 }
780
781 private void bindPipeconfIfRequired(DeviceId deviceId) {
782 if (pipeconfService.getPipeconf(deviceId).isPresent()
783 || !isPipelineProgrammable(deviceId)) {
784 // Nothing to do.
785 // Device has already a pipeconf or is not programmable.
786 return;
787 }
788 // Get pipeconf from netcfg or driver (default one).
789 final PiPipelineProgrammable pipelineProg = getBehaviour(
790 deviceId, PiPipelineProgrammable.class);
791 final PiPipeconfId pipeconfId = getPipeconfId(deviceId, pipelineProg);
792 if (pipeconfId == null) {
793 throw new DeviceTaskException("unable to find pipeconf");
794 }
795 // Store binding in pipeconf service.
796 pipeconfService.bindToDevice(pipeconfId, deviceId);
797 }
798
799 private PiPipeconfId getPipeconfId(DeviceId deviceId, PiPipelineProgrammable pipelineProg) {
800 // Places to look for a pipeconf ID (in priority order)):
801 // 1) netcfg
802 // 2) device driver (default one)
803 final PiPipeconfId pipeconfId = getPipeconfFromCfg(deviceId);
804 if (pipeconfId != null && !pipeconfId.id().isEmpty()) {
805 return pipeconfId;
806 }
807 if (pipelineProg != null
808 && pipelineProg.getDefaultPipeconf().isPresent()) {
809 final PiPipeconf defaultPipeconf = pipelineProg.getDefaultPipeconf().get();
810 log.info("Using default pipeconf {} for {}", defaultPipeconf.id(), deviceId);
811 return defaultPipeconf.id();
812 }
813 return null;
814 }
815
816 private PiPipeconfId getPipeconfFromCfg(DeviceId deviceId) {
817 BasicDeviceConfig config = cfgService.getConfig(
818 deviceId, BasicDeviceConfig.class);
819 if (config == null) {
820 return null;
821 }
822 return config.pipeconf() != null
823 ? new PiPipeconfId(config.pipeconf()) : null;
824 }
825
826 private DeviceHandshaker handshakerOrFail(DeviceId deviceId) {
827 final DeviceHandshaker handshaker = getBehaviour(
828 deviceId, DeviceHandshaker.class);
829 if (handshaker == null) {
830 throw new DeviceTaskException("missing handshaker behavior");
831 }
832 return handshaker;
833 }
834
835 private boolean configIsComplete(DeviceId deviceId) {
836 final BasicDeviceConfig basicDeviceCfg = cfgService.getConfig(
837 deviceId, BasicDeviceConfig.class);
838 return basicDeviceCfg != null && !isNullOrEmpty(basicDeviceCfg.driver());
839 }
840
841 private void assertConfig(DeviceId deviceId) {
842 if (!configIsComplete(deviceId)) {
843 throw new DeviceTaskException("configuration is not complete");
844 }
Thomas Vachuska5b38dc02018-05-10 15:24:40 -0700845 }
846
Andrea Campanella241896c2017-05-10 13:11:04 -0700847 private Driver getDriver(DeviceId deviceId) {
Andrea Campanella241896c2017-05-10 13:11:04 -0700848 try {
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200849 // DriverManager checks first using basic device config.
850 return driverService.getDriver(deviceId);
Andrea Campanella241896c2017-05-10 13:11:04 -0700851 } catch (ItemNotFoundException e) {
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200852 log.error("Driver not found for {}", deviceId);
853 return null;
Andrea Campanella241896c2017-05-10 13:11:04 -0700854 }
Andrea Campanella241896c2017-05-10 13:11:04 -0700855 }
856
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700857 private <T extends Behaviour> T getBehaviour(DeviceId deviceId, Class<T> type) {
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700858 Driver driver = getDriver(deviceId);
859 if (driver == null) {
Andrea Campanella241896c2017-05-10 13:11:04 -0700860 return null;
861 }
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700862 if (!driver.hasBehaviour(type)) {
863 return null;
864 }
865 final DriverData data = new DefaultDriverData(driver, deviceId);
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700866 final DefaultDriverHandler handler = new DefaultDriverHandler(data);
867 return driver.createBehaviour(handler, type);
Andrea Campanella241896c2017-05-10 13:11:04 -0700868 }
869
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800870 private boolean hasBehaviour(DeviceId deviceId, Class<? extends Behaviour> type) {
871 Driver driver = getDriver(deviceId);
872 if (driver == null) {
873 return false;
Carmelo Cascone96beb6f2018-06-27 18:07:12 +0200874 }
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800875 return driver.hasBehaviour(type);
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700876 }
877
878 private DeviceDescription getDeviceDescription(
879 DeviceId deviceId, boolean defaultAvailable) {
880 // Get one from driver or forge.
881 final DeviceDescriptionDiscovery deviceDiscovery = getBehaviour(
882 deviceId, DeviceDescriptionDiscovery.class);
Yi Tsengd7716482018-10-31 15:34:30 -0700883 if (deviceDiscovery == null) {
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700884 return forgeDeviceDescription(deviceId, defaultAvailable);
885 }
Yi Tsengd7716482018-10-31 15:34:30 -0700886
887 final DeviceDescription d = deviceDiscovery.discoverDeviceDetails();
888 if (d == null) {
889 return forgeDeviceDescription(deviceId, defaultAvailable);
890 }
891 // Enforce defaultAvailable flag over the one obtained from driver.
892 return new DefaultDeviceDescription(d, defaultAvailable, d.annotations());
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700893 }
894
895 private List<PortDescription> getPortDetails(DeviceId deviceId) {
896 final DeviceDescriptionDiscovery deviceDiscovery = getBehaviour(
897 deviceId, DeviceDescriptionDiscovery.class);
898 if (deviceDiscovery != null) {
899 return deviceDiscovery.discoverPortDetails();
900 } else {
901 return Collections.emptyList();
902 }
903 }
904
905 private DeviceDescription forgeDeviceDescription(
906 DeviceId deviceId, boolean defaultAvailable) {
907 // Uses handshaker and provider config to get driver data.
908 final DeviceHandshaker handshaker = getBehaviour(
909 deviceId, DeviceHandshaker.class);
910 final Driver driver = handshaker != null
911 ? handshaker.handler().driver() : null;
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700912 return new DefaultDeviceDescription(
913 deviceId.uri(),
914 Device.Type.SWITCH,
915 driver != null ? driver.manufacturer() : UNKNOWN,
916 driver != null ? driver.hwVersion() : UNKNOWN,
917 driver != null ? driver.swVersion() : UNKNOWN,
918 UNKNOWN,
919 new ChassisId(),
920 defaultAvailable,
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800921 DefaultAnnotations.EMPTY);
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700922 }
923
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800924 static boolean myScheme(DeviceId deviceId) {
925 return deviceId.uri().getScheme().equals(URI_SCHEME);
Carmelo Cascone96beb6f2018-06-27 18:07:12 +0200926 }
927
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700928 private boolean isPipelineProgrammable(DeviceId deviceId) {
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800929 return hasBehaviour(deviceId, PiPipelineProgrammable.class);
Carmelo Casconede3b6842018-09-05 17:45:10 -0700930 }
931
Carmelo Cascone96beb6f2018-06-27 18:07:12 +0200932 private <U> U getFutureWithDeadline(CompletableFuture<U> future, String opDescription,
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200933 DeviceId deviceId, U defaultValue, int timeout) {
Carmelo Cascone96beb6f2018-06-27 18:07:12 +0200934 try {
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200935 return future.get(timeout, TimeUnit.SECONDS);
Carmelo Cascone96beb6f2018-06-27 18:07:12 +0200936 } catch (InterruptedException e) {
937 log.error("Thread interrupted while {} on {}", opDescription, deviceId);
938 Thread.currentThread().interrupt();
939 } catch (ExecutionException e) {
940 log.error("Exception while {} on {}", opDescription, deviceId, e.getCause());
941 } catch (TimeoutException e) {
942 log.error("Operation TIMEOUT while {} on {}", opDescription, deviceId);
943 }
944 return defaultValue;
945 }
Andrea Campanella241896c2017-05-10 13:11:04 -0700946}