blob: cd738cde82c8f938e26d70d628d32b614ca6d679 [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 Campanella945ded22016-01-07 13:17:43 -080098import static org.slf4j.LoggerFactory.getLogger;
99
100/**
101 * Provider for devices that use REST as means of configuration communication.
102 */
Thomas Vachuska4167c3f2018-10-16 07:16:31 -0700103@Component(immediate = true, service = DeviceProvider.class,
104 property = {
105 POLL_FREQUENCY + ":Integer=" + POLL_FREQUENCY_DEFAULT,
106 })
Andrea Campanella945ded22016-01-07 13:17:43 -0800107public class RestDeviceProvider extends AbstractProvider
108 implements DeviceProvider {
109 private static final String APP_NAME = "org.onosproject.restsb";
Andrea Campanella59b549d2017-04-14 21:58:16 +0200110 protected static final String REST = "rest";
Andrea Campanella945ded22016-01-07 13:17:43 -0800111 private static final String PROVIDER = "org.onosproject.provider.rest.device";
112 private static final String IPADDRESS = "ipaddress";
Andrea Campanellab38023b2019-02-11 17:21:35 +0100113 private static final String DEVICENULL = "Rest device is null";
114 private static final String DRIVERNULL = "Driver is null";
Michele Santuaric372c222017-01-12 09:41:25 +0100115 private static final String UNKNOWN = "unknown";
Palash Kala4c71ee72017-05-24 17:43:59 +0900116 private static final int REST_TIMEOUT_SEC = 5;
Georgios Katsikas6a4d1662017-07-27 13:22:31 +0200117 private static final int EXECUTOR_THREAD_POOL_SIZE = 8;
Sarithabd0b1f12018-05-11 11:35:27 +0530118 private static final int DEVICE_POLL_SEC = 30;
Andrea Campanella945ded22016-01-07 13:17:43 -0800119 private final Logger log = getLogger(getClass());
120
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700121 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanella945ded22016-01-07 13:17:43 -0800122 protected DeviceProviderRegistry providerRegistry;
123
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700124 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanella945ded22016-01-07 13:17:43 -0800125 protected RestSBController controller;
126
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700127 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200128 protected NetworkConfigRegistry netCfgService;
129
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700130 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200131 protected ComponentConfigService compCfgService;
Andrea Campanella945ded22016-01-07 13:17:43 -0800132
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700133 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanella945ded22016-01-07 13:17:43 -0800134 protected CoreService coreService;
135
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700136 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanella6c71a052016-04-22 11:56:31 -0700137 protected DeviceService deviceService;
Andrea Campanellad8d92db2016-01-14 16:24:41 -0800138
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700139 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Michele Santuaric372c222017-01-12 09:41:25 +0100140 protected DriverService driverService;
141
Thomas Vachuska00b5d4f2018-10-30 15:13:20 -0700142 /** Configure poll frequency for port status and statistics; default is 30 seconds. */
Thomas Vachuska4167c3f2018-10-16 07:16:31 -0700143 private int pollFrequency = POLL_FREQUENCY_DEFAULT;
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200144
Andrea Campanella945ded22016-01-07 13:17:43 -0800145 private DeviceProviderService providerService;
Michele Santuaric372c222017-01-12 09:41:25 +0100146 private ApplicationId appId;
Andrea Campanella945ded22016-01-07 13:17:43 -0800147
Michal Mach13072e22017-06-21 09:12:24 +0200148 private ExecutorService executor;
Michal Mach67acb692017-06-21 12:05:36 +0200149 private final SharedScheduledExecutorService portStatisticsExecutor =
150 SharedScheduledExecutors.getPoolThreadExecutor();
Andrea Campanella784ee0f2016-02-17 15:50:59 -0800151
Sarithabd0b1f12018-05-11 11:35:27 +0530152 private final SharedScheduledExecutorService deviceConnectionExecutor =
153 SharedScheduledExecutors.getPoolThreadExecutor();
154 private ScheduledFuture<?> devicePollTask;
Michal Mach67acb692017-06-21 12:05:36 +0200155 private final List<ConfigFactory> factories = ImmutableList.of(
Andrea Campanella59b549d2017-04-14 21:58:16 +0200156 new ConfigFactory<DeviceId, RestDeviceConfig>(SubjectFactories.DEVICE_SUBJECT_FACTORY,
157 RestDeviceConfig.class,
158 REST) {
159 @Override
160 public RestDeviceConfig createConfig() {
161 return new RestDeviceConfig();
162 }
163 });
164
Michal Mach67acb692017-06-21 12:05:36 +0200165 private final NetworkConfigListener configListener = new InternalNetworkConfigListener();
Andrea Campanella945ded22016-01-07 13:17:43 -0800166
Michal Machd8099742017-06-19 14:32:35 +0200167 private ScheduledFuture<?> scheduledTask;
Andrea Campanella784ee0f2016-02-17 15:50:59 -0800168
Andrea Campanella945ded22016-01-07 13:17:43 -0800169
170 @Activate
171 public void activate() {
172 appId = coreService.registerApplication(APP_NAME);
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200173 compCfgService.registerProperties(getClass());
Andrea Campanella945ded22016-01-07 13:17:43 -0800174 providerService = providerRegistry.register(this);
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200175 factories.forEach(netCfgService::registerConfigFactory);
Georgios Katsikas6a4d1662017-07-27 13:22:31 +0200176 executor = Executors.newFixedThreadPool(
177 EXECUTOR_THREAD_POOL_SIZE, groupedThreads("onos/restsbprovider", "device-installer-%d", log)
178 );
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200179 netCfgService.addListener(configListener);
Andrea Campanella59b549d2017-04-14 21:58:16 +0200180 executor.execute(RestDeviceProvider.this::createAndConnectDevices);
Michal Machd8099742017-06-19 14:32:35 +0200181 scheduledTask = schedulePolling();
Sarithabd0b1f12018-05-11 11:35:27 +0530182 devicePollTask = scheduleDevicePolling();
Andrea Campanella945ded22016-01-07 13:17:43 -0800183 log.info("Started");
184 }
185
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200186 @Modified
187 public void modified(ComponentContext context) {
188 int previousPollFrequency = pollFrequency;
189
190 if (context != null) {
191 Dictionary<?, ?> properties = context.getProperties();
192 pollFrequency = Tools.getIntegerProperty(properties, POLL_FREQUENCY,
Thomas Vachuska4167c3f2018-10-16 07:16:31 -0700193 POLL_FREQUENCY_DEFAULT);
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200194 log.info("Configured. Poll frequency is configured to {} seconds", pollFrequency);
195 }
196
197 // Re-schedule only if frequency has changed
198 if (!scheduledTask.isCancelled() && (previousPollFrequency != pollFrequency)) {
199 log.info("Re-scheduling port statistics task with frequency {} seconds", pollFrequency);
200 scheduledTask.cancel(true);
201 scheduledTask = schedulePolling();
202 }
203 }
204
Andrea Campanella945ded22016-01-07 13:17:43 -0800205 @Deactivate
206 public void deactivate() {
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200207 compCfgService.unregisterProperties(getClass(), false);
208 netCfgService.removeListener(configListener);
Andrea Campanella945ded22016-01-07 13:17:43 -0800209 providerRegistry.unregister(this);
210 providerService = null;
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200211 factories.forEach(netCfgService::unregisterConfigFactory);
Michal Machd8099742017-06-19 14:32:35 +0200212 scheduledTask.cancel(true);
Lukasz Ryba4da35c52017-06-20 09:14:11 +0200213 executor.shutdown();
Sarithabd0b1f12018-05-11 11:35:27 +0530214 devicePollTask.cancel(true);
Andrea Campanella945ded22016-01-07 13:17:43 -0800215 log.info("Stopped");
216 }
217
218 public RestDeviceProvider() {
219 super(new ProviderId(REST, PROVIDER));
220 }
221
222 @Override
223 public void triggerProbe(DeviceId deviceId) {
224 // TODO: This will be implemented later.
225 log.info("Triggering probe on device {}", deviceId);
226 }
227
228 @Override
229 public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
Sarithabd0b1f12018-05-11 11:35:27 +0530230 log.debug("Received role {} request for device {}", newRole, deviceId);
231 RestSBDevice device = controller.getDevice(deviceId);
232 if (device != null && testDeviceConnection(device)) {
233 providerService.receivedRoleReply(deviceId, newRole, newRole);
234 } else {
235 log.warn("Device not present or available {}", deviceId);
236 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.NONE);
237 }
Andrea Campanella945ded22016-01-07 13:17:43 -0800238 }
239
Andrea Campanella945ded22016-01-07 13:17:43 -0800240 @Override
241 public boolean isReachable(DeviceId deviceId) {
242 RestSBDevice restDevice = controller.getDevice(deviceId);
Andrea Campanellab38023b2019-02-11 17:21:35 +0100243 return restDevice != null && restDevice.isActive();
Sarithabd0b1f12018-05-11 11:35:27 +0530244 }
245
246 private ScheduledFuture scheduleDevicePolling() {
247 return deviceConnectionExecutor.scheduleWithFixedDelay(() -> {
248 try {
249 controller.getDevices().values().stream().forEach(restSBDevice -> {
250 DeviceId deviceId = restSBDevice.deviceId();
251 if (deviceService.getDevice(deviceId) != null) {
252 boolean connected = testDeviceConnection(restSBDevice);
253 restSBDevice.setActive(connected);
254 if (deviceService.isAvailable(deviceId) && (!connected)) {
255 providerService.deviceDisconnected(deviceId);
256 } else if (!deviceService.isAvailable(deviceId) && connected) {
257 DeviceDescription devDesc = getDesc(restSBDevice);
258 checkNotNull(devDesc, "deviceDescription cannot be null");
259 providerService.deviceConnected(
260 deviceId, mergeAnn(deviceId, devDesc));
261 }
262 }
263 }
264 );
265 } catch (Exception e) {
266 log.error("Exception at schedule Device polling", e);
267 }
268 }, 1, DEVICE_POLL_SEC, TimeUnit.SECONDS);
269 }
270
271 private DeviceDescription getDesc(RestSBDevice restSBDev) {
272 DeviceId deviceId = restSBDev.deviceId();
Andrea Campanellab38023b2019-02-11 17:21:35 +0100273
274 Driver driver = getDriver(restSBDev);
Sarithabd0b1f12018-05-11 11:35:27 +0530275
Andrea Campanella60804c02018-11-06 15:06:36 +0100276 if (restSBDev.isProxy()) {
Sarithabd0b1f12018-05-11 11:35:27 +0530277 if (driver != null && driver.hasBehaviour(DevicesDiscovery.class)) {
278
279 //Creates the driver to communicate with the server
280 DevicesDiscovery devicesDiscovery =
281 devicesDiscovery(restSBDev, driver);
282 return devicesDiscovery.deviceDetails(deviceId);
283 } else {
284 log.warn("Driver not found for {}", restSBDev);
285 return null;
Michele Santuaric372c222017-01-12 09:41:25 +0100286 }
Andrea Campanella60804c02018-11-06 15:06:36 +0100287 } else if (driver != null && driver.hasBehaviour(DeviceDescriptionDiscovery.class)) {
288 DriverHandler h = driverService.createHandler(deviceId);
289 DeviceDescriptionDiscovery deviceDiscovery = h.behaviour(DeviceDescriptionDiscovery.class);
290 return deviceDiscovery.discoverDeviceDetails();
Andrea Campanella945ded22016-01-07 13:17:43 -0800291 }
Sarithabd0b1f12018-05-11 11:35:27 +0530292 ChassisId cid = new ChassisId();
293 String ipAddress = restSBDev.ip().toString();
294 SparseAnnotations annotations = DefaultAnnotations.builder()
295 .set(IPADDRESS, ipAddress)
296 .set(AnnotationKeys.PROTOCOL, REST.toUpperCase())
297 .build();
298 String manufacturer = UNKNOWN;
299 String hwVersion = UNKNOWN;
300 String swVersion = UNKNOWN;
301 String serialNumber = UNKNOWN;
302
303 Device device = deviceService.getDevice(deviceId);
304 if (device != null) {
305 manufacturer = device.manufacturer();
306 hwVersion = device.hwVersion();
307 swVersion = device.swVersion();
308 serialNumber = device.serialNumber();
309 }
310
311 return new DefaultDeviceDescription(
312 deviceId.uri(),
313 Device.Type.SWITCH,
314 manufacturer, hwVersion,
315 swVersion, serialNumber,
316 cid,
317 annotations);
Andrea Campanella945ded22016-01-07 13:17:43 -0800318 }
319
Michele Santuaric372c222017-01-12 09:41:25 +0100320 private void deviceAdded(RestSBDevice restSBDev) {
Andrea Campanellab38023b2019-02-11 17:21:35 +0100321 checkNotNull(restSBDev, DEVICENULL);
Michele Santuaric372c222017-01-12 09:41:25 +0100322
Andrea Campanellab38023b2019-02-11 17:21:35 +0100323 Driver driver = getDriver(restSBDev);
Georgios Katsikas15841e22018-07-28 14:27:28 +0200324
325 // Check if the server is controlling a single or multiple devices
Michele Santuaric372c222017-01-12 09:41:25 +0100326 if (restSBDev.isProxy()) {
Andrea Campanellab38023b2019-02-11 17:21:35 +0100327 if (driver.hasBehaviour(DevicesDiscovery.class)) {
Georgios Katsikas15841e22018-07-28 14:27:28 +0200328 DevicesDiscovery devicesDiscovery = devicesDiscovery(restSBDev, driver);
Michele Santuaric372c222017-01-12 09:41:25 +0100329 Set<DeviceId> deviceIds = devicesDiscovery.deviceIds();
330 restSBDev.setActive(true);
Michal Mach67acb692017-06-21 12:05:36 +0200331 deviceIds.forEach(deviceId -> {
Michele Santuaric372c222017-01-12 09:41:25 +0100332 controller.addProxiedDevice(deviceId, restSBDev);
Georgios Katsikas15841e22018-07-28 14:27:28 +0200333 DeviceDescription devDesc = devicesDiscovery.deviceDetails(deviceId);
334 checkNotNull(devDesc, "DeviceDescription cannot be null");
335 providerService.deviceConnected(deviceId, mergeAnn(restSBDev.deviceId(), devDesc));
Michele Santuaric372c222017-01-12 09:41:25 +0100336
337 if (driver.hasBehaviour(DeviceDescriptionDiscovery.class)) {
338 DriverHandler h = driverService.createHandler(deviceId);
Georgios Katsikas15841e22018-07-28 14:27:28 +0200339 DeviceDescriptionDiscovery devDisc = h.behaviour(DeviceDescriptionDiscovery.class);
340 providerService.updatePorts(deviceId, devDisc.discoverPortDetails());
Michele Santuaric372c222017-01-12 09:41:25 +0100341 }
342
343 checkAndUpdateDevice(deviceId);
Michele Santuaric372c222017-01-12 09:41:25 +0100344 });
345 } else {
Andrea Campanellab38023b2019-02-11 17:21:35 +0100346 log.warn("Device is proxy but driver does not have proxy discovery behaviour {}", restSBDev);
Michele Santuaric372c222017-01-12 09:41:25 +0100347 }
348 } else {
349 DeviceId deviceId = restSBDev.deviceId();
Sarithabd0b1f12018-05-11 11:35:27 +0530350
Georgios Katsikas15841e22018-07-28 14:27:28 +0200351 if (driver != null && driver.hasBehaviour(DevicesDiscovery.class)) {
352 restSBDev.setActive(true);
353 DevicesDiscovery devicesDiscovery = devicesDiscovery(restSBDev, driver);
354 DeviceDescription deviceDescription = devicesDiscovery.deviceDetails(deviceId);
355 checkNotNull(deviceDescription, "DeviceDescription cannot be null");
356 providerService.deviceConnected(deviceId, deviceDescription);
357
358 if (driver.hasBehaviour(DeviceDescriptionDiscovery.class)) {
359 DriverHandler h = driverService.createHandler(deviceId);
360 DeviceDescriptionDiscovery deviceDiscovery = h.behaviour(DeviceDescriptionDiscovery.class);
361 providerService.updatePorts(deviceId, deviceDiscovery.discoverPortDetails());
362 }
363 } else {
Sarithabd0b1f12018-05-11 11:35:27 +0530364 DeviceDescription deviceDescription = getDesc(restSBDev);
Georgios Katsikas15841e22018-07-28 14:27:28 +0200365 restSBDev.setActive(true);
366 providerService.deviceConnected(deviceId, deviceDescription);
367 }
Michele Santuaric372c222017-01-12 09:41:25 +0100368 checkAndUpdateDevice(deviceId);
Michele Santuaric372c222017-01-12 09:41:25 +0100369 }
370 }
371
Andrea Campanellab38023b2019-02-11 17:21:35 +0100372 private Driver getDriver(RestSBDevice restSBDev) {
373 String driverName = netCfgService.getConfig(restSBDev.deviceId(), BasicDeviceConfig.class).driver();
374
375 Driver driver = driverService.getDriver(driverName);
376
377 if (driver == null) {
378 driver = driverService.getDriver(restSBDev.manufacturer().get(),
379 restSBDev.hwVersion().get(),
380 restSBDev.swVersion().get());
381 }
382
383 checkNotNull(driver, DRIVERNULL);
384 return driver;
385 }
386
Michele Santuaric372c222017-01-12 09:41:25 +0100387 private DefaultDeviceDescription mergeAnn(DeviceId devId, DeviceDescription desc) {
388 return new DefaultDeviceDescription(
389 desc,
390 DefaultAnnotations.merge(
391 DefaultAnnotations.builder()
392 .set(AnnotationKeys.PROTOCOL, REST.toUpperCase())
393 // The rest server added as annotation to the device
394 .set(AnnotationKeys.REST_SERVER, devId.toString())
395 .build(),
396 desc.annotations()));
397 }
398
399 private DevicesDiscovery devicesDiscovery(RestSBDevice restSBDevice, Driver driver) {
400 DriverData driverData = new DefaultDriverData(driver, restSBDevice.deviceId());
Georgios Katsikas15841e22018-07-28 14:27:28 +0200401 DevicesDiscovery devicesDiscovery = driver.createBehaviour(driverData, DevicesDiscovery.class);
Michele Santuaric372c222017-01-12 09:41:25 +0100402 devicesDiscovery.setHandler(new DefaultDriverHandler(driverData));
403 return devicesDiscovery;
Andrea Campanella945ded22016-01-07 13:17:43 -0800404 }
405
Konstantinos Kanonakis3cd85552016-11-17 10:11:13 -0600406 private void checkAndUpdateDevice(DeviceId deviceId) {
407 if (deviceService.getDevice(deviceId) == null) {
Georgios Katsikas15841e22018-07-28 14:27:28 +0200408 log.warn("Device {} has not been added to store, maybe due to a problem in connectivity", deviceId);
Konstantinos Kanonakis3cd85552016-11-17 10:11:13 -0600409 } else {
410 boolean isReachable = isReachable(deviceId);
411 if (isReachable && deviceService.isAvailable(deviceId)) {
412 Device device = deviceService.getDevice(deviceId);
413 if (device.is(DeviceDescriptionDiscovery.class)) {
Michele Santuaric372c222017-01-12 09:41:25 +0100414 DeviceDescriptionDiscovery deviceDescriptionDiscovery =
415 device.as(DeviceDescriptionDiscovery.class);
416 DeviceDescription updatedDeviceDescription =
417 deviceDescriptionDiscovery.discoverDeviceDetails();
418 if (updatedDeviceDescription != null &&
419 !descriptionEquals(device, updatedDeviceDescription)) {
420 providerService.deviceConnected(
421 deviceId,
422 new DefaultDeviceDescription(
423 updatedDeviceDescription, true,
424 updatedDeviceDescription.annotations()));
Konstantinos Kanonakis3cd85552016-11-17 10:11:13 -0600425 //if ports are not discovered, retry the discovery
426 if (deviceService.getPorts(deviceId).isEmpty()) {
427 discoverPorts(deviceId);
428 }
429 }
430 } else {
431 log.warn("No DeviceDescriptionDiscovery behaviour for device {}", deviceId);
432 }
433 } else if (!isReachable && deviceService.isAvailable(deviceId)) {
434 providerService.deviceDisconnected(deviceId);
435 }
436 }
437 }
438
439 private boolean descriptionEquals(Device device, DeviceDescription updatedDeviceDescription) {
Michele Santuarid2c8f212017-01-09 18:23:45 +0100440 return Objects.equal(device.id().uri(), updatedDeviceDescription.deviceUri())
Konstantinos Kanonakis3cd85552016-11-17 10:11:13 -0600441 && Objects.equal(device.type(), updatedDeviceDescription.type())
442 && Objects.equal(device.manufacturer(), updatedDeviceDescription.manufacturer())
443 && Objects.equal(device.hwVersion(), updatedDeviceDescription.hwVersion())
444 && Objects.equal(device.swVersion(), updatedDeviceDescription.swVersion())
445 && Objects.equal(device.serialNumber(), updatedDeviceDescription.serialNumber())
446 && Objects.equal(device.chassisId(), updatedDeviceDescription.chassisId())
447 && Objects.equal(device.annotations(), updatedDeviceDescription.annotations());
448 }
449
Andrea Campanella86294db2016-03-07 11:42:49 -0800450 private void deviceRemoved(DeviceId deviceId) {
Andrea Campanellab38023b2019-02-11 17:21:35 +0100451 checkNotNull(deviceId, DEVICENULL);
Sarithabd0b1f12018-05-11 11:35:27 +0530452 log.debug("Device removed called for {}", deviceId);
Andrea Campanella945ded22016-01-07 13:17:43 -0800453 providerService.deviceDisconnected(deviceId);
Michal Mach67acb692017-06-21 12:05:36 +0200454 controller.getProxiedDevices(deviceId).forEach(device -> {
Michele Santuaric372c222017-01-12 09:41:25 +0100455 controller.removeProxiedDevice(device);
456 providerService.deviceDisconnected(device);
457 });
Andrea Campanella86294db2016-03-07 11:42:49 -0800458 controller.removeDevice(deviceId);
Andrea Campanella945ded22016-01-07 13:17:43 -0800459 }
460
Andrea Campanella59b549d2017-04-14 21:58:16 +0200461 //Method to connect devices provided via net-cfg under devices/ tree
462 private void createAndConnectDevices() {
463 Set<DeviceId> deviceSubjects =
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200464 netCfgService.getSubjects(DeviceId.class, RestDeviceConfig.class);
Sarithabd0b1f12018-05-11 11:35:27 +0530465 log.debug("Connecting and configuring devices with received configuration:{}",
466 deviceSubjects);
Andrea Campanella59b549d2017-04-14 21:58:16 +0200467 connectDevices(deviceSubjects.stream()
fahadnaeemkhan02ffa712017-12-01 19:49:45 -0800468 .filter(deviceId -> deviceService.getDevice(deviceId) == null)
469 .map(deviceId -> {
470 RestDeviceConfig config =
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200471 netCfgService.getConfig(deviceId, RestDeviceConfig.class);
Yuta HIGUCHIfc667052017-12-05 18:17:22 -0800472 return toInactiveRestSBDevice(config);
fahadnaeemkhan02ffa712017-12-01 19:49:45 -0800473 }).collect(Collectors.toSet()));
Andrea Campanella59b549d2017-04-14 21:58:16 +0200474 }
475
Yuta HIGUCHIfc667052017-12-05 18:17:22 -0800476 private RestSBDevice toInactiveRestSBDevice(RestDeviceConfig config) {
477 return new DefaultRestSBDevice(config.ip(),
478 config.port(),
479 config.username(),
480 config.password(),
481 config.protocol(),
482 config.url(),
483 false,
484 config.testUrl(),
485 config.manufacturer(),
486 config.hwVersion(),
487 config.swVersion(),
Georgios Katsikas15841e22018-07-28 14:27:28 +0200488 config.isProxy(),
Yuta HIGUCHIfc667052017-12-05 18:17:22 -0800489 config.authenticationScheme(),
490 config.token()
491 );
492 }
493
Andrea Campanella59b549d2017-04-14 21:58:16 +0200494 private void connectDevices(Set<RestSBDevice> devices) {
495 //Precomputing the devices to be removed
496 Set<RestSBDevice> toBeRemoved = new HashSet<>(controller.getDevices().values());
497 toBeRemoved.removeAll(devices);
498 //Adding new devices
499 devices.stream()
500 .filter(device -> {
501 device.setActive(false);
502 controller.addDevice(device);
503 return testDeviceConnection(device);
504 })
Michal Mach67acb692017-06-21 12:05:36 +0200505 .forEach(this::deviceAdded);
Andrea Campanella59b549d2017-04-14 21:58:16 +0200506 //Removing devices not wanted anymore
507 toBeRemoved.forEach(device -> deviceRemoved(device.deviceId()));
508 }
509
Yuta HIGUCHIfc667052017-12-05 18:17:22 -0800510 private void connectDevice(RestSBDevice device) {
511 // TODO borrowed from above,
512 // not sure why setting it to inactive
513 device.setActive(false);
514 controller.addDevice(device);
515 if (testDeviceConnection(device)) {
516 deviceAdded(device);
517 }
518 }
519
Michal Machd8099742017-06-19 14:32:35 +0200520 private ScheduledFuture schedulePolling() {
521 return portStatisticsExecutor.scheduleAtFixedRate(this::executePortStatisticsUpdate,
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200522 pollFrequency / 2, pollFrequency,
Michal Machd8099742017-06-19 14:32:35 +0200523 TimeUnit.SECONDS);
524 }
525
526 private void executePortStatisticsUpdate() {
527 controller.getDevices().keySet().forEach(this::updatePortStatistics);
528 }
529
530 private void updatePortStatistics(DeviceId deviceId) {
531 Device device = deviceService.getDevice(deviceId);
532 checkNotNull(device, "device cannot be null");
533
534 if (device.is(PortStatisticsDiscovery.class)) {
535 PortStatisticsDiscovery portStatisticsDiscovery = device.as(PortStatisticsDiscovery.class);
536 Collection<PortStatistics> portStatistics = portStatisticsDiscovery.discoverPortStatistics();
537 if (portStatistics != null && !portStatistics.isEmpty()) {
538 providerService.updatePortStatistics(deviceId, portStatistics);
539 }
540 } else {
541 log.debug("No port statistics getter behaviour for device {}", deviceId);
542 }
543 }
544
Andrea Campanella6c71a052016-04-22 11:56:31 -0700545 private void discoverPorts(DeviceId deviceId) {
546 Device device = deviceService.getDevice(deviceId);
Georgios Katsikas15841e22018-07-28 14:27:28 +0200547 DeviceDescriptionDiscovery deviceDescriptionDiscovery = device.as(DeviceDescriptionDiscovery.class);
Ray Milkey640fedd2018-02-08 09:02:26 -0800548 providerService.updatePorts(deviceId, deviceDescriptionDiscovery.discoverPortDetails());
Andrea Campanella6c71a052016-04-22 11:56:31 -0700549 }
550
Michele Santuaric372c222017-01-12 09:41:25 +0100551 private boolean testDeviceConnection(RestSBDevice dev) {
Andrea Campanella945ded22016-01-07 13:17:43 -0800552 try {
Palash Kala4c71ee72017-05-24 17:43:59 +0900553 Callable<Boolean> connectionSuccess;
554
Michele Santuaric372c222017-01-12 09:41:25 +0100555 if (dev.testUrl().isPresent()) {
Michal Mach67acb692017-06-21 12:05:36 +0200556 connectionSuccess = () ->
557 controller.get(dev.deviceId(), dev.testUrl().get(), MediaType.APPLICATION_JSON_TYPE) != null;
Palash Kala4c71ee72017-05-24 17:43:59 +0900558 } else {
Michal Mach67acb692017-06-21 12:05:36 +0200559 connectionSuccess = () ->
560 controller.get(dev.deviceId(), "", MediaType.APPLICATION_JSON_TYPE) != null;
Michele Santuaric372c222017-01-12 09:41:25 +0100561 }
Palash Kala4c71ee72017-05-24 17:43:59 +0900562
563 Future<Boolean> future = executor.submit(connectionSuccess);
564 try {
Michal Mach67acb692017-06-21 12:05:36 +0200565 return future.get(REST_TIMEOUT_SEC, TimeUnit.SECONDS);
Palash Kala4c71ee72017-05-24 17:43:59 +0900566 } catch (TimeoutException ex) {
Sean Condone45e12c2018-05-04 16:27:07 +0100567 log.warn("Connection to device {} timed out: {}", dev.deviceId(), ex.getMessage());
Palash Kala4c71ee72017-05-24 17:43:59 +0900568 return false;
569 } catch (InterruptedException ex) {
Sean Condone45e12c2018-05-04 16:27:07 +0100570 log.warn("Connection to device {} interrupted: {}", dev.deviceId(), ex.getMessage());
Ray Milkey5c7d4882018-02-05 14:50:39 -0800571 Thread.currentThread().interrupt();
Palash Kala4c71ee72017-05-24 17:43:59 +0900572 return false;
573 } catch (ExecutionException ex) {
Sean Condone45e12c2018-05-04 16:27:07 +0100574 log.warn("Connection to device {} had an execution exception.", dev.deviceId(), ex);
Palash Kala4c71ee72017-05-24 17:43:59 +0900575 return false;
576 }
Michele Santuaric372c222017-01-12 09:41:25 +0100577
Andrea Campanellac6ecc632016-03-10 17:57:06 -0800578 } catch (ProcessingException e) {
Michele Santuaric372c222017-01-12 09:41:25 +0100579 log.warn("Cannot connect to device {}", dev, e);
Andrea Campanella945ded22016-01-07 13:17:43 -0800580 }
581 return false;
582 }
583
584 private class InternalNetworkConfigListener implements NetworkConfigListener {
Andrea Campanella945ded22016-01-07 13:17:43 -0800585 @Override
586 public void event(NetworkConfigEvent event) {
Georgios Katsikas15841e22018-07-28 14:27:28 +0200587 if (!isRelevant(event)) {
588 log.warn("Irrelevant network configuration event: {}", event);
589 return;
590 }
591
Yuta HIGUCHIfc667052017-12-05 18:17:22 -0800592 ExecutorService bg = SharedExecutors.getSingleThreadExecutor();
593 if (event.type() == CONFIG_REMOVED) {
Sarithabd0b1f12018-05-11 11:35:27 +0530594 log.debug("Config {} event for rest device provider for {}",
595 event.type(), event.prevConfig().get().subject());
Yuta HIGUCHIfc667052017-12-05 18:17:22 -0800596 DeviceId did = (DeviceId) event.subject();
597 bg.execute(() -> deviceRemoved(did));
598 } else {
Sarithabd0b1f12018-05-11 11:35:27 +0530599 //CONFIG_ADDED or CONFIG_UPDATED
600 log.debug("Config {} event for rest device provider for {}",
601 event.type(), event.config().get().subject());
Yuta HIGUCHIfc667052017-12-05 18:17:22 -0800602 RestDeviceConfig cfg = (RestDeviceConfig) event.config().get();
603 RestSBDevice restSBDevice = toInactiveRestSBDevice(cfg);
604 bg.execute(() -> connectDevice(restSBDevice));
605 }
Andrea Campanella945ded22016-01-07 13:17:43 -0800606 }
607
608 @Override
609 public boolean isRelevant(NetworkConfigEvent event) {
Yuta HIGUCHI872fdbe2017-12-05 15:34:28 -0800610 return event.configClass().equals(RestDeviceConfig.class) &&
Andrea Campanella945ded22016-01-07 13:17:43 -0800611 (event.type() == CONFIG_ADDED ||
Yuta HIGUCHIfc667052017-12-05 18:17:22 -0800612 event.type() == CONFIG_UPDATED ||
613 event.type() == CONFIG_REMOVED);
Andrea Campanella945ded22016-01-07 13:17:43 -0800614 }
615 }
Saurav Dasa2d37502016-03-25 17:50:40 -0700616
617 @Override
618 public void changePortState(DeviceId deviceId, PortNumber portNumber,
619 boolean enable) {
fahadnaeemkhan482951f2017-08-24 16:35:17 -0700620 Device device = deviceService.getDevice(deviceId);
621 if (device != null) {
622 if (device.is(PortAdmin.class)) {
623 PortAdmin portAdmin = device.as(PortAdmin.class);
624 CompletableFuture<Boolean> modified;
625 if (enable) {
626 modified = portAdmin.enable(portNumber);
627 } else {
628 modified = portAdmin.disable(portNumber);
629 }
630 modified.thenAcceptAsync(result -> {
631 if (!result) {
632 log.warn("Device {} port {} state can't be changed to {}",
633 deviceId, portNumber, enable);
634 }
635 });
636
637 } else {
638 log.warn("Device {} does not support PortAdmin behavior", deviceId);
639 }
640 } else {
641 log.warn("unable to get the device {}, port {} state can't be changed to {}",
642 deviceId, portNumber, enable);
643 }
Saurav Dasa2d37502016-03-25 17:50:40 -0700644 }
Andrea Campanella945ded22016-01-07 13:17:43 -0800645}