blob: e716f568b282c1c736d83ca086d252c8e1b7de38 [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;
Carmelo Cascone61469462019-03-05 23:59:11 -080020import com.google.common.util.concurrent.Futures;
Andrea Campanella241896c2017-05-10 13:11:04 -070021import org.onlab.packet.ChassisId;
22import org.onlab.util.ItemNotFoundException;
Andrea Campanella19090322017-08-22 10:31:37 +020023import org.onlab.util.Tools;
Andrea Campanella4929a812017-10-09 18:38:23 +020024import org.onosproject.cfg.ComponentConfigService;
Carmelo Cascone3977ea42019-02-28 13:43:42 -080025import org.onosproject.cluster.ClusterService;
26import org.onosproject.cluster.NodeId;
Andrea Campanella241896c2017-05-10 13:11:04 -070027import org.onosproject.core.CoreService;
Yi Tsenge616d752018-11-27 10:53:27 -080028import org.onosproject.gnmi.api.GnmiController;
Carmelo Cascone3977ea42019-02-28 13:43:42 -080029import org.onosproject.mastership.MastershipInfo;
Andrea Campanella14e196d2017-07-24 18:11:36 +020030import org.onosproject.mastership.MastershipService;
Andrea Campanella241896c2017-05-10 13:11:04 -070031import org.onosproject.net.DefaultAnnotations;
32import org.onosproject.net.Device;
33import org.onosproject.net.DeviceId;
34import org.onosproject.net.MastershipRole;
35import org.onosproject.net.PortNumber;
Carmelo Cascone87892e22017-11-13 16:01:29 -080036import org.onosproject.net.behaviour.PiPipelineProgrammable;
Andrea Campanella241896c2017-05-10 13:11:04 -070037import org.onosproject.net.behaviour.PortAdmin;
Andrea Campanella241896c2017-05-10 13:11:04 -070038import org.onosproject.net.config.NetworkConfigEvent;
39import org.onosproject.net.config.NetworkConfigListener;
40import org.onosproject.net.config.NetworkConfigRegistry;
41import org.onosproject.net.config.basics.BasicDeviceConfig;
Andrea Campanella241896c2017-05-10 13:11:04 -070042import org.onosproject.net.device.DefaultDeviceDescription;
Carmelo Casconee5b28722018-06-22 17:28:28 +020043import org.onosproject.net.device.DeviceAgentEvent;
44import org.onosproject.net.device.DeviceAgentListener;
Andrea Campanella241896c2017-05-10 13:11:04 -070045import org.onosproject.net.device.DeviceDescription;
46import org.onosproject.net.device.DeviceDescriptionDiscovery;
Andrea Campanella241896c2017-05-10 13:11:04 -070047import org.onosproject.net.device.DeviceHandshaker;
Andrea Campanella241896c2017-05-10 13:11:04 -070048import org.onosproject.net.device.DeviceProvider;
49import org.onosproject.net.device.DeviceProviderRegistry;
50import org.onosproject.net.device.DeviceProviderService;
51import org.onosproject.net.device.DeviceService;
52import org.onosproject.net.device.PortDescription;
Andrea Campanella241896c2017-05-10 13:11:04 -070053import org.onosproject.net.driver.Behaviour;
54import org.onosproject.net.driver.DefaultDriverData;
55import org.onosproject.net.driver.DefaultDriverHandler;
56import org.onosproject.net.driver.Driver;
57import org.onosproject.net.driver.DriverData;
58import org.onosproject.net.driver.DriverService;
Carmelo Cascone59f57de2017-07-11 19:55:09 -040059import org.onosproject.net.pi.model.PiPipeconf;
Andrea Campanellabc112a92017-06-26 19:06:43 +020060import org.onosproject.net.pi.model.PiPipeconfId;
Carmelo Cascone39c28ca2017-11-15 13:03:57 -080061import org.onosproject.net.pi.service.PiPipeconfService;
Carmelo Cascone9e4972c2018-08-30 00:29:16 -070062import org.onosproject.net.pi.service.PiPipeconfWatchdogEvent;
63import org.onosproject.net.pi.service.PiPipeconfWatchdogListener;
64import org.onosproject.net.pi.service.PiPipeconfWatchdogService;
Andrea Campanella241896c2017-05-10 13:11:04 -070065import org.onosproject.net.provider.AbstractProvider;
66import org.onosproject.net.provider.ProviderId;
Carmelo Cascone3977ea42019-02-28 13:43:42 -080067import org.onosproject.provider.general.device.impl.DeviceTaskExecutor.DeviceTaskException;
Andrea Campanella19090322017-08-22 10:31:37 +020068import org.osgi.service.component.ComponentContext;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070069import org.osgi.service.component.annotations.Activate;
70import org.osgi.service.component.annotations.Component;
71import org.osgi.service.component.annotations.Deactivate;
72import org.osgi.service.component.annotations.Modified;
73import org.osgi.service.component.annotations.Reference;
74import org.osgi.service.component.annotations.ReferenceCardinality;
Andrea Campanella241896c2017-05-10 13:11:04 -070075import org.slf4j.Logger;
76
Andrea Campanellabc112a92017-06-26 19:06:43 +020077import java.util.Collections;
Andrea Campanella19090322017-08-22 10:31:37 +020078import java.util.Dictionary;
Andrea Campanella241896c2017-05-10 13:11:04 -070079import java.util.List;
Thomas Vachuska5b38dc02018-05-10 15:24:40 -070080import java.util.Map;
Carmelo Cascone3977ea42019-02-28 13:43:42 -080081import java.util.Objects;
Andrea Campanella241896c2017-05-10 13:11:04 -070082import java.util.concurrent.CompletableFuture;
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;
Andrea Campanella241896c2017-05-10 13:11:04 -070087
Carmelo Cascone3977ea42019-02-28 13:43:42 -080088import static com.google.common.base.Preconditions.checkNotNull;
89import static com.google.common.base.Strings.isNullOrEmpty;
Carmelo Cascone61469462019-03-05 23:59:11 -080090import static java.lang.String.format;
Carmelo Cascone3977ea42019-02-28 13:43:42 -080091import 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;
Carmelo Cascone3977ea42019-02-28 13:43:42 -080095import static org.onosproject.provider.general.device.impl.OsgiPropertyConstants.PROBE_INTERVAL;
96import static org.onosproject.provider.general.device.impl.OsgiPropertyConstants.PROBE_INTERVAL_DEFAULT;
97import static org.onosproject.provider.general.device.impl.OsgiPropertyConstants.STATS_POLL_INTERVAL;
98import static org.onosproject.provider.general.device.impl.OsgiPropertyConstants.STATS_POLL_INTERVAL_DEFAULT;
Andrea Campanella241896c2017-05-10 13:11:04 -070099import static org.slf4j.LoggerFactory.getLogger;
100
101/**
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800102 * Provider which uses drivers to discover devices, perform initial handshake,
103 * and notify the core of disconnection events. The implementation listens for
104 * events from netcfg or the drivers (via {@link DeviceAgentListener}) andP
105 * schedules task for each event.
Andrea Campanella241896c2017-05-10 13:11:04 -0700106 */
Thomas Vachuska4167c3f2018-10-16 07:16:31 -0700107@Component(immediate = true,
108 property = {
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800109 PROBE_INTERVAL + ":Integer=" + PROBE_INTERVAL_DEFAULT,
110 STATS_POLL_INTERVAL + ":Integer=" + STATS_POLL_INTERVAL_DEFAULT,
Thomas Vachuska4167c3f2018-10-16 07:16:31 -0700111 })
Andrea Campanella241896c2017-05-10 13:11:04 -0700112public class GeneralDeviceProvider extends AbstractProvider
113 implements DeviceProvider {
Carmelo Casconee5b28722018-06-22 17:28:28 +0200114
Andrea Campanella241896c2017-05-10 13:11:04 -0700115 private final Logger log = getLogger(getClass());
116
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800117 private static final String APP_NAME = "org.onosproject.generaldeviceprovider";
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700118 private static final String URI_SCHEME = "device";
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700119 private static final String DEVICE_PROVIDER_PACKAGE =
120 "org.onosproject.general.provider.device";
121 private static final int CORE_POOL_SIZE = 10;
122 private static final String UNKNOWN = "unknown";
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700123
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700124 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Carmelo Casconee5b28722018-06-22 17:28:28 +0200125 private DeviceProviderRegistry providerRegistry;
Andrea Campanella241896c2017-05-10 13:11:04 -0700126
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700127 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Carmelo Casconee5b28722018-06-22 17:28:28 +0200128 private ComponentConfigService componentConfigService;
Andrea Campanella4929a812017-10-09 18:38:23 +0200129
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700130 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Carmelo Casconee5b28722018-06-22 17:28:28 +0200131 private NetworkConfigRegistry cfgService;
Andrea Campanella241896c2017-05-10 13:11:04 -0700132
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700133 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Carmelo Casconee5b28722018-06-22 17:28:28 +0200134 private CoreService coreService;
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 DeviceService deviceService;
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 DriverService driverService;
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 MastershipService mastershipService;
Andrea Campanella14e196d2017-07-24 18:11:36 +0200144
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700145 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800146 private ClusterService clusterService;
147
148 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700149 private PiPipeconfService pipeconfService;
Andrea Campanella241896c2017-05-10 13:11:04 -0700150
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700151 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700152 private PiPipeconfWatchdogService pipeconfWatchdogService;
Andrea Campanella14e196d2017-07-24 18:11:36 +0200153
Yi Tsenge616d752018-11-27 10:53:27 -0800154 // FIXME: no longer general if we add a dependency to a protocol-specific
155 // service. Possible solutions are: rename this provider to
156 // StratumDeviceProvider, find a way to allow this provider to register for
157 // protocol specific events (e.g. port events) via drivers (similar to
158 // DeviceAgentListener).
159 @Reference(cardinality = ReferenceCardinality.MANDATORY)
160 private GnmiController gnmiController;
161
162 private GnmiDeviceStateSubscriber gnmiDeviceStateSubscriber;
163
Carmelo Cascone61469462019-03-05 23:59:11 -0800164 /**
165 * Configure interval for checking device availability; default is 10 sec.
166 */
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800167 private int probeInterval = PROBE_INTERVAL_DEFAULT;
Andrea Campanella19090322017-08-22 10:31:37 +0200168
Carmelo Cascone61469462019-03-05 23:59:11 -0800169 /**
170 * Configure poll frequency for port status and stats; default is 10 sec.
171 */
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800172 private int statsPollInterval = STATS_POLL_INTERVAL_DEFAULT;
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200173
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700174 private final Map<DeviceId, DeviceHandshaker> handshakersWithListeners = Maps.newConcurrentMap();
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800175 private final Map<DeviceId, Long> lastProbedAvailability = Maps.newConcurrentMap();
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700176 private final InternalPipeconfWatchdogListener pipeconfWatchdogListener = new InternalPipeconfWatchdogListener();
Carmelo Casconee5b28722018-06-22 17:28:28 +0200177 private final NetworkConfigListener cfgListener = new InternalNetworkConfigListener();
178 private final DeviceAgentListener deviceAgentListener = new InternalDeviceAgentListener();
Andrea Campanella241896c2017-05-10 13:11:04 -0700179
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800180 private ExecutorService mainExecutor;
181 private DeviceTaskExecutor<TaskType> taskExecutor;
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700182 private ScheduledExecutorService probeExecutor;
183 private ScheduledFuture<?> probeTask;
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800184 private StatsPoller statsPoller;
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700185 private DeviceProviderService providerService;
186
187 public GeneralDeviceProvider() {
188 super(new ProviderId(URI_SCHEME, DEVICE_PROVIDER_PACKAGE));
189 }
Andrea Campanella241896c2017-05-10 13:11:04 -0700190
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800191 protected DeviceProviderService providerService() {
192 return providerService;
193 }
194
Andrea Campanella241896c2017-05-10 13:11:04 -0700195 @Activate
Andrea Campanella1e573442018-05-17 17:07:13 +0200196 public void activate(ComponentContext context) {
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800197 mainExecutor = newFixedThreadPool(CORE_POOL_SIZE, groupedThreads(
198 "onos/gdp-task", "%d", log));
199 taskExecutor = new DeviceTaskExecutor<>(mainExecutor);
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700200 probeExecutor = newSingleThreadScheduledExecutor(groupedThreads(
201 "onos/gdp-probe", "%d", log));
Andrea Campanella241896c2017-05-10 13:11:04 -0700202 providerService = providerRegistry.register(this);
Andrea Campanella4929a812017-10-09 18:38:23 +0200203 componentConfigService.registerProperties(getClass());
Andrea Campanella241896c2017-05-10 13:11:04 -0700204 coreService.registerApplication(APP_NAME);
Andrea Campanella241896c2017-05-10 13:11:04 -0700205 cfgService.addListener(cfgListener);
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700206 pipeconfWatchdogService.addListener(pipeconfWatchdogListener);
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800207 gnmiDeviceStateSubscriber = new GnmiDeviceStateSubscriber(
208 gnmiController, deviceService, mastershipService, providerService);
Yi Tsenge616d752018-11-27 10:53:27 -0800209 gnmiDeviceStateSubscriber.activate();
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800210 startOrRescheduleProbeTask();
211 statsPoller = new StatsPoller(deviceService, mastershipService, providerService);
212 statsPoller.activate(statsPollInterval);
213 modified(context);
Andrea Campanella241896c2017-05-10 13:11:04 -0700214 log.info("Started");
215 }
216
Andrea Campanella19090322017-08-22 10:31:37 +0200217 @Modified
218 public void modified(ComponentContext context) {
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200219 if (context == null) {
220 return;
Andrea Campanella19090322017-08-22 10:31:37 +0200221 }
222
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200223 Dictionary<?, ?> properties = context.getProperties();
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800224 final int oldProbeFrequency = probeInterval;
225 probeInterval = Tools.getIntegerProperty(
226 properties, PROBE_INTERVAL, PROBE_INTERVAL_DEFAULT);
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200227 log.info("Configured. {} is configured to {} seconds",
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800228 PROBE_INTERVAL, probeInterval);
229 final int oldStatsPollFrequency = statsPollInterval;
230 statsPollInterval = Tools.getIntegerProperty(
231 properties, STATS_POLL_INTERVAL, STATS_POLL_INTERVAL_DEFAULT);
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200232 log.info("Configured. {} is configured to {} seconds",
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800233 STATS_POLL_INTERVAL, statsPollInterval);
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200234
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800235 if (oldProbeFrequency != probeInterval) {
236 startOrRescheduleProbeTask();
Andrea Campanella19090322017-08-22 10:31:37 +0200237 }
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200238
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800239 if (oldStatsPollFrequency != statsPollInterval) {
240 statsPoller.reschedule(statsPollInterval);
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200241 }
Andrea Campanella19090322017-08-22 10:31:37 +0200242 }
243
Andrea Campanella241896c2017-05-10 13:11:04 -0700244 @Deactivate
245 public void deactivate() {
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800246 // Shutdown stats poller.
247 statsPoller.deactivate();
248 statsPoller = null;
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700249 // Shutdown probe executor.
250 probeTask.cancel(true);
251 probeTask = null;
252 probeExecutor.shutdownNow();
253 try {
254 probeExecutor.awaitTermination(5, TimeUnit.SECONDS);
255 } catch (InterruptedException e) {
256 log.warn("probeExecutor not terminated properly");
257 }
258 probeExecutor = null;
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800259 // Shutdown main and task executor.
260 taskExecutor.cancel();
261 taskExecutor = null;
262 mainExecutor.shutdownNow();
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700263 try {
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800264 mainExecutor.awaitTermination(5, TimeUnit.SECONDS);
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700265 } catch (InterruptedException e) {
266 log.warn("connectionExecutor not terminated properly");
267 }
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800268 mainExecutor = null;
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700269 // Remove all device agent listeners
270 handshakersWithListeners.values().forEach(h -> h.removeDeviceAgentListener(id()));
271 handshakersWithListeners.clear();
272 // Other cleanup.
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800273 lastProbedAvailability.clear();
Andrea Campanella4929a812017-10-09 18:38:23 +0200274 componentConfigService.unregisterProperties(getClass(), false);
Andrea Campanella241896c2017-05-10 13:11:04 -0700275 cfgService.removeListener(cfgListener);
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700276 pipeconfWatchdogService.removeListener(pipeconfWatchdogListener);
Andrea Campanella241896c2017-05-10 13:11:04 -0700277 providerRegistry.unregister(this);
278 providerService = null;
Yi Tsenge616d752018-11-27 10:53:27 -0800279 gnmiDeviceStateSubscriber.deactivate();
280 gnmiDeviceStateSubscriber = null;
Andrea Campanella241896c2017-05-10 13:11:04 -0700281 log.info("Stopped");
282 }
283
Andrea Campanella241896c2017-05-10 13:11:04 -0700284 @Override
285 public void triggerProbe(DeviceId deviceId) {
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800286 checkNotNull(deviceId);
287 submitTask(deviceId, TaskType.PROBE_AVAILABILITY);
Andrea Campanella241896c2017-05-10 13:11:04 -0700288 }
289
290 @Override
291 public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
Carmelo Casconee5b28722018-06-22 17:28:28 +0200292
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800293 final MastershipInfo mastershipInfo = mastershipService.getMastershipFor(deviceId);
294 final NodeId localNodeId = clusterService.getLocalNode().id();
295
296 if (!mastershipInfo.getRole(localNodeId).equals(newRole)) {
297 log.warn("Inconsistent mastership info for {}! Requested {}, but " +
298 "mastership service reports {}, will apply the latter...",
299 deviceId, newRole, mastershipInfo.getRole(localNodeId));
300 newRole = mastershipInfo.getRole(localNodeId);
301 }
302
303 // Derive preference value.
304 final int preference;
305 switch (newRole) {
306 case MASTER:
307 preference = 0;
308 break;
309 case STANDBY:
310 preference = mastershipInfo.backups().indexOf(localNodeId) + 1;
311 if (preference == 0) {
312 // Not found in list.
313 log.error("Unable to derive mastership preference for {}, " +
314 "requested role {} but local node ID was " +
315 "not found among list of backup nodes " +
316 "reported by mastership service");
317 return;
318 }
319 break;
320 case NONE:
321 // No preference for NONE, apply as is.
322 log.info("Notifying role {} to {}", newRole, deviceId);
323 roleChanged(deviceId, newRole);
324 return;
325 default:
326 log.error("Unrecognized mastership role {}", newRole);
327 return;
328 }
329
330 log.info("Notifying role {} (preference {}) for term {} to {}",
331 newRole, preference, mastershipInfo.term(), deviceId);
332
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700333 final DeviceHandshaker handshaker = getBehaviour(
334 deviceId, DeviceHandshaker.class);
Carmelo Casconee5b28722018-06-22 17:28:28 +0200335 if (handshaker == null) {
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800336 log.error("Null handshaker. Unable to notify role {} to {}",
Carmelo Cascone96beb6f2018-06-27 18:07:12 +0200337 newRole, deviceId);
Carmelo Casconee5b28722018-06-22 17:28:28 +0200338 return;
339 }
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800340
341 try {
342 handshaker.roleChanged(preference, mastershipInfo.term());
343 } catch (UnsupportedOperationException e) {
344 // Preference-based method not supported.
345 handshaker.roleChanged(newRole);
346 }
Andrea Campanella241896c2017-05-10 13:11:04 -0700347 }
348
349 @Override
350 public boolean isReachable(DeviceId deviceId) {
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700351 final DeviceHandshaker handshaker = getBehaviour(
352 deviceId, DeviceHandshaker.class);
Andrea Campanellac1ecdd02018-01-12 12:48:24 +0100353 if (handshaker == null) {
354 return false;
355 }
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800356 return handshaker.isReachable();
Andrea Campanella241896c2017-05-10 13:11:04 -0700357 }
358
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800359 @Override
360 public boolean isAvailable(DeviceId deviceId) {
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700361 final DeviceHandshaker handshaker = getBehaviour(
362 deviceId, DeviceHandshaker.class);
363 if (handshaker == null) {
364 return false;
365 }
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800366 try {
367 // Try without probing the device...
368 return handshaker.isAvailable();
369 } catch (UnsupportedOperationException e) {
370 // Driver does not support that.
371 return probeAvailability(handshaker);
372 }
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700373 }
374
Andrea Campanella241896c2017-05-10 13:11:04 -0700375 @Override
376 public void changePortState(DeviceId deviceId, PortNumber portNumber,
377 boolean enable) {
Carmelo Cascone96beb6f2018-06-27 18:07:12 +0200378 if (!deviceService.getDevice(deviceId).is(PortAdmin.class)) {
379 log.warn("Missing PortAdmin behaviour on {}, aborting port state change",
380 deviceId);
381 return;
Andrea Campanella241896c2017-05-10 13:11:04 -0700382 }
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200383 final PortAdmin portAdmin = deviceService.getDevice(deviceId)
384 .as(PortAdmin.class);
385 final CompletableFuture<Boolean> modifyTask = enable
Carmelo Cascone96beb6f2018-06-27 18:07:12 +0200386 ? portAdmin.enable(portNumber)
387 : portAdmin.disable(portNumber);
Carmelo Cascone61469462019-03-05 23:59:11 -0800388 final String descr = format("%s port %s on %s",
389 (enable ? "enable" : "disable"),
390 portNumber, deviceId);
391 modifyTask.whenComplete((success, ex) -> {
392 if (ex != null) {
393 log.error("Exception while trying to " + descr, ex);
394 } else if (!success) {
395 log.warn("Unable to " + descr);
396 }
397 });
Andrea Campanella241896c2017-05-10 13:11:04 -0700398 }
399
Thomas Vachuska5b38dc02018-05-10 15:24:40 -0700400 @Override
401 public void triggerDisconnect(DeviceId deviceId) {
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800402 checkNotNull(deviceId);
403 log.info("Triggering disconnection of device {}", deviceId);
404 submitTask(deviceId, TaskType.CONNECTION_TEARDOWN);
405 }
406
407 /**
408 * Listener for configuration events.
409 */
410 private class InternalNetworkConfigListener implements NetworkConfigListener {
411 @Override
412 public void event(NetworkConfigEvent event) {
413 DeviceId deviceId = (DeviceId) event.subject();
414 switch (event.type()) {
415 case CONFIG_ADDED:
416 if (configIsComplete(deviceId)) {
417 submitTask(deviceId, TaskType.CONNECTION_SETUP);
418 }
419 break;
420 case CONFIG_UPDATED:
421 if (configIsComplete(deviceId) && mgmtAddrUpdated(event)) {
422 submitTask(deviceId, TaskType.CONNECTION_UPDATE);
423 }
424 break;
425 case CONFIG_REMOVED:
426 if (event.configClass().equals(BasicDeviceConfig.class)) {
427 submitTask(deviceId, TaskType.CONNECTION_TEARDOWN);
428 }
429 break;
430 default:
431 // Ignore
432 break;
433 }
434 }
435
436 private boolean mgmtAddrUpdated(NetworkConfigEvent event) {
437 if (!event.prevConfig().isPresent() || !event.config().isPresent()) {
438 return false;
439 }
440 final BasicDeviceConfig prev = (BasicDeviceConfig) event.prevConfig().get();
441 final BasicDeviceConfig current = (BasicDeviceConfig) event.config().get();
442 return !Objects.equals(prev.managementAddress(), current.managementAddress());
443 }
444
445 @Override
446 public boolean isRelevant(NetworkConfigEvent event) {
447 return event.configClass().equals(BasicDeviceConfig.class) &&
448 (event.subject() instanceof DeviceId) &&
449 myScheme((DeviceId) event.subject());
450 }
451 }
452
453 /**
454 * Listener for device agent events.
455 */
456 private class InternalDeviceAgentListener implements DeviceAgentListener {
457 @Override
458 public void event(DeviceAgentEvent event) {
459 DeviceId deviceId = event.subject();
460 switch (event.type()) {
461 case CHANNEL_OPEN:
462 submitTask(deviceId, TaskType.CHANNEL_OPEN);
463 break;
464 case CHANNEL_CLOSED:
465 case CHANNEL_ERROR:
466 submitTask(deviceId, TaskType.CHANNEL_CLOSED);
467 break;
468 case ROLE_MASTER:
469 submitTask(deviceId, TaskType.ROLE_MASTER);
470 break;
471 case ROLE_STANDBY:
472 submitTask(deviceId, TaskType.ROLE_STANDBY);
473 break;
474 case ROLE_NONE:
475 submitTask(deviceId, TaskType.ROLE_NONE);
476 break;
477 case NOT_MASTER:
478 submitTask(deviceId, TaskType.NOT_MASTER);
479 break;
480 default:
481 log.warn("Unrecognized device agent event {}", event.type());
482 }
483 }
484 }
485
486 /**
487 * Pipeline event listener.
488 */
489 private class InternalPipeconfWatchdogListener implements PiPipeconfWatchdogListener {
490 @Override
491 public void event(PiPipeconfWatchdogEvent event) {
492 final DeviceId deviceId = event.subject();
493 switch (event.type()) {
494 case PIPELINE_READY:
495 submitTask(deviceId, TaskType.PIPELINE_READY);
496 break;
497 case PIPELINE_UNKNOWN:
498 submitTask(deviceId, TaskType.PIPELINE_NOT_READY);
499 break;
500 default:
501 break;
502 }
503 }
504
505 @Override
506 public boolean isRelevant(PiPipeconfWatchdogEvent event) {
507 return myScheme(event.subject());
508 }
509 }
510
511 private void startOrRescheduleProbeTask() {
512 synchronized (this) {
513 if (probeTask != null) {
514 probeTask.cancel(false);
515 }
516 probeTask = probeExecutor.scheduleAtFixedRate(
517 this::submitProbeTasks,
518 0,
519 probeInterval,
520 TimeUnit.SECONDS);
521 }
522 }
523
524 private void submitProbeTasks() {
525 // Async trigger a task for all devices in the cfg.
526 log.debug("Starting probing for all devices");
527 cfgService.getSubjects(DeviceId.class).stream()
528 .filter(GeneralDeviceProvider::myScheme)
529 .forEach(this::submitProbeTask);
530 }
531
532 private void submitProbeTask(DeviceId deviceId) {
533 final DeviceHandshaker handshaker = handshakersWithListeners.get(deviceId);
534
535 if (handshaker == null) {
536 if (configIsComplete(deviceId)) {
537 // Device in config but we have not initiated a connection.
538 // Perhaps we missed the config event?
539 submitTask(deviceId, TaskType.CONNECTION_SETUP);
540 }
541 return;
542 }
543
Carmelo Casconec2be50a2019-04-10 00:15:39 -0700544 if (!handshaker.hasConnection()) {
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800545 // Device is in the core, but driver reports there is NOT a
546 // connection to it. Perhaps the netcfg changed and we didn't
547 // pick the event?
548 log.warn("Re-establishing lost connection to {}", deviceId);
549 submitTask(deviceId, TaskType.CONNECTION_TEARDOWN);
550 submitTask(deviceId, TaskType.CONNECTION_SETUP);
551 return;
552 }
553
554 // On probing offline devices, while we expect them to signal
555 // availability via CHANNEL_OPEN or similar events, periodic probing
556 // might be needed to stimulate some channel activity. We might consider
557 // requiring active probing of closed channels in the protocol layer.
558
559 final Long lastProbe = lastProbedAvailability.get(deviceId);
560 if (lastProbe != null &&
561 (currentTimeMillis() - lastProbe) < (probeInterval * 1000 / 3)) {
562 // This avoids overload of probe tasks which might involve sending
563 // messages over the network. We require a minimum interval of 1/3
564 // of the configured probeInterval between consecutive probe tasks.
565 if (log.isDebugEnabled()) {
566 log.debug("Dropping probe task for {} as it happened recently",
567 deviceId);
568 }
569 return;
570 }
571
572 submitTask(deviceId, TaskType.PROBE_AVAILABILITY);
573 }
574
575 /**
576 * Type of tasks performed by this provider.
577 */
578 enum TaskType {
579 CONNECTION_SETUP,
580 CONNECTION_UPDATE,
581 CONNECTION_TEARDOWN,
582 PIPELINE_READY,
583 CHANNEL_OPEN,
584 CHANNEL_CLOSED,
585 PIPELINE_NOT_READY,
586 PROBE_AVAILABILITY,
587 ROLE_MASTER,
588 ROLE_NONE,
589 ROLE_STANDBY,
590 NOT_MASTER,
591 }
592
593 private void submitTask(DeviceId deviceId, TaskType taskType) {
594 taskExecutor.submit(deviceId, taskType, taskRunnable(deviceId, taskType));
595 }
596
597 private Runnable taskRunnable(DeviceId deviceId, TaskType taskType) {
598 switch (taskType) {
599 case CONNECTION_SETUP:
600 return () -> handleConnectionSetup(deviceId);
601 case CONNECTION_UPDATE:
602 return () -> handleConnectionUpdate(deviceId);
603 case CONNECTION_TEARDOWN:
604 return () -> handleConnectionTeardown(deviceId);
605 case CHANNEL_OPEN:
Carmelo Casconec2be50a2019-04-10 00:15:39 -0700606 case PROBE_AVAILABILITY:
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800607 case PIPELINE_READY:
608 return () -> handleProbeAvailability(deviceId);
Carmelo Casconec2be50a2019-04-10 00:15:39 -0700609 case CHANNEL_CLOSED:
610 case PIPELINE_NOT_READY:
611 return () -> markOfflineIfNeeded(deviceId);
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800612 case ROLE_MASTER:
613 return () -> handleMastershipResponse(deviceId, MastershipRole.MASTER);
614 case ROLE_STANDBY:
615 return () -> handleMastershipResponse(deviceId, MastershipRole.STANDBY);
616 case ROLE_NONE:
617 return () -> handleMastershipResponse(deviceId, MastershipRole.NONE);
618 case NOT_MASTER:
619 return () -> handleNotMaster(deviceId);
620 default:
621 throw new IllegalArgumentException("Unrecognized task type " + taskType);
622 }
623 }
624
625 private void handleConnectionSetup(DeviceId deviceId) {
626 assertConfig(deviceId);
627 // Bind pipeconf (if any and if device is capable).
628 bindPipeconfIfRequired(deviceId);
629 // Get handshaker.
630 final DeviceHandshaker handshaker = handshakerOrFail(deviceId);
Carmelo Casconec2be50a2019-04-10 00:15:39 -0700631 if (handshaker.hasConnection() || handshakersWithListeners.containsKey(deviceId)) {
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800632 throw new DeviceTaskException("connection already exists");
633 }
634 // Add device agent listener.
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800635 handshakersWithListeners.put(deviceId, handshaker);
Carmelo Casconec2be50a2019-04-10 00:15:39 -0700636 handshaker.addDeviceAgentListener(id(), deviceAgentListener);
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800637 // Start connection via handshaker.
Carmelo Casconec2be50a2019-04-10 00:15:39 -0700638 if (!handshaker.connect()) {
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800639 // Failed! Remove listeners.
640 handshaker.removeDeviceAgentListener(id());
641 handshakersWithListeners.remove(deviceId);
Carmelo Casconec2be50a2019-04-10 00:15:39 -0700642 // Clean up connection state leftovers.
643 handshaker.disconnect();
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800644 throw new DeviceTaskException("connection failed");
645 }
646 createOrUpdateDevice(deviceId, false);
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800647 // From here we expect a CHANNEL_OPEN event to update availability.
648 }
649
650 private void handleConnectionUpdate(DeviceId deviceId) {
651 assertConfig(deviceId);
652 final DeviceHandshaker handshaker = handshakerOrFail(deviceId);
Carmelo Casconec2be50a2019-04-10 00:15:39 -0700653 if (!handshaker.hasConnection()) {
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800654 // If driver reports that a connection still exists, perhaps the
655 // part of the netcfg that changed does not affect the connection.
656 // Otherwise, remove any previous connection state from the old
657 // netcfg and create a new one.
658 log.warn("Detected change of connection endpoints for {}, will " +
659 "tear down existing connection and set up a new one...",
660 deviceId);
661 handleConnectionTeardown(deviceId);
662 handleConnectionSetup(deviceId);
663 }
664 }
665
666 private void createOrUpdateDevice(DeviceId deviceId, boolean available) {
667 if (deviceService.getDevice(deviceId) != null
668 && deviceService.isAvailable(deviceId) == available) {
669 // Other nodes might have advertised this device before us.
670 return;
671 }
672 assertConfig(deviceId);
Carmelo Casconec2be50a2019-04-10 00:15:39 -0700673 if (available) {
Carmelo Casconed51a5552019-04-13 01:22:25 -0700674 // Push port descriptions. If marking online, make sure to update
675 // ports before other subsystems pick up the device event.
Carmelo Casconec2be50a2019-04-10 00:15:39 -0700676 final List<PortDescription> ports = getPortDetails(deviceId);
677 providerService.updatePorts(deviceId, ports);
678 }
Carmelo Casconed51a5552019-04-13 01:22:25 -0700679 providerService.deviceConnected(deviceId, getDeviceDescription(
680 deviceId, available));
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800681 }
682
683 private boolean probeAvailability(DeviceHandshaker handshaker) {
684 lastProbedAvailability.put(handshaker.data().deviceId(), currentTimeMillis());
Carmelo Cascone61469462019-03-05 23:59:11 -0800685 return Futures.getUnchecked(handshaker.probeAvailability());
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800686 }
687
688 private boolean probeReachability(DeviceHandshaker handshaker) {
689 lastProbedAvailability.put(handshaker.data().deviceId(), currentTimeMillis());
Carmelo Cascone61469462019-03-05 23:59:11 -0800690 return Futures.getUnchecked(handshaker.probeReachability());
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800691 }
692
693 private void markOfflineIfNeeded(DeviceId deviceId) {
694 assertDeviceRegistered(deviceId);
695 if (deviceService.isAvailable(deviceId)) {
696 providerService.deviceDisconnected(deviceId);
697 }
698 }
699
700 private void handleProbeAvailability(DeviceId deviceId) {
701 assertDeviceRegistered(deviceId);
702
Carmelo Casconec2be50a2019-04-10 00:15:39 -0700703 // Make sure device has a valid mastership role.
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800704 final DeviceHandshaker handshaker = handshakerOrFail(deviceId);
705 final MastershipRole deviceRole = handshaker.getRole();
706 final MastershipRole expectedRole = mastershipService.getLocalRole(deviceId);
707 if (expectedRole == MastershipRole.NONE || expectedRole != deviceRole) {
708 // Device does NOT have a valid role...
709 if (!handshaker.isReachable() && !probeReachability(handshaker)) {
710 // ...but is not reachable. There isn't much we can do.
711 markOfflineIfNeeded(deviceId);
712 return;
713 }
714 // ...and is reachable, re-assert role.
715 roleChanged(deviceId, expectedRole == MastershipRole.NONE
716 ? mastershipService.requestRoleForSync(deviceId)
717 : expectedRole);
718 try {
719 // Wait for role to be notified and reachability state to be
720 // updated. This should be roughly equivalent to one RTT.
721 Thread.sleep(500);
722 } catch (InterruptedException e) {
723 Thread.currentThread().interrupt();
724 return;
725 }
726 }
727
728 // Check and update availability.
729 if (probeAvailability(handshakerOrFail(deviceId))) {
730 // Device ready to do its job.
731 createOrUpdateDevice(deviceId, true);
732 } else {
733 markOfflineIfNeeded(deviceId);
734 if (handshaker.isReachable() && isPipelineProgrammable(deviceId)) {
735 // If reachable, but not available, and pipeline programmable, there
736 // is a high chance it's because the pipeline is not READY
737 // (independently from what the pipeconf watchdog reports, as the
738 // status there might be outdated). Encourage pipeconf watchdog to
739 // perform a pipeline probe ASAP.
740 pipeconfWatchdogService.triggerProbe(deviceId);
741 }
742 }
743 }
744
745 private void handleMastershipResponse(DeviceId deviceId, MastershipRole response) {
746 assertDeviceRegistered(deviceId);
747 log.debug("Device {} asserted role {}", deviceId, response);
748 providerService.receivedRoleReply(deviceId, response);
749 }
750
751 private void handleNotMaster(DeviceId deviceId) {
752 assertDeviceRegistered(deviceId);
753 if (mastershipService.isLocalMaster(deviceId)) {
754 log.warn("Device {} notified that this node is not master, " +
755 "relinquishing mastership...", deviceId);
756 mastershipService.relinquishMastership(deviceId);
757 }
758 }
759
760 private void assertDeviceRegistered(DeviceId deviceId) {
761 if (deviceService.getDevice(deviceId) == null) {
762 throw new DeviceTaskException("device not registered in the core");
763 }
764 }
765
766 private void handleConnectionTeardown(DeviceId deviceId) {
767 if (deviceService.getDevice(deviceId) != null
768 && deviceService.isAvailable(deviceId)) {
769 providerService.deviceDisconnected(deviceId);
770 }
771 final DeviceHandshaker handshaker = handshakerOrFail(deviceId);
772 handshaker.removeDeviceAgentListener(id());
773 handshakersWithListeners.remove(deviceId);
774 handshaker.disconnect();
775 lastProbedAvailability.remove(deviceId);
776 }
777
778 private void bindPipeconfIfRequired(DeviceId deviceId) {
779 if (pipeconfService.getPipeconf(deviceId).isPresent()
780 || !isPipelineProgrammable(deviceId)) {
781 // Nothing to do.
782 // Device has already a pipeconf or is not programmable.
783 return;
784 }
785 // Get pipeconf from netcfg or driver (default one).
786 final PiPipelineProgrammable pipelineProg = getBehaviour(
787 deviceId, PiPipelineProgrammable.class);
788 final PiPipeconfId pipeconfId = getPipeconfId(deviceId, pipelineProg);
789 if (pipeconfId == null) {
790 throw new DeviceTaskException("unable to find pipeconf");
791 }
792 // Store binding in pipeconf service.
793 pipeconfService.bindToDevice(pipeconfId, deviceId);
794 }
795
796 private PiPipeconfId getPipeconfId(DeviceId deviceId, PiPipelineProgrammable pipelineProg) {
797 // Places to look for a pipeconf ID (in priority order)):
798 // 1) netcfg
799 // 2) device driver (default one)
800 final PiPipeconfId pipeconfId = getPipeconfFromCfg(deviceId);
801 if (pipeconfId != null && !pipeconfId.id().isEmpty()) {
802 return pipeconfId;
803 }
804 if (pipelineProg != null
805 && pipelineProg.getDefaultPipeconf().isPresent()) {
806 final PiPipeconf defaultPipeconf = pipelineProg.getDefaultPipeconf().get();
807 log.info("Using default pipeconf {} for {}", defaultPipeconf.id(), deviceId);
808 return defaultPipeconf.id();
809 }
810 return null;
811 }
812
813 private PiPipeconfId getPipeconfFromCfg(DeviceId deviceId) {
814 BasicDeviceConfig config = cfgService.getConfig(
815 deviceId, BasicDeviceConfig.class);
816 if (config == null) {
817 return null;
818 }
819 return config.pipeconf() != null
820 ? new PiPipeconfId(config.pipeconf()) : null;
821 }
822
823 private DeviceHandshaker handshakerOrFail(DeviceId deviceId) {
824 final DeviceHandshaker handshaker = getBehaviour(
825 deviceId, DeviceHandshaker.class);
826 if (handshaker == null) {
827 throw new DeviceTaskException("missing handshaker behavior");
828 }
829 return handshaker;
830 }
831
832 private boolean configIsComplete(DeviceId deviceId) {
833 final BasicDeviceConfig basicDeviceCfg = cfgService.getConfig(
834 deviceId, BasicDeviceConfig.class);
835 return basicDeviceCfg != null && !isNullOrEmpty(basicDeviceCfg.driver());
836 }
837
838 private void assertConfig(DeviceId deviceId) {
839 if (!configIsComplete(deviceId)) {
840 throw new DeviceTaskException("configuration is not complete");
841 }
Thomas Vachuska5b38dc02018-05-10 15:24:40 -0700842 }
843
Andrea Campanella241896c2017-05-10 13:11:04 -0700844 private Driver getDriver(DeviceId deviceId) {
Andrea Campanella241896c2017-05-10 13:11:04 -0700845 try {
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200846 // DriverManager checks first using basic device config.
847 return driverService.getDriver(deviceId);
Andrea Campanella241896c2017-05-10 13:11:04 -0700848 } catch (ItemNotFoundException e) {
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200849 log.error("Driver not found for {}", deviceId);
850 return null;
Andrea Campanella241896c2017-05-10 13:11:04 -0700851 }
Andrea Campanella241896c2017-05-10 13:11:04 -0700852 }
853
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700854 private <T extends Behaviour> T getBehaviour(DeviceId deviceId, Class<T> type) {
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700855 Driver driver = getDriver(deviceId);
856 if (driver == null) {
Andrea Campanella241896c2017-05-10 13:11:04 -0700857 return null;
858 }
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700859 if (!driver.hasBehaviour(type)) {
860 return null;
861 }
862 final DriverData data = new DefaultDriverData(driver, deviceId);
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700863 final DefaultDriverHandler handler = new DefaultDriverHandler(data);
864 return driver.createBehaviour(handler, type);
Andrea Campanella241896c2017-05-10 13:11:04 -0700865 }
866
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800867 private boolean hasBehaviour(DeviceId deviceId, Class<? extends Behaviour> type) {
868 Driver driver = getDriver(deviceId);
869 if (driver == null) {
870 return false;
Carmelo Cascone96beb6f2018-06-27 18:07:12 +0200871 }
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800872 return driver.hasBehaviour(type);
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700873 }
874
875 private DeviceDescription getDeviceDescription(
876 DeviceId deviceId, boolean defaultAvailable) {
877 // Get one from driver or forge.
878 final DeviceDescriptionDiscovery deviceDiscovery = getBehaviour(
879 deviceId, DeviceDescriptionDiscovery.class);
Yi Tsengd7716482018-10-31 15:34:30 -0700880 if (deviceDiscovery == null) {
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700881 return forgeDeviceDescription(deviceId, defaultAvailable);
882 }
Yi Tsengd7716482018-10-31 15:34:30 -0700883
884 final DeviceDescription d = deviceDiscovery.discoverDeviceDetails();
885 if (d == null) {
886 return forgeDeviceDescription(deviceId, defaultAvailable);
887 }
888 // Enforce defaultAvailable flag over the one obtained from driver.
889 return new DefaultDeviceDescription(d, defaultAvailable, d.annotations());
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700890 }
891
892 private List<PortDescription> getPortDetails(DeviceId deviceId) {
893 final DeviceDescriptionDiscovery deviceDiscovery = getBehaviour(
894 deviceId, DeviceDescriptionDiscovery.class);
895 if (deviceDiscovery != null) {
896 return deviceDiscovery.discoverPortDetails();
897 } else {
898 return Collections.emptyList();
899 }
900 }
901
902 private DeviceDescription forgeDeviceDescription(
903 DeviceId deviceId, boolean defaultAvailable) {
904 // Uses handshaker and provider config to get driver data.
905 final DeviceHandshaker handshaker = getBehaviour(
906 deviceId, DeviceHandshaker.class);
907 final Driver driver = handshaker != null
908 ? handshaker.handler().driver() : null;
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700909 return new DefaultDeviceDescription(
910 deviceId.uri(),
911 Device.Type.SWITCH,
912 driver != null ? driver.manufacturer() : UNKNOWN,
913 driver != null ? driver.hwVersion() : UNKNOWN,
914 driver != null ? driver.swVersion() : UNKNOWN,
915 UNKNOWN,
916 new ChassisId(),
917 defaultAvailable,
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800918 DefaultAnnotations.EMPTY);
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700919 }
920
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800921 static boolean myScheme(DeviceId deviceId) {
922 return deviceId.uri().getScheme().equals(URI_SCHEME);
Carmelo Cascone96beb6f2018-06-27 18:07:12 +0200923 }
924
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700925 private boolean isPipelineProgrammable(DeviceId deviceId) {
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800926 return hasBehaviour(deviceId, PiPipelineProgrammable.class);
Carmelo Casconede3b6842018-09-05 17:45:10 -0700927 }
Andrea Campanella241896c2017-05-10 13:11:04 -0700928}