blob: e63e6e07b5b88e9ceb991e744f23dc2060749ecd [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;
Andrea Campanella945ded22016-01-07 13:17:43 -080095import static org.slf4j.LoggerFactory.getLogger;
96
97/**
98 * Provider for devices that use REST as means of configuration communication.
99 */
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700100@Component(immediate = true, service = DeviceProvider.class)
Andrea Campanella945ded22016-01-07 13:17:43 -0800101public class RestDeviceProvider extends AbstractProvider
102 implements DeviceProvider {
103 private static final String APP_NAME = "org.onosproject.restsb";
Andrea Campanella59b549d2017-04-14 21:58:16 +0200104 protected static final String REST = "rest";
Andrea Campanella945ded22016-01-07 13:17:43 -0800105 private static final String PROVIDER = "org.onosproject.provider.rest.device";
106 private static final String IPADDRESS = "ipaddress";
Michal Mach67acb692017-06-21 12:05:36 +0200107 private static final String ISNOTNULL = "Rest device is not null";
Michele Santuaric372c222017-01-12 09:41:25 +0100108 private static final String UNKNOWN = "unknown";
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200109 private static final String POLL_FREQUENCY = "pollFrequency";
Palash Kala4c71ee72017-05-24 17:43:59 +0900110 private static final int REST_TIMEOUT_SEC = 5;
Georgios Katsikas6a4d1662017-07-27 13:22:31 +0200111 private static final int EXECUTOR_THREAD_POOL_SIZE = 8;
Andrea Campanella945ded22016-01-07 13:17:43 -0800112 private final Logger log = getLogger(getClass());
113
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700114 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanella945ded22016-01-07 13:17:43 -0800115 protected DeviceProviderRegistry providerRegistry;
116
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700117 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanella945ded22016-01-07 13:17:43 -0800118 protected RestSBController controller;
119
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700120 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200121 protected NetworkConfigRegistry netCfgService;
122
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700123 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200124 protected ComponentConfigService compCfgService;
Andrea Campanella945ded22016-01-07 13:17:43 -0800125
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700126 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanella945ded22016-01-07 13:17:43 -0800127 protected CoreService coreService;
128
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700129 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanella6c71a052016-04-22 11:56:31 -0700130 protected DeviceService deviceService;
Andrea Campanellad8d92db2016-01-14 16:24:41 -0800131
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700132 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Michele Santuaric372c222017-01-12 09:41:25 +0100133 protected DriverService driverService;
134
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200135 private static final int DEFAULT_POLL_FREQUENCY_SECONDS = 30;
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700136 //@Property(name = POLL_FREQUENCY, intValue = DEFAULT_POLL_FREQUENCY_SECONDS,
137 // label = "Configure poll frequency for port status and statistics; " +
138 // "default is 30 seconds")
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200139 private int pollFrequency = DEFAULT_POLL_FREQUENCY_SECONDS;
140
Andrea Campanella945ded22016-01-07 13:17:43 -0800141 private DeviceProviderService providerService;
Michele Santuaric372c222017-01-12 09:41:25 +0100142 private ApplicationId appId;
Andrea Campanella945ded22016-01-07 13:17:43 -0800143
Michal Mach13072e22017-06-21 09:12:24 +0200144 private ExecutorService executor;
Michal Mach67acb692017-06-21 12:05:36 +0200145 private final SharedScheduledExecutorService portStatisticsExecutor =
146 SharedScheduledExecutors.getPoolThreadExecutor();
Andrea Campanella784ee0f2016-02-17 15:50:59 -0800147
Michal Mach67acb692017-06-21 12:05:36 +0200148 private final List<ConfigFactory> factories = ImmutableList.of(
Andrea Campanella59b549d2017-04-14 21:58:16 +0200149 new ConfigFactory<DeviceId, RestDeviceConfig>(SubjectFactories.DEVICE_SUBJECT_FACTORY,
150 RestDeviceConfig.class,
151 REST) {
152 @Override
153 public RestDeviceConfig createConfig() {
154 return new RestDeviceConfig();
155 }
156 });
157
Michal Mach67acb692017-06-21 12:05:36 +0200158 private final NetworkConfigListener configListener = new InternalNetworkConfigListener();
Andrea Campanella945ded22016-01-07 13:17:43 -0800159
Michal Machd8099742017-06-19 14:32:35 +0200160 private ScheduledFuture<?> scheduledTask;
Andrea Campanella784ee0f2016-02-17 15:50:59 -0800161
Andrea Campanella945ded22016-01-07 13:17:43 -0800162
163 @Activate
164 public void activate() {
165 appId = coreService.registerApplication(APP_NAME);
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200166 compCfgService.registerProperties(getClass());
Andrea Campanella945ded22016-01-07 13:17:43 -0800167 providerService = providerRegistry.register(this);
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200168 factories.forEach(netCfgService::registerConfigFactory);
Georgios Katsikas6a4d1662017-07-27 13:22:31 +0200169 executor = Executors.newFixedThreadPool(
170 EXECUTOR_THREAD_POOL_SIZE, groupedThreads("onos/restsbprovider", "device-installer-%d", log)
171 );
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200172 netCfgService.addListener(configListener);
Andrea Campanella59b549d2017-04-14 21:58:16 +0200173 executor.execute(RestDeviceProvider.this::createAndConnectDevices);
Michal Machd8099742017-06-19 14:32:35 +0200174 scheduledTask = schedulePolling();
Andrea Campanella945ded22016-01-07 13:17:43 -0800175 log.info("Started");
176 }
177
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200178 @Modified
179 public void modified(ComponentContext context) {
180 int previousPollFrequency = pollFrequency;
181
182 if (context != null) {
183 Dictionary<?, ?> properties = context.getProperties();
184 pollFrequency = Tools.getIntegerProperty(properties, POLL_FREQUENCY,
185 DEFAULT_POLL_FREQUENCY_SECONDS);
186 log.info("Configured. Poll frequency is configured to {} seconds", pollFrequency);
187 }
188
189 // Re-schedule only if frequency has changed
190 if (!scheduledTask.isCancelled() && (previousPollFrequency != pollFrequency)) {
191 log.info("Re-scheduling port statistics task with frequency {} seconds", pollFrequency);
192 scheduledTask.cancel(true);
193 scheduledTask = schedulePolling();
194 }
195 }
196
Andrea Campanella945ded22016-01-07 13:17:43 -0800197 @Deactivate
198 public void deactivate() {
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200199 compCfgService.unregisterProperties(getClass(), false);
200 netCfgService.removeListener(configListener);
Andrea Campanella945ded22016-01-07 13:17:43 -0800201 providerRegistry.unregister(this);
202 providerService = null;
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200203 factories.forEach(netCfgService::unregisterConfigFactory);
Michal Machd8099742017-06-19 14:32:35 +0200204 scheduledTask.cancel(true);
Lukasz Ryba4da35c52017-06-20 09:14:11 +0200205 executor.shutdown();
Andrea Campanella945ded22016-01-07 13:17:43 -0800206 log.info("Stopped");
207 }
208
209 public RestDeviceProvider() {
210 super(new ProviderId(REST, PROVIDER));
211 }
212
213 @Override
214 public void triggerProbe(DeviceId deviceId) {
215 // TODO: This will be implemented later.
216 log.info("Triggering probe on device {}", deviceId);
217 }
218
219 @Override
220 public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
221 // TODO: This will be implemented later.
222 }
223
Andrea Campanella945ded22016-01-07 13:17:43 -0800224 @Override
225 public boolean isReachable(DeviceId deviceId) {
226 RestSBDevice restDevice = controller.getDevice(deviceId);
227 if (restDevice == null) {
Michele Santuaric372c222017-01-12 09:41:25 +0100228 restDevice = controller.getProxySBDevice(deviceId);
229 if (restDevice == null) {
230 log.debug("the requested device id: " +
231 deviceId.toString() +
232 " is not associated to any REST or REST " +
233 "proxy Device");
234 return false;
235 }
Andrea Campanella945ded22016-01-07 13:17:43 -0800236 }
237 return restDevice.isActive();
238 }
239
Michele Santuaric372c222017-01-12 09:41:25 +0100240 private void deviceAdded(RestSBDevice restSBDev) {
241 checkNotNull(restSBDev, ISNOTNULL);
242
Georgios Katsikas15841e22018-07-28 14:27:28 +0200243 Driver driver = driverService.getDriver(restSBDev.manufacturer().get(),
244 restSBDev.hwVersion().get(),
245 restSBDev.swVersion().get());
246
247 // Check if the server is controlling a single or multiple devices
Michele Santuaric372c222017-01-12 09:41:25 +0100248 if (restSBDev.isProxy()) {
Michele Santuaric372c222017-01-12 09:41:25 +0100249 if (driver != null && driver.hasBehaviour(DevicesDiscovery.class)) {
Georgios Katsikas15841e22018-07-28 14:27:28 +0200250 DevicesDiscovery devicesDiscovery = devicesDiscovery(restSBDev, driver);
Michele Santuaric372c222017-01-12 09:41:25 +0100251 Set<DeviceId> deviceIds = devicesDiscovery.deviceIds();
252 restSBDev.setActive(true);
Michal Mach67acb692017-06-21 12:05:36 +0200253 deviceIds.forEach(deviceId -> {
Michele Santuaric372c222017-01-12 09:41:25 +0100254 controller.addProxiedDevice(deviceId, restSBDev);
Georgios Katsikas15841e22018-07-28 14:27:28 +0200255 DeviceDescription devDesc = devicesDiscovery.deviceDetails(deviceId);
256 checkNotNull(devDesc, "DeviceDescription cannot be null");
257 providerService.deviceConnected(deviceId, mergeAnn(restSBDev.deviceId(), devDesc));
Michele Santuaric372c222017-01-12 09:41:25 +0100258
259 if (driver.hasBehaviour(DeviceDescriptionDiscovery.class)) {
260 DriverHandler h = driverService.createHandler(deviceId);
Georgios Katsikas15841e22018-07-28 14:27:28 +0200261 DeviceDescriptionDiscovery devDisc = h.behaviour(DeviceDescriptionDiscovery.class);
262 providerService.updatePorts(deviceId, devDisc.discoverPortDetails());
Michele Santuaric372c222017-01-12 09:41:25 +0100263 }
264
265 checkAndUpdateDevice(deviceId);
Michele Santuaric372c222017-01-12 09:41:25 +0100266 });
267 } else {
268 log.warn("Driver not found for {}", restSBDev);
269 }
270 } else {
271 DeviceId deviceId = restSBDev.deviceId();
Georgios Katsikas15841e22018-07-28 14:27:28 +0200272 if (driver != null && driver.hasBehaviour(DevicesDiscovery.class)) {
273 restSBDev.setActive(true);
274 DevicesDiscovery devicesDiscovery = devicesDiscovery(restSBDev, driver);
275 DeviceDescription deviceDescription = devicesDiscovery.deviceDetails(deviceId);
276 checkNotNull(deviceDescription, "DeviceDescription cannot be null");
277 providerService.deviceConnected(deviceId, deviceDescription);
278
279 if (driver.hasBehaviour(DeviceDescriptionDiscovery.class)) {
280 DriverHandler h = driverService.createHandler(deviceId);
281 DeviceDescriptionDiscovery deviceDiscovery = h.behaviour(DeviceDescriptionDiscovery.class);
282 providerService.updatePorts(deviceId, deviceDiscovery.discoverPortDetails());
283 }
284 } else {
285 ChassisId cid = new ChassisId();
286 String ipAddress = restSBDev.ip().toString();
287 SparseAnnotations annotations = DefaultAnnotations.builder()
288 .set(IPADDRESS, ipAddress)
289 .set(AnnotationKeys.PROTOCOL, REST.toUpperCase())
290 .build();
291 DeviceDescription deviceDescription = new DefaultDeviceDescription(
292 deviceId.uri(),
293 Device.Type.SWITCH,
294 UNKNOWN, UNKNOWN,
295 UNKNOWN, UNKNOWN,
296 cid,
297 annotations);
298 restSBDev.setActive(true);
299 providerService.deviceConnected(deviceId, deviceDescription);
300 }
301
Michele Santuaric372c222017-01-12 09:41:25 +0100302 checkAndUpdateDevice(deviceId);
Michele Santuaric372c222017-01-12 09:41:25 +0100303 }
304 }
305
306 private DefaultDeviceDescription mergeAnn(DeviceId devId, DeviceDescription desc) {
307 return new DefaultDeviceDescription(
308 desc,
309 DefaultAnnotations.merge(
310 DefaultAnnotations.builder()
311 .set(AnnotationKeys.PROTOCOL, REST.toUpperCase())
312 // The rest server added as annotation to the device
313 .set(AnnotationKeys.REST_SERVER, devId.toString())
314 .build(),
315 desc.annotations()));
316 }
317
318 private DevicesDiscovery devicesDiscovery(RestSBDevice restSBDevice, Driver driver) {
319 DriverData driverData = new DefaultDriverData(driver, restSBDevice.deviceId());
Georgios Katsikas15841e22018-07-28 14:27:28 +0200320 DevicesDiscovery devicesDiscovery = driver.createBehaviour(driverData, DevicesDiscovery.class);
Michele Santuaric372c222017-01-12 09:41:25 +0100321 devicesDiscovery.setHandler(new DefaultDriverHandler(driverData));
322 return devicesDiscovery;
Andrea Campanella945ded22016-01-07 13:17:43 -0800323 }
324
Konstantinos Kanonakis3cd85552016-11-17 10:11:13 -0600325 private void checkAndUpdateDevice(DeviceId deviceId) {
326 if (deviceService.getDevice(deviceId) == null) {
Georgios Katsikas15841e22018-07-28 14:27:28 +0200327 log.warn("Device {} has not been added to store, maybe due to a problem in connectivity", deviceId);
Konstantinos Kanonakis3cd85552016-11-17 10:11:13 -0600328 } else {
329 boolean isReachable = isReachable(deviceId);
330 if (isReachable && deviceService.isAvailable(deviceId)) {
331 Device device = deviceService.getDevice(deviceId);
332 if (device.is(DeviceDescriptionDiscovery.class)) {
Michele Santuaric372c222017-01-12 09:41:25 +0100333 DeviceDescriptionDiscovery deviceDescriptionDiscovery =
334 device.as(DeviceDescriptionDiscovery.class);
335 DeviceDescription updatedDeviceDescription =
336 deviceDescriptionDiscovery.discoverDeviceDetails();
337 if (updatedDeviceDescription != null &&
338 !descriptionEquals(device, updatedDeviceDescription)) {
339 providerService.deviceConnected(
340 deviceId,
341 new DefaultDeviceDescription(
342 updatedDeviceDescription, true,
343 updatedDeviceDescription.annotations()));
Konstantinos Kanonakis3cd85552016-11-17 10:11:13 -0600344 //if ports are not discovered, retry the discovery
345 if (deviceService.getPorts(deviceId).isEmpty()) {
346 discoverPorts(deviceId);
347 }
348 }
349 } else {
350 log.warn("No DeviceDescriptionDiscovery behaviour for device {}", deviceId);
351 }
352 } else if (!isReachable && deviceService.isAvailable(deviceId)) {
353 providerService.deviceDisconnected(deviceId);
354 }
355 }
356 }
357
358 private boolean descriptionEquals(Device device, DeviceDescription updatedDeviceDescription) {
Michele Santuarid2c8f212017-01-09 18:23:45 +0100359 return Objects.equal(device.id().uri(), updatedDeviceDescription.deviceUri())
Konstantinos Kanonakis3cd85552016-11-17 10:11:13 -0600360 && Objects.equal(device.type(), updatedDeviceDescription.type())
361 && Objects.equal(device.manufacturer(), updatedDeviceDescription.manufacturer())
362 && Objects.equal(device.hwVersion(), updatedDeviceDescription.hwVersion())
363 && Objects.equal(device.swVersion(), updatedDeviceDescription.swVersion())
364 && Objects.equal(device.serialNumber(), updatedDeviceDescription.serialNumber())
365 && Objects.equal(device.chassisId(), updatedDeviceDescription.chassisId())
366 && Objects.equal(device.annotations(), updatedDeviceDescription.annotations());
367 }
368
Andrea Campanella86294db2016-03-07 11:42:49 -0800369 private void deviceRemoved(DeviceId deviceId) {
Michele Santuaric372c222017-01-12 09:41:25 +0100370 checkNotNull(deviceId, ISNOTNULL);
Andrea Campanella945ded22016-01-07 13:17:43 -0800371 providerService.deviceDisconnected(deviceId);
Michal Mach67acb692017-06-21 12:05:36 +0200372 controller.getProxiedDevices(deviceId).forEach(device -> {
Michele Santuaric372c222017-01-12 09:41:25 +0100373 controller.removeProxiedDevice(device);
374 providerService.deviceDisconnected(device);
375 });
Andrea Campanella86294db2016-03-07 11:42:49 -0800376 controller.removeDevice(deviceId);
Andrea Campanella945ded22016-01-07 13:17:43 -0800377 }
378
Andrea Campanella59b549d2017-04-14 21:58:16 +0200379 //Method to connect devices provided via net-cfg under devices/ tree
380 private void createAndConnectDevices() {
381 Set<DeviceId> deviceSubjects =
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200382 netCfgService.getSubjects(DeviceId.class, RestDeviceConfig.class);
Andrea Campanella59b549d2017-04-14 21:58:16 +0200383 connectDevices(deviceSubjects.stream()
fahadnaeemkhan02ffa712017-12-01 19:49:45 -0800384 .filter(deviceId -> deviceService.getDevice(deviceId) == null)
385 .map(deviceId -> {
386 RestDeviceConfig config =
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200387 netCfgService.getConfig(deviceId, RestDeviceConfig.class);
Yuta HIGUCHIfc667052017-12-05 18:17:22 -0800388 return toInactiveRestSBDevice(config);
fahadnaeemkhan02ffa712017-12-01 19:49:45 -0800389 }).collect(Collectors.toSet()));
Andrea Campanella59b549d2017-04-14 21:58:16 +0200390 }
391
Yuta HIGUCHIfc667052017-12-05 18:17:22 -0800392 private RestSBDevice toInactiveRestSBDevice(RestDeviceConfig config) {
393 return new DefaultRestSBDevice(config.ip(),
394 config.port(),
395 config.username(),
396 config.password(),
397 config.protocol(),
398 config.url(),
399 false,
400 config.testUrl(),
401 config.manufacturer(),
402 config.hwVersion(),
403 config.swVersion(),
Georgios Katsikas15841e22018-07-28 14:27:28 +0200404 config.isProxy(),
Yuta HIGUCHIfc667052017-12-05 18:17:22 -0800405 config.authenticationScheme(),
406 config.token()
407 );
408 }
409
Andrea Campanella59b549d2017-04-14 21:58:16 +0200410 private void connectDevices(Set<RestSBDevice> devices) {
411 //Precomputing the devices to be removed
412 Set<RestSBDevice> toBeRemoved = new HashSet<>(controller.getDevices().values());
413 toBeRemoved.removeAll(devices);
414 //Adding new devices
415 devices.stream()
416 .filter(device -> {
417 device.setActive(false);
418 controller.addDevice(device);
419 return testDeviceConnection(device);
420 })
Michal Mach67acb692017-06-21 12:05:36 +0200421 .forEach(this::deviceAdded);
Andrea Campanella59b549d2017-04-14 21:58:16 +0200422 //Removing devices not wanted anymore
423 toBeRemoved.forEach(device -> deviceRemoved(device.deviceId()));
424 }
425
Yuta HIGUCHIfc667052017-12-05 18:17:22 -0800426 private void connectDevice(RestSBDevice device) {
427 // TODO borrowed from above,
428 // not sure why setting it to inactive
429 device.setActive(false);
430 controller.addDevice(device);
431 if (testDeviceConnection(device)) {
432 deviceAdded(device);
433 }
434 }
435
Michal Machd8099742017-06-19 14:32:35 +0200436 private ScheduledFuture schedulePolling() {
437 return portStatisticsExecutor.scheduleAtFixedRate(this::executePortStatisticsUpdate,
Georgios Katsikas0f12bc62018-07-06 09:06:42 +0200438 pollFrequency / 2, pollFrequency,
Michal Machd8099742017-06-19 14:32:35 +0200439 TimeUnit.SECONDS);
440 }
441
442 private void executePortStatisticsUpdate() {
443 controller.getDevices().keySet().forEach(this::updatePortStatistics);
444 }
445
446 private void updatePortStatistics(DeviceId deviceId) {
447 Device device = deviceService.getDevice(deviceId);
448 checkNotNull(device, "device cannot be null");
449
450 if (device.is(PortStatisticsDiscovery.class)) {
451 PortStatisticsDiscovery portStatisticsDiscovery = device.as(PortStatisticsDiscovery.class);
452 Collection<PortStatistics> portStatistics = portStatisticsDiscovery.discoverPortStatistics();
453 if (portStatistics != null && !portStatistics.isEmpty()) {
454 providerService.updatePortStatistics(deviceId, portStatistics);
455 }
456 } else {
457 log.debug("No port statistics getter behaviour for device {}", deviceId);
458 }
459 }
460
Andrea Campanella6c71a052016-04-22 11:56:31 -0700461 private void discoverPorts(DeviceId deviceId) {
462 Device device = deviceService.getDevice(deviceId);
Georgios Katsikas15841e22018-07-28 14:27:28 +0200463 DeviceDescriptionDiscovery deviceDescriptionDiscovery = device.as(DeviceDescriptionDiscovery.class);
Ray Milkey640fedd2018-02-08 09:02:26 -0800464 providerService.updatePorts(deviceId, deviceDescriptionDiscovery.discoverPortDetails());
Andrea Campanella6c71a052016-04-22 11:56:31 -0700465 }
466
Michele Santuaric372c222017-01-12 09:41:25 +0100467 private boolean testDeviceConnection(RestSBDevice dev) {
Andrea Campanella945ded22016-01-07 13:17:43 -0800468 try {
Palash Kala4c71ee72017-05-24 17:43:59 +0900469 Callable<Boolean> connectionSuccess;
470
Michele Santuaric372c222017-01-12 09:41:25 +0100471 if (dev.testUrl().isPresent()) {
Michal Mach67acb692017-06-21 12:05:36 +0200472 connectionSuccess = () ->
473 controller.get(dev.deviceId(), dev.testUrl().get(), MediaType.APPLICATION_JSON_TYPE) != null;
Palash Kala4c71ee72017-05-24 17:43:59 +0900474 } else {
Michal Mach67acb692017-06-21 12:05:36 +0200475 connectionSuccess = () ->
476 controller.get(dev.deviceId(), "", MediaType.APPLICATION_JSON_TYPE) != null;
Michele Santuaric372c222017-01-12 09:41:25 +0100477 }
Palash Kala4c71ee72017-05-24 17:43:59 +0900478
479 Future<Boolean> future = executor.submit(connectionSuccess);
480 try {
Michal Mach67acb692017-06-21 12:05:36 +0200481 return future.get(REST_TIMEOUT_SEC, TimeUnit.SECONDS);
Palash Kala4c71ee72017-05-24 17:43:59 +0900482 } catch (TimeoutException ex) {
Sean Condone45e12c2018-05-04 16:27:07 +0100483 log.warn("Connection to device {} timed out: {}", dev.deviceId(), ex.getMessage());
Palash Kala4c71ee72017-05-24 17:43:59 +0900484 return false;
485 } catch (InterruptedException ex) {
Sean Condone45e12c2018-05-04 16:27:07 +0100486 log.warn("Connection to device {} interrupted: {}", dev.deviceId(), ex.getMessage());
Ray Milkey5c7d4882018-02-05 14:50:39 -0800487 Thread.currentThread().interrupt();
Palash Kala4c71ee72017-05-24 17:43:59 +0900488 return false;
489 } catch (ExecutionException ex) {
Sean Condone45e12c2018-05-04 16:27:07 +0100490 log.warn("Connection to device {} had an execution exception.", dev.deviceId(), ex);
Palash Kala4c71ee72017-05-24 17:43:59 +0900491 return false;
492 }
Michele Santuaric372c222017-01-12 09:41:25 +0100493
Andrea Campanellac6ecc632016-03-10 17:57:06 -0800494 } catch (ProcessingException e) {
Michele Santuaric372c222017-01-12 09:41:25 +0100495 log.warn("Cannot connect to device {}", dev, e);
Andrea Campanella945ded22016-01-07 13:17:43 -0800496 }
497 return false;
498 }
499
500 private class InternalNetworkConfigListener implements NetworkConfigListener {
Andrea Campanella945ded22016-01-07 13:17:43 -0800501 @Override
502 public void event(NetworkConfigEvent event) {
Georgios Katsikas15841e22018-07-28 14:27:28 +0200503 if (!isRelevant(event)) {
504 log.warn("Irrelevant network configuration event: {}", event);
505 return;
506 }
507
Yuta HIGUCHIfc667052017-12-05 18:17:22 -0800508 ExecutorService bg = SharedExecutors.getSingleThreadExecutor();
509 if (event.type() == CONFIG_REMOVED) {
510 DeviceId did = (DeviceId) event.subject();
511 bg.execute(() -> deviceRemoved(did));
512 } else {
513 // CONFIG_ADDED or CONFIG_UPDATED
514 RestDeviceConfig cfg = (RestDeviceConfig) event.config().get();
515 RestSBDevice restSBDevice = toInactiveRestSBDevice(cfg);
516 bg.execute(() -> connectDevice(restSBDevice));
517 }
Andrea Campanella945ded22016-01-07 13:17:43 -0800518 }
519
520 @Override
521 public boolean isRelevant(NetworkConfigEvent event) {
Yuta HIGUCHI872fdbe2017-12-05 15:34:28 -0800522 return event.configClass().equals(RestDeviceConfig.class) &&
Andrea Campanella945ded22016-01-07 13:17:43 -0800523 (event.type() == CONFIG_ADDED ||
Yuta HIGUCHIfc667052017-12-05 18:17:22 -0800524 event.type() == CONFIG_UPDATED ||
525 event.type() == CONFIG_REMOVED);
Andrea Campanella945ded22016-01-07 13:17:43 -0800526 }
527 }
Saurav Dasa2d37502016-03-25 17:50:40 -0700528
529 @Override
530 public void changePortState(DeviceId deviceId, PortNumber portNumber,
531 boolean enable) {
fahadnaeemkhan482951f2017-08-24 16:35:17 -0700532 Device device = deviceService.getDevice(deviceId);
533 if (device != null) {
534 if (device.is(PortAdmin.class)) {
535 PortAdmin portAdmin = device.as(PortAdmin.class);
536 CompletableFuture<Boolean> modified;
537 if (enable) {
538 modified = portAdmin.enable(portNumber);
539 } else {
540 modified = portAdmin.disable(portNumber);
541 }
542 modified.thenAcceptAsync(result -> {
543 if (!result) {
544 log.warn("Device {} port {} state can't be changed to {}",
545 deviceId, portNumber, enable);
546 }
547 });
548
549 } else {
550 log.warn("Device {} does not support PortAdmin behavior", deviceId);
551 }
552 } else {
553 log.warn("unable to get the device {}, port {} state can't be changed to {}",
554 deviceId, portNumber, enable);
555 }
Saurav Dasa2d37502016-03-25 17:50:40 -0700556 }
Andrea Campanella945ded22016-01-07 13:17:43 -0800557}