blob: c6a0ae9a6c598a45f5f3604e11d5a32b341bed0d [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;
Andrea Campanella945ded22016-01-07 13:17:43 -0800116 private final Logger log = getLogger(getClass());
117
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700118 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanella945ded22016-01-07 13:17:43 -0800119 protected DeviceProviderRegistry providerRegistry;
120
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700121 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanella945ded22016-01-07 13:17:43 -0800122 protected RestSBController controller;
123
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700124 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200125 protected NetworkConfigRegistry netCfgService;
126
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700127 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200128 protected ComponentConfigService compCfgService;
Andrea Campanella945ded22016-01-07 13:17:43 -0800129
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700130 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanella945ded22016-01-07 13:17:43 -0800131 protected CoreService coreService;
132
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700133 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanella6c71a052016-04-22 11:56:31 -0700134 protected DeviceService deviceService;
Andrea Campanellad8d92db2016-01-14 16:24:41 -0800135
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700136 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Michele Santuaric372c222017-01-12 09:41:25 +0100137 protected DriverService driverService;
138
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700139 //@Property(name = POLL_FREQUENCY, intValue = DEFAULT_POLL_FREQUENCY_SECONDS,
140 // label = "Configure poll frequency for port status and statistics; " +
141 // "default is 30 seconds")
Thomas Vachuska4167c3f2018-10-16 07:16:31 -0700142 private int pollFrequency = POLL_FREQUENCY_DEFAULT;
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200143
Andrea Campanella945ded22016-01-07 13:17:43 -0800144 private DeviceProviderService providerService;
Michele Santuaric372c222017-01-12 09:41:25 +0100145 private ApplicationId appId;
Andrea Campanella945ded22016-01-07 13:17:43 -0800146
Michal Mach13072e22017-06-21 09:12:24 +0200147 private ExecutorService executor;
Michal Mach67acb692017-06-21 12:05:36 +0200148 private final SharedScheduledExecutorService portStatisticsExecutor =
149 SharedScheduledExecutors.getPoolThreadExecutor();
Andrea Campanella784ee0f2016-02-17 15:50:59 -0800150
Michal Mach67acb692017-06-21 12:05:36 +0200151 private final List<ConfigFactory> factories = ImmutableList.of(
Andrea Campanella59b549d2017-04-14 21:58:16 +0200152 new ConfigFactory<DeviceId, RestDeviceConfig>(SubjectFactories.DEVICE_SUBJECT_FACTORY,
153 RestDeviceConfig.class,
154 REST) {
155 @Override
156 public RestDeviceConfig createConfig() {
157 return new RestDeviceConfig();
158 }
159 });
160
Michal Mach67acb692017-06-21 12:05:36 +0200161 private final NetworkConfigListener configListener = new InternalNetworkConfigListener();
Andrea Campanella945ded22016-01-07 13:17:43 -0800162
Michal Machd8099742017-06-19 14:32:35 +0200163 private ScheduledFuture<?> scheduledTask;
Andrea Campanella784ee0f2016-02-17 15:50:59 -0800164
Andrea Campanella945ded22016-01-07 13:17:43 -0800165
166 @Activate
167 public void activate() {
168 appId = coreService.registerApplication(APP_NAME);
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200169 compCfgService.registerProperties(getClass());
Andrea Campanella945ded22016-01-07 13:17:43 -0800170 providerService = providerRegistry.register(this);
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200171 factories.forEach(netCfgService::registerConfigFactory);
Georgios Katsikas6a4d1662017-07-27 13:22:31 +0200172 executor = Executors.newFixedThreadPool(
173 EXECUTOR_THREAD_POOL_SIZE, groupedThreads("onos/restsbprovider", "device-installer-%d", log)
174 );
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200175 netCfgService.addListener(configListener);
Andrea Campanella59b549d2017-04-14 21:58:16 +0200176 executor.execute(RestDeviceProvider.this::createAndConnectDevices);
Michal Machd8099742017-06-19 14:32:35 +0200177 scheduledTask = schedulePolling();
Andrea Campanella945ded22016-01-07 13:17:43 -0800178 log.info("Started");
179 }
180
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200181 @Modified
182 public void modified(ComponentContext context) {
183 int previousPollFrequency = pollFrequency;
184
185 if (context != null) {
186 Dictionary<?, ?> properties = context.getProperties();
187 pollFrequency = Tools.getIntegerProperty(properties, POLL_FREQUENCY,
Thomas Vachuska4167c3f2018-10-16 07:16:31 -0700188 POLL_FREQUENCY_DEFAULT);
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200189 log.info("Configured. Poll frequency is configured to {} seconds", pollFrequency);
190 }
191
192 // Re-schedule only if frequency has changed
193 if (!scheduledTask.isCancelled() && (previousPollFrequency != pollFrequency)) {
194 log.info("Re-scheduling port statistics task with frequency {} seconds", pollFrequency);
195 scheduledTask.cancel(true);
196 scheduledTask = schedulePolling();
197 }
198 }
199
Andrea Campanella945ded22016-01-07 13:17:43 -0800200 @Deactivate
201 public void deactivate() {
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200202 compCfgService.unregisterProperties(getClass(), false);
203 netCfgService.removeListener(configListener);
Andrea Campanella945ded22016-01-07 13:17:43 -0800204 providerRegistry.unregister(this);
205 providerService = null;
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200206 factories.forEach(netCfgService::unregisterConfigFactory);
Michal Machd8099742017-06-19 14:32:35 +0200207 scheduledTask.cancel(true);
Lukasz Ryba4da35c52017-06-20 09:14:11 +0200208 executor.shutdown();
Andrea Campanella945ded22016-01-07 13:17:43 -0800209 log.info("Stopped");
210 }
211
212 public RestDeviceProvider() {
213 super(new ProviderId(REST, PROVIDER));
214 }
215
216 @Override
217 public void triggerProbe(DeviceId deviceId) {
218 // TODO: This will be implemented later.
219 log.info("Triggering probe on device {}", deviceId);
220 }
221
222 @Override
223 public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
224 // TODO: This will be implemented later.
225 }
226
Andrea Campanella945ded22016-01-07 13:17:43 -0800227 @Override
228 public boolean isReachable(DeviceId deviceId) {
229 RestSBDevice restDevice = controller.getDevice(deviceId);
230 if (restDevice == null) {
Michele Santuaric372c222017-01-12 09:41:25 +0100231 restDevice = controller.getProxySBDevice(deviceId);
232 if (restDevice == null) {
233 log.debug("the requested device id: " +
234 deviceId.toString() +
235 " is not associated to any REST or REST " +
236 "proxy Device");
237 return false;
238 }
Andrea Campanella945ded22016-01-07 13:17:43 -0800239 }
240 return restDevice.isActive();
241 }
242
Michele Santuaric372c222017-01-12 09:41:25 +0100243 private void deviceAdded(RestSBDevice restSBDev) {
244 checkNotNull(restSBDev, ISNOTNULL);
245
Georgios Katsikas15841e22018-07-28 14:27:28 +0200246 Driver driver = driverService.getDriver(restSBDev.manufacturer().get(),
247 restSBDev.hwVersion().get(),
248 restSBDev.swVersion().get());
249
250 // Check if the server is controlling a single or multiple devices
Michele Santuaric372c222017-01-12 09:41:25 +0100251 if (restSBDev.isProxy()) {
Michele Santuaric372c222017-01-12 09:41:25 +0100252 if (driver != null && driver.hasBehaviour(DevicesDiscovery.class)) {
Georgios Katsikas15841e22018-07-28 14:27:28 +0200253 DevicesDiscovery devicesDiscovery = devicesDiscovery(restSBDev, driver);
Michele Santuaric372c222017-01-12 09:41:25 +0100254 Set<DeviceId> deviceIds = devicesDiscovery.deviceIds();
255 restSBDev.setActive(true);
Michal Mach67acb692017-06-21 12:05:36 +0200256 deviceIds.forEach(deviceId -> {
Michele Santuaric372c222017-01-12 09:41:25 +0100257 controller.addProxiedDevice(deviceId, restSBDev);
Georgios Katsikas15841e22018-07-28 14:27:28 +0200258 DeviceDescription devDesc = devicesDiscovery.deviceDetails(deviceId);
259 checkNotNull(devDesc, "DeviceDescription cannot be null");
260 providerService.deviceConnected(deviceId, mergeAnn(restSBDev.deviceId(), devDesc));
Michele Santuaric372c222017-01-12 09:41:25 +0100261
262 if (driver.hasBehaviour(DeviceDescriptionDiscovery.class)) {
263 DriverHandler h = driverService.createHandler(deviceId);
Georgios Katsikas15841e22018-07-28 14:27:28 +0200264 DeviceDescriptionDiscovery devDisc = h.behaviour(DeviceDescriptionDiscovery.class);
265 providerService.updatePorts(deviceId, devDisc.discoverPortDetails());
Michele Santuaric372c222017-01-12 09:41:25 +0100266 }
267
268 checkAndUpdateDevice(deviceId);
Michele Santuaric372c222017-01-12 09:41:25 +0100269 });
270 } else {
271 log.warn("Driver not found for {}", restSBDev);
272 }
273 } else {
274 DeviceId deviceId = restSBDev.deviceId();
Georgios Katsikas15841e22018-07-28 14:27:28 +0200275 if (driver != null && driver.hasBehaviour(DevicesDiscovery.class)) {
276 restSBDev.setActive(true);
277 DevicesDiscovery devicesDiscovery = devicesDiscovery(restSBDev, driver);
278 DeviceDescription deviceDescription = devicesDiscovery.deviceDetails(deviceId);
279 checkNotNull(deviceDescription, "DeviceDescription cannot be null");
280 providerService.deviceConnected(deviceId, deviceDescription);
281
282 if (driver.hasBehaviour(DeviceDescriptionDiscovery.class)) {
283 DriverHandler h = driverService.createHandler(deviceId);
284 DeviceDescriptionDiscovery deviceDiscovery = h.behaviour(DeviceDescriptionDiscovery.class);
285 providerService.updatePorts(deviceId, deviceDiscovery.discoverPortDetails());
286 }
287 } else {
288 ChassisId cid = new ChassisId();
289 String ipAddress = restSBDev.ip().toString();
290 SparseAnnotations annotations = DefaultAnnotations.builder()
291 .set(IPADDRESS, ipAddress)
292 .set(AnnotationKeys.PROTOCOL, REST.toUpperCase())
293 .build();
294 DeviceDescription deviceDescription = new DefaultDeviceDescription(
295 deviceId.uri(),
296 Device.Type.SWITCH,
297 UNKNOWN, UNKNOWN,
298 UNKNOWN, UNKNOWN,
299 cid,
300 annotations);
301 restSBDev.setActive(true);
302 providerService.deviceConnected(deviceId, deviceDescription);
303 }
304
Michele Santuaric372c222017-01-12 09:41:25 +0100305 checkAndUpdateDevice(deviceId);
Michele Santuaric372c222017-01-12 09:41:25 +0100306 }
307 }
308
309 private DefaultDeviceDescription mergeAnn(DeviceId devId, DeviceDescription desc) {
310 return new DefaultDeviceDescription(
311 desc,
312 DefaultAnnotations.merge(
313 DefaultAnnotations.builder()
314 .set(AnnotationKeys.PROTOCOL, REST.toUpperCase())
315 // The rest server added as annotation to the device
316 .set(AnnotationKeys.REST_SERVER, devId.toString())
317 .build(),
318 desc.annotations()));
319 }
320
321 private DevicesDiscovery devicesDiscovery(RestSBDevice restSBDevice, Driver driver) {
322 DriverData driverData = new DefaultDriverData(driver, restSBDevice.deviceId());
Georgios Katsikas15841e22018-07-28 14:27:28 +0200323 DevicesDiscovery devicesDiscovery = driver.createBehaviour(driverData, DevicesDiscovery.class);
Michele Santuaric372c222017-01-12 09:41:25 +0100324 devicesDiscovery.setHandler(new DefaultDriverHandler(driverData));
325 return devicesDiscovery;
Andrea Campanella945ded22016-01-07 13:17:43 -0800326 }
327
Konstantinos Kanonakis3cd85552016-11-17 10:11:13 -0600328 private void checkAndUpdateDevice(DeviceId deviceId) {
329 if (deviceService.getDevice(deviceId) == null) {
Georgios Katsikas15841e22018-07-28 14:27:28 +0200330 log.warn("Device {} has not been added to store, maybe due to a problem in connectivity", deviceId);
Konstantinos Kanonakis3cd85552016-11-17 10:11:13 -0600331 } else {
332 boolean isReachable = isReachable(deviceId);
333 if (isReachable && deviceService.isAvailable(deviceId)) {
334 Device device = deviceService.getDevice(deviceId);
335 if (device.is(DeviceDescriptionDiscovery.class)) {
Michele Santuaric372c222017-01-12 09:41:25 +0100336 DeviceDescriptionDiscovery deviceDescriptionDiscovery =
337 device.as(DeviceDescriptionDiscovery.class);
338 DeviceDescription updatedDeviceDescription =
339 deviceDescriptionDiscovery.discoverDeviceDetails();
340 if (updatedDeviceDescription != null &&
341 !descriptionEquals(device, updatedDeviceDescription)) {
342 providerService.deviceConnected(
343 deviceId,
344 new DefaultDeviceDescription(
345 updatedDeviceDescription, true,
346 updatedDeviceDescription.annotations()));
Konstantinos Kanonakis3cd85552016-11-17 10:11:13 -0600347 //if ports are not discovered, retry the discovery
348 if (deviceService.getPorts(deviceId).isEmpty()) {
349 discoverPorts(deviceId);
350 }
351 }
352 } else {
353 log.warn("No DeviceDescriptionDiscovery behaviour for device {}", deviceId);
354 }
355 } else if (!isReachable && deviceService.isAvailable(deviceId)) {
356 providerService.deviceDisconnected(deviceId);
357 }
358 }
359 }
360
361 private boolean descriptionEquals(Device device, DeviceDescription updatedDeviceDescription) {
Michele Santuarid2c8f212017-01-09 18:23:45 +0100362 return Objects.equal(device.id().uri(), updatedDeviceDescription.deviceUri())
Konstantinos Kanonakis3cd85552016-11-17 10:11:13 -0600363 && Objects.equal(device.type(), updatedDeviceDescription.type())
364 && Objects.equal(device.manufacturer(), updatedDeviceDescription.manufacturer())
365 && Objects.equal(device.hwVersion(), updatedDeviceDescription.hwVersion())
366 && Objects.equal(device.swVersion(), updatedDeviceDescription.swVersion())
367 && Objects.equal(device.serialNumber(), updatedDeviceDescription.serialNumber())
368 && Objects.equal(device.chassisId(), updatedDeviceDescription.chassisId())
369 && Objects.equal(device.annotations(), updatedDeviceDescription.annotations());
370 }
371
Andrea Campanella86294db2016-03-07 11:42:49 -0800372 private void deviceRemoved(DeviceId deviceId) {
Michele Santuaric372c222017-01-12 09:41:25 +0100373 checkNotNull(deviceId, ISNOTNULL);
Andrea Campanella945ded22016-01-07 13:17:43 -0800374 providerService.deviceDisconnected(deviceId);
Michal Mach67acb692017-06-21 12:05:36 +0200375 controller.getProxiedDevices(deviceId).forEach(device -> {
Michele Santuaric372c222017-01-12 09:41:25 +0100376 controller.removeProxiedDevice(device);
377 providerService.deviceDisconnected(device);
378 });
Andrea Campanella86294db2016-03-07 11:42:49 -0800379 controller.removeDevice(deviceId);
Andrea Campanella945ded22016-01-07 13:17:43 -0800380 }
381
Andrea Campanella59b549d2017-04-14 21:58:16 +0200382 //Method to connect devices provided via net-cfg under devices/ tree
383 private void createAndConnectDevices() {
384 Set<DeviceId> deviceSubjects =
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200385 netCfgService.getSubjects(DeviceId.class, RestDeviceConfig.class);
Andrea Campanella59b549d2017-04-14 21:58:16 +0200386 connectDevices(deviceSubjects.stream()
fahadnaeemkhan02ffa712017-12-01 19:49:45 -0800387 .filter(deviceId -> deviceService.getDevice(deviceId) == null)
388 .map(deviceId -> {
389 RestDeviceConfig config =
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200390 netCfgService.getConfig(deviceId, RestDeviceConfig.class);
Yuta HIGUCHIfc667052017-12-05 18:17:22 -0800391 return toInactiveRestSBDevice(config);
fahadnaeemkhan02ffa712017-12-01 19:49:45 -0800392 }).collect(Collectors.toSet()));
Andrea Campanella59b549d2017-04-14 21:58:16 +0200393 }
394
Yuta HIGUCHIfc667052017-12-05 18:17:22 -0800395 private RestSBDevice toInactiveRestSBDevice(RestDeviceConfig config) {
396 return new DefaultRestSBDevice(config.ip(),
397 config.port(),
398 config.username(),
399 config.password(),
400 config.protocol(),
401 config.url(),
402 false,
403 config.testUrl(),
404 config.manufacturer(),
405 config.hwVersion(),
406 config.swVersion(),
Georgios Katsikas15841e22018-07-28 14:27:28 +0200407 config.isProxy(),
Yuta HIGUCHIfc667052017-12-05 18:17:22 -0800408 config.authenticationScheme(),
409 config.token()
410 );
411 }
412
Andrea Campanella59b549d2017-04-14 21:58:16 +0200413 private void connectDevices(Set<RestSBDevice> devices) {
414 //Precomputing the devices to be removed
415 Set<RestSBDevice> toBeRemoved = new HashSet<>(controller.getDevices().values());
416 toBeRemoved.removeAll(devices);
417 //Adding new devices
418 devices.stream()
419 .filter(device -> {
420 device.setActive(false);
421 controller.addDevice(device);
422 return testDeviceConnection(device);
423 })
Michal Mach67acb692017-06-21 12:05:36 +0200424 .forEach(this::deviceAdded);
Andrea Campanella59b549d2017-04-14 21:58:16 +0200425 //Removing devices not wanted anymore
426 toBeRemoved.forEach(device -> deviceRemoved(device.deviceId()));
427 }
428
Yuta HIGUCHIfc667052017-12-05 18:17:22 -0800429 private void connectDevice(RestSBDevice device) {
430 // TODO borrowed from above,
431 // not sure why setting it to inactive
432 device.setActive(false);
433 controller.addDevice(device);
434 if (testDeviceConnection(device)) {
435 deviceAdded(device);
436 }
437 }
438
Michal Machd8099742017-06-19 14:32:35 +0200439 private ScheduledFuture schedulePolling() {
440 return portStatisticsExecutor.scheduleAtFixedRate(this::executePortStatisticsUpdate,
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200441 pollFrequency / 2, pollFrequency,
Michal Machd8099742017-06-19 14:32:35 +0200442 TimeUnit.SECONDS);
443 }
444
445 private void executePortStatisticsUpdate() {
446 controller.getDevices().keySet().forEach(this::updatePortStatistics);
447 }
448
449 private void updatePortStatistics(DeviceId deviceId) {
450 Device device = deviceService.getDevice(deviceId);
451 checkNotNull(device, "device cannot be null");
452
453 if (device.is(PortStatisticsDiscovery.class)) {
454 PortStatisticsDiscovery portStatisticsDiscovery = device.as(PortStatisticsDiscovery.class);
455 Collection<PortStatistics> portStatistics = portStatisticsDiscovery.discoverPortStatistics();
456 if (portStatistics != null && !portStatistics.isEmpty()) {
457 providerService.updatePortStatistics(deviceId, portStatistics);
458 }
459 } else {
460 log.debug("No port statistics getter behaviour for device {}", deviceId);
461 }
462 }
463
Andrea Campanella6c71a052016-04-22 11:56:31 -0700464 private void discoverPorts(DeviceId deviceId) {
465 Device device = deviceService.getDevice(deviceId);
Georgios Katsikas15841e22018-07-28 14:27:28 +0200466 DeviceDescriptionDiscovery deviceDescriptionDiscovery = device.as(DeviceDescriptionDiscovery.class);
Ray Milkey640fedd2018-02-08 09:02:26 -0800467 providerService.updatePorts(deviceId, deviceDescriptionDiscovery.discoverPortDetails());
Andrea Campanella6c71a052016-04-22 11:56:31 -0700468 }
469
Michele Santuaric372c222017-01-12 09:41:25 +0100470 private boolean testDeviceConnection(RestSBDevice dev) {
Andrea Campanella945ded22016-01-07 13:17:43 -0800471 try {
Palash Kala4c71ee72017-05-24 17:43:59 +0900472 Callable<Boolean> connectionSuccess;
473
Michele Santuaric372c222017-01-12 09:41:25 +0100474 if (dev.testUrl().isPresent()) {
Michal Mach67acb692017-06-21 12:05:36 +0200475 connectionSuccess = () ->
476 controller.get(dev.deviceId(), dev.testUrl().get(), MediaType.APPLICATION_JSON_TYPE) != null;
Palash Kala4c71ee72017-05-24 17:43:59 +0900477 } else {
Michal Mach67acb692017-06-21 12:05:36 +0200478 connectionSuccess = () ->
479 controller.get(dev.deviceId(), "", MediaType.APPLICATION_JSON_TYPE) != null;
Michele Santuaric372c222017-01-12 09:41:25 +0100480 }
Palash Kala4c71ee72017-05-24 17:43:59 +0900481
482 Future<Boolean> future = executor.submit(connectionSuccess);
483 try {
Michal Mach67acb692017-06-21 12:05:36 +0200484 return future.get(REST_TIMEOUT_SEC, TimeUnit.SECONDS);
Palash Kala4c71ee72017-05-24 17:43:59 +0900485 } catch (TimeoutException ex) {
Sean Condone45e12c2018-05-04 16:27:07 +0100486 log.warn("Connection to device {} timed out: {}", dev.deviceId(), ex.getMessage());
Palash Kala4c71ee72017-05-24 17:43:59 +0900487 return false;
488 } catch (InterruptedException ex) {
Sean Condone45e12c2018-05-04 16:27:07 +0100489 log.warn("Connection to device {} interrupted: {}", dev.deviceId(), ex.getMessage());
Ray Milkey5c7d4882018-02-05 14:50:39 -0800490 Thread.currentThread().interrupt();
Palash Kala4c71ee72017-05-24 17:43:59 +0900491 return false;
492 } catch (ExecutionException ex) {
Sean Condone45e12c2018-05-04 16:27:07 +0100493 log.warn("Connection to device {} had an execution exception.", dev.deviceId(), ex);
Palash Kala4c71ee72017-05-24 17:43:59 +0900494 return false;
495 }
Michele Santuaric372c222017-01-12 09:41:25 +0100496
Andrea Campanellac6ecc632016-03-10 17:57:06 -0800497 } catch (ProcessingException e) {
Michele Santuaric372c222017-01-12 09:41:25 +0100498 log.warn("Cannot connect to device {}", dev, e);
Andrea Campanella945ded22016-01-07 13:17:43 -0800499 }
500 return false;
501 }
502
503 private class InternalNetworkConfigListener implements NetworkConfigListener {
Andrea Campanella945ded22016-01-07 13:17:43 -0800504 @Override
505 public void event(NetworkConfigEvent event) {
Georgios Katsikas15841e22018-07-28 14:27:28 +0200506 if (!isRelevant(event)) {
507 log.warn("Irrelevant network configuration event: {}", event);
508 return;
509 }
510
Yuta HIGUCHIfc667052017-12-05 18:17:22 -0800511 ExecutorService bg = SharedExecutors.getSingleThreadExecutor();
512 if (event.type() == CONFIG_REMOVED) {
513 DeviceId did = (DeviceId) event.subject();
514 bg.execute(() -> deviceRemoved(did));
515 } else {
516 // CONFIG_ADDED or CONFIG_UPDATED
517 RestDeviceConfig cfg = (RestDeviceConfig) event.config().get();
518 RestSBDevice restSBDevice = toInactiveRestSBDevice(cfg);
519 bg.execute(() -> connectDevice(restSBDevice));
520 }
Andrea Campanella945ded22016-01-07 13:17:43 -0800521 }
522
523 @Override
524 public boolean isRelevant(NetworkConfigEvent event) {
Yuta HIGUCHI872fdbe2017-12-05 15:34:28 -0800525 return event.configClass().equals(RestDeviceConfig.class) &&
Andrea Campanella945ded22016-01-07 13:17:43 -0800526 (event.type() == CONFIG_ADDED ||
Yuta HIGUCHIfc667052017-12-05 18:17:22 -0800527 event.type() == CONFIG_UPDATED ||
528 event.type() == CONFIG_REMOVED);
Andrea Campanella945ded22016-01-07 13:17:43 -0800529 }
530 }
Saurav Dasa2d37502016-03-25 17:50:40 -0700531
532 @Override
533 public void changePortState(DeviceId deviceId, PortNumber portNumber,
534 boolean enable) {
fahadnaeemkhan482951f2017-08-24 16:35:17 -0700535 Device device = deviceService.getDevice(deviceId);
536 if (device != null) {
537 if (device.is(PortAdmin.class)) {
538 PortAdmin portAdmin = device.as(PortAdmin.class);
539 CompletableFuture<Boolean> modified;
540 if (enable) {
541 modified = portAdmin.enable(portNumber);
542 } else {
543 modified = portAdmin.disable(portNumber);
544 }
545 modified.thenAcceptAsync(result -> {
546 if (!result) {
547 log.warn("Device {} port {} state can't be changed to {}",
548 deviceId, portNumber, enable);
549 }
550 });
551
552 } else {
553 log.warn("Device {} does not support PortAdmin behavior", deviceId);
554 }
555 } else {
556 log.warn("unable to get the device {}, port {} state can't be changed to {}",
557 deviceId, portNumber, enable);
558 }
Saurav Dasa2d37502016-03-25 17:50:40 -0700559 }
Andrea Campanella945ded22016-01-07 13:17:43 -0800560}