blob: 3dc2a8d2fa45eaea267bdaa125bdaff03c648098 [file] [log] [blame]
Andrea Campanella945ded22016-01-07 13:17:43 -08001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2016-present Open Networking Foundation
Andrea Campanella945ded22016-01-07 13:17:43 -08003 *
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.rest.device.impl;
18
Konstantinos Kanonakis3cd85552016-11-17 10:11:13 -060019import com.google.common.base.Objects;
Andrea Campanella59b549d2017-04-14 21:58:16 +020020import com.google.common.collect.ImmutableList;
Andrea Campanella945ded22016-01-07 13:17:43 -080021import org.onlab.packet.ChassisId;
Yuta HIGUCHIfc667052017-12-05 18:17:22 -080022import org.onlab.util.SharedExecutors;
Michal Machd8099742017-06-19 14:32:35 +020023import org.onlab.util.SharedScheduledExecutorService;
24import org.onlab.util.SharedScheduledExecutors;
Georgios Katsikas0f12bc62018-07-06 09:06:42 +020025import org.onlab.util.Tools;
26import org.onosproject.cfg.ComponentConfigService;
Andrea Campanella945ded22016-01-07 13:17:43 -080027import org.onosproject.core.ApplicationId;
28import org.onosproject.core.CoreService;
Marc De Leenheerb0d131c2016-03-01 20:34:59 -080029import org.onosproject.net.AnnotationKeys;
Andrea Campanella945ded22016-01-07 13:17:43 -080030import org.onosproject.net.DefaultAnnotations;
31import org.onosproject.net.Device;
32import org.onosproject.net.DeviceId;
33import org.onosproject.net.MastershipRole;
Saurav Dasa2d37502016-03-25 17:50:40 -070034import org.onosproject.net.PortNumber;
Andrea Campanella945ded22016-01-07 13:17:43 -080035import org.onosproject.net.SparseAnnotations;
Michele Santuaric372c222017-01-12 09:41:25 +010036import org.onosproject.net.behaviour.DevicesDiscovery;
fahadnaeemkhan02ffa712017-12-01 19:49:45 -080037import org.onosproject.net.behaviour.PortAdmin;
Andrea Campanella945ded22016-01-07 13:17:43 -080038import org.onosproject.net.config.ConfigFactory;
39import org.onosproject.net.config.NetworkConfigEvent;
40import org.onosproject.net.config.NetworkConfigListener;
41import org.onosproject.net.config.NetworkConfigRegistry;
Andrea Campanellab38023b2019-02-11 17:21:35 +010042import org.onosproject.net.config.basics.BasicDeviceConfig;
Andrea Campanella59b549d2017-04-14 21:58:16 +020043import org.onosproject.net.config.basics.SubjectFactories;
Andrea Campanella945ded22016-01-07 13:17:43 -080044import org.onosproject.net.device.DefaultDeviceDescription;
45import org.onosproject.net.device.DeviceDescription;
Andrea Campanella6c71a052016-04-22 11:56:31 -070046import org.onosproject.net.device.DeviceDescriptionDiscovery;
Andrea Campanella945ded22016-01-07 13:17:43 -080047import org.onosproject.net.device.DeviceProvider;
48import org.onosproject.net.device.DeviceProviderRegistry;
49import org.onosproject.net.device.DeviceProviderService;
Andrea Campanella6c71a052016-04-22 11:56:31 -070050import org.onosproject.net.device.DeviceService;
Michal Machd8099742017-06-19 14:32:35 +020051import org.onosproject.net.device.PortStatistics;
52import org.onosproject.net.device.PortStatisticsDiscovery;
Michele Santuaric372c222017-01-12 09:41:25 +010053import org.onosproject.net.driver.DefaultDriverData;
54import org.onosproject.net.driver.DefaultDriverHandler;
55import org.onosproject.net.driver.Driver;
56import org.onosproject.net.driver.DriverData;
57import org.onosproject.net.driver.DriverHandler;
58import org.onosproject.net.driver.DriverService;
Andrea Campanella945ded22016-01-07 13:17:43 -080059import org.onosproject.net.provider.AbstractProvider;
60import org.onosproject.net.provider.ProviderId;
Andrea Campanella59b549d2017-04-14 21:58:16 +020061import org.onosproject.protocol.rest.DefaultRestSBDevice;
Andrea Campanella945ded22016-01-07 13:17:43 -080062import org.onosproject.protocol.rest.RestSBController;
63import org.onosproject.protocol.rest.RestSBDevice;
Georgios Katsikas0f12bc62018-07-06 09:06:42 +020064import org.osgi.service.component.ComponentContext;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070065import org.osgi.service.component.annotations.Activate;
66import org.osgi.service.component.annotations.Component;
67import org.osgi.service.component.annotations.Deactivate;
68import org.osgi.service.component.annotations.Modified;
69import org.osgi.service.component.annotations.Reference;
70import org.osgi.service.component.annotations.ReferenceCardinality;
Andrea Campanella945ded22016-01-07 13:17:43 -080071import org.slf4j.Logger;
72
Andrea Campanellac6ecc632016-03-10 17:57:06 -080073import javax.ws.rs.ProcessingException;
Michal Mach67acb692017-06-21 12:05:36 +020074import javax.ws.rs.core.MediaType;
Michal Machd8099742017-06-19 14:32:35 +020075import java.util.Collection;
Georgios Katsikas0f12bc62018-07-06 09:06:42 +020076import java.util.Dictionary;
Andrea Campanella945ded22016-01-07 13:17:43 -080077import java.util.HashSet;
Andrea Campanella59b549d2017-04-14 21:58:16 +020078import java.util.List;
Andrea Campanella945ded22016-01-07 13:17:43 -080079import java.util.Set;
Palash Kala4c71ee72017-05-24 17:43:59 +090080import java.util.concurrent.Callable;
fahadnaeemkhan02ffa712017-12-01 19:49:45 -080081import java.util.concurrent.CompletableFuture;
Palash Kala4c71ee72017-05-24 17:43:59 +090082import java.util.concurrent.ExecutionException;
Andrea Campanella784ee0f2016-02-17 15:50:59 -080083import java.util.concurrent.ExecutorService;
84import java.util.concurrent.Executors;
Palash Kala4c71ee72017-05-24 17:43:59 +090085import java.util.concurrent.Future;
Michal Machd8099742017-06-19 14:32:35 +020086import java.util.concurrent.ScheduledFuture;
Palash Kala4c71ee72017-05-24 17:43:59 +090087import java.util.concurrent.TimeUnit;
88import java.util.concurrent.TimeoutException;
Andrea Campanella59b549d2017-04-14 21:58:16 +020089import java.util.stream.Collectors;
Andrea Campanella945ded22016-01-07 13:17:43 -080090
Michele Santuaric372c222017-01-12 09:41:25 +010091import static com.google.common.base.Preconditions.checkNotNull;
Andrea Campanella784ee0f2016-02-17 15:50:59 -080092import static org.onlab.util.Tools.groupedThreads;
Andrea Campanella945ded22016-01-07 13:17:43 -080093import static org.onosproject.net.config.NetworkConfigEvent.Type.CONFIG_ADDED;
Yuta HIGUCHIfc667052017-12-05 18:17:22 -080094import static org.onosproject.net.config.NetworkConfigEvent.Type.CONFIG_REMOVED;
Andrea Campanella945ded22016-01-07 13:17:43 -080095import static org.onosproject.net.config.NetworkConfigEvent.Type.CONFIG_UPDATED;
Thomas Vachuska4167c3f2018-10-16 07:16:31 -070096import static org.onosproject.provider.rest.device.impl.OsgiPropertyConstants.POLL_FREQUENCY;
97import static org.onosproject.provider.rest.device.impl.OsgiPropertyConstants.POLL_FREQUENCY_DEFAULT;
Andrea Campanella02f86c82019-03-18 16:57:27 -070098import static org.onosproject.provider.rest.device.impl.OsgiPropertyConstants.TIMEOUT;
99import static org.onosproject.provider.rest.device.impl.OsgiPropertyConstants.TIMEOUT_DEFAULT;
Andrea Campanella945ded22016-01-07 13:17:43 -0800100import static org.slf4j.LoggerFactory.getLogger;
101
102/**
103 * Provider for devices that use REST as means of configuration communication.
104 */
Andrea Campanella02f86c82019-03-18 16:57:27 -0700105@Component(immediate = true,
Thomas Vachuska4167c3f2018-10-16 07:16:31 -0700106 property = {
107 POLL_FREQUENCY + ":Integer=" + POLL_FREQUENCY_DEFAULT,
Andrea Campanella02f86c82019-03-18 16:57:27 -0700108 TIMEOUT + ":Integer=" + TIMEOUT_DEFAULT,
Thomas Vachuska4167c3f2018-10-16 07:16:31 -0700109 })
Andrea Campanella945ded22016-01-07 13:17:43 -0800110public class RestDeviceProvider extends AbstractProvider
111 implements DeviceProvider {
112 private static final String APP_NAME = "org.onosproject.restsb";
Andrea Campanella59b549d2017-04-14 21:58:16 +0200113 protected static final String REST = "rest";
Andrea Campanella945ded22016-01-07 13:17:43 -0800114 private static final String PROVIDER = "org.onosproject.provider.rest.device";
115 private static final String IPADDRESS = "ipaddress";
Andrea Campanellab38023b2019-02-11 17:21:35 +0100116 private static final String DEVICENULL = "Rest device is null";
117 private static final String DRIVERNULL = "Driver is null";
Michele Santuaric372c222017-01-12 09:41:25 +0100118 private static final String UNKNOWN = "unknown";
Georgios Katsikas6a4d1662017-07-27 13:22:31 +0200119 private static final int EXECUTOR_THREAD_POOL_SIZE = 8;
Andrea Campanella945ded22016-01-07 13:17:43 -0800120 private final Logger log = getLogger(getClass());
121
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700122 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanella945ded22016-01-07 13:17:43 -0800123 protected DeviceProviderRegistry providerRegistry;
124
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700125 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanella945ded22016-01-07 13:17:43 -0800126 protected RestSBController controller;
127
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700128 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200129 protected NetworkConfigRegistry netCfgService;
130
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700131 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200132 protected ComponentConfigService compCfgService;
Andrea Campanella945ded22016-01-07 13:17:43 -0800133
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700134 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanella945ded22016-01-07 13:17:43 -0800135 protected CoreService coreService;
136
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700137 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanella6c71a052016-04-22 11:56:31 -0700138 protected DeviceService deviceService;
Andrea Campanellad8d92db2016-01-14 16:24:41 -0800139
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700140 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Michele Santuaric372c222017-01-12 09:41:25 +0100141 protected DriverService driverService;
142
Thomas Vachuska00b5d4f2018-10-30 15:13:20 -0700143 /** Configure poll frequency for port status and statistics; default is 30 seconds. */
Thomas Vachuska4167c3f2018-10-16 07:16:31 -0700144 private int pollFrequency = POLL_FREQUENCY_DEFAULT;
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200145
Andrea Campanella02f86c82019-03-18 16:57:27 -0700146 /** Configure timeout for a device reply; default is 5 seconds. */
147 private int replyTimeout = TIMEOUT_DEFAULT;
148
Andrea Campanella945ded22016-01-07 13:17:43 -0800149 private DeviceProviderService providerService;
Michele Santuaric372c222017-01-12 09:41:25 +0100150 private ApplicationId appId;
Andrea Campanella945ded22016-01-07 13:17:43 -0800151
Michal Mach13072e22017-06-21 09:12:24 +0200152 private ExecutorService executor;
Michal Mach67acb692017-06-21 12:05:36 +0200153 private final SharedScheduledExecutorService portStatisticsExecutor =
154 SharedScheduledExecutors.getPoolThreadExecutor();
Andrea Campanella784ee0f2016-02-17 15:50:59 -0800155
Sarithabd0b1f12018-05-11 11:35:27 +0530156 private final SharedScheduledExecutorService deviceConnectionExecutor =
157 SharedScheduledExecutors.getPoolThreadExecutor();
158 private ScheduledFuture<?> devicePollTask;
Michal Mach67acb692017-06-21 12:05:36 +0200159 private final List<ConfigFactory> factories = ImmutableList.of(
Andrea Campanella59b549d2017-04-14 21:58:16 +0200160 new ConfigFactory<DeviceId, RestDeviceConfig>(SubjectFactories.DEVICE_SUBJECT_FACTORY,
161 RestDeviceConfig.class,
162 REST) {
163 @Override
164 public RestDeviceConfig createConfig() {
165 return new RestDeviceConfig();
166 }
167 });
168
Michal Mach67acb692017-06-21 12:05:36 +0200169 private final NetworkConfigListener configListener = new InternalNetworkConfigListener();
Andrea Campanella945ded22016-01-07 13:17:43 -0800170
Michal Machd8099742017-06-19 14:32:35 +0200171 private ScheduledFuture<?> scheduledTask;
Andrea Campanella784ee0f2016-02-17 15:50:59 -0800172
Andrea Campanella945ded22016-01-07 13:17:43 -0800173
174 @Activate
175 public void activate() {
176 appId = coreService.registerApplication(APP_NAME);
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200177 compCfgService.registerProperties(getClass());
Andrea Campanella945ded22016-01-07 13:17:43 -0800178 providerService = providerRegistry.register(this);
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200179 factories.forEach(netCfgService::registerConfigFactory);
Georgios Katsikas6a4d1662017-07-27 13:22:31 +0200180 executor = Executors.newFixedThreadPool(
181 EXECUTOR_THREAD_POOL_SIZE, groupedThreads("onos/restsbprovider", "device-installer-%d", log)
182 );
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200183 netCfgService.addListener(configListener);
Andrea Campanella59b549d2017-04-14 21:58:16 +0200184 executor.execute(RestDeviceProvider.this::createAndConnectDevices);
Michal Machd8099742017-06-19 14:32:35 +0200185 scheduledTask = schedulePolling();
Sarithabd0b1f12018-05-11 11:35:27 +0530186 devicePollTask = scheduleDevicePolling();
Andrea Campanella945ded22016-01-07 13:17:43 -0800187 log.info("Started");
188 }
189
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200190 @Modified
191 public void modified(ComponentContext context) {
192 int previousPollFrequency = pollFrequency;
193
194 if (context != null) {
195 Dictionary<?, ?> properties = context.getProperties();
196 pollFrequency = Tools.getIntegerProperty(properties, POLL_FREQUENCY,
Thomas Vachuska4167c3f2018-10-16 07:16:31 -0700197 POLL_FREQUENCY_DEFAULT);
Andrea Campanella02f86c82019-03-18 16:57:27 -0700198 replyTimeout = Tools.getIntegerProperty(properties, TIMEOUT,
199 TIMEOUT_DEFAULT);
200 log.info("Configured. Poll frequency = {} seconds, reply timeout = {} seconds ",
201 pollFrequency, replyTimeout);
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200202 }
203
204 // Re-schedule only if frequency has changed
205 if (!scheduledTask.isCancelled() && (previousPollFrequency != pollFrequency)) {
206 log.info("Re-scheduling port statistics task with frequency {} seconds", pollFrequency);
207 scheduledTask.cancel(true);
208 scheduledTask = schedulePolling();
209 }
210 }
211
Andrea Campanella945ded22016-01-07 13:17:43 -0800212 @Deactivate
213 public void deactivate() {
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200214 compCfgService.unregisterProperties(getClass(), false);
215 netCfgService.removeListener(configListener);
Andrea Campanella945ded22016-01-07 13:17:43 -0800216 providerRegistry.unregister(this);
217 providerService = null;
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200218 factories.forEach(netCfgService::unregisterConfigFactory);
Michal Machd8099742017-06-19 14:32:35 +0200219 scheduledTask.cancel(true);
Lukasz Ryba4da35c52017-06-20 09:14:11 +0200220 executor.shutdown();
Sarithabd0b1f12018-05-11 11:35:27 +0530221 devicePollTask.cancel(true);
Andrea Campanella945ded22016-01-07 13:17:43 -0800222 log.info("Stopped");
223 }
224
225 public RestDeviceProvider() {
226 super(new ProviderId(REST, PROVIDER));
227 }
228
229 @Override
230 public void triggerProbe(DeviceId deviceId) {
231 // TODO: This will be implemented later.
232 log.info("Triggering probe on device {}", deviceId);
233 }
234
235 @Override
236 public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
Sarithabd0b1f12018-05-11 11:35:27 +0530237 log.debug("Received role {} request for device {}", newRole, deviceId);
238 RestSBDevice device = controller.getDevice(deviceId);
239 if (device != null && testDeviceConnection(device)) {
240 providerService.receivedRoleReply(deviceId, newRole, newRole);
241 } else {
242 log.warn("Device not present or available {}", deviceId);
243 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.NONE);
244 }
Andrea Campanella945ded22016-01-07 13:17:43 -0800245 }
246
Andrea Campanella945ded22016-01-07 13:17:43 -0800247 @Override
248 public boolean isReachable(DeviceId deviceId) {
249 RestSBDevice restDevice = controller.getDevice(deviceId);
Andrea Campanellab38023b2019-02-11 17:21:35 +0100250 return restDevice != null && restDevice.isActive();
Sarithabd0b1f12018-05-11 11:35:27 +0530251 }
252
253 private ScheduledFuture scheduleDevicePolling() {
254 return deviceConnectionExecutor.scheduleWithFixedDelay(() -> {
255 try {
256 controller.getDevices().values().stream().forEach(restSBDevice -> {
257 DeviceId deviceId = restSBDevice.deviceId();
258 if (deviceService.getDevice(deviceId) != null) {
259 boolean connected = testDeviceConnection(restSBDevice);
260 restSBDevice.setActive(connected);
261 if (deviceService.isAvailable(deviceId) && (!connected)) {
262 providerService.deviceDisconnected(deviceId);
263 } else if (!deviceService.isAvailable(deviceId) && connected) {
264 DeviceDescription devDesc = getDesc(restSBDevice);
265 checkNotNull(devDesc, "deviceDescription cannot be null");
266 providerService.deviceConnected(
267 deviceId, mergeAnn(deviceId, devDesc));
268 }
269 }
270 }
271 );
272 } catch (Exception e) {
273 log.error("Exception at schedule Device polling", e);
274 }
Andrea Campanella02f86c82019-03-18 16:57:27 -0700275 }, 1, pollFrequency, TimeUnit.SECONDS);
Sarithabd0b1f12018-05-11 11:35:27 +0530276 }
277
278 private DeviceDescription getDesc(RestSBDevice restSBDev) {
279 DeviceId deviceId = restSBDev.deviceId();
Andrea Campanellab38023b2019-02-11 17:21:35 +0100280
281 Driver driver = getDriver(restSBDev);
Sarithabd0b1f12018-05-11 11:35:27 +0530282
Andrea Campanella60804c02018-11-06 15:06:36 +0100283 if (restSBDev.isProxy()) {
Sarithabd0b1f12018-05-11 11:35:27 +0530284 if (driver != null && driver.hasBehaviour(DevicesDiscovery.class)) {
285
286 //Creates the driver to communicate with the server
287 DevicesDiscovery devicesDiscovery =
288 devicesDiscovery(restSBDev, driver);
289 return devicesDiscovery.deviceDetails(deviceId);
290 } else {
291 log.warn("Driver not found for {}", restSBDev);
292 return null;
Michele Santuaric372c222017-01-12 09:41:25 +0100293 }
Andrea Campanella60804c02018-11-06 15:06:36 +0100294 } else if (driver != null && driver.hasBehaviour(DeviceDescriptionDiscovery.class)) {
295 DriverHandler h = driverService.createHandler(deviceId);
296 DeviceDescriptionDiscovery deviceDiscovery = h.behaviour(DeviceDescriptionDiscovery.class);
297 return deviceDiscovery.discoverDeviceDetails();
Andrea Campanella945ded22016-01-07 13:17:43 -0800298 }
Sarithabd0b1f12018-05-11 11:35:27 +0530299 ChassisId cid = new ChassisId();
300 String ipAddress = restSBDev.ip().toString();
301 SparseAnnotations annotations = DefaultAnnotations.builder()
302 .set(IPADDRESS, ipAddress)
303 .set(AnnotationKeys.PROTOCOL, REST.toUpperCase())
304 .build();
305 String manufacturer = UNKNOWN;
306 String hwVersion = UNKNOWN;
307 String swVersion = UNKNOWN;
308 String serialNumber = UNKNOWN;
309
310 Device device = deviceService.getDevice(deviceId);
311 if (device != null) {
312 manufacturer = device.manufacturer();
313 hwVersion = device.hwVersion();
314 swVersion = device.swVersion();
315 serialNumber = device.serialNumber();
316 }
317
318 return new DefaultDeviceDescription(
319 deviceId.uri(),
320 Device.Type.SWITCH,
321 manufacturer, hwVersion,
322 swVersion, serialNumber,
323 cid,
324 annotations);
Andrea Campanella945ded22016-01-07 13:17:43 -0800325 }
326
Michele Santuaric372c222017-01-12 09:41:25 +0100327 private void deviceAdded(RestSBDevice restSBDev) {
Andrea Campanellab38023b2019-02-11 17:21:35 +0100328 checkNotNull(restSBDev, DEVICENULL);
Michele Santuaric372c222017-01-12 09:41:25 +0100329
Andrea Campanellab38023b2019-02-11 17:21:35 +0100330 Driver driver = getDriver(restSBDev);
Georgios Katsikas15841e22018-07-28 14:27:28 +0200331
332 // Check if the server is controlling a single or multiple devices
Michele Santuaric372c222017-01-12 09:41:25 +0100333 if (restSBDev.isProxy()) {
Andrea Campanellab38023b2019-02-11 17:21:35 +0100334 if (driver.hasBehaviour(DevicesDiscovery.class)) {
Georgios Katsikas15841e22018-07-28 14:27:28 +0200335 DevicesDiscovery devicesDiscovery = devicesDiscovery(restSBDev, driver);
Michele Santuaric372c222017-01-12 09:41:25 +0100336 Set<DeviceId> deviceIds = devicesDiscovery.deviceIds();
337 restSBDev.setActive(true);
Michal Mach67acb692017-06-21 12:05:36 +0200338 deviceIds.forEach(deviceId -> {
Michele Santuaric372c222017-01-12 09:41:25 +0100339 controller.addProxiedDevice(deviceId, restSBDev);
Georgios Katsikas15841e22018-07-28 14:27:28 +0200340 DeviceDescription devDesc = devicesDiscovery.deviceDetails(deviceId);
341 checkNotNull(devDesc, "DeviceDescription cannot be null");
342 providerService.deviceConnected(deviceId, mergeAnn(restSBDev.deviceId(), devDesc));
Michele Santuaric372c222017-01-12 09:41:25 +0100343
344 if (driver.hasBehaviour(DeviceDescriptionDiscovery.class)) {
345 DriverHandler h = driverService.createHandler(deviceId);
Georgios Katsikas15841e22018-07-28 14:27:28 +0200346 DeviceDescriptionDiscovery devDisc = h.behaviour(DeviceDescriptionDiscovery.class);
347 providerService.updatePorts(deviceId, devDisc.discoverPortDetails());
Michele Santuaric372c222017-01-12 09:41:25 +0100348 }
349
350 checkAndUpdateDevice(deviceId);
Michele Santuaric372c222017-01-12 09:41:25 +0100351 });
352 } else {
Andrea Campanellab38023b2019-02-11 17:21:35 +0100353 log.warn("Device is proxy but driver does not have proxy discovery behaviour {}", restSBDev);
Michele Santuaric372c222017-01-12 09:41:25 +0100354 }
355 } else {
356 DeviceId deviceId = restSBDev.deviceId();
Sarithabd0b1f12018-05-11 11:35:27 +0530357
Georgios Katsikas15841e22018-07-28 14:27:28 +0200358 if (driver != null && driver.hasBehaviour(DevicesDiscovery.class)) {
359 restSBDev.setActive(true);
360 DevicesDiscovery devicesDiscovery = devicesDiscovery(restSBDev, driver);
361 DeviceDescription deviceDescription = devicesDiscovery.deviceDetails(deviceId);
362 checkNotNull(deviceDescription, "DeviceDescription cannot be null");
363 providerService.deviceConnected(deviceId, deviceDescription);
364
365 if (driver.hasBehaviour(DeviceDescriptionDiscovery.class)) {
366 DriverHandler h = driverService.createHandler(deviceId);
367 DeviceDescriptionDiscovery deviceDiscovery = h.behaviour(DeviceDescriptionDiscovery.class);
368 providerService.updatePorts(deviceId, deviceDiscovery.discoverPortDetails());
369 }
370 } else {
Sarithabd0b1f12018-05-11 11:35:27 +0530371 DeviceDescription deviceDescription = getDesc(restSBDev);
Georgios Katsikas15841e22018-07-28 14:27:28 +0200372 restSBDev.setActive(true);
373 providerService.deviceConnected(deviceId, deviceDescription);
374 }
Michele Santuaric372c222017-01-12 09:41:25 +0100375 checkAndUpdateDevice(deviceId);
Michele Santuaric372c222017-01-12 09:41:25 +0100376 }
377 }
378
Andrea Campanellab38023b2019-02-11 17:21:35 +0100379 private Driver getDriver(RestSBDevice restSBDev) {
380 String driverName = netCfgService.getConfig(restSBDev.deviceId(), BasicDeviceConfig.class).driver();
381
382 Driver driver = driverService.getDriver(driverName);
383
384 if (driver == null) {
385 driver = driverService.getDriver(restSBDev.manufacturer().get(),
386 restSBDev.hwVersion().get(),
387 restSBDev.swVersion().get());
388 }
389
390 checkNotNull(driver, DRIVERNULL);
391 return driver;
392 }
393
Michele Santuaric372c222017-01-12 09:41:25 +0100394 private DefaultDeviceDescription mergeAnn(DeviceId devId, DeviceDescription desc) {
395 return new DefaultDeviceDescription(
396 desc,
397 DefaultAnnotations.merge(
398 DefaultAnnotations.builder()
399 .set(AnnotationKeys.PROTOCOL, REST.toUpperCase())
400 // The rest server added as annotation to the device
401 .set(AnnotationKeys.REST_SERVER, devId.toString())
402 .build(),
403 desc.annotations()));
404 }
405
406 private DevicesDiscovery devicesDiscovery(RestSBDevice restSBDevice, Driver driver) {
407 DriverData driverData = new DefaultDriverData(driver, restSBDevice.deviceId());
Georgios Katsikas15841e22018-07-28 14:27:28 +0200408 DevicesDiscovery devicesDiscovery = driver.createBehaviour(driverData, DevicesDiscovery.class);
Michele Santuaric372c222017-01-12 09:41:25 +0100409 devicesDiscovery.setHandler(new DefaultDriverHandler(driverData));
410 return devicesDiscovery;
Andrea Campanella945ded22016-01-07 13:17:43 -0800411 }
412
Konstantinos Kanonakis3cd85552016-11-17 10:11:13 -0600413 private void checkAndUpdateDevice(DeviceId deviceId) {
414 if (deviceService.getDevice(deviceId) == null) {
Georgios Katsikas15841e22018-07-28 14:27:28 +0200415 log.warn("Device {} has not been added to store, maybe due to a problem in connectivity", deviceId);
Konstantinos Kanonakis3cd85552016-11-17 10:11:13 -0600416 } else {
417 boolean isReachable = isReachable(deviceId);
418 if (isReachable && deviceService.isAvailable(deviceId)) {
419 Device device = deviceService.getDevice(deviceId);
420 if (device.is(DeviceDescriptionDiscovery.class)) {
Michele Santuaric372c222017-01-12 09:41:25 +0100421 DeviceDescriptionDiscovery deviceDescriptionDiscovery =
422 device.as(DeviceDescriptionDiscovery.class);
423 DeviceDescription updatedDeviceDescription =
424 deviceDescriptionDiscovery.discoverDeviceDetails();
425 if (updatedDeviceDescription != null &&
426 !descriptionEquals(device, updatedDeviceDescription)) {
427 providerService.deviceConnected(
428 deviceId,
429 new DefaultDeviceDescription(
430 updatedDeviceDescription, true,
431 updatedDeviceDescription.annotations()));
Konstantinos Kanonakis3cd85552016-11-17 10:11:13 -0600432 //if ports are not discovered, retry the discovery
433 if (deviceService.getPorts(deviceId).isEmpty()) {
434 discoverPorts(deviceId);
435 }
436 }
437 } else {
438 log.warn("No DeviceDescriptionDiscovery behaviour for device {}", deviceId);
439 }
440 } else if (!isReachable && deviceService.isAvailable(deviceId)) {
441 providerService.deviceDisconnected(deviceId);
442 }
443 }
444 }
445
446 private boolean descriptionEquals(Device device, DeviceDescription updatedDeviceDescription) {
Michele Santuarid2c8f212017-01-09 18:23:45 +0100447 return Objects.equal(device.id().uri(), updatedDeviceDescription.deviceUri())
Konstantinos Kanonakis3cd85552016-11-17 10:11:13 -0600448 && Objects.equal(device.type(), updatedDeviceDescription.type())
449 && Objects.equal(device.manufacturer(), updatedDeviceDescription.manufacturer())
450 && Objects.equal(device.hwVersion(), updatedDeviceDescription.hwVersion())
451 && Objects.equal(device.swVersion(), updatedDeviceDescription.swVersion())
452 && Objects.equal(device.serialNumber(), updatedDeviceDescription.serialNumber())
453 && Objects.equal(device.chassisId(), updatedDeviceDescription.chassisId())
454 && Objects.equal(device.annotations(), updatedDeviceDescription.annotations());
455 }
456
Andrea Campanella86294db2016-03-07 11:42:49 -0800457 private void deviceRemoved(DeviceId deviceId) {
Andrea Campanellab38023b2019-02-11 17:21:35 +0100458 checkNotNull(deviceId, DEVICENULL);
Sarithabd0b1f12018-05-11 11:35:27 +0530459 log.debug("Device removed called for {}", deviceId);
Andrea Campanella945ded22016-01-07 13:17:43 -0800460 providerService.deviceDisconnected(deviceId);
Michal Mach67acb692017-06-21 12:05:36 +0200461 controller.getProxiedDevices(deviceId).forEach(device -> {
Michele Santuaric372c222017-01-12 09:41:25 +0100462 controller.removeProxiedDevice(device);
463 providerService.deviceDisconnected(device);
464 });
Andrea Campanella86294db2016-03-07 11:42:49 -0800465 controller.removeDevice(deviceId);
Andrea Campanella945ded22016-01-07 13:17:43 -0800466 }
467
Andrea Campanella59b549d2017-04-14 21:58:16 +0200468 //Method to connect devices provided via net-cfg under devices/ tree
469 private void createAndConnectDevices() {
470 Set<DeviceId> deviceSubjects =
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200471 netCfgService.getSubjects(DeviceId.class, RestDeviceConfig.class);
Sarithabd0b1f12018-05-11 11:35:27 +0530472 log.debug("Connecting and configuring devices with received configuration:{}",
473 deviceSubjects);
Andrea Campanella59b549d2017-04-14 21:58:16 +0200474 connectDevices(deviceSubjects.stream()
fahadnaeemkhan02ffa712017-12-01 19:49:45 -0800475 .filter(deviceId -> deviceService.getDevice(deviceId) == null)
476 .map(deviceId -> {
477 RestDeviceConfig config =
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200478 netCfgService.getConfig(deviceId, RestDeviceConfig.class);
Yuta HIGUCHIfc667052017-12-05 18:17:22 -0800479 return toInactiveRestSBDevice(config);
fahadnaeemkhan02ffa712017-12-01 19:49:45 -0800480 }).collect(Collectors.toSet()));
Andrea Campanella59b549d2017-04-14 21:58:16 +0200481 }
482
Yuta HIGUCHIfc667052017-12-05 18:17:22 -0800483 private RestSBDevice toInactiveRestSBDevice(RestDeviceConfig config) {
484 return new DefaultRestSBDevice(config.ip(),
485 config.port(),
486 config.username(),
487 config.password(),
488 config.protocol(),
489 config.url(),
490 false,
491 config.testUrl(),
492 config.manufacturer(),
493 config.hwVersion(),
494 config.swVersion(),
Georgios Katsikas15841e22018-07-28 14:27:28 +0200495 config.isProxy(),
Yuta HIGUCHIfc667052017-12-05 18:17:22 -0800496 config.authenticationScheme(),
497 config.token()
498 );
499 }
500
Andrea Campanella59b549d2017-04-14 21:58:16 +0200501 private void connectDevices(Set<RestSBDevice> devices) {
502 //Precomputing the devices to be removed
503 Set<RestSBDevice> toBeRemoved = new HashSet<>(controller.getDevices().values());
504 toBeRemoved.removeAll(devices);
505 //Adding new devices
506 devices.stream()
507 .filter(device -> {
508 device.setActive(false);
509 controller.addDevice(device);
510 return testDeviceConnection(device);
511 })
Michal Mach67acb692017-06-21 12:05:36 +0200512 .forEach(this::deviceAdded);
Andrea Campanella59b549d2017-04-14 21:58:16 +0200513 //Removing devices not wanted anymore
514 toBeRemoved.forEach(device -> deviceRemoved(device.deviceId()));
515 }
516
Yuta HIGUCHIfc667052017-12-05 18:17:22 -0800517 private void connectDevice(RestSBDevice device) {
518 // TODO borrowed from above,
519 // not sure why setting it to inactive
520 device.setActive(false);
521 controller.addDevice(device);
522 if (testDeviceConnection(device)) {
523 deviceAdded(device);
524 }
525 }
526
Michal Machd8099742017-06-19 14:32:35 +0200527 private ScheduledFuture schedulePolling() {
528 return portStatisticsExecutor.scheduleAtFixedRate(this::executePortStatisticsUpdate,
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200529 pollFrequency / 2, pollFrequency,
Michal Machd8099742017-06-19 14:32:35 +0200530 TimeUnit.SECONDS);
531 }
532
533 private void executePortStatisticsUpdate() {
534 controller.getDevices().keySet().forEach(this::updatePortStatistics);
535 }
536
537 private void updatePortStatistics(DeviceId deviceId) {
538 Device device = deviceService.getDevice(deviceId);
539 checkNotNull(device, "device cannot be null");
540
541 if (device.is(PortStatisticsDiscovery.class)) {
542 PortStatisticsDiscovery portStatisticsDiscovery = device.as(PortStatisticsDiscovery.class);
543 Collection<PortStatistics> portStatistics = portStatisticsDiscovery.discoverPortStatistics();
544 if (portStatistics != null && !portStatistics.isEmpty()) {
545 providerService.updatePortStatistics(deviceId, portStatistics);
546 }
547 } else {
548 log.debug("No port statistics getter behaviour for device {}", deviceId);
549 }
550 }
551
Andrea Campanella6c71a052016-04-22 11:56:31 -0700552 private void discoverPorts(DeviceId deviceId) {
553 Device device = deviceService.getDevice(deviceId);
Georgios Katsikas15841e22018-07-28 14:27:28 +0200554 DeviceDescriptionDiscovery deviceDescriptionDiscovery = device.as(DeviceDescriptionDiscovery.class);
Ray Milkey640fedd2018-02-08 09:02:26 -0800555 providerService.updatePorts(deviceId, deviceDescriptionDiscovery.discoverPortDetails());
Andrea Campanella6c71a052016-04-22 11:56:31 -0700556 }
557
Michele Santuaric372c222017-01-12 09:41:25 +0100558 private boolean testDeviceConnection(RestSBDevice dev) {
Andrea Campanella945ded22016-01-07 13:17:43 -0800559 try {
Palash Kala4c71ee72017-05-24 17:43:59 +0900560 Callable<Boolean> connectionSuccess;
561
Michele Santuaric372c222017-01-12 09:41:25 +0100562 if (dev.testUrl().isPresent()) {
Michal Mach67acb692017-06-21 12:05:36 +0200563 connectionSuccess = () ->
564 controller.get(dev.deviceId(), dev.testUrl().get(), MediaType.APPLICATION_JSON_TYPE) != null;
Palash Kala4c71ee72017-05-24 17:43:59 +0900565 } else {
Michal Mach67acb692017-06-21 12:05:36 +0200566 connectionSuccess = () ->
567 controller.get(dev.deviceId(), "", MediaType.APPLICATION_JSON_TYPE) != null;
Michele Santuaric372c222017-01-12 09:41:25 +0100568 }
Palash Kala4c71ee72017-05-24 17:43:59 +0900569
570 Future<Boolean> future = executor.submit(connectionSuccess);
571 try {
Andrea Campanella02f86c82019-03-18 16:57:27 -0700572 return future.get(replyTimeout, TimeUnit.SECONDS);
Palash Kala4c71ee72017-05-24 17:43:59 +0900573 } catch (TimeoutException ex) {
Sean Condone45e12c2018-05-04 16:27:07 +0100574 log.warn("Connection to device {} timed out: {}", dev.deviceId(), ex.getMessage());
Palash Kala4c71ee72017-05-24 17:43:59 +0900575 return false;
576 } catch (InterruptedException ex) {
Sean Condone45e12c2018-05-04 16:27:07 +0100577 log.warn("Connection to device {} interrupted: {}", dev.deviceId(), ex.getMessage());
Ray Milkey5c7d4882018-02-05 14:50:39 -0800578 Thread.currentThread().interrupt();
Palash Kala4c71ee72017-05-24 17:43:59 +0900579 return false;
580 } catch (ExecutionException ex) {
Sean Condone45e12c2018-05-04 16:27:07 +0100581 log.warn("Connection to device {} had an execution exception.", dev.deviceId(), ex);
Palash Kala4c71ee72017-05-24 17:43:59 +0900582 return false;
583 }
Michele Santuaric372c222017-01-12 09:41:25 +0100584
Andrea Campanellac6ecc632016-03-10 17:57:06 -0800585 } catch (ProcessingException e) {
Michele Santuaric372c222017-01-12 09:41:25 +0100586 log.warn("Cannot connect to device {}", dev, e);
Andrea Campanella945ded22016-01-07 13:17:43 -0800587 }
588 return false;
589 }
590
Andrea Campanellab9e491b2019-02-18 17:45:01 +0100591 private Runnable exceptionSafe(Runnable runnable) {
592 return () -> {
593 try {
594 runnable.run();
595 } catch (Exception e) {
596 log.error("Unhandled Exception", e);
597 }
598 };
599 }
600
Andrea Campanella945ded22016-01-07 13:17:43 -0800601 private class InternalNetworkConfigListener implements NetworkConfigListener {
Andrea Campanella945ded22016-01-07 13:17:43 -0800602 @Override
603 public void event(NetworkConfigEvent event) {
Georgios Katsikas15841e22018-07-28 14:27:28 +0200604 if (!isRelevant(event)) {
605 log.warn("Irrelevant network configuration event: {}", event);
606 return;
607 }
608
Yuta HIGUCHIfc667052017-12-05 18:17:22 -0800609 ExecutorService bg = SharedExecutors.getSingleThreadExecutor();
610 if (event.type() == CONFIG_REMOVED) {
Sarithabd0b1f12018-05-11 11:35:27 +0530611 log.debug("Config {} event for rest device provider for {}",
612 event.type(), event.prevConfig().get().subject());
Yuta HIGUCHIfc667052017-12-05 18:17:22 -0800613 DeviceId did = (DeviceId) event.subject();
614 bg.execute(() -> deviceRemoved(did));
615 } else {
Sarithabd0b1f12018-05-11 11:35:27 +0530616 //CONFIG_ADDED or CONFIG_UPDATED
617 log.debug("Config {} event for rest device provider for {}",
618 event.type(), event.config().get().subject());
Yuta HIGUCHIfc667052017-12-05 18:17:22 -0800619 RestDeviceConfig cfg = (RestDeviceConfig) event.config().get();
620 RestSBDevice restSBDevice = toInactiveRestSBDevice(cfg);
Andrea Campanellab9e491b2019-02-18 17:45:01 +0100621 bg.execute(exceptionSafe(() -> connectDevice(restSBDevice)));
Yuta HIGUCHIfc667052017-12-05 18:17:22 -0800622 }
Andrea Campanella945ded22016-01-07 13:17:43 -0800623 }
624
625 @Override
626 public boolean isRelevant(NetworkConfigEvent event) {
Yuta HIGUCHI872fdbe2017-12-05 15:34:28 -0800627 return event.configClass().equals(RestDeviceConfig.class) &&
Andrea Campanella945ded22016-01-07 13:17:43 -0800628 (event.type() == CONFIG_ADDED ||
Yuta HIGUCHIfc667052017-12-05 18:17:22 -0800629 event.type() == CONFIG_UPDATED ||
630 event.type() == CONFIG_REMOVED);
Andrea Campanella945ded22016-01-07 13:17:43 -0800631 }
632 }
Saurav Dasa2d37502016-03-25 17:50:40 -0700633
634 @Override
635 public void changePortState(DeviceId deviceId, PortNumber portNumber,
636 boolean enable) {
fahadnaeemkhan482951f2017-08-24 16:35:17 -0700637 Device device = deviceService.getDevice(deviceId);
638 if (device != null) {
639 if (device.is(PortAdmin.class)) {
640 PortAdmin portAdmin = device.as(PortAdmin.class);
641 CompletableFuture<Boolean> modified;
642 if (enable) {
643 modified = portAdmin.enable(portNumber);
644 } else {
645 modified = portAdmin.disable(portNumber);
646 }
647 modified.thenAcceptAsync(result -> {
648 if (!result) {
649 log.warn("Device {} port {} state can't be changed to {}",
650 deviceId, portNumber, enable);
651 }
652 });
653
654 } else {
655 log.warn("Device {} does not support PortAdmin behavior", deviceId);
656 }
657 } else {
658 log.warn("unable to get the device {}, port {} state can't be changed to {}",
659 deviceId, portNumber, enable);
660 }
Saurav Dasa2d37502016-03-25 17:50:40 -0700661 }
Andrea Campanella945ded22016-01-07 13:17:43 -0800662}