blob: 9aa5c125d1aa0a260eb3cf1bccf0c5c832db3901 [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();
Andrea Campanella60804c02018-11-06 15:06:36 +0100271 Driver driver = driverService.getDriver(restSBDev.manufacturer().get(),
272 restSBDev.hwVersion().get(),
273 restSBDev.swVersion().get());
Sarithabd0b1f12018-05-11 11:35:27 +0530274
Andrea Campanella60804c02018-11-06 15:06:36 +0100275 if (restSBDev.isProxy()) {
Sarithabd0b1f12018-05-11 11:35:27 +0530276 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 Campanella60804c02018-11-06 15:06:36 +0100286 } else if (driver != null && driver.hasBehaviour(DeviceDescriptionDiscovery.class)) {
287 DriverHandler h = driverService.createHandler(deviceId);
288 DeviceDescriptionDiscovery deviceDiscovery = h.behaviour(DeviceDescriptionDiscovery.class);
289 return deviceDiscovery.discoverDeviceDetails();
Andrea Campanella945ded22016-01-07 13:17:43 -0800290 }
Sarithabd0b1f12018-05-11 11:35:27 +0530291 ChassisId cid = new ChassisId();
292 String ipAddress = restSBDev.ip().toString();
293 SparseAnnotations annotations = DefaultAnnotations.builder()
294 .set(IPADDRESS, ipAddress)
295 .set(AnnotationKeys.PROTOCOL, REST.toUpperCase())
296 .build();
297 String manufacturer = UNKNOWN;
298 String hwVersion = UNKNOWN;
299 String swVersion = UNKNOWN;
300 String serialNumber = UNKNOWN;
301
302 Device device = deviceService.getDevice(deviceId);
303 if (device != null) {
304 manufacturer = device.manufacturer();
305 hwVersion = device.hwVersion();
306 swVersion = device.swVersion();
307 serialNumber = device.serialNumber();
308 }
309
310 return new DefaultDeviceDescription(
311 deviceId.uri(),
312 Device.Type.SWITCH,
313 manufacturer, hwVersion,
314 swVersion, serialNumber,
315 cid,
316 annotations);
Andrea Campanella945ded22016-01-07 13:17:43 -0800317 }
318
Michele Santuaric372c222017-01-12 09:41:25 +0100319 private void deviceAdded(RestSBDevice restSBDev) {
320 checkNotNull(restSBDev, ISNOTNULL);
321
Georgios Katsikas15841e22018-07-28 14:27:28 +0200322 Driver driver = driverService.getDriver(restSBDev.manufacturer().get(),
323 restSBDev.hwVersion().get(),
324 restSBDev.swVersion().get());
325
326 // Check if the server is controlling a single or multiple devices
Michele Santuaric372c222017-01-12 09:41:25 +0100327 if (restSBDev.isProxy()) {
Michele Santuaric372c222017-01-12 09:41:25 +0100328 if (driver != null && driver.hasBehaviour(DevicesDiscovery.class)) {
Georgios Katsikas15841e22018-07-28 14:27:28 +0200329 DevicesDiscovery devicesDiscovery = devicesDiscovery(restSBDev, driver);
Michele Santuaric372c222017-01-12 09:41:25 +0100330 Set<DeviceId> deviceIds = devicesDiscovery.deviceIds();
331 restSBDev.setActive(true);
Michal Mach67acb692017-06-21 12:05:36 +0200332 deviceIds.forEach(deviceId -> {
Michele Santuaric372c222017-01-12 09:41:25 +0100333 controller.addProxiedDevice(deviceId, restSBDev);
Georgios Katsikas15841e22018-07-28 14:27:28 +0200334 DeviceDescription devDesc = devicesDiscovery.deviceDetails(deviceId);
335 checkNotNull(devDesc, "DeviceDescription cannot be null");
336 providerService.deviceConnected(deviceId, mergeAnn(restSBDev.deviceId(), devDesc));
Michele Santuaric372c222017-01-12 09:41:25 +0100337
338 if (driver.hasBehaviour(DeviceDescriptionDiscovery.class)) {
339 DriverHandler h = driverService.createHandler(deviceId);
Georgios Katsikas15841e22018-07-28 14:27:28 +0200340 DeviceDescriptionDiscovery devDisc = h.behaviour(DeviceDescriptionDiscovery.class);
341 providerService.updatePorts(deviceId, devDisc.discoverPortDetails());
Michele Santuaric372c222017-01-12 09:41:25 +0100342 }
343
344 checkAndUpdateDevice(deviceId);
Michele Santuaric372c222017-01-12 09:41:25 +0100345 });
346 } else {
347 log.warn("Driver not found for {}", restSBDev);
348 }
349 } else {
350 DeviceId deviceId = restSBDev.deviceId();
Sarithabd0b1f12018-05-11 11:35:27 +0530351
Georgios Katsikas15841e22018-07-28 14:27:28 +0200352 if (driver != null && driver.hasBehaviour(DevicesDiscovery.class)) {
353 restSBDev.setActive(true);
354 DevicesDiscovery devicesDiscovery = devicesDiscovery(restSBDev, driver);
355 DeviceDescription deviceDescription = devicesDiscovery.deviceDetails(deviceId);
356 checkNotNull(deviceDescription, "DeviceDescription cannot be null");
357 providerService.deviceConnected(deviceId, deviceDescription);
358
359 if (driver.hasBehaviour(DeviceDescriptionDiscovery.class)) {
360 DriverHandler h = driverService.createHandler(deviceId);
361 DeviceDescriptionDiscovery deviceDiscovery = h.behaviour(DeviceDescriptionDiscovery.class);
362 providerService.updatePorts(deviceId, deviceDiscovery.discoverPortDetails());
363 }
364 } else {
Sarithabd0b1f12018-05-11 11:35:27 +0530365 DeviceDescription deviceDescription = getDesc(restSBDev);
Georgios Katsikas15841e22018-07-28 14:27:28 +0200366 restSBDev.setActive(true);
367 providerService.deviceConnected(deviceId, deviceDescription);
368 }
Michele Santuaric372c222017-01-12 09:41:25 +0100369 checkAndUpdateDevice(deviceId);
Michele Santuaric372c222017-01-12 09:41:25 +0100370 }
371 }
372
373 private DefaultDeviceDescription mergeAnn(DeviceId devId, DeviceDescription desc) {
374 return new DefaultDeviceDescription(
375 desc,
376 DefaultAnnotations.merge(
377 DefaultAnnotations.builder()
378 .set(AnnotationKeys.PROTOCOL, REST.toUpperCase())
379 // The rest server added as annotation to the device
380 .set(AnnotationKeys.REST_SERVER, devId.toString())
381 .build(),
382 desc.annotations()));
383 }
384
385 private DevicesDiscovery devicesDiscovery(RestSBDevice restSBDevice, Driver driver) {
386 DriverData driverData = new DefaultDriverData(driver, restSBDevice.deviceId());
Georgios Katsikas15841e22018-07-28 14:27:28 +0200387 DevicesDiscovery devicesDiscovery = driver.createBehaviour(driverData, DevicesDiscovery.class);
Michele Santuaric372c222017-01-12 09:41:25 +0100388 devicesDiscovery.setHandler(new DefaultDriverHandler(driverData));
389 return devicesDiscovery;
Andrea Campanella945ded22016-01-07 13:17:43 -0800390 }
391
Konstantinos Kanonakis3cd85552016-11-17 10:11:13 -0600392 private void checkAndUpdateDevice(DeviceId deviceId) {
393 if (deviceService.getDevice(deviceId) == null) {
Georgios Katsikas15841e22018-07-28 14:27:28 +0200394 log.warn("Device {} has not been added to store, maybe due to a problem in connectivity", deviceId);
Konstantinos Kanonakis3cd85552016-11-17 10:11:13 -0600395 } else {
396 boolean isReachable = isReachable(deviceId);
397 if (isReachable && deviceService.isAvailable(deviceId)) {
398 Device device = deviceService.getDevice(deviceId);
399 if (device.is(DeviceDescriptionDiscovery.class)) {
Michele Santuaric372c222017-01-12 09:41:25 +0100400 DeviceDescriptionDiscovery deviceDescriptionDiscovery =
401 device.as(DeviceDescriptionDiscovery.class);
402 DeviceDescription updatedDeviceDescription =
403 deviceDescriptionDiscovery.discoverDeviceDetails();
404 if (updatedDeviceDescription != null &&
405 !descriptionEquals(device, updatedDeviceDescription)) {
406 providerService.deviceConnected(
407 deviceId,
408 new DefaultDeviceDescription(
409 updatedDeviceDescription, true,
410 updatedDeviceDescription.annotations()));
Konstantinos Kanonakis3cd85552016-11-17 10:11:13 -0600411 //if ports are not discovered, retry the discovery
412 if (deviceService.getPorts(deviceId).isEmpty()) {
413 discoverPorts(deviceId);
414 }
415 }
416 } else {
417 log.warn("No DeviceDescriptionDiscovery behaviour for device {}", deviceId);
418 }
419 } else if (!isReachable && deviceService.isAvailable(deviceId)) {
420 providerService.deviceDisconnected(deviceId);
421 }
422 }
423 }
424
425 private boolean descriptionEquals(Device device, DeviceDescription updatedDeviceDescription) {
Michele Santuarid2c8f212017-01-09 18:23:45 +0100426 return Objects.equal(device.id().uri(), updatedDeviceDescription.deviceUri())
Konstantinos Kanonakis3cd85552016-11-17 10:11:13 -0600427 && Objects.equal(device.type(), updatedDeviceDescription.type())
428 && Objects.equal(device.manufacturer(), updatedDeviceDescription.manufacturer())
429 && Objects.equal(device.hwVersion(), updatedDeviceDescription.hwVersion())
430 && Objects.equal(device.swVersion(), updatedDeviceDescription.swVersion())
431 && Objects.equal(device.serialNumber(), updatedDeviceDescription.serialNumber())
432 && Objects.equal(device.chassisId(), updatedDeviceDescription.chassisId())
433 && Objects.equal(device.annotations(), updatedDeviceDescription.annotations());
434 }
435
Andrea Campanella86294db2016-03-07 11:42:49 -0800436 private void deviceRemoved(DeviceId deviceId) {
Michele Santuaric372c222017-01-12 09:41:25 +0100437 checkNotNull(deviceId, ISNOTNULL);
Sarithabd0b1f12018-05-11 11:35:27 +0530438 log.debug("Device removed called for {}", deviceId);
Andrea Campanella945ded22016-01-07 13:17:43 -0800439 providerService.deviceDisconnected(deviceId);
Michal Mach67acb692017-06-21 12:05:36 +0200440 controller.getProxiedDevices(deviceId).forEach(device -> {
Michele Santuaric372c222017-01-12 09:41:25 +0100441 controller.removeProxiedDevice(device);
442 providerService.deviceDisconnected(device);
443 });
Andrea Campanella86294db2016-03-07 11:42:49 -0800444 controller.removeDevice(deviceId);
Andrea Campanella945ded22016-01-07 13:17:43 -0800445 }
446
Andrea Campanella59b549d2017-04-14 21:58:16 +0200447 //Method to connect devices provided via net-cfg under devices/ tree
448 private void createAndConnectDevices() {
449 Set<DeviceId> deviceSubjects =
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200450 netCfgService.getSubjects(DeviceId.class, RestDeviceConfig.class);
Sarithabd0b1f12018-05-11 11:35:27 +0530451 log.debug("Connecting and configuring devices with received configuration:{}",
452 deviceSubjects);
Andrea Campanella59b549d2017-04-14 21:58:16 +0200453 connectDevices(deviceSubjects.stream()
fahadnaeemkhan02ffa712017-12-01 19:49:45 -0800454 .filter(deviceId -> deviceService.getDevice(deviceId) == null)
455 .map(deviceId -> {
456 RestDeviceConfig config =
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200457 netCfgService.getConfig(deviceId, RestDeviceConfig.class);
Yuta HIGUCHIfc667052017-12-05 18:17:22 -0800458 return toInactiveRestSBDevice(config);
fahadnaeemkhan02ffa712017-12-01 19:49:45 -0800459 }).collect(Collectors.toSet()));
Andrea Campanella59b549d2017-04-14 21:58:16 +0200460 }
461
Yuta HIGUCHIfc667052017-12-05 18:17:22 -0800462 private RestSBDevice toInactiveRestSBDevice(RestDeviceConfig config) {
463 return new DefaultRestSBDevice(config.ip(),
464 config.port(),
465 config.username(),
466 config.password(),
467 config.protocol(),
468 config.url(),
469 false,
470 config.testUrl(),
471 config.manufacturer(),
472 config.hwVersion(),
473 config.swVersion(),
Georgios Katsikas15841e22018-07-28 14:27:28 +0200474 config.isProxy(),
Yuta HIGUCHIfc667052017-12-05 18:17:22 -0800475 config.authenticationScheme(),
476 config.token()
477 );
478 }
479
Andrea Campanella59b549d2017-04-14 21:58:16 +0200480 private void connectDevices(Set<RestSBDevice> devices) {
481 //Precomputing the devices to be removed
482 Set<RestSBDevice> toBeRemoved = new HashSet<>(controller.getDevices().values());
483 toBeRemoved.removeAll(devices);
484 //Adding new devices
485 devices.stream()
486 .filter(device -> {
487 device.setActive(false);
488 controller.addDevice(device);
489 return testDeviceConnection(device);
490 })
Michal Mach67acb692017-06-21 12:05:36 +0200491 .forEach(this::deviceAdded);
Andrea Campanella59b549d2017-04-14 21:58:16 +0200492 //Removing devices not wanted anymore
493 toBeRemoved.forEach(device -> deviceRemoved(device.deviceId()));
494 }
495
Yuta HIGUCHIfc667052017-12-05 18:17:22 -0800496 private void connectDevice(RestSBDevice device) {
497 // TODO borrowed from above,
498 // not sure why setting it to inactive
499 device.setActive(false);
500 controller.addDevice(device);
501 if (testDeviceConnection(device)) {
502 deviceAdded(device);
503 }
504 }
505
Michal Machd8099742017-06-19 14:32:35 +0200506 private ScheduledFuture schedulePolling() {
507 return portStatisticsExecutor.scheduleAtFixedRate(this::executePortStatisticsUpdate,
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200508 pollFrequency / 2, pollFrequency,
Michal Machd8099742017-06-19 14:32:35 +0200509 TimeUnit.SECONDS);
510 }
511
512 private void executePortStatisticsUpdate() {
513 controller.getDevices().keySet().forEach(this::updatePortStatistics);
514 }
515
516 private void updatePortStatistics(DeviceId deviceId) {
517 Device device = deviceService.getDevice(deviceId);
518 checkNotNull(device, "device cannot be null");
519
520 if (device.is(PortStatisticsDiscovery.class)) {
521 PortStatisticsDiscovery portStatisticsDiscovery = device.as(PortStatisticsDiscovery.class);
522 Collection<PortStatistics> portStatistics = portStatisticsDiscovery.discoverPortStatistics();
523 if (portStatistics != null && !portStatistics.isEmpty()) {
524 providerService.updatePortStatistics(deviceId, portStatistics);
525 }
526 } else {
527 log.debug("No port statistics getter behaviour for device {}", deviceId);
528 }
529 }
530
Andrea Campanella6c71a052016-04-22 11:56:31 -0700531 private void discoverPorts(DeviceId deviceId) {
532 Device device = deviceService.getDevice(deviceId);
Georgios Katsikas15841e22018-07-28 14:27:28 +0200533 DeviceDescriptionDiscovery deviceDescriptionDiscovery = device.as(DeviceDescriptionDiscovery.class);
Ray Milkey640fedd2018-02-08 09:02:26 -0800534 providerService.updatePorts(deviceId, deviceDescriptionDiscovery.discoverPortDetails());
Andrea Campanella6c71a052016-04-22 11:56:31 -0700535 }
536
Michele Santuaric372c222017-01-12 09:41:25 +0100537 private boolean testDeviceConnection(RestSBDevice dev) {
Andrea Campanella945ded22016-01-07 13:17:43 -0800538 try {
Palash Kala4c71ee72017-05-24 17:43:59 +0900539 Callable<Boolean> connectionSuccess;
540
Michele Santuaric372c222017-01-12 09:41:25 +0100541 if (dev.testUrl().isPresent()) {
Michal Mach67acb692017-06-21 12:05:36 +0200542 connectionSuccess = () ->
543 controller.get(dev.deviceId(), dev.testUrl().get(), MediaType.APPLICATION_JSON_TYPE) != null;
Palash Kala4c71ee72017-05-24 17:43:59 +0900544 } else {
Michal Mach67acb692017-06-21 12:05:36 +0200545 connectionSuccess = () ->
546 controller.get(dev.deviceId(), "", MediaType.APPLICATION_JSON_TYPE) != null;
Michele Santuaric372c222017-01-12 09:41:25 +0100547 }
Palash Kala4c71ee72017-05-24 17:43:59 +0900548
549 Future<Boolean> future = executor.submit(connectionSuccess);
550 try {
Michal Mach67acb692017-06-21 12:05:36 +0200551 return future.get(REST_TIMEOUT_SEC, TimeUnit.SECONDS);
Palash Kala4c71ee72017-05-24 17:43:59 +0900552 } catch (TimeoutException ex) {
Sean Condone45e12c2018-05-04 16:27:07 +0100553 log.warn("Connection to device {} timed out: {}", dev.deviceId(), ex.getMessage());
Palash Kala4c71ee72017-05-24 17:43:59 +0900554 return false;
555 } catch (InterruptedException ex) {
Sean Condone45e12c2018-05-04 16:27:07 +0100556 log.warn("Connection to device {} interrupted: {}", dev.deviceId(), ex.getMessage());
Ray Milkey5c7d4882018-02-05 14:50:39 -0800557 Thread.currentThread().interrupt();
Palash Kala4c71ee72017-05-24 17:43:59 +0900558 return false;
559 } catch (ExecutionException ex) {
Sean Condone45e12c2018-05-04 16:27:07 +0100560 log.warn("Connection to device {} had an execution exception.", dev.deviceId(), ex);
Palash Kala4c71ee72017-05-24 17:43:59 +0900561 return false;
562 }
Michele Santuaric372c222017-01-12 09:41:25 +0100563
Andrea Campanellac6ecc632016-03-10 17:57:06 -0800564 } catch (ProcessingException e) {
Michele Santuaric372c222017-01-12 09:41:25 +0100565 log.warn("Cannot connect to device {}", dev, e);
Andrea Campanella945ded22016-01-07 13:17:43 -0800566 }
567 return false;
568 }
569
570 private class InternalNetworkConfigListener implements NetworkConfigListener {
Andrea Campanella945ded22016-01-07 13:17:43 -0800571 @Override
572 public void event(NetworkConfigEvent event) {
Georgios Katsikas15841e22018-07-28 14:27:28 +0200573 if (!isRelevant(event)) {
574 log.warn("Irrelevant network configuration event: {}", event);
575 return;
576 }
577
Yuta HIGUCHIfc667052017-12-05 18:17:22 -0800578 ExecutorService bg = SharedExecutors.getSingleThreadExecutor();
579 if (event.type() == CONFIG_REMOVED) {
Sarithabd0b1f12018-05-11 11:35:27 +0530580 log.debug("Config {} event for rest device provider for {}",
581 event.type(), event.prevConfig().get().subject());
Yuta HIGUCHIfc667052017-12-05 18:17:22 -0800582 DeviceId did = (DeviceId) event.subject();
583 bg.execute(() -> deviceRemoved(did));
584 } else {
Sarithabd0b1f12018-05-11 11:35:27 +0530585 //CONFIG_ADDED or CONFIG_UPDATED
586 log.debug("Config {} event for rest device provider for {}",
587 event.type(), event.config().get().subject());
Yuta HIGUCHIfc667052017-12-05 18:17:22 -0800588 RestDeviceConfig cfg = (RestDeviceConfig) event.config().get();
589 RestSBDevice restSBDevice = toInactiveRestSBDevice(cfg);
590 bg.execute(() -> connectDevice(restSBDevice));
591 }
Andrea Campanella945ded22016-01-07 13:17:43 -0800592 }
593
594 @Override
595 public boolean isRelevant(NetworkConfigEvent event) {
Yuta HIGUCHI872fdbe2017-12-05 15:34:28 -0800596 return event.configClass().equals(RestDeviceConfig.class) &&
Andrea Campanella945ded22016-01-07 13:17:43 -0800597 (event.type() == CONFIG_ADDED ||
Yuta HIGUCHIfc667052017-12-05 18:17:22 -0800598 event.type() == CONFIG_UPDATED ||
599 event.type() == CONFIG_REMOVED);
Andrea Campanella945ded22016-01-07 13:17:43 -0800600 }
601 }
Saurav Dasa2d37502016-03-25 17:50:40 -0700602
603 @Override
604 public void changePortState(DeviceId deviceId, PortNumber portNumber,
605 boolean enable) {
fahadnaeemkhan482951f2017-08-24 16:35:17 -0700606 Device device = deviceService.getDevice(deviceId);
607 if (device != null) {
608 if (device.is(PortAdmin.class)) {
609 PortAdmin portAdmin = device.as(PortAdmin.class);
610 CompletableFuture<Boolean> modified;
611 if (enable) {
612 modified = portAdmin.enable(portNumber);
613 } else {
614 modified = portAdmin.disable(portNumber);
615 }
616 modified.thenAcceptAsync(result -> {
617 if (!result) {
618 log.warn("Device {} port {} state can't be changed to {}",
619 deviceId, portNumber, enable);
620 }
621 });
622
623 } else {
624 log.warn("Device {} does not support PortAdmin behavior", deviceId);
625 }
626 } else {
627 log.warn("unable to get the device {}, port {} state can't be changed to {}",
628 deviceId, portNumber, enable);
629 }
Saurav Dasa2d37502016-03-25 17:50:40 -0700630 }
Andrea Campanella945ded22016-01-07 13:17:43 -0800631}