blob: 93f9ca9b3e1040c2b1d20e09e200248429127e63 [file] [log] [blame]
Andrea Campanella945ded22016-01-07 13:17:43 -08001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2016-present Open Networking Laboratory
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 Campanella945ded22016-01-07 13:17:43 -080020import org.apache.felix.scr.annotations.Activate;
21import org.apache.felix.scr.annotations.Component;
22import org.apache.felix.scr.annotations.Deactivate;
23import org.apache.felix.scr.annotations.Reference;
24import org.apache.felix.scr.annotations.ReferenceCardinality;
25import org.onlab.packet.ChassisId;
26import org.onosproject.core.ApplicationId;
27import org.onosproject.core.CoreService;
28import org.onosproject.incubator.net.config.basics.ConfigException;
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;
Andrea Campanellad8d92db2016-01-14 16:24:41 -080037import org.onosproject.net.behaviour.PortDiscovery;
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;
42import org.onosproject.net.device.DefaultDeviceDescription;
43import org.onosproject.net.device.DeviceDescription;
Andrea Campanella6c71a052016-04-22 11:56:31 -070044import org.onosproject.net.device.DeviceDescriptionDiscovery;
Andrea Campanella945ded22016-01-07 13:17:43 -080045import org.onosproject.net.device.DeviceProvider;
46import org.onosproject.net.device.DeviceProviderRegistry;
47import org.onosproject.net.device.DeviceProviderService;
Andrea Campanella6c71a052016-04-22 11:56:31 -070048import org.onosproject.net.device.DeviceService;
Michele Santuaric372c222017-01-12 09:41:25 +010049import org.onosproject.net.driver.DefaultDriverData;
50import org.onosproject.net.driver.DefaultDriverHandler;
51import org.onosproject.net.driver.Driver;
52import org.onosproject.net.driver.DriverData;
53import org.onosproject.net.driver.DriverHandler;
54import org.onosproject.net.driver.DriverService;
Andrea Campanella945ded22016-01-07 13:17:43 -080055import org.onosproject.net.provider.AbstractProvider;
56import org.onosproject.net.provider.ProviderId;
57import org.onosproject.protocol.rest.RestSBController;
58import org.onosproject.protocol.rest.RestSBDevice;
59import org.slf4j.Logger;
60
Andrea Campanellac6ecc632016-03-10 17:57:06 -080061import javax.ws.rs.ProcessingException;
Andrea Campanella945ded22016-01-07 13:17:43 -080062import java.util.HashSet;
Andrea Campanella945ded22016-01-07 13:17:43 -080063import java.util.Set;
Andrea Campanella784ee0f2016-02-17 15:50:59 -080064import java.util.concurrent.ExecutorService;
65import java.util.concurrent.Executors;
Andrea Campanella945ded22016-01-07 13:17:43 -080066
Michele Santuaric372c222017-01-12 09:41:25 +010067import static com.google.common.base.Preconditions.checkNotNull;
Andrea Campanella784ee0f2016-02-17 15:50:59 -080068import static org.onlab.util.Tools.groupedThreads;
Andrea Campanella945ded22016-01-07 13:17:43 -080069import static org.onosproject.net.config.NetworkConfigEvent.Type.CONFIG_ADDED;
70import static org.onosproject.net.config.NetworkConfigEvent.Type.CONFIG_UPDATED;
71import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
72import static org.slf4j.LoggerFactory.getLogger;
73
74/**
75 * Provider for devices that use REST as means of configuration communication.
76 */
77@Component(immediate = true)
78public class RestDeviceProvider extends AbstractProvider
79 implements DeviceProvider {
80 private static final String APP_NAME = "org.onosproject.restsb";
81 private static final String REST = "rest";
Konstantinos Kanonakis3cd85552016-11-17 10:11:13 -060082 private static final String JSON = "json";
Andrea Campanella945ded22016-01-07 13:17:43 -080083 private static final String PROVIDER = "org.onosproject.provider.rest.device";
84 private static final String IPADDRESS = "ipaddress";
Andrea Campanella2947e622016-01-27 09:23:46 -080085 private static final String HTTPS = "https";
86 private static final String AUTHORIZATION_PROPERTY = "authorization";
87 private static final String BASIC_AUTH_PREFIX = "Basic ";
88 private static final String URL_SEPARATOR = "://";
Michele Santuaric372c222017-01-12 09:41:25 +010089 protected static final String ISNOTNULL = "Rest device is not null";
90 private static final String UNKNOWN = "unknown";
Andrea Campanella945ded22016-01-07 13:17:43 -080091 private final Logger log = getLogger(getClass());
92
93 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
94 protected DeviceProviderRegistry providerRegistry;
95
96 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
97 protected RestSBController controller;
98
99 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
100 protected NetworkConfigRegistry cfgService;
101
102 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
103 protected CoreService coreService;
104
Andrea Campanellad8d92db2016-01-14 16:24:41 -0800105 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Andrea Campanella6c71a052016-04-22 11:56:31 -0700106 protected DeviceService deviceService;
Andrea Campanellad8d92db2016-01-14 16:24:41 -0800107
Michele Santuaric372c222017-01-12 09:41:25 +0100108 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
109 protected DriverService driverService;
110
Andrea Campanella945ded22016-01-07 13:17:43 -0800111
112 private DeviceProviderService providerService;
Michele Santuaric372c222017-01-12 09:41:25 +0100113 private ApplicationId appId;
Andrea Campanella945ded22016-01-07 13:17:43 -0800114
Andrea Campanella784ee0f2016-02-17 15:50:59 -0800115 private final ExecutorService executor =
Andrea Campanella90f044f2016-03-02 09:14:57 -0800116 Executors.newFixedThreadPool(5, groupedThreads("onos/restsbprovider", "device-installer-%d", log));
Andrea Campanella784ee0f2016-02-17 15:50:59 -0800117
Andrea Campanella945ded22016-01-07 13:17:43 -0800118 private final ConfigFactory factory =
119 new ConfigFactory<ApplicationId, RestProviderConfig>(APP_SUBJECT_FACTORY,
120 RestProviderConfig.class,
Konstantinos Kanonakisb3e97042016-11-15 11:33:07 -0600121 "devices",
Andrea Campanella945ded22016-01-07 13:17:43 -0800122 true) {
123 @Override
124 public RestProviderConfig createConfig() {
125 return new RestProviderConfig();
126 }
127 };
128 private final NetworkConfigListener cfgLister = new InternalNetworkConfigListener();
Andrea Campanella945ded22016-01-07 13:17:43 -0800129
Andrea Campanella784ee0f2016-02-17 15:50:59 -0800130 private Set<DeviceId> addedDevices = new HashSet<>();
131
Andrea Campanella945ded22016-01-07 13:17:43 -0800132
133 @Activate
134 public void activate() {
135 appId = coreService.registerApplication(APP_NAME);
136 providerService = providerRegistry.register(this);
137 cfgService.registerConfigFactory(factory);
138 cfgService.addListener(cfgLister);
Andrea Campanella7d8449b2016-03-02 10:16:42 -0800139 executor.execute(RestDeviceProvider.this::connectDevices);
Andrea Campanella945ded22016-01-07 13:17:43 -0800140 log.info("Started");
141 }
142
Andrea Campanella945ded22016-01-07 13:17:43 -0800143 @Deactivate
144 public void deactivate() {
Andrea Campanella86294db2016-03-07 11:42:49 -0800145 cfgService.removeListener(cfgLister);
146 controller.getDevices().keySet().forEach(this::deviceRemoved);
Andrea Campanella945ded22016-01-07 13:17:43 -0800147 providerRegistry.unregister(this);
148 providerService = null;
149 cfgService.unregisterConfigFactory(factory);
Andrea Campanella945ded22016-01-07 13:17:43 -0800150 log.info("Stopped");
151 }
152
153 public RestDeviceProvider() {
154 super(new ProviderId(REST, PROVIDER));
155 }
156
157 @Override
158 public void triggerProbe(DeviceId deviceId) {
159 // TODO: This will be implemented later.
160 log.info("Triggering probe on device {}", deviceId);
161 }
162
163 @Override
164 public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
165 // TODO: This will be implemented later.
166 }
167
168
169 @Override
170 public boolean isReachable(DeviceId deviceId) {
171 RestSBDevice restDevice = controller.getDevice(deviceId);
172 if (restDevice == null) {
Michele Santuaric372c222017-01-12 09:41:25 +0100173 restDevice = controller.getProxySBDevice(deviceId);
174 if (restDevice == null) {
175 log.debug("the requested device id: " +
176 deviceId.toString() +
177 " is not associated to any REST or REST " +
178 "proxy Device");
179 return false;
180 }
Andrea Campanella945ded22016-01-07 13:17:43 -0800181 }
182 return restDevice.isActive();
183 }
184
Michele Santuaric372c222017-01-12 09:41:25 +0100185 private void deviceAdded(RestSBDevice restSBDev) {
186 checkNotNull(restSBDev, ISNOTNULL);
187
188 //check if the server is controlling a single or multiple devices
189 if (restSBDev.isProxy()) {
190
191 Driver driver = driverService.getDriver(restSBDev.manufacturer().get(),
192 restSBDev.hwVersion().get(),
193 restSBDev.swVersion().get());
194
195 if (driver != null && driver.hasBehaviour(DevicesDiscovery.class)) {
196
197 //Creates the driver to communicate with the server
198 DevicesDiscovery devicesDiscovery =
199 devicesDiscovery(restSBDev, driver);
200 Set<DeviceId> deviceIds = devicesDiscovery.deviceIds();
201 restSBDev.setActive(true);
202 deviceIds.stream().forEach(deviceId -> {
203 controller.addProxiedDevice(deviceId, restSBDev);
204 DeviceDescription devDesc =
205 devicesDiscovery.deviceDetails(deviceId);
206 checkNotNull(devDesc,
207 "deviceDescription cannot be null");
208 providerService.deviceConnected(
209 deviceId, mergeAnn(restSBDev.deviceId(), devDesc));
210
211 if (driver.hasBehaviour(DeviceDescriptionDiscovery.class)) {
212 DriverHandler h = driverService.createHandler(deviceId);
213 DeviceDescriptionDiscovery devDisc =
214 h.behaviour(DeviceDescriptionDiscovery.class);
215 providerService.updatePorts(deviceId,
216 devDisc.discoverPortDetails());
217 }
218
219 checkAndUpdateDevice(deviceId);
220 addedDevices.add(deviceId);
221 });
222 } else {
223 log.warn("Driver not found for {}", restSBDev);
224 }
225 } else {
226 DeviceId deviceId = restSBDev.deviceId();
227 ChassisId cid = new ChassisId();
228 String ipAddress = restSBDev.ip().toString();
229 SparseAnnotations annotations = DefaultAnnotations.builder()
230 .set(IPADDRESS, ipAddress)
231 .set(AnnotationKeys.PROTOCOL, REST.toUpperCase())
232 .build();
233 DeviceDescription deviceDescription = new DefaultDeviceDescription(
234 deviceId.uri(),
235 Device.Type.SWITCH,
236 UNKNOWN, UNKNOWN,
237 UNKNOWN, UNKNOWN,
238 cid,
239 annotations);
240 restSBDev.setActive(true);
241 providerService.deviceConnected(deviceId, deviceDescription);
242 checkAndUpdateDevice(deviceId);
243 addedDevices.add(deviceId);
244 }
245 }
246
247 private DefaultDeviceDescription mergeAnn(DeviceId devId, DeviceDescription desc) {
248 return new DefaultDeviceDescription(
249 desc,
250 DefaultAnnotations.merge(
251 DefaultAnnotations.builder()
252 .set(AnnotationKeys.PROTOCOL, REST.toUpperCase())
253 // The rest server added as annotation to the device
254 .set(AnnotationKeys.REST_SERVER, devId.toString())
255 .build(),
256 desc.annotations()));
257 }
258
259 private DevicesDiscovery devicesDiscovery(RestSBDevice restSBDevice, Driver driver) {
260 DriverData driverData = new DefaultDriverData(driver, restSBDevice.deviceId());
261 DevicesDiscovery devicesDiscovery = driver.createBehaviour(driverData,
262 DevicesDiscovery.class);
263 devicesDiscovery.setHandler(new DefaultDriverHandler(driverData));
264 return devicesDiscovery;
Andrea Campanella945ded22016-01-07 13:17:43 -0800265 }
266
Konstantinos Kanonakis3cd85552016-11-17 10:11:13 -0600267 private void checkAndUpdateDevice(DeviceId deviceId) {
268 if (deviceService.getDevice(deviceId) == null) {
269 log.warn("Device {} has not been added to store, " +
270 "maybe due to a problem in connectivity", deviceId);
271 } else {
272 boolean isReachable = isReachable(deviceId);
273 if (isReachable && deviceService.isAvailable(deviceId)) {
274 Device device = deviceService.getDevice(deviceId);
275 if (device.is(DeviceDescriptionDiscovery.class)) {
Michele Santuaric372c222017-01-12 09:41:25 +0100276 DeviceDescriptionDiscovery deviceDescriptionDiscovery =
277 device.as(DeviceDescriptionDiscovery.class);
278 DeviceDescription updatedDeviceDescription =
279 deviceDescriptionDiscovery.discoverDeviceDetails();
280 if (updatedDeviceDescription != null &&
281 !descriptionEquals(device, updatedDeviceDescription)) {
282 providerService.deviceConnected(
283 deviceId,
284 new DefaultDeviceDescription(
285 updatedDeviceDescription, true,
286 updatedDeviceDescription.annotations()));
Konstantinos Kanonakis3cd85552016-11-17 10:11:13 -0600287 //if ports are not discovered, retry the discovery
288 if (deviceService.getPorts(deviceId).isEmpty()) {
289 discoverPorts(deviceId);
290 }
291 }
292 } else {
293 log.warn("No DeviceDescriptionDiscovery behaviour for device {}", deviceId);
294 }
295 } else if (!isReachable && deviceService.isAvailable(deviceId)) {
296 providerService.deviceDisconnected(deviceId);
297 }
298 }
299 }
300
301 private boolean descriptionEquals(Device device, DeviceDescription updatedDeviceDescription) {
Michele Santuarid2c8f212017-01-09 18:23:45 +0100302 return Objects.equal(device.id().uri(), updatedDeviceDescription.deviceUri())
Konstantinos Kanonakis3cd85552016-11-17 10:11:13 -0600303 && Objects.equal(device.type(), updatedDeviceDescription.type())
304 && Objects.equal(device.manufacturer(), updatedDeviceDescription.manufacturer())
305 && Objects.equal(device.hwVersion(), updatedDeviceDescription.hwVersion())
306 && Objects.equal(device.swVersion(), updatedDeviceDescription.swVersion())
307 && Objects.equal(device.serialNumber(), updatedDeviceDescription.serialNumber())
308 && Objects.equal(device.chassisId(), updatedDeviceDescription.chassisId())
309 && Objects.equal(device.annotations(), updatedDeviceDescription.annotations());
310 }
311
Andrea Campanella86294db2016-03-07 11:42:49 -0800312 private void deviceRemoved(DeviceId deviceId) {
Michele Santuaric372c222017-01-12 09:41:25 +0100313 checkNotNull(deviceId, ISNOTNULL);
Andrea Campanella945ded22016-01-07 13:17:43 -0800314 providerService.deviceDisconnected(deviceId);
Michele Santuaric372c222017-01-12 09:41:25 +0100315 controller.getProxiedDevices(deviceId).stream().forEach(device -> {
316 controller.removeProxiedDevice(device);
317 providerService.deviceDisconnected(device);
318 });
Andrea Campanella86294db2016-03-07 11:42:49 -0800319 controller.removeDevice(deviceId);
Andrea Campanella945ded22016-01-07 13:17:43 -0800320 }
321
322 private void connectDevices() {
323 RestProviderConfig cfg = cfgService.getConfig(appId, RestProviderConfig.class);
324 try {
325 if (cfg != null && cfg.getDevicesAddresses() != null) {
326 //Precomputing the devices to be removed
327 Set<RestSBDevice> toBeRemoved = new HashSet<>(controller.getDevices().values());
328 toBeRemoved.removeAll(cfg.getDevicesAddresses());
329 //Adding new devices
330 cfg.getDevicesAddresses().stream()
Andrea Campanellac6ecc632016-03-10 17:57:06 -0800331 .filter(device -> {
332 device.setActive(false);
333 controller.addDevice(device);
334 return testDeviceConnection(device);
335 })
Andrea Campanella945ded22016-01-07 13:17:43 -0800336 .forEach(device -> {
337 deviceAdded(device);
338 });
339 //Removing devices not wanted anymore
Sho SHIMIZUa09e1bb2016-08-01 14:25:25 -0700340 toBeRemoved.forEach(device -> deviceRemoved(device.deviceId()));
Andrea Campanella945ded22016-01-07 13:17:43 -0800341 }
342 } catch (ConfigException e) {
343 log.error("Configuration error {}", e);
344 }
Andrea Campanella2947e622016-01-27 09:23:46 -0800345 log.debug("REST Devices {}", controller.getDevices());
Andrea Campanella784ee0f2016-02-17 15:50:59 -0800346 addedDevices.clear();
Andrea Campanella945ded22016-01-07 13:17:43 -0800347 }
348
Andrea Campanella6c71a052016-04-22 11:56:31 -0700349 private void discoverPorts(DeviceId deviceId) {
350 Device device = deviceService.getDevice(deviceId);
351 //TODO remove when PortDiscovery is removed from master
352 if (device.is(PortDiscovery.class)) {
353 PortDiscovery portConfig = device.as(PortDiscovery.class);
Konstantinos Kanonakis3cd85552016-11-17 10:11:13 -0600354 providerService.updatePorts(deviceId, portConfig.getPorts());
355 } else {
Andrea Campanella6c71a052016-04-22 11:56:31 -0700356 DeviceDescriptionDiscovery deviceDescriptionDiscovery =
357 device.as(DeviceDescriptionDiscovery.class);
358 providerService.updatePorts(deviceId, deviceDescriptionDiscovery.discoverPortDetails());
Andrea Campanella6c71a052016-04-22 11:56:31 -0700359 }
360 }
361
Michele Santuaric372c222017-01-12 09:41:25 +0100362 private boolean testDeviceConnection(RestSBDevice dev) {
Andrea Campanella945ded22016-01-07 13:17:43 -0800363 try {
Michele Santuaric372c222017-01-12 09:41:25 +0100364 if (dev.testUrl().isPresent()) {
365 return controller
366 .get(dev.deviceId(), dev.testUrl().get(), JSON) != null;
367 }
368 return controller.get(dev.deviceId(), "", JSON) != null;
369
Andrea Campanellac6ecc632016-03-10 17:57:06 -0800370 } catch (ProcessingException e) {
Michele Santuaric372c222017-01-12 09:41:25 +0100371 log.warn("Cannot connect to device {}", dev, e);
Andrea Campanella945ded22016-01-07 13:17:43 -0800372 }
373 return false;
374 }
375
376 private class InternalNetworkConfigListener implements NetworkConfigListener {
Andrea Campanella945ded22016-01-07 13:17:43 -0800377 @Override
378 public void event(NetworkConfigEvent event) {
Andrea Campanella90f044f2016-03-02 09:14:57 -0800379 executor.execute(RestDeviceProvider.this::connectDevices);
Andrea Campanella945ded22016-01-07 13:17:43 -0800380 }
381
382 @Override
383 public boolean isRelevant(NetworkConfigEvent event) {
384 //TODO refactor
385 return event.configClass().equals(RestProviderConfig.class) &&
386 (event.type() == CONFIG_ADDED ||
387 event.type() == CONFIG_UPDATED);
388 }
389 }
Saurav Dasa2d37502016-03-25 17:50:40 -0700390
391 @Override
392 public void changePortState(DeviceId deviceId, PortNumber portNumber,
393 boolean enable) {
394 // TODO if required
395 }
Andrea Campanella945ded22016-01-07 13:17:43 -0800396}