blob: 56afd938c943bc10829e199f7a73c7ec1b1ee179 [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
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700140 //@Property(name = POLL_FREQUENCY, intValue = DEFAULT_POLL_FREQUENCY_SECONDS,
141 // label = "Configure poll frequency for port status and statistics; " +
142 // "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);
Sarithabd0b1f12018-05-11 11:35:27 +0530243 return restDevice != null ? restDevice.isActive() : false;
244 }
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();
273 if (restSBDev.isProxy()) {
274 Driver driver = driverService.getDriver(restSBDev.manufacturer().get(),
275 restSBDev.hwVersion().get(),
276 restSBDev.swVersion().get());
277
278 if (driver != null && driver.hasBehaviour(DevicesDiscovery.class)) {
279
280 //Creates the driver to communicate with the server
281 DevicesDiscovery devicesDiscovery =
282 devicesDiscovery(restSBDev, driver);
283 return devicesDiscovery.deviceDetails(deviceId);
284 } else {
285 log.warn("Driver not found for {}", restSBDev);
286 return null;
Michele Santuaric372c222017-01-12 09:41:25 +0100287 }
Andrea Campanella945ded22016-01-07 13:17:43 -0800288 }
Sarithabd0b1f12018-05-11 11:35:27 +0530289 ChassisId cid = new ChassisId();
290 String ipAddress = restSBDev.ip().toString();
291 SparseAnnotations annotations = DefaultAnnotations.builder()
292 .set(IPADDRESS, ipAddress)
293 .set(AnnotationKeys.PROTOCOL, REST.toUpperCase())
294 .build();
295 String manufacturer = UNKNOWN;
296 String hwVersion = UNKNOWN;
297 String swVersion = UNKNOWN;
298 String serialNumber = UNKNOWN;
299
300 Device device = deviceService.getDevice(deviceId);
301 if (device != null) {
302 manufacturer = device.manufacturer();
303 hwVersion = device.hwVersion();
304 swVersion = device.swVersion();
305 serialNumber = device.serialNumber();
306 }
307
308 return new DefaultDeviceDescription(
309 deviceId.uri(),
310 Device.Type.SWITCH,
311 manufacturer, hwVersion,
312 swVersion, serialNumber,
313 cid,
314 annotations);
Andrea Campanella945ded22016-01-07 13:17:43 -0800315 }
316
Michele Santuaric372c222017-01-12 09:41:25 +0100317 private void deviceAdded(RestSBDevice restSBDev) {
318 checkNotNull(restSBDev, ISNOTNULL);
319
Georgios Katsikas15841e22018-07-28 14:27:28 +0200320 Driver driver = driverService.getDriver(restSBDev.manufacturer().get(),
321 restSBDev.hwVersion().get(),
322 restSBDev.swVersion().get());
323
324 // Check if the server is controlling a single or multiple devices
Michele Santuaric372c222017-01-12 09:41:25 +0100325 if (restSBDev.isProxy()) {
Michele Santuaric372c222017-01-12 09:41:25 +0100326 if (driver != null && driver.hasBehaviour(DevicesDiscovery.class)) {
Georgios Katsikas15841e22018-07-28 14:27:28 +0200327 DevicesDiscovery devicesDiscovery = devicesDiscovery(restSBDev, driver);
Michele Santuaric372c222017-01-12 09:41:25 +0100328 Set<DeviceId> deviceIds = devicesDiscovery.deviceIds();
329 restSBDev.setActive(true);
Michal Mach67acb692017-06-21 12:05:36 +0200330 deviceIds.forEach(deviceId -> {
Michele Santuaric372c222017-01-12 09:41:25 +0100331 controller.addProxiedDevice(deviceId, restSBDev);
Georgios Katsikas15841e22018-07-28 14:27:28 +0200332 DeviceDescription devDesc = devicesDiscovery.deviceDetails(deviceId);
333 checkNotNull(devDesc, "DeviceDescription cannot be null");
334 providerService.deviceConnected(deviceId, mergeAnn(restSBDev.deviceId(), devDesc));
Michele Santuaric372c222017-01-12 09:41:25 +0100335
336 if (driver.hasBehaviour(DeviceDescriptionDiscovery.class)) {
337 DriverHandler h = driverService.createHandler(deviceId);
Georgios Katsikas15841e22018-07-28 14:27:28 +0200338 DeviceDescriptionDiscovery devDisc = h.behaviour(DeviceDescriptionDiscovery.class);
339 providerService.updatePorts(deviceId, devDisc.discoverPortDetails());
Michele Santuaric372c222017-01-12 09:41:25 +0100340 }
341
342 checkAndUpdateDevice(deviceId);
Michele Santuaric372c222017-01-12 09:41:25 +0100343 });
344 } else {
345 log.warn("Driver not found for {}", restSBDev);
346 }
347 } else {
348 DeviceId deviceId = restSBDev.deviceId();
Sarithabd0b1f12018-05-11 11:35:27 +0530349
Georgios Katsikas15841e22018-07-28 14:27:28 +0200350 if (driver != null && driver.hasBehaviour(DevicesDiscovery.class)) {
351 restSBDev.setActive(true);
352 DevicesDiscovery devicesDiscovery = devicesDiscovery(restSBDev, driver);
353 DeviceDescription deviceDescription = devicesDiscovery.deviceDetails(deviceId);
354 checkNotNull(deviceDescription, "DeviceDescription cannot be null");
355 providerService.deviceConnected(deviceId, deviceDescription);
356
357 if (driver.hasBehaviour(DeviceDescriptionDiscovery.class)) {
358 DriverHandler h = driverService.createHandler(deviceId);
359 DeviceDescriptionDiscovery deviceDiscovery = h.behaviour(DeviceDescriptionDiscovery.class);
360 providerService.updatePorts(deviceId, deviceDiscovery.discoverPortDetails());
361 }
362 } else {
Sarithabd0b1f12018-05-11 11:35:27 +0530363 DeviceDescription deviceDescription = getDesc(restSBDev);
Georgios Katsikas15841e22018-07-28 14:27:28 +0200364 restSBDev.setActive(true);
365 providerService.deviceConnected(deviceId, deviceDescription);
366 }
Michele Santuaric372c222017-01-12 09:41:25 +0100367 checkAndUpdateDevice(deviceId);
Michele Santuaric372c222017-01-12 09:41:25 +0100368 }
369 }
370
371 private DefaultDeviceDescription mergeAnn(DeviceId devId, DeviceDescription desc) {
372 return new DefaultDeviceDescription(
373 desc,
374 DefaultAnnotations.merge(
375 DefaultAnnotations.builder()
376 .set(AnnotationKeys.PROTOCOL, REST.toUpperCase())
377 // The rest server added as annotation to the device
378 .set(AnnotationKeys.REST_SERVER, devId.toString())
379 .build(),
380 desc.annotations()));
381 }
382
383 private DevicesDiscovery devicesDiscovery(RestSBDevice restSBDevice, Driver driver) {
384 DriverData driverData = new DefaultDriverData(driver, restSBDevice.deviceId());
Georgios Katsikas15841e22018-07-28 14:27:28 +0200385 DevicesDiscovery devicesDiscovery = driver.createBehaviour(driverData, DevicesDiscovery.class);
Michele Santuaric372c222017-01-12 09:41:25 +0100386 devicesDiscovery.setHandler(new DefaultDriverHandler(driverData));
387 return devicesDiscovery;
Andrea Campanella945ded22016-01-07 13:17:43 -0800388 }
389
Konstantinos Kanonakis3cd85552016-11-17 10:11:13 -0600390 private void checkAndUpdateDevice(DeviceId deviceId) {
391 if (deviceService.getDevice(deviceId) == null) {
Georgios Katsikas15841e22018-07-28 14:27:28 +0200392 log.warn("Device {} has not been added to store, maybe due to a problem in connectivity", deviceId);
Konstantinos Kanonakis3cd85552016-11-17 10:11:13 -0600393 } else {
394 boolean isReachable = isReachable(deviceId);
395 if (isReachable && deviceService.isAvailable(deviceId)) {
396 Device device = deviceService.getDevice(deviceId);
397 if (device.is(DeviceDescriptionDiscovery.class)) {
Michele Santuaric372c222017-01-12 09:41:25 +0100398 DeviceDescriptionDiscovery deviceDescriptionDiscovery =
399 device.as(DeviceDescriptionDiscovery.class);
400 DeviceDescription updatedDeviceDescription =
401 deviceDescriptionDiscovery.discoverDeviceDetails();
402 if (updatedDeviceDescription != null &&
403 !descriptionEquals(device, updatedDeviceDescription)) {
404 providerService.deviceConnected(
405 deviceId,
406 new DefaultDeviceDescription(
407 updatedDeviceDescription, true,
408 updatedDeviceDescription.annotations()));
Konstantinos Kanonakis3cd85552016-11-17 10:11:13 -0600409 //if ports are not discovered, retry the discovery
410 if (deviceService.getPorts(deviceId).isEmpty()) {
411 discoverPorts(deviceId);
412 }
413 }
414 } else {
415 log.warn("No DeviceDescriptionDiscovery behaviour for device {}", deviceId);
416 }
417 } else if (!isReachable && deviceService.isAvailable(deviceId)) {
418 providerService.deviceDisconnected(deviceId);
419 }
420 }
421 }
422
423 private boolean descriptionEquals(Device device, DeviceDescription updatedDeviceDescription) {
Michele Santuarid2c8f212017-01-09 18:23:45 +0100424 return Objects.equal(device.id().uri(), updatedDeviceDescription.deviceUri())
Konstantinos Kanonakis3cd85552016-11-17 10:11:13 -0600425 && Objects.equal(device.type(), updatedDeviceDescription.type())
426 && Objects.equal(device.manufacturer(), updatedDeviceDescription.manufacturer())
427 && Objects.equal(device.hwVersion(), updatedDeviceDescription.hwVersion())
428 && Objects.equal(device.swVersion(), updatedDeviceDescription.swVersion())
429 && Objects.equal(device.serialNumber(), updatedDeviceDescription.serialNumber())
430 && Objects.equal(device.chassisId(), updatedDeviceDescription.chassisId())
431 && Objects.equal(device.annotations(), updatedDeviceDescription.annotations());
432 }
433
Andrea Campanella86294db2016-03-07 11:42:49 -0800434 private void deviceRemoved(DeviceId deviceId) {
Michele Santuaric372c222017-01-12 09:41:25 +0100435 checkNotNull(deviceId, ISNOTNULL);
Sarithabd0b1f12018-05-11 11:35:27 +0530436 log.debug("Device removed called for {}", deviceId);
Andrea Campanella945ded22016-01-07 13:17:43 -0800437 providerService.deviceDisconnected(deviceId);
Michal Mach67acb692017-06-21 12:05:36 +0200438 controller.getProxiedDevices(deviceId).forEach(device -> {
Michele Santuaric372c222017-01-12 09:41:25 +0100439 controller.removeProxiedDevice(device);
440 providerService.deviceDisconnected(device);
441 });
Andrea Campanella86294db2016-03-07 11:42:49 -0800442 controller.removeDevice(deviceId);
Andrea Campanella945ded22016-01-07 13:17:43 -0800443 }
444
Andrea Campanella59b549d2017-04-14 21:58:16 +0200445 //Method to connect devices provided via net-cfg under devices/ tree
446 private void createAndConnectDevices() {
447 Set<DeviceId> deviceSubjects =
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200448 netCfgService.getSubjects(DeviceId.class, RestDeviceConfig.class);
Sarithabd0b1f12018-05-11 11:35:27 +0530449 log.debug("Connecting and configuring devices with received configuration:{}",
450 deviceSubjects);
Andrea Campanella59b549d2017-04-14 21:58:16 +0200451 connectDevices(deviceSubjects.stream()
fahadnaeemkhan02ffa712017-12-01 19:49:45 -0800452 .filter(deviceId -> deviceService.getDevice(deviceId) == null)
453 .map(deviceId -> {
454 RestDeviceConfig config =
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200455 netCfgService.getConfig(deviceId, RestDeviceConfig.class);
Yuta HIGUCHIfc667052017-12-05 18:17:22 -0800456 return toInactiveRestSBDevice(config);
fahadnaeemkhan02ffa712017-12-01 19:49:45 -0800457 }).collect(Collectors.toSet()));
Andrea Campanella59b549d2017-04-14 21:58:16 +0200458 }
459
Yuta HIGUCHIfc667052017-12-05 18:17:22 -0800460 private RestSBDevice toInactiveRestSBDevice(RestDeviceConfig config) {
461 return new DefaultRestSBDevice(config.ip(),
462 config.port(),
463 config.username(),
464 config.password(),
465 config.protocol(),
466 config.url(),
467 false,
468 config.testUrl(),
469 config.manufacturer(),
470 config.hwVersion(),
471 config.swVersion(),
Georgios Katsikas15841e22018-07-28 14:27:28 +0200472 config.isProxy(),
Yuta HIGUCHIfc667052017-12-05 18:17:22 -0800473 config.authenticationScheme(),
474 config.token()
475 );
476 }
477
Andrea Campanella59b549d2017-04-14 21:58:16 +0200478 private void connectDevices(Set<RestSBDevice> devices) {
479 //Precomputing the devices to be removed
480 Set<RestSBDevice> toBeRemoved = new HashSet<>(controller.getDevices().values());
481 toBeRemoved.removeAll(devices);
482 //Adding new devices
483 devices.stream()
484 .filter(device -> {
485 device.setActive(false);
486 controller.addDevice(device);
487 return testDeviceConnection(device);
488 })
Michal Mach67acb692017-06-21 12:05:36 +0200489 .forEach(this::deviceAdded);
Andrea Campanella59b549d2017-04-14 21:58:16 +0200490 //Removing devices not wanted anymore
491 toBeRemoved.forEach(device -> deviceRemoved(device.deviceId()));
492 }
493
Yuta HIGUCHIfc667052017-12-05 18:17:22 -0800494 private void connectDevice(RestSBDevice device) {
495 // TODO borrowed from above,
496 // not sure why setting it to inactive
497 device.setActive(false);
498 controller.addDevice(device);
499 if (testDeviceConnection(device)) {
500 deviceAdded(device);
501 }
502 }
503
Michal Machd8099742017-06-19 14:32:35 +0200504 private ScheduledFuture schedulePolling() {
505 return portStatisticsExecutor.scheduleAtFixedRate(this::executePortStatisticsUpdate,
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200506 pollFrequency / 2, pollFrequency,
Michal Machd8099742017-06-19 14:32:35 +0200507 TimeUnit.SECONDS);
508 }
509
510 private void executePortStatisticsUpdate() {
511 controller.getDevices().keySet().forEach(this::updatePortStatistics);
512 }
513
514 private void updatePortStatistics(DeviceId deviceId) {
515 Device device = deviceService.getDevice(deviceId);
516 checkNotNull(device, "device cannot be null");
517
518 if (device.is(PortStatisticsDiscovery.class)) {
519 PortStatisticsDiscovery portStatisticsDiscovery = device.as(PortStatisticsDiscovery.class);
520 Collection<PortStatistics> portStatistics = portStatisticsDiscovery.discoverPortStatistics();
521 if (portStatistics != null && !portStatistics.isEmpty()) {
522 providerService.updatePortStatistics(deviceId, portStatistics);
523 }
524 } else {
525 log.debug("No port statistics getter behaviour for device {}", deviceId);
526 }
527 }
528
Andrea Campanella6c71a052016-04-22 11:56:31 -0700529 private void discoverPorts(DeviceId deviceId) {
530 Device device = deviceService.getDevice(deviceId);
Georgios Katsikas15841e22018-07-28 14:27:28 +0200531 DeviceDescriptionDiscovery deviceDescriptionDiscovery = device.as(DeviceDescriptionDiscovery.class);
Ray Milkey640fedd2018-02-08 09:02:26 -0800532 providerService.updatePorts(deviceId, deviceDescriptionDiscovery.discoverPortDetails());
Andrea Campanella6c71a052016-04-22 11:56:31 -0700533 }
534
Michele Santuaric372c222017-01-12 09:41:25 +0100535 private boolean testDeviceConnection(RestSBDevice dev) {
Andrea Campanella945ded22016-01-07 13:17:43 -0800536 try {
Palash Kala4c71ee72017-05-24 17:43:59 +0900537 Callable<Boolean> connectionSuccess;
538
Michele Santuaric372c222017-01-12 09:41:25 +0100539 if (dev.testUrl().isPresent()) {
Michal Mach67acb692017-06-21 12:05:36 +0200540 connectionSuccess = () ->
541 controller.get(dev.deviceId(), dev.testUrl().get(), MediaType.APPLICATION_JSON_TYPE) != null;
Palash Kala4c71ee72017-05-24 17:43:59 +0900542 } else {
Michal Mach67acb692017-06-21 12:05:36 +0200543 connectionSuccess = () ->
544 controller.get(dev.deviceId(), "", MediaType.APPLICATION_JSON_TYPE) != null;
Michele Santuaric372c222017-01-12 09:41:25 +0100545 }
Palash Kala4c71ee72017-05-24 17:43:59 +0900546
547 Future<Boolean> future = executor.submit(connectionSuccess);
548 try {
Michal Mach67acb692017-06-21 12:05:36 +0200549 return future.get(REST_TIMEOUT_SEC, TimeUnit.SECONDS);
Palash Kala4c71ee72017-05-24 17:43:59 +0900550 } catch (TimeoutException ex) {
Sean Condone45e12c2018-05-04 16:27:07 +0100551 log.warn("Connection to device {} timed out: {}", dev.deviceId(), ex.getMessage());
Palash Kala4c71ee72017-05-24 17:43:59 +0900552 return false;
553 } catch (InterruptedException ex) {
Sean Condone45e12c2018-05-04 16:27:07 +0100554 log.warn("Connection to device {} interrupted: {}", dev.deviceId(), ex.getMessage());
Ray Milkey5c7d4882018-02-05 14:50:39 -0800555 Thread.currentThread().interrupt();
Palash Kala4c71ee72017-05-24 17:43:59 +0900556 return false;
557 } catch (ExecutionException ex) {
Sean Condone45e12c2018-05-04 16:27:07 +0100558 log.warn("Connection to device {} had an execution exception.", dev.deviceId(), ex);
Palash Kala4c71ee72017-05-24 17:43:59 +0900559 return false;
560 }
Michele Santuaric372c222017-01-12 09:41:25 +0100561
Andrea Campanellac6ecc632016-03-10 17:57:06 -0800562 } catch (ProcessingException e) {
Michele Santuaric372c222017-01-12 09:41:25 +0100563 log.warn("Cannot connect to device {}", dev, e);
Andrea Campanella945ded22016-01-07 13:17:43 -0800564 }
565 return false;
566 }
567
568 private class InternalNetworkConfigListener implements NetworkConfigListener {
Andrea Campanella945ded22016-01-07 13:17:43 -0800569 @Override
570 public void event(NetworkConfigEvent event) {
Georgios Katsikas15841e22018-07-28 14:27:28 +0200571 if (!isRelevant(event)) {
572 log.warn("Irrelevant network configuration event: {}", event);
573 return;
574 }
575
Yuta HIGUCHIfc667052017-12-05 18:17:22 -0800576 ExecutorService bg = SharedExecutors.getSingleThreadExecutor();
577 if (event.type() == CONFIG_REMOVED) {
Sarithabd0b1f12018-05-11 11:35:27 +0530578 log.debug("Config {} event for rest device provider for {}",
579 event.type(), event.prevConfig().get().subject());
Yuta HIGUCHIfc667052017-12-05 18:17:22 -0800580 DeviceId did = (DeviceId) event.subject();
581 bg.execute(() -> deviceRemoved(did));
582 } else {
Sarithabd0b1f12018-05-11 11:35:27 +0530583 //CONFIG_ADDED or CONFIG_UPDATED
584 log.debug("Config {} event for rest device provider for {}",
585 event.type(), event.config().get().subject());
Yuta HIGUCHIfc667052017-12-05 18:17:22 -0800586 RestDeviceConfig cfg = (RestDeviceConfig) event.config().get();
587 RestSBDevice restSBDevice = toInactiveRestSBDevice(cfg);
588 bg.execute(() -> connectDevice(restSBDevice));
589 }
Andrea Campanella945ded22016-01-07 13:17:43 -0800590 }
591
592 @Override
593 public boolean isRelevant(NetworkConfigEvent event) {
Yuta HIGUCHI872fdbe2017-12-05 15:34:28 -0800594 return event.configClass().equals(RestDeviceConfig.class) &&
Andrea Campanella945ded22016-01-07 13:17:43 -0800595 (event.type() == CONFIG_ADDED ||
Yuta HIGUCHIfc667052017-12-05 18:17:22 -0800596 event.type() == CONFIG_UPDATED ||
597 event.type() == CONFIG_REMOVED);
Andrea Campanella945ded22016-01-07 13:17:43 -0800598 }
599 }
Saurav Dasa2d37502016-03-25 17:50:40 -0700600
601 @Override
602 public void changePortState(DeviceId deviceId, PortNumber portNumber,
603 boolean enable) {
fahadnaeemkhan482951f2017-08-24 16:35:17 -0700604 Device device = deviceService.getDevice(deviceId);
605 if (device != null) {
606 if (device.is(PortAdmin.class)) {
607 PortAdmin portAdmin = device.as(PortAdmin.class);
608 CompletableFuture<Boolean> modified;
609 if (enable) {
610 modified = portAdmin.enable(portNumber);
611 } else {
612 modified = portAdmin.disable(portNumber);
613 }
614 modified.thenAcceptAsync(result -> {
615 if (!result) {
616 log.warn("Device {} port {} state can't be changed to {}",
617 deviceId, portNumber, enable);
618 }
619 });
620
621 } else {
622 log.warn("Device {} does not support PortAdmin behavior", deviceId);
623 }
624 } else {
625 log.warn("unable to get the device {}, port {} state can't be changed to {}",
626 deviceId, portNumber, enable);
627 }
Saurav Dasa2d37502016-03-25 17:50:40 -0700628 }
Andrea Campanella945ded22016-01-07 13:17:43 -0800629}