blob: ac0fda7c8af241ce9c7e7137bfb23c16fa53a074 [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 Cascone1dfc7862019-04-17 16:37:44 -070020import com.google.common.collect.Sets;
Carmelo Cascone61469462019-03-05 23:59:11 -080021import com.google.common.util.concurrent.Futures;
Andrea Campanella241896c2017-05-10 13:11:04 -070022import org.onlab.packet.ChassisId;
23import org.onlab.util.ItemNotFoundException;
Carmelo Cascone4b616312019-04-17 14:15:45 -070024import org.onlab.util.SharedScheduledExecutors;
Andrea Campanella19090322017-08-22 10:31:37 +020025import org.onlab.util.Tools;
Andrea Campanella4929a812017-10-09 18:38:23 +020026import org.onosproject.cfg.ComponentConfigService;
Carmelo Cascone3977ea42019-02-28 13:43:42 -080027import org.onosproject.cluster.ClusterService;
28import org.onosproject.cluster.NodeId;
Andrea Campanella241896c2017-05-10 13:11:04 -070029import org.onosproject.core.CoreService;
Yi Tsenge616d752018-11-27 10:53:27 -080030import org.onosproject.gnmi.api.GnmiController;
Carmelo Cascone3977ea42019-02-28 13:43:42 -080031import org.onosproject.mastership.MastershipInfo;
Andrea Campanella14e196d2017-07-24 18:11:36 +020032import org.onosproject.mastership.MastershipService;
Andrea Campanella241896c2017-05-10 13:11:04 -070033import org.onosproject.net.DefaultAnnotations;
34import org.onosproject.net.Device;
35import org.onosproject.net.DeviceId;
36import org.onosproject.net.MastershipRole;
37import org.onosproject.net.PortNumber;
Carmelo Cascone87892e22017-11-13 16:01:29 -080038import org.onosproject.net.behaviour.PiPipelineProgrammable;
Andrea Campanella241896c2017-05-10 13:11:04 -070039import org.onosproject.net.behaviour.PortAdmin;
Andrea Campanella241896c2017-05-10 13:11:04 -070040import org.onosproject.net.config.NetworkConfigEvent;
41import org.onosproject.net.config.NetworkConfigListener;
42import org.onosproject.net.config.NetworkConfigRegistry;
43import org.onosproject.net.config.basics.BasicDeviceConfig;
Andrea Campanella241896c2017-05-10 13:11:04 -070044import org.onosproject.net.device.DefaultDeviceDescription;
Thomas Vachuska1b3cf362019-08-29 17:11:18 -070045import org.onosproject.net.device.DeviceAdminService;
Carmelo Casconee5b28722018-06-22 17:28:28 +020046import org.onosproject.net.device.DeviceAgentEvent;
47import org.onosproject.net.device.DeviceAgentListener;
Andrea Campanella241896c2017-05-10 13:11:04 -070048import org.onosproject.net.device.DeviceDescription;
49import org.onosproject.net.device.DeviceDescriptionDiscovery;
Thomas Vachuska1b3cf362019-08-29 17:11:18 -070050import org.onosproject.net.device.DeviceEvent;
Andrea Campanella241896c2017-05-10 13:11:04 -070051import org.onosproject.net.device.DeviceHandshaker;
Thomas Vachuska1b3cf362019-08-29 17:11:18 -070052import org.onosproject.net.device.DeviceListener;
Andrea Campanella241896c2017-05-10 13:11:04 -070053import org.onosproject.net.device.DeviceProvider;
54import org.onosproject.net.device.DeviceProviderRegistry;
55import org.onosproject.net.device.DeviceProviderService;
Andrea Campanella241896c2017-05-10 13:11:04 -070056import org.onosproject.net.device.PortDescription;
Andrea Campanella241896c2017-05-10 13:11:04 -070057import org.onosproject.net.driver.Behaviour;
58import org.onosproject.net.driver.DefaultDriverData;
59import org.onosproject.net.driver.DefaultDriverHandler;
60import org.onosproject.net.driver.Driver;
61import org.onosproject.net.driver.DriverData;
62import org.onosproject.net.driver.DriverService;
Carmelo Cascone59f57de2017-07-11 19:55:09 -040063import org.onosproject.net.pi.model.PiPipeconf;
Andrea Campanellabc112a92017-06-26 19:06:43 +020064import org.onosproject.net.pi.model.PiPipeconfId;
Carmelo Cascone39c28ca2017-11-15 13:03:57 -080065import org.onosproject.net.pi.service.PiPipeconfService;
Carmelo Cascone9e4972c2018-08-30 00:29:16 -070066import org.onosproject.net.pi.service.PiPipeconfWatchdogEvent;
67import org.onosproject.net.pi.service.PiPipeconfWatchdogListener;
68import org.onosproject.net.pi.service.PiPipeconfWatchdogService;
Andrea Campanella241896c2017-05-10 13:11:04 -070069import org.onosproject.net.provider.AbstractProvider;
70import org.onosproject.net.provider.ProviderId;
Carmelo Cascone3977ea42019-02-28 13:43:42 -080071import org.onosproject.provider.general.device.impl.DeviceTaskExecutor.DeviceTaskException;
Andrea Campanella19090322017-08-22 10:31:37 +020072import org.osgi.service.component.ComponentContext;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070073import org.osgi.service.component.annotations.Activate;
74import org.osgi.service.component.annotations.Component;
75import org.osgi.service.component.annotations.Deactivate;
76import org.osgi.service.component.annotations.Modified;
77import org.osgi.service.component.annotations.Reference;
78import org.osgi.service.component.annotations.ReferenceCardinality;
Andrea Campanella241896c2017-05-10 13:11:04 -070079import org.slf4j.Logger;
80
Andrea Campanellabc112a92017-06-26 19:06:43 +020081import java.util.Collections;
Andrea Campanella19090322017-08-22 10:31:37 +020082import java.util.Dictionary;
Andrea Campanella241896c2017-05-10 13:11:04 -070083import java.util.List;
Thomas Vachuska5b38dc02018-05-10 15:24:40 -070084import java.util.Map;
Carmelo Cascone3977ea42019-02-28 13:43:42 -080085import java.util.Objects;
Carmelo Cascone1dfc7862019-04-17 16:37:44 -070086import java.util.Set;
Andrea Campanella241896c2017-05-10 13:11:04 -070087import java.util.concurrent.CompletableFuture;
Carmelo Casconee5b28722018-06-22 17:28:28 +020088import java.util.concurrent.ExecutorService;
Andrea Campanella19090322017-08-22 10:31:37 +020089import java.util.concurrent.ScheduledFuture;
Andrea Campanella241896c2017-05-10 13:11:04 -070090import java.util.concurrent.TimeUnit;
Andrea Campanella241896c2017-05-10 13:11:04 -070091
Carmelo Cascone3977ea42019-02-28 13:43:42 -080092import static com.google.common.base.Preconditions.checkNotNull;
93import static com.google.common.base.Strings.isNullOrEmpty;
Carmelo Cascone61469462019-03-05 23:59:11 -080094import static java.lang.String.format;
Carmelo Cascone3977ea42019-02-28 13:43:42 -080095import static java.lang.System.currentTimeMillis;
Carmelo Casconee5b28722018-06-22 17:28:28 +020096import static java.util.concurrent.Executors.newFixedThreadPool;
Andrea Campanella241896c2017-05-10 13:11:04 -070097import static org.onlab.util.Tools.groupedThreads;
Carmelo Cascone4b616312019-04-17 14:15:45 -070098import static org.onosproject.provider.general.device.impl.OsgiPropertyConstants.CHECKUP_INTERVAL;
99import static org.onosproject.provider.general.device.impl.OsgiPropertyConstants.CHECKUP_INTERVAL_DEFAULT;
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800100import static org.onosproject.provider.general.device.impl.OsgiPropertyConstants.STATS_POLL_INTERVAL;
101import static org.onosproject.provider.general.device.impl.OsgiPropertyConstants.STATS_POLL_INTERVAL_DEFAULT;
Andrea Campanella241896c2017-05-10 13:11:04 -0700102import static org.slf4j.LoggerFactory.getLogger;
103
104/**
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800105 * Provider which uses drivers to discover devices, perform initial handshake,
106 * and notify the core of disconnection events. The implementation listens for
107 * events from netcfg or the drivers (via {@link DeviceAgentListener}) andP
108 * schedules task for each event.
Andrea Campanella241896c2017-05-10 13:11:04 -0700109 */
Thomas Vachuska4167c3f2018-10-16 07:16:31 -0700110@Component(immediate = true,
111 property = {
Carmelo Cascone4b616312019-04-17 14:15:45 -0700112 CHECKUP_INTERVAL + ":Integer=" + CHECKUP_INTERVAL_DEFAULT,
pierventref497d132022-02-15 12:25:27 +0100113 STATS_POLL_INTERVAL + ":Integer=" + STATS_POLL_INTERVAL_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)
Thomas Vachuska1b3cf362019-08-29 17:11:18 -0700140 private DeviceAdminService 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 Cascone61469462019-03-05 23:59:11 -0800167 /**
168 * Configure interval for checking device availability; default is 10 sec.
169 */
Carmelo Cascone4b616312019-04-17 14:15:45 -0700170 private int checkupInterval = CHECKUP_INTERVAL_DEFAULT;
Andrea Campanella19090322017-08-22 10:31:37 +0200171
Carmelo Cascone61469462019-03-05 23:59:11 -0800172 /**
173 * Configure poll frequency for port status and stats; default is 10 sec.
174 */
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800175 private int statsPollInterval = STATS_POLL_INTERVAL_DEFAULT;
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200176
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700177 private final Map<DeviceId, DeviceHandshaker> handshakersWithListeners = Maps.newConcurrentMap();
Carmelo Cascone4b616312019-04-17 14:15:45 -0700178 private final Map<DeviceId, Long> lastCheckups = Maps.newConcurrentMap();
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700179 private final InternalPipeconfWatchdogListener pipeconfWatchdogListener = new InternalPipeconfWatchdogListener();
Carmelo Casconee5b28722018-06-22 17:28:28 +0200180 private final NetworkConfigListener cfgListener = new InternalNetworkConfigListener();
181 private final DeviceAgentListener deviceAgentListener = new InternalDeviceAgentListener();
Thomas Vachuska1b3cf362019-08-29 17:11:18 -0700182 private final DeviceListener deviceListener = new InternalDeviceListener();
Andrea Campanella241896c2017-05-10 13:11:04 -0700183
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800184 private ExecutorService mainExecutor;
185 private DeviceTaskExecutor<TaskType> taskExecutor;
Carmelo Cascone4b616312019-04-17 14:15:45 -0700186 private ScheduledFuture<?> checkupTask;
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800187 private StatsPoller statsPoller;
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700188 private DeviceProviderService providerService;
189
190 public GeneralDeviceProvider() {
191 super(new ProviderId(URI_SCHEME, DEVICE_PROVIDER_PACKAGE));
192 }
Andrea Campanella241896c2017-05-10 13:11:04 -0700193
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800194 protected DeviceProviderService providerService() {
195 return providerService;
196 }
197
Andrea Campanella241896c2017-05-10 13:11:04 -0700198 @Activate
Andrea Campanella1e573442018-05-17 17:07:13 +0200199 public void activate(ComponentContext context) {
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800200 mainExecutor = newFixedThreadPool(CORE_POOL_SIZE, groupedThreads(
Carmelo Cascone4b616312019-04-17 14:15:45 -0700201 "onos/gdp", "%d", log));
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800202 taskExecutor = new DeviceTaskExecutor<>(mainExecutor);
Andrea Campanella241896c2017-05-10 13:11:04 -0700203 providerService = providerRegistry.register(this);
Andrea Campanella4929a812017-10-09 18:38:23 +0200204 componentConfigService.registerProperties(getClass());
Andrea Campanella241896c2017-05-10 13:11:04 -0700205 coreService.registerApplication(APP_NAME);
Andrea Campanella241896c2017-05-10 13:11:04 -0700206 cfgService.addListener(cfgListener);
Thomas Vachuska1b3cf362019-08-29 17:11:18 -0700207 deviceService.addListener(deviceListener);
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 Cascone4b616312019-04-17 14:15:45 -0700212 startOrReschedulePeriodicCheckupTasks();
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800213 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 Cascone4b616312019-04-17 14:15:45 -0700226 final int oldCheckupInterval = checkupInterval;
227 checkupInterval = Tools.getIntegerProperty(
228 properties, CHECKUP_INTERVAL, CHECKUP_INTERVAL_DEFAULT);
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200229 log.info("Configured. {} is configured to {} seconds",
Carmelo Cascone4b616312019-04-17 14:15:45 -0700230 CHECKUP_INTERVAL, checkupInterval);
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800231 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
Carmelo Cascone4b616312019-04-17 14:15:45 -0700237 if (oldCheckupInterval != checkupInterval) {
238 startOrReschedulePeriodicCheckupTasks();
Andrea Campanella19090322017-08-22 10:31:37 +0200239 }
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200240
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800241 if (oldStatsPollFrequency != statsPollInterval) {
242 statsPoller.reschedule(statsPollInterval);
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200243 }
Andrea Campanella19090322017-08-22 10:31:37 +0200244 }
245
Andrea Campanella241896c2017-05-10 13:11:04 -0700246 @Deactivate
247 public void deactivate() {
Thomas Vachuska1b3cf362019-08-29 17:11:18 -0700248 deviceService.removeListener(deviceListener);
249
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800250 // Shutdown stats poller.
251 statsPoller.deactivate();
252 statsPoller = null;
Carmelo Cascone4b616312019-04-17 14:15:45 -0700253 // Shutdown periodic checkup task.
254 checkupTask.cancel(false);
255 checkupTask = null;
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800256 // Shutdown main and task executor.
257 taskExecutor.cancel();
258 taskExecutor = null;
259 mainExecutor.shutdownNow();
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700260 try {
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800261 mainExecutor.awaitTermination(5, TimeUnit.SECONDS);
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700262 } catch (InterruptedException e) {
263 log.warn("connectionExecutor not terminated properly");
264 }
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800265 mainExecutor = null;
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700266 // Remove all device agent listeners
267 handshakersWithListeners.values().forEach(h -> h.removeDeviceAgentListener(id()));
268 handshakersWithListeners.clear();
269 // Other cleanup.
Carmelo Cascone4b616312019-04-17 14:15:45 -0700270 lastCheckups.clear();
Andrea Campanella4929a812017-10-09 18:38:23 +0200271 componentConfigService.unregisterProperties(getClass(), false);
Andrea Campanella241896c2017-05-10 13:11:04 -0700272 cfgService.removeListener(cfgListener);
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700273 pipeconfWatchdogService.removeListener(pipeconfWatchdogListener);
Andrea Campanella241896c2017-05-10 13:11:04 -0700274 providerRegistry.unregister(this);
275 providerService = null;
Yi Tsenge616d752018-11-27 10:53:27 -0800276 gnmiDeviceStateSubscriber.deactivate();
277 gnmiDeviceStateSubscriber = null;
Andrea Campanella241896c2017-05-10 13:11:04 -0700278 log.info("Stopped");
279 }
280
Andrea Campanella241896c2017-05-10 13:11:04 -0700281 @Override
282 public void triggerProbe(DeviceId deviceId) {
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800283 checkNotNull(deviceId);
Carmelo Cascone4b616312019-04-17 14:15:45 -0700284 submitTask(deviceId, TaskType.CHECKUP);
Andrea Campanella241896c2017-05-10 13:11:04 -0700285 }
286
287 @Override
288 public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800289 final MastershipInfo mastershipInfo = mastershipService.getMastershipFor(deviceId);
290 final NodeId localNodeId = clusterService.getLocalNode().id();
291
292 if (!mastershipInfo.getRole(localNodeId).equals(newRole)) {
293 log.warn("Inconsistent mastership info for {}! Requested {}, but " +
294 "mastership service reports {}, will apply the latter...",
295 deviceId, newRole, mastershipInfo.getRole(localNodeId));
296 newRole = mastershipInfo.getRole(localNodeId);
297 }
298
Carmelo Cascone4b616312019-04-17 14:15:45 -0700299 final DeviceHandshaker handshaker = getBehaviour(
300 deviceId, DeviceHandshaker.class);
301 if (handshaker == null) {
302 log.error("Null handshaker. Unable to notify role {} to {}",
303 newRole, deviceId);
304 return;
305 }
306
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800307 // Derive preference value.
308 final int preference;
309 switch (newRole) {
310 case MASTER:
311 preference = 0;
312 break;
313 case STANDBY:
314 preference = mastershipInfo.backups().indexOf(localNodeId) + 1;
315 if (preference == 0) {
316 // Not found in list.
317 log.error("Unable to derive mastership preference for {}, " +
318 "requested role {} but local node ID was " +
319 "not found among list of backup nodes " +
Carmelo Cascone4b616312019-04-17 14:15:45 -0700320 "reported by mastership service",
321 deviceId, newRole);
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800322 return;
323 }
324 break;
325 case NONE:
326 // No preference for NONE, apply as is.
327 log.info("Notifying role {} to {}", newRole, deviceId);
Carmelo Cascone4b616312019-04-17 14:15:45 -0700328 handshaker.roleChanged(newRole);
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800329 return;
330 default:
331 log.error("Unrecognized mastership role {}", newRole);
332 return;
333 }
334
335 log.info("Notifying role {} (preference {}) for term {} to {}",
336 newRole, preference, mastershipInfo.term(), deviceId);
337
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800338 try {
339 handshaker.roleChanged(preference, mastershipInfo.term());
340 } catch (UnsupportedOperationException e) {
341 // Preference-based method not supported.
342 handshaker.roleChanged(newRole);
343 }
Andrea Campanella241896c2017-05-10 13:11:04 -0700344 }
345
346 @Override
347 public boolean isReachable(DeviceId deviceId) {
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700348 final DeviceHandshaker handshaker = getBehaviour(
349 deviceId, DeviceHandshaker.class);
Andrea Campanellac1ecdd02018-01-12 12:48:24 +0100350 if (handshaker == null) {
351 return false;
352 }
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800353 return handshaker.isReachable();
Andrea Campanella241896c2017-05-10 13:11:04 -0700354 }
355
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800356 @Override
357 public boolean isAvailable(DeviceId deviceId) {
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700358 final DeviceHandshaker handshaker = getBehaviour(
359 deviceId, DeviceHandshaker.class);
360 if (handshaker == null) {
361 return false;
362 }
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800363 try {
364 // Try without probing the device...
365 return handshaker.isAvailable();
366 } catch (UnsupportedOperationException e) {
367 // Driver does not support that.
368 return probeAvailability(handshaker);
369 }
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700370 }
371
Andrea Campanella241896c2017-05-10 13:11:04 -0700372 @Override
373 public void changePortState(DeviceId deviceId, PortNumber portNumber,
374 boolean enable) {
Carmelo Cascone96beb6f2018-06-27 18:07:12 +0200375 if (!deviceService.getDevice(deviceId).is(PortAdmin.class)) {
376 log.warn("Missing PortAdmin behaviour on {}, aborting port state change",
377 deviceId);
378 return;
Andrea Campanella241896c2017-05-10 13:11:04 -0700379 }
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200380 final PortAdmin portAdmin = deviceService.getDevice(deviceId)
381 .as(PortAdmin.class);
382 final CompletableFuture<Boolean> modifyTask = enable
Carmelo Cascone96beb6f2018-06-27 18:07:12 +0200383 ? portAdmin.enable(portNumber)
384 : portAdmin.disable(portNumber);
Carmelo Cascone61469462019-03-05 23:59:11 -0800385 final String descr = format("%s port %s on %s",
386 (enable ? "enable" : "disable"),
387 portNumber, deviceId);
388 modifyTask.whenComplete((success, ex) -> {
389 if (ex != null) {
390 log.error("Exception while trying to " + descr, ex);
391 } else if (!success) {
392 log.warn("Unable to " + descr);
393 }
394 });
Andrea Campanella241896c2017-05-10 13:11:04 -0700395 }
396
Thomas Vachuska5b38dc02018-05-10 15:24:40 -0700397 @Override
398 public void triggerDisconnect(DeviceId deviceId) {
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800399 checkNotNull(deviceId);
400 log.info("Triggering disconnection of device {}", deviceId);
401 submitTask(deviceId, TaskType.CONNECTION_TEARDOWN);
402 }
403
pierventred7cae132022-02-03 21:34:04 +0100404 @Override
405 public CompletableFuture<Boolean> probeReachability(DeviceId deviceId) {
406 final DeviceHandshaker handshaker = getBehaviour(
407 deviceId, DeviceHandshaker.class);
408 if (handshaker == null) {
409 return CompletableFuture.completedFuture(false);
410 }
411 return handshaker.probeReachability();
412 }
413
414 private boolean probeReachabilitySync(DeviceId deviceId) {
415 // Wait 3/4 of the checkup interval
416 return Tools.futureGetOrElse(probeReachability(deviceId), (checkupInterval * 3000 / 4),
417 TimeUnit.MILLISECONDS, Boolean.FALSE);
418 }
419
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800420 /**
421 * Listener for configuration events.
422 */
423 private class InternalNetworkConfigListener implements NetworkConfigListener {
424 @Override
425 public void event(NetworkConfigEvent event) {
426 DeviceId deviceId = (DeviceId) event.subject();
427 switch (event.type()) {
428 case CONFIG_ADDED:
Carmelo Cascone4b616312019-04-17 14:15:45 -0700429 if (configIsPresent(deviceId)) {
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800430 submitTask(deviceId, TaskType.CONNECTION_SETUP);
431 }
432 break;
433 case CONFIG_UPDATED:
Carmelo Cascone4b616312019-04-17 14:15:45 -0700434 if (configIsPresent(deviceId) && mgmtAddrUpdated(event)) {
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800435 submitTask(deviceId, TaskType.CONNECTION_UPDATE);
436 }
437 break;
438 case CONFIG_REMOVED:
439 if (event.configClass().equals(BasicDeviceConfig.class)) {
440 submitTask(deviceId, TaskType.CONNECTION_TEARDOWN);
441 }
442 break;
443 default:
444 // Ignore
445 break;
446 }
447 }
448
449 private boolean mgmtAddrUpdated(NetworkConfigEvent event) {
450 if (!event.prevConfig().isPresent() || !event.config().isPresent()) {
451 return false;
452 }
453 final BasicDeviceConfig prev = (BasicDeviceConfig) event.prevConfig().get();
454 final BasicDeviceConfig current = (BasicDeviceConfig) event.config().get();
455 return !Objects.equals(prev.managementAddress(), current.managementAddress());
456 }
457
458 @Override
459 public boolean isRelevant(NetworkConfigEvent event) {
460 return event.configClass().equals(BasicDeviceConfig.class) &&
461 (event.subject() instanceof DeviceId) &&
462 myScheme((DeviceId) event.subject());
463 }
464 }
465
466 /**
467 * Listener for device agent events.
468 */
469 private class InternalDeviceAgentListener implements DeviceAgentListener {
470 @Override
471 public void event(DeviceAgentEvent event) {
472 DeviceId deviceId = event.subject();
473 switch (event.type()) {
474 case CHANNEL_OPEN:
475 submitTask(deviceId, TaskType.CHANNEL_OPEN);
476 break;
477 case CHANNEL_CLOSED:
478 case CHANNEL_ERROR:
479 submitTask(deviceId, TaskType.CHANNEL_CLOSED);
480 break;
481 case ROLE_MASTER:
482 submitTask(deviceId, TaskType.ROLE_MASTER);
483 break;
484 case ROLE_STANDBY:
485 submitTask(deviceId, TaskType.ROLE_STANDBY);
486 break;
487 case ROLE_NONE:
Carmelo Cascone4b616312019-04-17 14:15:45 -0700488 // FIXME: in case of device disconnection, agents will
489 // signal role NONE, preventing the DeviceManager to mark
490 // the device as offline, as only the master can do that. We
491 // should change the DeviceManager. For now, we disable
492 // signaling role NONE.
493 // submitTask(deviceId, TaskType.ROLE_NONE);
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800494 break;
495 case NOT_MASTER:
496 submitTask(deviceId, TaskType.NOT_MASTER);
497 break;
498 default:
499 log.warn("Unrecognized device agent event {}", event.type());
500 }
501 }
502 }
503
504 /**
505 * Pipeline event listener.
506 */
507 private class InternalPipeconfWatchdogListener implements PiPipeconfWatchdogListener {
508 @Override
509 public void event(PiPipeconfWatchdogEvent event) {
510 final DeviceId deviceId = event.subject();
511 switch (event.type()) {
512 case PIPELINE_READY:
513 submitTask(deviceId, TaskType.PIPELINE_READY);
514 break;
515 case PIPELINE_UNKNOWN:
516 submitTask(deviceId, TaskType.PIPELINE_NOT_READY);
517 break;
518 default:
519 break;
520 }
521 }
522
523 @Override
524 public boolean isRelevant(PiPipeconfWatchdogEvent event) {
525 return myScheme(event.subject());
526 }
527 }
528
Carmelo Cascone4b616312019-04-17 14:15:45 -0700529 private void startOrReschedulePeriodicCheckupTasks() {
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800530 synchronized (this) {
Carmelo Cascone4b616312019-04-17 14:15:45 -0700531 if (checkupTask != null) {
532 checkupTask.cancel(false);
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800533 }
Carmelo Cascone4b616312019-04-17 14:15:45 -0700534 checkupTask = SharedScheduledExecutors.getPoolThreadExecutor()
535 .scheduleAtFixedRate(
536 this::submitCheckupTasksForAllDevices,
537 1,
538 checkupInterval,
pierventreb4120ff2021-04-09 14:32:11 +0200539 TimeUnit.SECONDS,
540 true);
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800541 }
542 }
543
Carmelo Cascone4b616312019-04-17 14:15:45 -0700544 private void submitCheckupTasksForAllDevices() {
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800545 // Async trigger a task for all devices in the cfg.
Carmelo Cascone4b616312019-04-17 14:15:45 -0700546 log.debug("Submitting checkup task for all devices...");
Carmelo Cascone1dfc7862019-04-17 16:37:44 -0700547 final Set<DeviceId> deviceToCheck = Sets.newHashSet();
548 // All devices in the core and in the config that we care about.
549 deviceService.getDevices().forEach(d -> {
550 if (myScheme(d.id())) {
551 deviceToCheck.add(d.id());
552 }
553 });
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800554 cfgService.getSubjects(DeviceId.class).stream()
555 .filter(GeneralDeviceProvider::myScheme)
Carmelo Cascone1dfc7862019-04-17 16:37:44 -0700556 .filter(this::configIsPresent)
557 .forEach(deviceToCheck::add);
558 deviceToCheck.forEach(d -> submitTask(d, TaskType.CHECKUP));
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800559 }
560
561 /**
562 * Type of tasks performed by this provider.
563 */
564 enum TaskType {
565 CONNECTION_SETUP,
566 CONNECTION_UPDATE,
567 CONNECTION_TEARDOWN,
568 PIPELINE_READY,
569 CHANNEL_OPEN,
570 CHANNEL_CLOSED,
571 PIPELINE_NOT_READY,
Carmelo Cascone4b616312019-04-17 14:15:45 -0700572 CHECKUP,
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800573 ROLE_MASTER,
574 ROLE_NONE,
575 ROLE_STANDBY,
576 NOT_MASTER,
577 }
578
579 private void submitTask(DeviceId deviceId, TaskType taskType) {
580 taskExecutor.submit(deviceId, taskType, taskRunnable(deviceId, taskType));
581 }
582
583 private Runnable taskRunnable(DeviceId deviceId, TaskType taskType) {
584 switch (taskType) {
585 case CONNECTION_SETUP:
586 return () -> handleConnectionSetup(deviceId);
587 case CONNECTION_UPDATE:
588 return () -> handleConnectionUpdate(deviceId);
589 case CONNECTION_TEARDOWN:
590 return () -> handleConnectionTeardown(deviceId);
591 case CHANNEL_OPEN:
Carmelo Cascone4b616312019-04-17 14:15:45 -0700592 case CHECKUP:
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800593 case PIPELINE_READY:
Carmelo Cascone4b616312019-04-17 14:15:45 -0700594 return () -> doCheckupAndRepair(deviceId);
Carmelo Casconec2be50a2019-04-10 00:15:39 -0700595 case CHANNEL_CLOSED:
596 case PIPELINE_NOT_READY:
597 return () -> markOfflineIfNeeded(deviceId);
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800598 case ROLE_MASTER:
599 return () -> handleMastershipResponse(deviceId, MastershipRole.MASTER);
600 case ROLE_STANDBY:
601 return () -> handleMastershipResponse(deviceId, MastershipRole.STANDBY);
602 case ROLE_NONE:
603 return () -> handleMastershipResponse(deviceId, MastershipRole.NONE);
604 case NOT_MASTER:
605 return () -> handleNotMaster(deviceId);
606 default:
607 throw new IllegalArgumentException("Unrecognized task type " + taskType);
608 }
609 }
610
611 private void handleConnectionSetup(DeviceId deviceId) {
612 assertConfig(deviceId);
613 // Bind pipeconf (if any and if device is capable).
614 bindPipeconfIfRequired(deviceId);
615 // Get handshaker.
616 final DeviceHandshaker handshaker = handshakerOrFail(deviceId);
Carmelo Casconec2be50a2019-04-10 00:15:39 -0700617 if (handshaker.hasConnection() || handshakersWithListeners.containsKey(deviceId)) {
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800618 throw new DeviceTaskException("connection already exists");
619 }
620 // Add device agent listener.
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800621 handshakersWithListeners.put(deviceId, handshaker);
Carmelo Casconec2be50a2019-04-10 00:15:39 -0700622 handshaker.addDeviceAgentListener(id(), deviceAgentListener);
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800623 // Start connection via handshaker.
Carmelo Casconec2be50a2019-04-10 00:15:39 -0700624 if (!handshaker.connect()) {
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800625 // Failed! Remove listeners.
626 handshaker.removeDeviceAgentListener(id());
627 handshakersWithListeners.remove(deviceId);
Carmelo Casconec2be50a2019-04-10 00:15:39 -0700628 // Clean up connection state leftovers.
629 handshaker.disconnect();
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800630 throw new DeviceTaskException("connection failed");
631 }
632 createOrUpdateDevice(deviceId, false);
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800633 // From here we expect a CHANNEL_OPEN event to update availability.
634 }
635
636 private void handleConnectionUpdate(DeviceId deviceId) {
637 assertConfig(deviceId);
638 final DeviceHandshaker handshaker = handshakerOrFail(deviceId);
Carmelo Casconec2be50a2019-04-10 00:15:39 -0700639 if (!handshaker.hasConnection()) {
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800640 // If driver reports that a connection still exists, perhaps the
641 // part of the netcfg that changed does not affect the connection.
642 // Otherwise, remove any previous connection state from the old
643 // netcfg and create a new one.
644 log.warn("Detected change of connection endpoints for {}, will " +
645 "tear down existing connection and set up a new one...",
646 deviceId);
647 handleConnectionTeardown(deviceId);
648 handleConnectionSetup(deviceId);
649 }
650 }
651
652 private void createOrUpdateDevice(DeviceId deviceId, boolean available) {
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800653 assertConfig(deviceId);
pierventreb4120ff2021-04-09 14:32:11 +0200654
Carmelo Casconec2be50a2019-04-10 00:15:39 -0700655 if (available) {
Carmelo Casconed51a5552019-04-13 01:22:25 -0700656 // Push port descriptions. If marking online, make sure to update
657 // ports before other subsystems pick up the device event.
Carmelo Casconec2be50a2019-04-10 00:15:39 -0700658 final List<PortDescription> ports = getPortDetails(deviceId);
659 providerService.updatePorts(deviceId, ports);
660 }
pierventreb4120ff2021-04-09 14:32:11 +0200661
pierventree1f80102021-10-01 22:01:22 +0200662 DeviceDescription deviceDescription = getDeviceDescription(deviceId, available);
663 DeviceDescription storeDescription = providerService.getDeviceDescription(deviceId);
664 if (deviceService.getDevice(deviceId) != null &&
665 deviceService.isAvailable(deviceId) == available &&
666 storeDescription != null) {
667 /*
668 * FIXME SDFAB-650 rethink the APIs and abstractions around the DeviceStore.
669 * Device registration is a two-step process for the GDP. Initially, the device is
670 * registered with default avail. to false. Later, the checkup task will update the
671 * description with the default avail to true in order to mark it available. Today,
672 * there is only one API to mark online a device from the device provider which is
673 * deviceConnected which assumes an update on the device description. The device provider
674 * is the only one able to update the device description and we have to make sure that
675 * the default avail. is flipped to true as it is used to mark as online the device when
676 * it is created or updated. Otherwise, if an ONOS instance fails and restarts, when re-joining
677 * the cluster, it will get the device marked as offline and will not be able to update
678 * its status until it become the master. This process concurs with the markOnline done
679 * by the background thread in the DeviceManager and its the reason why we cannot just check
680 * the device availability but we need to compare also the desc. Checking here the equality,
681 * as in general we may want to upgrade the device description at run time.
682 */
683 DeviceDescription testDeviceDescription = DefaultDeviceDescription.copyReplacingAnnotation(
684 deviceDescription, storeDescription.annotations());
685 if (testDeviceDescription.equals(storeDescription)) {
686 return;
687 }
pierventreb4120ff2021-04-09 14:32:11 +0200688 }
pierventree1f80102021-10-01 22:01:22 +0200689
690 providerService.deviceConnected(deviceId, deviceDescription);
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800691 }
692
693 private boolean probeAvailability(DeviceHandshaker handshaker) {
Carmelo Cascone61469462019-03-05 23:59:11 -0800694 return Futures.getUnchecked(handshaker.probeAvailability());
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800695 }
696
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800697 private void markOfflineIfNeeded(DeviceId deviceId) {
698 assertDeviceRegistered(deviceId);
699 if (deviceService.isAvailable(deviceId)) {
700 providerService.deviceDisconnected(deviceId);
701 }
702 }
703
Carmelo Cascone4b616312019-04-17 14:15:45 -0700704 private void doCheckupAndRepair(DeviceId deviceId) {
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800705
Carmelo Cascone4b616312019-04-17 14:15:45 -0700706 // This task should be invoked periodically for each device known by
707 // this provider, or as a consequence of events signaling potential
708 // availability changes of the device. We check that everything is in
709 // order, repair what's wrong, and eventually mark the the device as
710 // available (or not) in the core.
711
712 if (!configIsPresent(deviceId)) {
713 // We should have a connection only for devices in the config.
714 submitTask(deviceId, TaskType.CONNECTION_TEARDOWN);
715 return;
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800716 }
717
Carmelo Cascone4b616312019-04-17 14:15:45 -0700718 final DeviceHandshaker handshaker = handshakersWithListeners.get(deviceId);
719 if (handshaker == null) {
720 // Device in config but we have not initiated a connection.
721 // Perhaps we missed the config event?
722 submitTask(deviceId, TaskType.CONNECTION_SETUP);
723 return;
724 }
725
726 // If here, we have a handshaker meaning we already connected once to
727 // the device...
728 if (!handshaker.hasConnection()) {
729 // ... but now the driver reports there is NOT a connection.
730 // Perhaps the netcfg changed and we didn't pick the event?
731 log.warn("Re-establishing lost connection to {}", deviceId);
732 submitTask(deviceId, TaskType.CONNECTION_TEARDOWN);
733 submitTask(deviceId, TaskType.CONNECTION_SETUP);
734 return;
735 }
736
737 // If here, device should be registered in the core.
738 assertDeviceRegistered(deviceId);
739
pierventred7cae132022-02-03 21:34:04 +0100740 if (!handshaker.isReachable() || !probeReachabilitySync(deviceId)) {
Carmelo Cascone4b616312019-04-17 14:15:45 -0700741 // Device appears to be offline.
742 markOfflineIfNeeded(deviceId);
pierventred7cae132022-02-03 21:34:04 +0100743 // We expect the protocol layer to implement some sort of
Carmelo Cascone4b616312019-04-17 14:15:45 -0700744 // connection backoff mechanism and to signal availability via
pierventred7cae132022-02-03 21:34:04 +0100745 // CHANNEL_OPEN events.
Carmelo Cascone4b616312019-04-17 14:15:45 -0700746 return;
747 }
748
749 // If here, device is reachable. Now do mastership and availability
750 // checkups. To avoid overload of checkup tasks which might involve
751 // sending messages over the network and triggering mastership
752 // elections. We require a minimum interval of 1/3 of the configured
753 // checkupInterval between consecutive checkup tasks when the device is
754 // known to be available.
755
756 final Long lastCheckup = lastCheckups.get(deviceId);
757 final boolean isAvailable = deviceService.isAvailable(deviceId);
758 if (isAvailable && lastCheckup != null &&
759 (currentTimeMillis() - lastCheckup) < (checkupInterval * 1000 / 3)) {
760 if (log.isDebugEnabled()) {
761 log.debug("Dropping checkup task for {} as it happened recently",
762 deviceId);
763 }
764 return;
765 }
766 lastCheckups.put(deviceId, currentTimeMillis());
767
768 // Make sure device has a valid mastership role.
769 final MastershipRole expectedRole = mastershipService.getLocalRole(deviceId);
770 if (expectedRole == MastershipRole.NONE) {
771 log.debug("Detected invalid role ({}) for {}, waiting for mastership " +
772 "service to fix this...",
773 expectedRole, deviceId);
774 // Gentle nudge to fix things...
775 mastershipService.requestRoleForSync(deviceId);
776 return;
777 }
778
779 final MastershipRole deviceRole = handshaker.getRole();
pierventred510b7d2022-02-03 21:56:37 +0100780 // FIXME: we should be checking the mastership term as well.
Carmelo Cascone4b616312019-04-17 14:15:45 -0700781 if (expectedRole != deviceRole) {
pierventred510b7d2022-02-03 21:56:37 +0100782 // Let's be greedy, if the role is NONE likely is due to the lazy channel
783 if (deviceRole == MastershipRole.NONE) {
784 log.warn("Detected role mismatch for {}, core expects {}, " +
785 "but device reports {}, reassert the role... ",
786 deviceId, expectedRole, deviceRole);
787 /* If we are experience a severe issue, eventually
788 the DeviceManager will move the mastership */
789 roleChanged(deviceId, expectedRole);
790 } else {
791 log.debug("Detected role mismatch for {}, core expects {}, " +
792 "but device reports {}, waiting for mastership " +
793 "service to fix this...",
794 deviceId, expectedRole, deviceRole);
795 // Gentle nudge to fix things...
796 providerService.receivedRoleReply(deviceId, deviceRole);
797 }
Carmelo Cascone4b616312019-04-17 14:15:45 -0700798 return;
799 }
800
801 // Check and update availability, which differently from reachability
802 // describes the ability of the device to forward packets.
803 if (probeAvailability(handshaker)) {
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800804 // Device ready to do its job.
805 createOrUpdateDevice(deviceId, true);
806 } else {
807 markOfflineIfNeeded(deviceId);
Carmelo Cascone4b616312019-04-17 14:15:45 -0700808 if (isPipelineProgrammable(deviceId)) {
809 // If reachable, but not available, and pipeline programmable,
810 // there is a high chance it's because the pipeline is not READY
811 // (independently from what the pipeconf watchdog reports, as
812 // the status there might be outdated). Encourage pipeconf
813 // watchdog to perform a pipeline probe ASAP.
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800814 pipeconfWatchdogService.triggerProbe(deviceId);
815 }
816 }
817 }
818
819 private void handleMastershipResponse(DeviceId deviceId, MastershipRole response) {
820 assertDeviceRegistered(deviceId);
821 log.debug("Device {} asserted role {}", deviceId, response);
822 providerService.receivedRoleReply(deviceId, response);
823 }
824
825 private void handleNotMaster(DeviceId deviceId) {
826 assertDeviceRegistered(deviceId);
Carmelo Cascone4b616312019-04-17 14:15:45 -0700827 handleMastershipResponse(deviceId, handshakerOrFail(deviceId).getRole());
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800828 }
829
830 private void assertDeviceRegistered(DeviceId deviceId) {
Carmelo Cascone4b616312019-04-17 14:15:45 -0700831 if (!deviceIsRegistered(deviceId)) {
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800832 throw new DeviceTaskException("device not registered in the core");
833 }
834 }
835
Carmelo Cascone4b616312019-04-17 14:15:45 -0700836 private boolean deviceIsRegistered(DeviceId deviceId) {
837 return deviceService.getDevice(deviceId) != null;
838 }
839
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800840 private void handleConnectionTeardown(DeviceId deviceId) {
841 if (deviceService.getDevice(deviceId) != null
842 && deviceService.isAvailable(deviceId)) {
843 providerService.deviceDisconnected(deviceId);
844 }
845 final DeviceHandshaker handshaker = handshakerOrFail(deviceId);
846 handshaker.removeDeviceAgentListener(id());
847 handshakersWithListeners.remove(deviceId);
848 handshaker.disconnect();
Carmelo Cascone4b616312019-04-17 14:15:45 -0700849 lastCheckups.remove(deviceId);
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800850 }
851
852 private void bindPipeconfIfRequired(DeviceId deviceId) {
853 if (pipeconfService.getPipeconf(deviceId).isPresent()
854 || !isPipelineProgrammable(deviceId)) {
855 // Nothing to do.
856 // Device has already a pipeconf or is not programmable.
857 return;
858 }
859 // Get pipeconf from netcfg or driver (default one).
860 final PiPipelineProgrammable pipelineProg = getBehaviour(
861 deviceId, PiPipelineProgrammable.class);
862 final PiPipeconfId pipeconfId = getPipeconfId(deviceId, pipelineProg);
863 if (pipeconfId == null) {
864 throw new DeviceTaskException("unable to find pipeconf");
865 }
Carmelo Cascone4b616312019-04-17 14:15:45 -0700866 if (!pipeconfService.getPipeconf(pipeconfId).isPresent()) {
867 throw new DeviceTaskException(format(
868 "pipeconf %s not registered", pipeconfId));
869 }
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800870 // Store binding in pipeconf service.
871 pipeconfService.bindToDevice(pipeconfId, deviceId);
872 }
873
874 private PiPipeconfId getPipeconfId(DeviceId deviceId, PiPipelineProgrammable pipelineProg) {
875 // Places to look for a pipeconf ID (in priority order)):
876 // 1) netcfg
877 // 2) device driver (default one)
878 final PiPipeconfId pipeconfId = getPipeconfFromCfg(deviceId);
879 if (pipeconfId != null && !pipeconfId.id().isEmpty()) {
880 return pipeconfId;
881 }
882 if (pipelineProg != null
883 && pipelineProg.getDefaultPipeconf().isPresent()) {
884 final PiPipeconf defaultPipeconf = pipelineProg.getDefaultPipeconf().get();
885 log.info("Using default pipeconf {} for {}", defaultPipeconf.id(), deviceId);
886 return defaultPipeconf.id();
887 }
888 return null;
889 }
890
891 private PiPipeconfId getPipeconfFromCfg(DeviceId deviceId) {
892 BasicDeviceConfig config = cfgService.getConfig(
893 deviceId, BasicDeviceConfig.class);
894 if (config == null) {
895 return null;
896 }
897 return config.pipeconf() != null
898 ? new PiPipeconfId(config.pipeconf()) : null;
899 }
900
901 private DeviceHandshaker handshakerOrFail(DeviceId deviceId) {
902 final DeviceHandshaker handshaker = getBehaviour(
903 deviceId, DeviceHandshaker.class);
904 if (handshaker == null) {
905 throw new DeviceTaskException("missing handshaker behavior");
906 }
907 return handshaker;
908 }
909
Carmelo Cascone4b616312019-04-17 14:15:45 -0700910 private boolean configIsPresent(DeviceId deviceId) {
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800911 final BasicDeviceConfig basicDeviceCfg = cfgService.getConfig(
912 deviceId, BasicDeviceConfig.class);
913 return basicDeviceCfg != null && !isNullOrEmpty(basicDeviceCfg.driver());
914 }
915
916 private void assertConfig(DeviceId deviceId) {
Carmelo Cascone4b616312019-04-17 14:15:45 -0700917 if (!configIsPresent(deviceId)) {
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800918 throw new DeviceTaskException("configuration is not complete");
919 }
Thomas Vachuska5b38dc02018-05-10 15:24:40 -0700920 }
921
Andrea Campanella241896c2017-05-10 13:11:04 -0700922 private Driver getDriver(DeviceId deviceId) {
Andrea Campanella241896c2017-05-10 13:11:04 -0700923 try {
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200924 // DriverManager checks first using basic device config.
925 return driverService.getDriver(deviceId);
Andrea Campanella241896c2017-05-10 13:11:04 -0700926 } catch (ItemNotFoundException e) {
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200927 log.error("Driver not found for {}", deviceId);
928 return null;
Andrea Campanella241896c2017-05-10 13:11:04 -0700929 }
Andrea Campanella241896c2017-05-10 13:11:04 -0700930 }
931
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700932 private <T extends Behaviour> T getBehaviour(DeviceId deviceId, Class<T> type) {
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700933 Driver driver = getDriver(deviceId);
934 if (driver == null) {
Andrea Campanella241896c2017-05-10 13:11:04 -0700935 return null;
936 }
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700937 if (!driver.hasBehaviour(type)) {
938 return null;
939 }
940 final DriverData data = new DefaultDriverData(driver, deviceId);
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700941 final DefaultDriverHandler handler = new DefaultDriverHandler(data);
942 return driver.createBehaviour(handler, type);
Andrea Campanella241896c2017-05-10 13:11:04 -0700943 }
944
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800945 private boolean hasBehaviour(DeviceId deviceId, Class<? extends Behaviour> type) {
946 Driver driver = getDriver(deviceId);
947 if (driver == null) {
948 return false;
Carmelo Cascone96beb6f2018-06-27 18:07:12 +0200949 }
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800950 return driver.hasBehaviour(type);
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700951 }
952
953 private DeviceDescription getDeviceDescription(
954 DeviceId deviceId, boolean defaultAvailable) {
955 // Get one from driver or forge.
956 final DeviceDescriptionDiscovery deviceDiscovery = getBehaviour(
957 deviceId, DeviceDescriptionDiscovery.class);
Yi Tsengd7716482018-10-31 15:34:30 -0700958 if (deviceDiscovery == null) {
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700959 return forgeDeviceDescription(deviceId, defaultAvailable);
960 }
Yi Tsengd7716482018-10-31 15:34:30 -0700961
962 final DeviceDescription d = deviceDiscovery.discoverDeviceDetails();
963 if (d == null) {
964 return forgeDeviceDescription(deviceId, defaultAvailable);
965 }
966 // Enforce defaultAvailable flag over the one obtained from driver.
967 return new DefaultDeviceDescription(d, defaultAvailable, d.annotations());
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700968 }
969
970 private List<PortDescription> getPortDetails(DeviceId deviceId) {
971 final DeviceDescriptionDiscovery deviceDiscovery = getBehaviour(
972 deviceId, DeviceDescriptionDiscovery.class);
973 if (deviceDiscovery != null) {
974 return deviceDiscovery.discoverPortDetails();
975 } else {
976 return Collections.emptyList();
977 }
978 }
979
980 private DeviceDescription forgeDeviceDescription(
981 DeviceId deviceId, boolean defaultAvailable) {
982 // Uses handshaker and provider config to get driver data.
983 final DeviceHandshaker handshaker = getBehaviour(
984 deviceId, DeviceHandshaker.class);
985 final Driver driver = handshaker != null
986 ? handshaker.handler().driver() : null;
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700987 return new DefaultDeviceDescription(
988 deviceId.uri(),
989 Device.Type.SWITCH,
990 driver != null ? driver.manufacturer() : UNKNOWN,
991 driver != null ? driver.hwVersion() : UNKNOWN,
992 driver != null ? driver.swVersion() : UNKNOWN,
993 UNKNOWN,
994 new ChassisId(),
995 defaultAvailable,
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800996 DefaultAnnotations.EMPTY);
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700997 }
998
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800999 static boolean myScheme(DeviceId deviceId) {
1000 return deviceId.uri().getScheme().equals(URI_SCHEME);
Carmelo Cascone96beb6f2018-06-27 18:07:12 +02001001 }
1002
Carmelo Cascone9e4972c2018-08-30 00:29:16 -07001003 private boolean isPipelineProgrammable(DeviceId deviceId) {
Carmelo Cascone3977ea42019-02-28 13:43:42 -08001004 return hasBehaviour(deviceId, PiPipelineProgrammable.class);
Carmelo Casconede3b6842018-09-05 17:45:10 -07001005 }
Thomas Vachuska1b3cf362019-08-29 17:11:18 -07001006
1007 private class InternalDeviceListener implements DeviceListener {
1008 @Override
1009 public void event(DeviceEvent event) {
1010 log.info("Triggering disconnect for device {}", event.subject().id());
1011 triggerDisconnect(event.subject().id());
1012 }
1013
1014 @Override
1015 public boolean isRelevant(DeviceEvent event) {
Thomas Vachuska24c0ca42019-08-29 18:19:16 -07001016 return DeviceEvent.Type.DEVICE_REMOVED == event.type() && myScheme(event.subject().id());
Thomas Vachuska1b3cf362019-08-29 17:11:18 -07001017 }
1018 }
Andrea Campanella241896c2017-05-10 13:11:04 -07001019}