blob: e6d8b7b23e5ee1c3c356378d94a3ea5bae1339f3 [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.apache.felix.scr.annotations.Activate;
22import org.apache.felix.scr.annotations.Component;
23import org.apache.felix.scr.annotations.Deactivate;
Georgios Katsikas0f12bc62018-07-06 09:06:42 +020024import org.apache.felix.scr.annotations.Modified;
25import org.apache.felix.scr.annotations.Property;
Andrea Campanella945ded22016-01-07 13:17:43 -080026import org.apache.felix.scr.annotations.Reference;
27import org.apache.felix.scr.annotations.ReferenceCardinality;
28import org.onlab.packet.ChassisId;
Yuta HIGUCHIfc667052017-12-05 18:17:22 -080029import org.onlab.util.SharedExecutors;
Michal Machd8099742017-06-19 14:32:35 +020030import org.onlab.util.SharedScheduledExecutorService;
31import org.onlab.util.SharedScheduledExecutors;
Georgios Katsikas0f12bc62018-07-06 09:06:42 +020032import org.onlab.util.Tools;
33import org.onosproject.cfg.ComponentConfigService;
Andrea Campanella945ded22016-01-07 13:17:43 -080034import org.onosproject.core.ApplicationId;
35import org.onosproject.core.CoreService;
Marc De Leenheerb0d131c2016-03-01 20:34:59 -080036import org.onosproject.net.AnnotationKeys;
Andrea Campanella945ded22016-01-07 13:17:43 -080037import org.onosproject.net.DefaultAnnotations;
38import org.onosproject.net.Device;
39import org.onosproject.net.DeviceId;
40import org.onosproject.net.MastershipRole;
Saurav Dasa2d37502016-03-25 17:50:40 -070041import org.onosproject.net.PortNumber;
Andrea Campanella945ded22016-01-07 13:17:43 -080042import org.onosproject.net.SparseAnnotations;
Michele Santuaric372c222017-01-12 09:41:25 +010043import org.onosproject.net.behaviour.DevicesDiscovery;
fahadnaeemkhan02ffa712017-12-01 19:49:45 -080044import org.onosproject.net.behaviour.PortAdmin;
Andrea Campanella945ded22016-01-07 13:17:43 -080045import org.onosproject.net.config.ConfigFactory;
46import org.onosproject.net.config.NetworkConfigEvent;
47import org.onosproject.net.config.NetworkConfigListener;
48import org.onosproject.net.config.NetworkConfigRegistry;
Andrea Campanella59b549d2017-04-14 21:58:16 +020049import org.onosproject.net.config.basics.SubjectFactories;
Andrea Campanella945ded22016-01-07 13:17:43 -080050import org.onosproject.net.device.DefaultDeviceDescription;
51import org.onosproject.net.device.DeviceDescription;
Andrea Campanella6c71a052016-04-22 11:56:31 -070052import org.onosproject.net.device.DeviceDescriptionDiscovery;
Andrea Campanella945ded22016-01-07 13:17:43 -080053import org.onosproject.net.device.DeviceProvider;
54import org.onosproject.net.device.DeviceProviderRegistry;
55import org.onosproject.net.device.DeviceProviderService;
Andrea Campanella6c71a052016-04-22 11:56:31 -070056import org.onosproject.net.device.DeviceService;
Michal Machd8099742017-06-19 14:32:35 +020057import org.onosproject.net.device.PortStatistics;
58import org.onosproject.net.device.PortStatisticsDiscovery;
Michele Santuaric372c222017-01-12 09:41:25 +010059import org.onosproject.net.driver.DefaultDriverData;
60import org.onosproject.net.driver.DefaultDriverHandler;
61import org.onosproject.net.driver.Driver;
62import org.onosproject.net.driver.DriverData;
63import org.onosproject.net.driver.DriverHandler;
64import org.onosproject.net.driver.DriverService;
Andrea Campanella945ded22016-01-07 13:17:43 -080065import org.onosproject.net.provider.AbstractProvider;
66import org.onosproject.net.provider.ProviderId;
Andrea Campanella59b549d2017-04-14 21:58:16 +020067import org.onosproject.protocol.rest.DefaultRestSBDevice;
Andrea Campanella945ded22016-01-07 13:17:43 -080068import org.onosproject.protocol.rest.RestSBController;
69import org.onosproject.protocol.rest.RestSBDevice;
Georgios Katsikas0f12bc62018-07-06 09:06:42 +020070import org.osgi.service.component.ComponentContext;
Andrea Campanella945ded22016-01-07 13:17:43 -080071import org.slf4j.Logger;
72
Andrea Campanellac6ecc632016-03-10 17:57:06 -080073import javax.ws.rs.ProcessingException;
Michal Mach67acb692017-06-21 12:05:36 +020074import javax.ws.rs.core.MediaType;
Michal Machd8099742017-06-19 14:32:35 +020075import java.util.Collection;
Georgios Katsikas0f12bc62018-07-06 09:06:42 +020076import java.util.Dictionary;
Andrea Campanella945ded22016-01-07 13:17:43 -080077import java.util.HashSet;
Andrea Campanella59b549d2017-04-14 21:58:16 +020078import java.util.List;
Andrea Campanella945ded22016-01-07 13:17:43 -080079import java.util.Set;
Palash Kala4c71ee72017-05-24 17:43:59 +090080import java.util.concurrent.Callable;
fahadnaeemkhan02ffa712017-12-01 19:49:45 -080081import java.util.concurrent.CompletableFuture;
Palash Kala4c71ee72017-05-24 17:43:59 +090082import java.util.concurrent.ExecutionException;
Andrea Campanella784ee0f2016-02-17 15:50:59 -080083import java.util.concurrent.ExecutorService;
84import java.util.concurrent.Executors;
Palash Kala4c71ee72017-05-24 17:43:59 +090085import java.util.concurrent.Future;
Michal Machd8099742017-06-19 14:32:35 +020086import java.util.concurrent.ScheduledFuture;
Palash Kala4c71ee72017-05-24 17:43:59 +090087import java.util.concurrent.TimeUnit;
88import java.util.concurrent.TimeoutException;
Andrea Campanella59b549d2017-04-14 21:58:16 +020089import java.util.stream.Collectors;
Andrea Campanella945ded22016-01-07 13:17:43 -080090
Michele Santuaric372c222017-01-12 09:41:25 +010091import static com.google.common.base.Preconditions.checkNotNull;
Andrea Campanella784ee0f2016-02-17 15:50:59 -080092import static org.onlab.util.Tools.groupedThreads;
Andrea Campanella945ded22016-01-07 13:17:43 -080093import static org.onosproject.net.config.NetworkConfigEvent.Type.CONFIG_ADDED;
Yuta HIGUCHIfc667052017-12-05 18:17:22 -080094import static org.onosproject.net.config.NetworkConfigEvent.Type.CONFIG_REMOVED;
Andrea Campanella945ded22016-01-07 13:17:43 -080095import static org.onosproject.net.config.NetworkConfigEvent.Type.CONFIG_UPDATED;
Andrea Campanella945ded22016-01-07 13:17:43 -080096import static org.slf4j.LoggerFactory.getLogger;
97
98/**
99 * Provider for devices that use REST as means of configuration communication.
100 */
101@Component(immediate = true)
102public class RestDeviceProvider extends AbstractProvider
103 implements DeviceProvider {
104 private static final String APP_NAME = "org.onosproject.restsb";
Andrea Campanella59b549d2017-04-14 21:58:16 +0200105 protected static final String REST = "rest";
Andrea Campanella945ded22016-01-07 13:17:43 -0800106 private static final String PROVIDER = "org.onosproject.provider.rest.device";
107 private static final String IPADDRESS = "ipaddress";
Michal Mach67acb692017-06-21 12:05:36 +0200108 private static final String ISNOTNULL = "Rest device is not null";
Michele Santuaric372c222017-01-12 09:41:25 +0100109 private static final String UNKNOWN = "unknown";
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200110 private static final String POLL_FREQUENCY = "pollFrequency";
Palash Kala4c71ee72017-05-24 17:43:59 +0900111 private static final int REST_TIMEOUT_SEC = 5;
Georgios Katsikas6a4d1662017-07-27 13:22:31 +0200112 private static final int EXECUTOR_THREAD_POOL_SIZE = 8;
Andrea Campanella945ded22016-01-07 13:17:43 -0800113 private final Logger log = getLogger(getClass());
114
115 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
116 protected DeviceProviderRegistry providerRegistry;
117
118 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
119 protected RestSBController controller;
120
121 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200122 protected NetworkConfigRegistry netCfgService;
123
124 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
125 protected ComponentConfigService compCfgService;
Andrea Campanella945ded22016-01-07 13:17:43 -0800126
127 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
128 protected CoreService coreService;
129
Andrea Campanellad8d92db2016-01-14 16:24:41 -0800130 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Andrea Campanella6c71a052016-04-22 11:56:31 -0700131 protected DeviceService deviceService;
Andrea Campanellad8d92db2016-01-14 16:24:41 -0800132
Michele Santuaric372c222017-01-12 09:41:25 +0100133 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
134 protected DriverService driverService;
135
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200136 private static final int DEFAULT_POLL_FREQUENCY_SECONDS = 30;
137 @Property(name = POLL_FREQUENCY, intValue = DEFAULT_POLL_FREQUENCY_SECONDS,
138 label = "Configure poll frequency for port status and statistics; " +
139 "default is 30 seconds")
140 private int pollFrequency = DEFAULT_POLL_FREQUENCY_SECONDS;
141
Andrea Campanella945ded22016-01-07 13:17:43 -0800142 private DeviceProviderService providerService;
Michele Santuaric372c222017-01-12 09:41:25 +0100143 private ApplicationId appId;
Andrea Campanella945ded22016-01-07 13:17:43 -0800144
Michal Mach13072e22017-06-21 09:12:24 +0200145 private ExecutorService executor;
Michal Mach67acb692017-06-21 12:05:36 +0200146 private final SharedScheduledExecutorService portStatisticsExecutor =
147 SharedScheduledExecutors.getPoolThreadExecutor();
Andrea Campanella784ee0f2016-02-17 15:50:59 -0800148
Michal Mach67acb692017-06-21 12:05:36 +0200149 private final List<ConfigFactory> factories = ImmutableList.of(
Andrea Campanella59b549d2017-04-14 21:58:16 +0200150 new ConfigFactory<DeviceId, RestDeviceConfig>(SubjectFactories.DEVICE_SUBJECT_FACTORY,
151 RestDeviceConfig.class,
152 REST) {
153 @Override
154 public RestDeviceConfig createConfig() {
155 return new RestDeviceConfig();
156 }
157 });
158
Michal Mach67acb692017-06-21 12:05:36 +0200159 private final NetworkConfigListener configListener = new InternalNetworkConfigListener();
Andrea Campanella945ded22016-01-07 13:17:43 -0800160
Michal Machd8099742017-06-19 14:32:35 +0200161 private ScheduledFuture<?> scheduledTask;
Andrea Campanella784ee0f2016-02-17 15:50:59 -0800162
Andrea Campanella945ded22016-01-07 13:17:43 -0800163
164 @Activate
165 public void activate() {
166 appId = coreService.registerApplication(APP_NAME);
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200167 compCfgService.registerProperties(getClass());
Andrea Campanella945ded22016-01-07 13:17:43 -0800168 providerService = providerRegistry.register(this);
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200169 factories.forEach(netCfgService::registerConfigFactory);
Georgios Katsikas6a4d1662017-07-27 13:22:31 +0200170 executor = Executors.newFixedThreadPool(
171 EXECUTOR_THREAD_POOL_SIZE, groupedThreads("onos/restsbprovider", "device-installer-%d", log)
172 );
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200173 netCfgService.addListener(configListener);
Andrea Campanella59b549d2017-04-14 21:58:16 +0200174 executor.execute(RestDeviceProvider.this::createAndConnectDevices);
Michal Machd8099742017-06-19 14:32:35 +0200175 scheduledTask = schedulePolling();
Andrea Campanella945ded22016-01-07 13:17:43 -0800176 log.info("Started");
177 }
178
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200179 @Modified
180 public void modified(ComponentContext context) {
181 int previousPollFrequency = pollFrequency;
182
183 if (context != null) {
184 Dictionary<?, ?> properties = context.getProperties();
185 pollFrequency = Tools.getIntegerProperty(properties, POLL_FREQUENCY,
186 DEFAULT_POLL_FREQUENCY_SECONDS);
187 log.info("Configured. Poll frequency is configured to {} seconds", pollFrequency);
188 }
189
190 // Re-schedule only if frequency has changed
191 if (!scheduledTask.isCancelled() && (previousPollFrequency != pollFrequency)) {
192 log.info("Re-scheduling port statistics task with frequency {} seconds", pollFrequency);
193 scheduledTask.cancel(true);
194 scheduledTask = schedulePolling();
195 }
196 }
197
Andrea Campanella945ded22016-01-07 13:17:43 -0800198 @Deactivate
199 public void deactivate() {
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200200 compCfgService.unregisterProperties(getClass(), false);
201 netCfgService.removeListener(configListener);
Andrea Campanella945ded22016-01-07 13:17:43 -0800202 providerRegistry.unregister(this);
203 providerService = null;
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200204 factories.forEach(netCfgService::unregisterConfigFactory);
Michal Machd8099742017-06-19 14:32:35 +0200205 scheduledTask.cancel(true);
Lukasz Ryba4da35c52017-06-20 09:14:11 +0200206 executor.shutdown();
Andrea Campanella945ded22016-01-07 13:17:43 -0800207 log.info("Stopped");
208 }
209
210 public RestDeviceProvider() {
211 super(new ProviderId(REST, PROVIDER));
212 }
213
214 @Override
215 public void triggerProbe(DeviceId deviceId) {
216 // TODO: This will be implemented later.
217 log.info("Triggering probe on device {}", deviceId);
218 }
219
220 @Override
221 public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
222 // TODO: This will be implemented later.
223 }
224
Andrea Campanella945ded22016-01-07 13:17:43 -0800225 @Override
226 public boolean isReachable(DeviceId deviceId) {
227 RestSBDevice restDevice = controller.getDevice(deviceId);
228 if (restDevice == null) {
Michele Santuaric372c222017-01-12 09:41:25 +0100229 restDevice = controller.getProxySBDevice(deviceId);
230 if (restDevice == null) {
231 log.debug("the requested device id: " +
232 deviceId.toString() +
233 " is not associated to any REST or REST " +
234 "proxy Device");
235 return false;
236 }
Andrea Campanella945ded22016-01-07 13:17:43 -0800237 }
238 return restDevice.isActive();
239 }
240
Michele Santuaric372c222017-01-12 09:41:25 +0100241 private void deviceAdded(RestSBDevice restSBDev) {
242 checkNotNull(restSBDev, ISNOTNULL);
243
Georgios Katsikas15841e22018-07-28 14:27:28 +0200244 Driver driver = driverService.getDriver(restSBDev.manufacturer().get(),
245 restSBDev.hwVersion().get(),
246 restSBDev.swVersion().get());
247
248 // Check if the server is controlling a single or multiple devices
Michele Santuaric372c222017-01-12 09:41:25 +0100249 if (restSBDev.isProxy()) {
Michele Santuaric372c222017-01-12 09:41:25 +0100250 if (driver != null && driver.hasBehaviour(DevicesDiscovery.class)) {
Georgios Katsikas15841e22018-07-28 14:27:28 +0200251 DevicesDiscovery devicesDiscovery = devicesDiscovery(restSBDev, driver);
Michele Santuaric372c222017-01-12 09:41:25 +0100252 Set<DeviceId> deviceIds = devicesDiscovery.deviceIds();
253 restSBDev.setActive(true);
Michal Mach67acb692017-06-21 12:05:36 +0200254 deviceIds.forEach(deviceId -> {
Michele Santuaric372c222017-01-12 09:41:25 +0100255 controller.addProxiedDevice(deviceId, restSBDev);
Georgios Katsikas15841e22018-07-28 14:27:28 +0200256 DeviceDescription devDesc = devicesDiscovery.deviceDetails(deviceId);
257 checkNotNull(devDesc, "DeviceDescription cannot be null");
258 providerService.deviceConnected(deviceId, mergeAnn(restSBDev.deviceId(), devDesc));
Michele Santuaric372c222017-01-12 09:41:25 +0100259
260 if (driver.hasBehaviour(DeviceDescriptionDiscovery.class)) {
261 DriverHandler h = driverService.createHandler(deviceId);
Georgios Katsikas15841e22018-07-28 14:27:28 +0200262 DeviceDescriptionDiscovery devDisc = h.behaviour(DeviceDescriptionDiscovery.class);
263 providerService.updatePorts(deviceId, devDisc.discoverPortDetails());
Michele Santuaric372c222017-01-12 09:41:25 +0100264 }
265
266 checkAndUpdateDevice(deviceId);
Michele Santuaric372c222017-01-12 09:41:25 +0100267 });
268 } else {
269 log.warn("Driver not found for {}", restSBDev);
270 }
271 } else {
272 DeviceId deviceId = restSBDev.deviceId();
Georgios Katsikas15841e22018-07-28 14:27:28 +0200273 if (driver != null && driver.hasBehaviour(DevicesDiscovery.class)) {
274 restSBDev.setActive(true);
275 DevicesDiscovery devicesDiscovery = devicesDiscovery(restSBDev, driver);
276 DeviceDescription deviceDescription = devicesDiscovery.deviceDetails(deviceId);
277 checkNotNull(deviceDescription, "DeviceDescription cannot be null");
278 providerService.deviceConnected(deviceId, deviceDescription);
279
280 if (driver.hasBehaviour(DeviceDescriptionDiscovery.class)) {
281 DriverHandler h = driverService.createHandler(deviceId);
282 DeviceDescriptionDiscovery deviceDiscovery = h.behaviour(DeviceDescriptionDiscovery.class);
283 providerService.updatePorts(deviceId, deviceDiscovery.discoverPortDetails());
284 }
285 } else {
286 ChassisId cid = new ChassisId();
287 String ipAddress = restSBDev.ip().toString();
288 SparseAnnotations annotations = DefaultAnnotations.builder()
289 .set(IPADDRESS, ipAddress)
290 .set(AnnotationKeys.PROTOCOL, REST.toUpperCase())
291 .build();
292 DeviceDescription deviceDescription = new DefaultDeviceDescription(
293 deviceId.uri(),
294 Device.Type.SWITCH,
295 UNKNOWN, UNKNOWN,
296 UNKNOWN, UNKNOWN,
297 cid,
298 annotations);
299 restSBDev.setActive(true);
300 providerService.deviceConnected(deviceId, deviceDescription);
301 }
302
Michele Santuaric372c222017-01-12 09:41:25 +0100303 checkAndUpdateDevice(deviceId);
Michele Santuaric372c222017-01-12 09:41:25 +0100304 }
305 }
306
307 private DefaultDeviceDescription mergeAnn(DeviceId devId, DeviceDescription desc) {
308 return new DefaultDeviceDescription(
309 desc,
310 DefaultAnnotations.merge(
311 DefaultAnnotations.builder()
312 .set(AnnotationKeys.PROTOCOL, REST.toUpperCase())
313 // The rest server added as annotation to the device
314 .set(AnnotationKeys.REST_SERVER, devId.toString())
315 .build(),
316 desc.annotations()));
317 }
318
319 private DevicesDiscovery devicesDiscovery(RestSBDevice restSBDevice, Driver driver) {
320 DriverData driverData = new DefaultDriverData(driver, restSBDevice.deviceId());
Georgios Katsikas15841e22018-07-28 14:27:28 +0200321 DevicesDiscovery devicesDiscovery = driver.createBehaviour(driverData, DevicesDiscovery.class);
Michele Santuaric372c222017-01-12 09:41:25 +0100322 devicesDiscovery.setHandler(new DefaultDriverHandler(driverData));
323 return devicesDiscovery;
Andrea Campanella945ded22016-01-07 13:17:43 -0800324 }
325
Konstantinos Kanonakis3cd85552016-11-17 10:11:13 -0600326 private void checkAndUpdateDevice(DeviceId deviceId) {
327 if (deviceService.getDevice(deviceId) == null) {
Georgios Katsikas15841e22018-07-28 14:27:28 +0200328 log.warn("Device {} has not been added to store, maybe due to a problem in connectivity", deviceId);
Konstantinos Kanonakis3cd85552016-11-17 10:11:13 -0600329 } else {
330 boolean isReachable = isReachable(deviceId);
331 if (isReachable && deviceService.isAvailable(deviceId)) {
332 Device device = deviceService.getDevice(deviceId);
333 if (device.is(DeviceDescriptionDiscovery.class)) {
Michele Santuaric372c222017-01-12 09:41:25 +0100334 DeviceDescriptionDiscovery deviceDescriptionDiscovery =
335 device.as(DeviceDescriptionDiscovery.class);
336 DeviceDescription updatedDeviceDescription =
337 deviceDescriptionDiscovery.discoverDeviceDetails();
338 if (updatedDeviceDescription != null &&
339 !descriptionEquals(device, updatedDeviceDescription)) {
340 providerService.deviceConnected(
341 deviceId,
342 new DefaultDeviceDescription(
343 updatedDeviceDescription, true,
344 updatedDeviceDescription.annotations()));
Konstantinos Kanonakis3cd85552016-11-17 10:11:13 -0600345 //if ports are not discovered, retry the discovery
346 if (deviceService.getPorts(deviceId).isEmpty()) {
347 discoverPorts(deviceId);
348 }
349 }
350 } else {
351 log.warn("No DeviceDescriptionDiscovery behaviour for device {}", deviceId);
352 }
353 } else if (!isReachable && deviceService.isAvailable(deviceId)) {
354 providerService.deviceDisconnected(deviceId);
355 }
356 }
357 }
358
359 private boolean descriptionEquals(Device device, DeviceDescription updatedDeviceDescription) {
Michele Santuarid2c8f212017-01-09 18:23:45 +0100360 return Objects.equal(device.id().uri(), updatedDeviceDescription.deviceUri())
Konstantinos Kanonakis3cd85552016-11-17 10:11:13 -0600361 && Objects.equal(device.type(), updatedDeviceDescription.type())
362 && Objects.equal(device.manufacturer(), updatedDeviceDescription.manufacturer())
363 && Objects.equal(device.hwVersion(), updatedDeviceDescription.hwVersion())
364 && Objects.equal(device.swVersion(), updatedDeviceDescription.swVersion())
365 && Objects.equal(device.serialNumber(), updatedDeviceDescription.serialNumber())
366 && Objects.equal(device.chassisId(), updatedDeviceDescription.chassisId())
367 && Objects.equal(device.annotations(), updatedDeviceDescription.annotations());
368 }
369
Andrea Campanella86294db2016-03-07 11:42:49 -0800370 private void deviceRemoved(DeviceId deviceId) {
Michele Santuaric372c222017-01-12 09:41:25 +0100371 checkNotNull(deviceId, ISNOTNULL);
Andrea Campanella945ded22016-01-07 13:17:43 -0800372 providerService.deviceDisconnected(deviceId);
Michal Mach67acb692017-06-21 12:05:36 +0200373 controller.getProxiedDevices(deviceId).forEach(device -> {
Michele Santuaric372c222017-01-12 09:41:25 +0100374 controller.removeProxiedDevice(device);
375 providerService.deviceDisconnected(device);
376 });
Andrea Campanella86294db2016-03-07 11:42:49 -0800377 controller.removeDevice(deviceId);
Andrea Campanella945ded22016-01-07 13:17:43 -0800378 }
379
Andrea Campanella59b549d2017-04-14 21:58:16 +0200380 //Method to connect devices provided via net-cfg under devices/ tree
381 private void createAndConnectDevices() {
382 Set<DeviceId> deviceSubjects =
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200383 netCfgService.getSubjects(DeviceId.class, RestDeviceConfig.class);
Andrea Campanella59b549d2017-04-14 21:58:16 +0200384 connectDevices(deviceSubjects.stream()
fahadnaeemkhan02ffa712017-12-01 19:49:45 -0800385 .filter(deviceId -> deviceService.getDevice(deviceId) == null)
386 .map(deviceId -> {
387 RestDeviceConfig config =
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200388 netCfgService.getConfig(deviceId, RestDeviceConfig.class);
Yuta HIGUCHIfc667052017-12-05 18:17:22 -0800389 return toInactiveRestSBDevice(config);
fahadnaeemkhan02ffa712017-12-01 19:49:45 -0800390 }).collect(Collectors.toSet()));
Andrea Campanella59b549d2017-04-14 21:58:16 +0200391 }
392
Yuta HIGUCHIfc667052017-12-05 18:17:22 -0800393 private RestSBDevice toInactiveRestSBDevice(RestDeviceConfig config) {
394 return new DefaultRestSBDevice(config.ip(),
395 config.port(),
396 config.username(),
397 config.password(),
398 config.protocol(),
399 config.url(),
400 false,
401 config.testUrl(),
402 config.manufacturer(),
403 config.hwVersion(),
404 config.swVersion(),
Georgios Katsikas15841e22018-07-28 14:27:28 +0200405 config.isProxy(),
Yuta HIGUCHIfc667052017-12-05 18:17:22 -0800406 config.authenticationScheme(),
407 config.token()
408 );
409 }
410
Andrea Campanella59b549d2017-04-14 21:58:16 +0200411 private void connectDevices(Set<RestSBDevice> devices) {
412 //Precomputing the devices to be removed
413 Set<RestSBDevice> toBeRemoved = new HashSet<>(controller.getDevices().values());
414 toBeRemoved.removeAll(devices);
415 //Adding new devices
416 devices.stream()
417 .filter(device -> {
418 device.setActive(false);
419 controller.addDevice(device);
420 return testDeviceConnection(device);
421 })
Michal Mach67acb692017-06-21 12:05:36 +0200422 .forEach(this::deviceAdded);
Andrea Campanella59b549d2017-04-14 21:58:16 +0200423 //Removing devices not wanted anymore
424 toBeRemoved.forEach(device -> deviceRemoved(device.deviceId()));
425 }
426
Yuta HIGUCHIfc667052017-12-05 18:17:22 -0800427 private void connectDevice(RestSBDevice device) {
428 // TODO borrowed from above,
429 // not sure why setting it to inactive
430 device.setActive(false);
431 controller.addDevice(device);
432 if (testDeviceConnection(device)) {
433 deviceAdded(device);
434 }
435 }
436
Michal Machd8099742017-06-19 14:32:35 +0200437 private ScheduledFuture schedulePolling() {
438 return portStatisticsExecutor.scheduleAtFixedRate(this::executePortStatisticsUpdate,
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200439 pollFrequency / 2, pollFrequency,
Michal Machd8099742017-06-19 14:32:35 +0200440 TimeUnit.SECONDS);
441 }
442
443 private void executePortStatisticsUpdate() {
444 controller.getDevices().keySet().forEach(this::updatePortStatistics);
445 }
446
447 private void updatePortStatistics(DeviceId deviceId) {
448 Device device = deviceService.getDevice(deviceId);
449 checkNotNull(device, "device cannot be null");
450
451 if (device.is(PortStatisticsDiscovery.class)) {
452 PortStatisticsDiscovery portStatisticsDiscovery = device.as(PortStatisticsDiscovery.class);
453 Collection<PortStatistics> portStatistics = portStatisticsDiscovery.discoverPortStatistics();
454 if (portStatistics != null && !portStatistics.isEmpty()) {
455 providerService.updatePortStatistics(deviceId, portStatistics);
456 }
457 } else {
458 log.debug("No port statistics getter behaviour for device {}", deviceId);
459 }
460 }
461
Andrea Campanella6c71a052016-04-22 11:56:31 -0700462 private void discoverPorts(DeviceId deviceId) {
463 Device device = deviceService.getDevice(deviceId);
Georgios Katsikas15841e22018-07-28 14:27:28 +0200464 DeviceDescriptionDiscovery deviceDescriptionDiscovery = device.as(DeviceDescriptionDiscovery.class);
Ray Milkey640fedd2018-02-08 09:02:26 -0800465 providerService.updatePorts(deviceId, deviceDescriptionDiscovery.discoverPortDetails());
Andrea Campanella6c71a052016-04-22 11:56:31 -0700466 }
467
Michele Santuaric372c222017-01-12 09:41:25 +0100468 private boolean testDeviceConnection(RestSBDevice dev) {
Andrea Campanella945ded22016-01-07 13:17:43 -0800469 try {
Palash Kala4c71ee72017-05-24 17:43:59 +0900470 Callable<Boolean> connectionSuccess;
471
Michele Santuaric372c222017-01-12 09:41:25 +0100472 if (dev.testUrl().isPresent()) {
Michal Mach67acb692017-06-21 12:05:36 +0200473 connectionSuccess = () ->
474 controller.get(dev.deviceId(), dev.testUrl().get(), MediaType.APPLICATION_JSON_TYPE) != null;
Palash Kala4c71ee72017-05-24 17:43:59 +0900475 } else {
Michal Mach67acb692017-06-21 12:05:36 +0200476 connectionSuccess = () ->
477 controller.get(dev.deviceId(), "", MediaType.APPLICATION_JSON_TYPE) != null;
Michele Santuaric372c222017-01-12 09:41:25 +0100478 }
Palash Kala4c71ee72017-05-24 17:43:59 +0900479
480 Future<Boolean> future = executor.submit(connectionSuccess);
481 try {
Michal Mach67acb692017-06-21 12:05:36 +0200482 return future.get(REST_TIMEOUT_SEC, TimeUnit.SECONDS);
Palash Kala4c71ee72017-05-24 17:43:59 +0900483 } catch (TimeoutException ex) {
Sean Condone45e12c2018-05-04 16:27:07 +0100484 log.warn("Connection to device {} timed out: {}", dev.deviceId(), ex.getMessage());
Palash Kala4c71ee72017-05-24 17:43:59 +0900485 return false;
486 } catch (InterruptedException ex) {
Sean Condone45e12c2018-05-04 16:27:07 +0100487 log.warn("Connection to device {} interrupted: {}", dev.deviceId(), ex.getMessage());
Ray Milkey5c7d4882018-02-05 14:50:39 -0800488 Thread.currentThread().interrupt();
Palash Kala4c71ee72017-05-24 17:43:59 +0900489 return false;
490 } catch (ExecutionException ex) {
Sean Condone45e12c2018-05-04 16:27:07 +0100491 log.warn("Connection to device {} had an execution exception.", dev.deviceId(), ex);
Palash Kala4c71ee72017-05-24 17:43:59 +0900492 return false;
493 }
Michele Santuaric372c222017-01-12 09:41:25 +0100494
Andrea Campanellac6ecc632016-03-10 17:57:06 -0800495 } catch (ProcessingException e) {
Michele Santuaric372c222017-01-12 09:41:25 +0100496 log.warn("Cannot connect to device {}", dev, e);
Andrea Campanella945ded22016-01-07 13:17:43 -0800497 }
498 return false;
499 }
500
501 private class InternalNetworkConfigListener implements NetworkConfigListener {
Andrea Campanella945ded22016-01-07 13:17:43 -0800502 @Override
503 public void event(NetworkConfigEvent event) {
Georgios Katsikas15841e22018-07-28 14:27:28 +0200504 if (!isRelevant(event)) {
505 log.warn("Irrelevant network configuration event: {}", event);
506 return;
507 }
508
Yuta HIGUCHIfc667052017-12-05 18:17:22 -0800509 ExecutorService bg = SharedExecutors.getSingleThreadExecutor();
510 if (event.type() == CONFIG_REMOVED) {
511 DeviceId did = (DeviceId) event.subject();
512 bg.execute(() -> deviceRemoved(did));
513 } else {
514 // CONFIG_ADDED or CONFIG_UPDATED
515 RestDeviceConfig cfg = (RestDeviceConfig) event.config().get();
516 RestSBDevice restSBDevice = toInactiveRestSBDevice(cfg);
517 bg.execute(() -> connectDevice(restSBDevice));
518 }
Andrea Campanella945ded22016-01-07 13:17:43 -0800519 }
520
521 @Override
522 public boolean isRelevant(NetworkConfigEvent event) {
Yuta HIGUCHI872fdbe2017-12-05 15:34:28 -0800523 return event.configClass().equals(RestDeviceConfig.class) &&
Andrea Campanella945ded22016-01-07 13:17:43 -0800524 (event.type() == CONFIG_ADDED ||
Yuta HIGUCHIfc667052017-12-05 18:17:22 -0800525 event.type() == CONFIG_UPDATED ||
526 event.type() == CONFIG_REMOVED);
Andrea Campanella945ded22016-01-07 13:17:43 -0800527 }
528 }
Saurav Dasa2d37502016-03-25 17:50:40 -0700529
530 @Override
531 public void changePortState(DeviceId deviceId, PortNumber portNumber,
532 boolean enable) {
fahadnaeemkhan482951f2017-08-24 16:35:17 -0700533 Device device = deviceService.getDevice(deviceId);
534 if (device != null) {
535 if (device.is(PortAdmin.class)) {
536 PortAdmin portAdmin = device.as(PortAdmin.class);
537 CompletableFuture<Boolean> modified;
538 if (enable) {
539 modified = portAdmin.enable(portNumber);
540 } else {
541 modified = portAdmin.disable(portNumber);
542 }
543 modified.thenAcceptAsync(result -> {
544 if (!result) {
545 log.warn("Device {} port {} state can't be changed to {}",
546 deviceId, portNumber, enable);
547 }
548 });
549
550 } else {
551 log.warn("Device {} does not support PortAdmin behavior", deviceId);
552 }
553 } else {
554 log.warn("unable to get the device {}, port {} state can't be changed to {}",
555 deviceId, portNumber, enable);
556 }
Saurav Dasa2d37502016-03-25 17:50:40 -0700557 }
Andrea Campanella945ded22016-01-07 13:17:43 -0800558}