blob: a302203bea399154e2fc3859f09cd4af30b234e5 [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 Campanella59b549d2017-04-14 21:58:16 +020042import org.onosproject.net.config.basics.SubjectFactories;
Andrea Campanella945ded22016-01-07 13:17:43 -080043import org.onosproject.net.device.DefaultDeviceDescription;
44import org.onosproject.net.device.DeviceDescription;
Andrea Campanella6c71a052016-04-22 11:56:31 -070045import org.onosproject.net.device.DeviceDescriptionDiscovery;
Andrea Campanella945ded22016-01-07 13:17:43 -080046import org.onosproject.net.device.DeviceProvider;
47import org.onosproject.net.device.DeviceProviderRegistry;
48import org.onosproject.net.device.DeviceProviderService;
Andrea Campanella6c71a052016-04-22 11:56:31 -070049import org.onosproject.net.device.DeviceService;
Michal Machd8099742017-06-19 14:32:35 +020050import org.onosproject.net.device.PortStatistics;
51import org.onosproject.net.device.PortStatisticsDiscovery;
Michele Santuaric372c222017-01-12 09:41:25 +010052import org.onosproject.net.driver.DefaultDriverData;
53import org.onosproject.net.driver.DefaultDriverHandler;
54import org.onosproject.net.driver.Driver;
55import org.onosproject.net.driver.DriverData;
56import org.onosproject.net.driver.DriverHandler;
57import org.onosproject.net.driver.DriverService;
Andrea Campanella945ded22016-01-07 13:17:43 -080058import org.onosproject.net.provider.AbstractProvider;
59import org.onosproject.net.provider.ProviderId;
Andrea Campanella59b549d2017-04-14 21:58:16 +020060import org.onosproject.protocol.rest.DefaultRestSBDevice;
Andrea Campanella945ded22016-01-07 13:17:43 -080061import org.onosproject.protocol.rest.RestSBController;
62import org.onosproject.protocol.rest.RestSBDevice;
Georgios Katsikas0f12bc62018-07-06 09:06:42 +020063import org.osgi.service.component.ComponentContext;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070064import org.osgi.service.component.annotations.Activate;
65import org.osgi.service.component.annotations.Component;
66import org.osgi.service.component.annotations.Deactivate;
67import org.osgi.service.component.annotations.Modified;
68import org.osgi.service.component.annotations.Reference;
69import org.osgi.service.component.annotations.ReferenceCardinality;
Andrea Campanella945ded22016-01-07 13:17:43 -080070import org.slf4j.Logger;
71
Andrea Campanellac6ecc632016-03-10 17:57:06 -080072import javax.ws.rs.ProcessingException;
Michal Mach67acb692017-06-21 12:05:36 +020073import javax.ws.rs.core.MediaType;
Michal Machd8099742017-06-19 14:32:35 +020074import java.util.Collection;
Georgios Katsikas0f12bc62018-07-06 09:06:42 +020075import java.util.Dictionary;
Andrea Campanella945ded22016-01-07 13:17:43 -080076import java.util.HashSet;
Andrea Campanella59b549d2017-04-14 21:58:16 +020077import java.util.List;
Andrea Campanella945ded22016-01-07 13:17:43 -080078import java.util.Set;
Palash Kala4c71ee72017-05-24 17:43:59 +090079import java.util.concurrent.Callable;
fahadnaeemkhan02ffa712017-12-01 19:49:45 -080080import java.util.concurrent.CompletableFuture;
Palash Kala4c71ee72017-05-24 17:43:59 +090081import java.util.concurrent.ExecutionException;
Andrea Campanella784ee0f2016-02-17 15:50:59 -080082import java.util.concurrent.ExecutorService;
83import java.util.concurrent.Executors;
Palash Kala4c71ee72017-05-24 17:43:59 +090084import java.util.concurrent.Future;
Michal Machd8099742017-06-19 14:32:35 +020085import java.util.concurrent.ScheduledFuture;
Palash Kala4c71ee72017-05-24 17:43:59 +090086import java.util.concurrent.TimeUnit;
87import java.util.concurrent.TimeoutException;
Andrea Campanella59b549d2017-04-14 21:58:16 +020088import java.util.stream.Collectors;
Andrea Campanella945ded22016-01-07 13:17:43 -080089
Michele Santuaric372c222017-01-12 09:41:25 +010090import static com.google.common.base.Preconditions.checkNotNull;
Andrea Campanella784ee0f2016-02-17 15:50:59 -080091import static org.onlab.util.Tools.groupedThreads;
Andrea Campanella945ded22016-01-07 13:17:43 -080092import static org.onosproject.net.config.NetworkConfigEvent.Type.CONFIG_ADDED;
Yuta HIGUCHIfc667052017-12-05 18:17:22 -080093import static org.onosproject.net.config.NetworkConfigEvent.Type.CONFIG_REMOVED;
Andrea Campanella945ded22016-01-07 13:17:43 -080094import static org.onosproject.net.config.NetworkConfigEvent.Type.CONFIG_UPDATED;
Thomas Vachuska4167c3f2018-10-16 07:16:31 -070095import static org.onosproject.provider.rest.device.impl.OsgiPropertyConstants.POLL_FREQUENCY;
96import static org.onosproject.provider.rest.device.impl.OsgiPropertyConstants.POLL_FREQUENCY_DEFAULT;
Andrea Campanella945ded22016-01-07 13:17:43 -080097import static org.slf4j.LoggerFactory.getLogger;
98
99/**
100 * Provider for devices that use REST as means of configuration communication.
101 */
Thomas Vachuska4167c3f2018-10-16 07:16:31 -0700102@Component(immediate = true, service = DeviceProvider.class,
103 property = {
104 POLL_FREQUENCY + ":Integer=" + POLL_FREQUENCY_DEFAULT,
105 })
Andrea Campanella945ded22016-01-07 13:17:43 -0800106public class RestDeviceProvider extends AbstractProvider
107 implements DeviceProvider {
108 private static final String APP_NAME = "org.onosproject.restsb";
Andrea Campanella59b549d2017-04-14 21:58:16 +0200109 protected static final String REST = "rest";
Andrea Campanella945ded22016-01-07 13:17:43 -0800110 private static final String PROVIDER = "org.onosproject.provider.rest.device";
111 private static final String IPADDRESS = "ipaddress";
Michal Mach67acb692017-06-21 12:05:36 +0200112 private static final String ISNOTNULL = "Rest device is not null";
Michele Santuaric372c222017-01-12 09:41:25 +0100113 private static final String UNKNOWN = "unknown";
Palash Kala4c71ee72017-05-24 17:43:59 +0900114 private static final int REST_TIMEOUT_SEC = 5;
Georgios Katsikas6a4d1662017-07-27 13:22:31 +0200115 private static final int EXECUTOR_THREAD_POOL_SIZE = 8;
Sarithabd0b1f12018-05-11 11:35:27 +0530116 private static final int DEVICE_POLL_SEC = 30;
Andrea Campanella945ded22016-01-07 13:17:43 -0800117 private final Logger log = getLogger(getClass());
118
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700119 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanella945ded22016-01-07 13:17:43 -0800120 protected DeviceProviderRegistry providerRegistry;
121
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700122 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanella945ded22016-01-07 13:17:43 -0800123 protected RestSBController controller;
124
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700125 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200126 protected NetworkConfigRegistry netCfgService;
127
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700128 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200129 protected ComponentConfigService compCfgService;
Andrea Campanella945ded22016-01-07 13:17:43 -0800130
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700131 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanella945ded22016-01-07 13:17:43 -0800132 protected CoreService coreService;
133
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700134 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanella6c71a052016-04-22 11:56:31 -0700135 protected DeviceService deviceService;
Andrea Campanellad8d92db2016-01-14 16:24:41 -0800136
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700137 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Michele Santuaric372c222017-01-12 09:41:25 +0100138 protected DriverService driverService;
139
Thomas Vachuska00b5d4f2018-10-30 15:13:20 -0700140 /** Configure poll frequency for port status and statistics; default is 30 seconds. */
Thomas Vachuska4167c3f2018-10-16 07:16:31 -0700141 private int pollFrequency = POLL_FREQUENCY_DEFAULT;
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200142
Andrea Campanella945ded22016-01-07 13:17:43 -0800143 private DeviceProviderService providerService;
Michele Santuaric372c222017-01-12 09:41:25 +0100144 private ApplicationId appId;
Andrea Campanella945ded22016-01-07 13:17:43 -0800145
Michal Mach13072e22017-06-21 09:12:24 +0200146 private ExecutorService executor;
Michal Mach67acb692017-06-21 12:05:36 +0200147 private final SharedScheduledExecutorService portStatisticsExecutor =
148 SharedScheduledExecutors.getPoolThreadExecutor();
Andrea Campanella784ee0f2016-02-17 15:50:59 -0800149
Sarithabd0b1f12018-05-11 11:35:27 +0530150 private final SharedScheduledExecutorService deviceConnectionExecutor =
151 SharedScheduledExecutors.getPoolThreadExecutor();
152 private ScheduledFuture<?> devicePollTask;
Michal Mach67acb692017-06-21 12:05:36 +0200153 private final List<ConfigFactory> factories = ImmutableList.of(
Andrea Campanella59b549d2017-04-14 21:58:16 +0200154 new ConfigFactory<DeviceId, RestDeviceConfig>(SubjectFactories.DEVICE_SUBJECT_FACTORY,
155 RestDeviceConfig.class,
156 REST) {
157 @Override
158 public RestDeviceConfig createConfig() {
159 return new RestDeviceConfig();
160 }
161 });
162
Michal Mach67acb692017-06-21 12:05:36 +0200163 private final NetworkConfigListener configListener = new InternalNetworkConfigListener();
Andrea Campanella945ded22016-01-07 13:17:43 -0800164
Michal Machd8099742017-06-19 14:32:35 +0200165 private ScheduledFuture<?> scheduledTask;
Andrea Campanella784ee0f2016-02-17 15:50:59 -0800166
Andrea Campanella945ded22016-01-07 13:17:43 -0800167
168 @Activate
169 public void activate() {
170 appId = coreService.registerApplication(APP_NAME);
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200171 compCfgService.registerProperties(getClass());
Andrea Campanella945ded22016-01-07 13:17:43 -0800172 providerService = providerRegistry.register(this);
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200173 factories.forEach(netCfgService::registerConfigFactory);
Georgios Katsikas6a4d1662017-07-27 13:22:31 +0200174 executor = Executors.newFixedThreadPool(
175 EXECUTOR_THREAD_POOL_SIZE, groupedThreads("onos/restsbprovider", "device-installer-%d", log)
176 );
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200177 netCfgService.addListener(configListener);
Andrea Campanella59b549d2017-04-14 21:58:16 +0200178 executor.execute(RestDeviceProvider.this::createAndConnectDevices);
Michal Machd8099742017-06-19 14:32:35 +0200179 scheduledTask = schedulePolling();
Sarithabd0b1f12018-05-11 11:35:27 +0530180 devicePollTask = scheduleDevicePolling();
Andrea Campanella945ded22016-01-07 13:17:43 -0800181 log.info("Started");
182 }
183
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200184 @Modified
185 public void modified(ComponentContext context) {
186 int previousPollFrequency = pollFrequency;
187
188 if (context != null) {
189 Dictionary<?, ?> properties = context.getProperties();
190 pollFrequency = Tools.getIntegerProperty(properties, POLL_FREQUENCY,
Thomas Vachuska4167c3f2018-10-16 07:16:31 -0700191 POLL_FREQUENCY_DEFAULT);
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200192 log.info("Configured. Poll frequency is configured to {} seconds", pollFrequency);
193 }
194
195 // Re-schedule only if frequency has changed
196 if (!scheduledTask.isCancelled() && (previousPollFrequency != pollFrequency)) {
197 log.info("Re-scheduling port statistics task with frequency {} seconds", pollFrequency);
198 scheduledTask.cancel(true);
199 scheduledTask = schedulePolling();
200 }
201 }
202
Andrea Campanella945ded22016-01-07 13:17:43 -0800203 @Deactivate
204 public void deactivate() {
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200205 compCfgService.unregisterProperties(getClass(), false);
206 netCfgService.removeListener(configListener);
Andrea Campanella945ded22016-01-07 13:17:43 -0800207 providerRegistry.unregister(this);
208 providerService = null;
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200209 factories.forEach(netCfgService::unregisterConfigFactory);
Michal Machd8099742017-06-19 14:32:35 +0200210 scheduledTask.cancel(true);
Lukasz Ryba4da35c52017-06-20 09:14:11 +0200211 executor.shutdown();
Sarithabd0b1f12018-05-11 11:35:27 +0530212 devicePollTask.cancel(true);
Andrea Campanella945ded22016-01-07 13:17:43 -0800213 log.info("Stopped");
214 }
215
216 public RestDeviceProvider() {
217 super(new ProviderId(REST, PROVIDER));
218 }
219
220 @Override
221 public void triggerProbe(DeviceId deviceId) {
222 // TODO: This will be implemented later.
223 log.info("Triggering probe on device {}", deviceId);
224 }
225
226 @Override
227 public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
Sarithabd0b1f12018-05-11 11:35:27 +0530228 log.debug("Received role {} request for device {}", newRole, deviceId);
229 RestSBDevice device = controller.getDevice(deviceId);
230 if (device != null && testDeviceConnection(device)) {
231 providerService.receivedRoleReply(deviceId, newRole, newRole);
232 } else {
233 log.warn("Device not present or available {}", deviceId);
234 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.NONE);
235 }
Andrea Campanella945ded22016-01-07 13:17:43 -0800236 }
237
Andrea Campanella945ded22016-01-07 13:17:43 -0800238 @Override
239 public boolean isReachable(DeviceId deviceId) {
240 RestSBDevice restDevice = controller.getDevice(deviceId);
Sarithabd0b1f12018-05-11 11:35:27 +0530241 return restDevice != null ? restDevice.isActive() : false;
242 }
243
244 private ScheduledFuture scheduleDevicePolling() {
245 return deviceConnectionExecutor.scheduleWithFixedDelay(() -> {
246 try {
247 controller.getDevices().values().stream().forEach(restSBDevice -> {
248 DeviceId deviceId = restSBDevice.deviceId();
249 if (deviceService.getDevice(deviceId) != null) {
250 boolean connected = testDeviceConnection(restSBDevice);
251 restSBDevice.setActive(connected);
252 if (deviceService.isAvailable(deviceId) && (!connected)) {
253 providerService.deviceDisconnected(deviceId);
254 } else if (!deviceService.isAvailable(deviceId) && connected) {
255 DeviceDescription devDesc = getDesc(restSBDevice);
256 checkNotNull(devDesc, "deviceDescription cannot be null");
257 providerService.deviceConnected(
258 deviceId, mergeAnn(deviceId, devDesc));
259 }
260 }
261 }
262 );
263 } catch (Exception e) {
264 log.error("Exception at schedule Device polling", e);
265 }
266 }, 1, DEVICE_POLL_SEC, TimeUnit.SECONDS);
267 }
268
269 private DeviceDescription getDesc(RestSBDevice restSBDev) {
270 DeviceId deviceId = restSBDev.deviceId();
271 if (restSBDev.isProxy()) {
272 Driver driver = driverService.getDriver(restSBDev.manufacturer().get(),
273 restSBDev.hwVersion().get(),
274 restSBDev.swVersion().get());
275
276 if (driver != null && driver.hasBehaviour(DevicesDiscovery.class)) {
277
278 //Creates the driver to communicate with the server
279 DevicesDiscovery devicesDiscovery =
280 devicesDiscovery(restSBDev, driver);
281 return devicesDiscovery.deviceDetails(deviceId);
282 } else {
283 log.warn("Driver not found for {}", restSBDev);
284 return null;
Michele Santuaric372c222017-01-12 09:41:25 +0100285 }
Andrea Campanella945ded22016-01-07 13:17:43 -0800286 }
Sarithabd0b1f12018-05-11 11:35:27 +0530287 ChassisId cid = new ChassisId();
288 String ipAddress = restSBDev.ip().toString();
289 SparseAnnotations annotations = DefaultAnnotations.builder()
290 .set(IPADDRESS, ipAddress)
291 .set(AnnotationKeys.PROTOCOL, REST.toUpperCase())
292 .build();
293 String manufacturer = UNKNOWN;
294 String hwVersion = UNKNOWN;
295 String swVersion = UNKNOWN;
296 String serialNumber = UNKNOWN;
297
298 Device device = deviceService.getDevice(deviceId);
299 if (device != null) {
300 manufacturer = device.manufacturer();
301 hwVersion = device.hwVersion();
302 swVersion = device.swVersion();
303 serialNumber = device.serialNumber();
304 }
305
306 return new DefaultDeviceDescription(
307 deviceId.uri(),
308 Device.Type.SWITCH,
309 manufacturer, hwVersion,
310 swVersion, serialNumber,
311 cid,
312 annotations);
Andrea Campanella945ded22016-01-07 13:17:43 -0800313 }
314
Michele Santuaric372c222017-01-12 09:41:25 +0100315 private void deviceAdded(RestSBDevice restSBDev) {
316 checkNotNull(restSBDev, ISNOTNULL);
317
Georgios Katsikas15841e22018-07-28 14:27:28 +0200318 Driver driver = driverService.getDriver(restSBDev.manufacturer().get(),
319 restSBDev.hwVersion().get(),
320 restSBDev.swVersion().get());
321
322 // Check if the server is controlling a single or multiple devices
Michele Santuaric372c222017-01-12 09:41:25 +0100323 if (restSBDev.isProxy()) {
Michele Santuaric372c222017-01-12 09:41:25 +0100324 if (driver != null && driver.hasBehaviour(DevicesDiscovery.class)) {
Georgios Katsikas15841e22018-07-28 14:27:28 +0200325 DevicesDiscovery devicesDiscovery = devicesDiscovery(restSBDev, driver);
Michele Santuaric372c222017-01-12 09:41:25 +0100326 Set<DeviceId> deviceIds = devicesDiscovery.deviceIds();
327 restSBDev.setActive(true);
Michal Mach67acb692017-06-21 12:05:36 +0200328 deviceIds.forEach(deviceId -> {
Michele Santuaric372c222017-01-12 09:41:25 +0100329 controller.addProxiedDevice(deviceId, restSBDev);
Georgios Katsikas15841e22018-07-28 14:27:28 +0200330 DeviceDescription devDesc = devicesDiscovery.deviceDetails(deviceId);
331 checkNotNull(devDesc, "DeviceDescription cannot be null");
332 providerService.deviceConnected(deviceId, mergeAnn(restSBDev.deviceId(), devDesc));
Michele Santuaric372c222017-01-12 09:41:25 +0100333
334 if (driver.hasBehaviour(DeviceDescriptionDiscovery.class)) {
335 DriverHandler h = driverService.createHandler(deviceId);
Georgios Katsikas15841e22018-07-28 14:27:28 +0200336 DeviceDescriptionDiscovery devDisc = h.behaviour(DeviceDescriptionDiscovery.class);
337 providerService.updatePorts(deviceId, devDisc.discoverPortDetails());
Michele Santuaric372c222017-01-12 09:41:25 +0100338 }
339
340 checkAndUpdateDevice(deviceId);
Michele Santuaric372c222017-01-12 09:41:25 +0100341 });
342 } else {
343 log.warn("Driver not found for {}", restSBDev);
344 }
345 } else {
346 DeviceId deviceId = restSBDev.deviceId();
Sarithabd0b1f12018-05-11 11:35:27 +0530347
Georgios Katsikas15841e22018-07-28 14:27:28 +0200348 if (driver != null && driver.hasBehaviour(DevicesDiscovery.class)) {
349 restSBDev.setActive(true);
350 DevicesDiscovery devicesDiscovery = devicesDiscovery(restSBDev, driver);
351 DeviceDescription deviceDescription = devicesDiscovery.deviceDetails(deviceId);
352 checkNotNull(deviceDescription, "DeviceDescription cannot be null");
353 providerService.deviceConnected(deviceId, deviceDescription);
354
355 if (driver.hasBehaviour(DeviceDescriptionDiscovery.class)) {
356 DriverHandler h = driverService.createHandler(deviceId);
357 DeviceDescriptionDiscovery deviceDiscovery = h.behaviour(DeviceDescriptionDiscovery.class);
358 providerService.updatePorts(deviceId, deviceDiscovery.discoverPortDetails());
359 }
360 } else {
Sarithabd0b1f12018-05-11 11:35:27 +0530361 DeviceDescription deviceDescription = getDesc(restSBDev);
Georgios Katsikas15841e22018-07-28 14:27:28 +0200362 restSBDev.setActive(true);
363 providerService.deviceConnected(deviceId, deviceDescription);
364 }
Michele Santuaric372c222017-01-12 09:41:25 +0100365 checkAndUpdateDevice(deviceId);
Michele Santuaric372c222017-01-12 09:41:25 +0100366 }
367 }
368
369 private DefaultDeviceDescription mergeAnn(DeviceId devId, DeviceDescription desc) {
370 return new DefaultDeviceDescription(
371 desc,
372 DefaultAnnotations.merge(
373 DefaultAnnotations.builder()
374 .set(AnnotationKeys.PROTOCOL, REST.toUpperCase())
375 // The rest server added as annotation to the device
376 .set(AnnotationKeys.REST_SERVER, devId.toString())
377 .build(),
378 desc.annotations()));
379 }
380
381 private DevicesDiscovery devicesDiscovery(RestSBDevice restSBDevice, Driver driver) {
382 DriverData driverData = new DefaultDriverData(driver, restSBDevice.deviceId());
Georgios Katsikas15841e22018-07-28 14:27:28 +0200383 DevicesDiscovery devicesDiscovery = driver.createBehaviour(driverData, DevicesDiscovery.class);
Michele Santuaric372c222017-01-12 09:41:25 +0100384 devicesDiscovery.setHandler(new DefaultDriverHandler(driverData));
385 return devicesDiscovery;
Andrea Campanella945ded22016-01-07 13:17:43 -0800386 }
387
Konstantinos Kanonakis3cd85552016-11-17 10:11:13 -0600388 private void checkAndUpdateDevice(DeviceId deviceId) {
389 if (deviceService.getDevice(deviceId) == null) {
Georgios Katsikas15841e22018-07-28 14:27:28 +0200390 log.warn("Device {} has not been added to store, maybe due to a problem in connectivity", deviceId);
Konstantinos Kanonakis3cd85552016-11-17 10:11:13 -0600391 } else {
392 boolean isReachable = isReachable(deviceId);
393 if (isReachable && deviceService.isAvailable(deviceId)) {
394 Device device = deviceService.getDevice(deviceId);
395 if (device.is(DeviceDescriptionDiscovery.class)) {
Michele Santuaric372c222017-01-12 09:41:25 +0100396 DeviceDescriptionDiscovery deviceDescriptionDiscovery =
397 device.as(DeviceDescriptionDiscovery.class);
398 DeviceDescription updatedDeviceDescription =
399 deviceDescriptionDiscovery.discoverDeviceDetails();
400 if (updatedDeviceDescription != null &&
401 !descriptionEquals(device, updatedDeviceDescription)) {
402 providerService.deviceConnected(
403 deviceId,
404 new DefaultDeviceDescription(
405 updatedDeviceDescription, true,
406 updatedDeviceDescription.annotations()));
Konstantinos Kanonakis3cd85552016-11-17 10:11:13 -0600407 //if ports are not discovered, retry the discovery
408 if (deviceService.getPorts(deviceId).isEmpty()) {
409 discoverPorts(deviceId);
410 }
411 }
412 } else {
413 log.warn("No DeviceDescriptionDiscovery behaviour for device {}", deviceId);
414 }
415 } else if (!isReachable && deviceService.isAvailable(deviceId)) {
416 providerService.deviceDisconnected(deviceId);
417 }
418 }
419 }
420
421 private boolean descriptionEquals(Device device, DeviceDescription updatedDeviceDescription) {
Michele Santuarid2c8f212017-01-09 18:23:45 +0100422 return Objects.equal(device.id().uri(), updatedDeviceDescription.deviceUri())
Konstantinos Kanonakis3cd85552016-11-17 10:11:13 -0600423 && Objects.equal(device.type(), updatedDeviceDescription.type())
424 && Objects.equal(device.manufacturer(), updatedDeviceDescription.manufacturer())
425 && Objects.equal(device.hwVersion(), updatedDeviceDescription.hwVersion())
426 && Objects.equal(device.swVersion(), updatedDeviceDescription.swVersion())
427 && Objects.equal(device.serialNumber(), updatedDeviceDescription.serialNumber())
428 && Objects.equal(device.chassisId(), updatedDeviceDescription.chassisId())
429 && Objects.equal(device.annotations(), updatedDeviceDescription.annotations());
430 }
431
Andrea Campanella86294db2016-03-07 11:42:49 -0800432 private void deviceRemoved(DeviceId deviceId) {
Michele Santuaric372c222017-01-12 09:41:25 +0100433 checkNotNull(deviceId, ISNOTNULL);
Sarithabd0b1f12018-05-11 11:35:27 +0530434 log.debug("Device removed called for {}", deviceId);
Andrea Campanella945ded22016-01-07 13:17:43 -0800435 providerService.deviceDisconnected(deviceId);
Michal Mach67acb692017-06-21 12:05:36 +0200436 controller.getProxiedDevices(deviceId).forEach(device -> {
Michele Santuaric372c222017-01-12 09:41:25 +0100437 controller.removeProxiedDevice(device);
438 providerService.deviceDisconnected(device);
439 });
Andrea Campanella86294db2016-03-07 11:42:49 -0800440 controller.removeDevice(deviceId);
Andrea Campanella945ded22016-01-07 13:17:43 -0800441 }
442
Andrea Campanella59b549d2017-04-14 21:58:16 +0200443 //Method to connect devices provided via net-cfg under devices/ tree
444 private void createAndConnectDevices() {
445 Set<DeviceId> deviceSubjects =
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200446 netCfgService.getSubjects(DeviceId.class, RestDeviceConfig.class);
Sarithabd0b1f12018-05-11 11:35:27 +0530447 log.debug("Connecting and configuring devices with received configuration:{}",
448 deviceSubjects);
Andrea Campanella59b549d2017-04-14 21:58:16 +0200449 connectDevices(deviceSubjects.stream()
fahadnaeemkhan02ffa712017-12-01 19:49:45 -0800450 .filter(deviceId -> deviceService.getDevice(deviceId) == null)
451 .map(deviceId -> {
452 RestDeviceConfig config =
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200453 netCfgService.getConfig(deviceId, RestDeviceConfig.class);
Yuta HIGUCHIfc667052017-12-05 18:17:22 -0800454 return toInactiveRestSBDevice(config);
fahadnaeemkhan02ffa712017-12-01 19:49:45 -0800455 }).collect(Collectors.toSet()));
Andrea Campanella59b549d2017-04-14 21:58:16 +0200456 }
457
Yuta HIGUCHIfc667052017-12-05 18:17:22 -0800458 private RestSBDevice toInactiveRestSBDevice(RestDeviceConfig config) {
459 return new DefaultRestSBDevice(config.ip(),
460 config.port(),
461 config.username(),
462 config.password(),
463 config.protocol(),
464 config.url(),
465 false,
466 config.testUrl(),
467 config.manufacturer(),
468 config.hwVersion(),
469 config.swVersion(),
Georgios Katsikas15841e22018-07-28 14:27:28 +0200470 config.isProxy(),
Yuta HIGUCHIfc667052017-12-05 18:17:22 -0800471 config.authenticationScheme(),
472 config.token()
473 );
474 }
475
Andrea Campanella59b549d2017-04-14 21:58:16 +0200476 private void connectDevices(Set<RestSBDevice> devices) {
477 //Precomputing the devices to be removed
478 Set<RestSBDevice> toBeRemoved = new HashSet<>(controller.getDevices().values());
479 toBeRemoved.removeAll(devices);
480 //Adding new devices
481 devices.stream()
482 .filter(device -> {
483 device.setActive(false);
484 controller.addDevice(device);
485 return testDeviceConnection(device);
486 })
Michal Mach67acb692017-06-21 12:05:36 +0200487 .forEach(this::deviceAdded);
Andrea Campanella59b549d2017-04-14 21:58:16 +0200488 //Removing devices not wanted anymore
489 toBeRemoved.forEach(device -> deviceRemoved(device.deviceId()));
490 }
491
Yuta HIGUCHIfc667052017-12-05 18:17:22 -0800492 private void connectDevice(RestSBDevice device) {
493 // TODO borrowed from above,
494 // not sure why setting it to inactive
495 device.setActive(false);
496 controller.addDevice(device);
497 if (testDeviceConnection(device)) {
498 deviceAdded(device);
499 }
500 }
501
Michal Machd8099742017-06-19 14:32:35 +0200502 private ScheduledFuture schedulePolling() {
503 return portStatisticsExecutor.scheduleAtFixedRate(this::executePortStatisticsUpdate,
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200504 pollFrequency / 2, pollFrequency,
Michal Machd8099742017-06-19 14:32:35 +0200505 TimeUnit.SECONDS);
506 }
507
508 private void executePortStatisticsUpdate() {
509 controller.getDevices().keySet().forEach(this::updatePortStatistics);
510 }
511
512 private void updatePortStatistics(DeviceId deviceId) {
513 Device device = deviceService.getDevice(deviceId);
514 checkNotNull(device, "device cannot be null");
515
516 if (device.is(PortStatisticsDiscovery.class)) {
517 PortStatisticsDiscovery portStatisticsDiscovery = device.as(PortStatisticsDiscovery.class);
518 Collection<PortStatistics> portStatistics = portStatisticsDiscovery.discoverPortStatistics();
519 if (portStatistics != null && !portStatistics.isEmpty()) {
520 providerService.updatePortStatistics(deviceId, portStatistics);
521 }
522 } else {
523 log.debug("No port statistics getter behaviour for device {}", deviceId);
524 }
525 }
526
Andrea Campanella6c71a052016-04-22 11:56:31 -0700527 private void discoverPorts(DeviceId deviceId) {
528 Device device = deviceService.getDevice(deviceId);
Georgios Katsikas15841e22018-07-28 14:27:28 +0200529 DeviceDescriptionDiscovery deviceDescriptionDiscovery = device.as(DeviceDescriptionDiscovery.class);
Ray Milkey640fedd2018-02-08 09:02:26 -0800530 providerService.updatePorts(deviceId, deviceDescriptionDiscovery.discoverPortDetails());
Andrea Campanella6c71a052016-04-22 11:56:31 -0700531 }
532
Michele Santuaric372c222017-01-12 09:41:25 +0100533 private boolean testDeviceConnection(RestSBDevice dev) {
Andrea Campanella945ded22016-01-07 13:17:43 -0800534 try {
Palash Kala4c71ee72017-05-24 17:43:59 +0900535 Callable<Boolean> connectionSuccess;
536
Michele Santuaric372c222017-01-12 09:41:25 +0100537 if (dev.testUrl().isPresent()) {
Michal Mach67acb692017-06-21 12:05:36 +0200538 connectionSuccess = () ->
539 controller.get(dev.deviceId(), dev.testUrl().get(), MediaType.APPLICATION_JSON_TYPE) != null;
Palash Kala4c71ee72017-05-24 17:43:59 +0900540 } else {
Michal Mach67acb692017-06-21 12:05:36 +0200541 connectionSuccess = () ->
542 controller.get(dev.deviceId(), "", MediaType.APPLICATION_JSON_TYPE) != null;
Michele Santuaric372c222017-01-12 09:41:25 +0100543 }
Palash Kala4c71ee72017-05-24 17:43:59 +0900544
545 Future<Boolean> future = executor.submit(connectionSuccess);
546 try {
Michal Mach67acb692017-06-21 12:05:36 +0200547 return future.get(REST_TIMEOUT_SEC, TimeUnit.SECONDS);
Palash Kala4c71ee72017-05-24 17:43:59 +0900548 } catch (TimeoutException ex) {
Sean Condone45e12c2018-05-04 16:27:07 +0100549 log.warn("Connection to device {} timed out: {}", dev.deviceId(), ex.getMessage());
Palash Kala4c71ee72017-05-24 17:43:59 +0900550 return false;
551 } catch (InterruptedException ex) {
Sean Condone45e12c2018-05-04 16:27:07 +0100552 log.warn("Connection to device {} interrupted: {}", dev.deviceId(), ex.getMessage());
Ray Milkey5c7d4882018-02-05 14:50:39 -0800553 Thread.currentThread().interrupt();
Palash Kala4c71ee72017-05-24 17:43:59 +0900554 return false;
555 } catch (ExecutionException ex) {
Sean Condone45e12c2018-05-04 16:27:07 +0100556 log.warn("Connection to device {} had an execution exception.", dev.deviceId(), ex);
Palash Kala4c71ee72017-05-24 17:43:59 +0900557 return false;
558 }
Michele Santuaric372c222017-01-12 09:41:25 +0100559
Andrea Campanellac6ecc632016-03-10 17:57:06 -0800560 } catch (ProcessingException e) {
Michele Santuaric372c222017-01-12 09:41:25 +0100561 log.warn("Cannot connect to device {}", dev, e);
Andrea Campanella945ded22016-01-07 13:17:43 -0800562 }
563 return false;
564 }
565
566 private class InternalNetworkConfigListener implements NetworkConfigListener {
Andrea Campanella945ded22016-01-07 13:17:43 -0800567 @Override
568 public void event(NetworkConfigEvent event) {
Georgios Katsikas15841e22018-07-28 14:27:28 +0200569 if (!isRelevant(event)) {
570 log.warn("Irrelevant network configuration event: {}", event);
571 return;
572 }
573
Yuta HIGUCHIfc667052017-12-05 18:17:22 -0800574 ExecutorService bg = SharedExecutors.getSingleThreadExecutor();
575 if (event.type() == CONFIG_REMOVED) {
Sarithabd0b1f12018-05-11 11:35:27 +0530576 log.debug("Config {} event for rest device provider for {}",
577 event.type(), event.prevConfig().get().subject());
Yuta HIGUCHIfc667052017-12-05 18:17:22 -0800578 DeviceId did = (DeviceId) event.subject();
579 bg.execute(() -> deviceRemoved(did));
580 } else {
Sarithabd0b1f12018-05-11 11:35:27 +0530581 //CONFIG_ADDED or CONFIG_UPDATED
582 log.debug("Config {} event for rest device provider for {}",
583 event.type(), event.config().get().subject());
Yuta HIGUCHIfc667052017-12-05 18:17:22 -0800584 RestDeviceConfig cfg = (RestDeviceConfig) event.config().get();
585 RestSBDevice restSBDevice = toInactiveRestSBDevice(cfg);
586 bg.execute(() -> connectDevice(restSBDevice));
587 }
Andrea Campanella945ded22016-01-07 13:17:43 -0800588 }
589
590 @Override
591 public boolean isRelevant(NetworkConfigEvent event) {
Yuta HIGUCHI872fdbe2017-12-05 15:34:28 -0800592 return event.configClass().equals(RestDeviceConfig.class) &&
Andrea Campanella945ded22016-01-07 13:17:43 -0800593 (event.type() == CONFIG_ADDED ||
Yuta HIGUCHIfc667052017-12-05 18:17:22 -0800594 event.type() == CONFIG_UPDATED ||
595 event.type() == CONFIG_REMOVED);
Andrea Campanella945ded22016-01-07 13:17:43 -0800596 }
597 }
Saurav Dasa2d37502016-03-25 17:50:40 -0700598
599 @Override
600 public void changePortState(DeviceId deviceId, PortNumber portNumber,
601 boolean enable) {
fahadnaeemkhan482951f2017-08-24 16:35:17 -0700602 Device device = deviceService.getDevice(deviceId);
603 if (device != null) {
604 if (device.is(PortAdmin.class)) {
605 PortAdmin portAdmin = device.as(PortAdmin.class);
606 CompletableFuture<Boolean> modified;
607 if (enable) {
608 modified = portAdmin.enable(portNumber);
609 } else {
610 modified = portAdmin.disable(portNumber);
611 }
612 modified.thenAcceptAsync(result -> {
613 if (!result) {
614 log.warn("Device {} port {} state can't be changed to {}",
615 deviceId, portNumber, enable);
616 }
617 });
618
619 } else {
620 log.warn("Device {} does not support PortAdmin behavior", deviceId);
621 }
622 } else {
623 log.warn("unable to get the device {}, port {} state can't be changed to {}",
624 deviceId, portNumber, enable);
625 }
Saurav Dasa2d37502016-03-25 17:50:40 -0700626 }
Andrea Campanella945ded22016-01-07 13:17:43 -0800627}