blob: 48f6a6891e1710b91346bb52261301d99d3dc90e [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 com.google.common.base.Preconditions;
21import org.apache.felix.scr.annotations.Activate;
22import org.apache.felix.scr.annotations.Component;
23import org.apache.felix.scr.annotations.Deactivate;
24import org.apache.felix.scr.annotations.Reference;
25import org.apache.felix.scr.annotations.ReferenceCardinality;
26import org.onlab.packet.ChassisId;
27import org.onosproject.core.ApplicationId;
28import org.onosproject.core.CoreService;
29import org.onosproject.incubator.net.config.basics.ConfigException;
Marc De Leenheerb0d131c2016-03-01 20:34:59 -080030import org.onosproject.net.AnnotationKeys;
Andrea Campanella945ded22016-01-07 13:17:43 -080031import org.onosproject.net.DefaultAnnotations;
32import org.onosproject.net.Device;
33import org.onosproject.net.DeviceId;
34import org.onosproject.net.MastershipRole;
Saurav Dasa2d37502016-03-25 17:50:40 -070035import org.onosproject.net.PortNumber;
Andrea Campanella945ded22016-01-07 13:17:43 -080036import org.onosproject.net.SparseAnnotations;
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;
Andrea Campanella945ded22016-01-07 13:17:43 -080049import org.onosproject.net.provider.AbstractProvider;
50import org.onosproject.net.provider.ProviderId;
51import org.onosproject.protocol.rest.RestSBController;
52import org.onosproject.protocol.rest.RestSBDevice;
53import org.slf4j.Logger;
54
Andrea Campanellac6ecc632016-03-10 17:57:06 -080055import javax.ws.rs.ProcessingException;
Andrea Campanella945ded22016-01-07 13:17:43 -080056import java.util.HashSet;
Andrea Campanella945ded22016-01-07 13:17:43 -080057import java.util.Set;
Andrea Campanella784ee0f2016-02-17 15:50:59 -080058import java.util.concurrent.ExecutorService;
59import java.util.concurrent.Executors;
Andrea Campanella945ded22016-01-07 13:17:43 -080060
Andrea Campanella784ee0f2016-02-17 15:50:59 -080061import static org.onlab.util.Tools.groupedThreads;
Andrea Campanella945ded22016-01-07 13:17:43 -080062import static org.onosproject.net.config.NetworkConfigEvent.Type.CONFIG_ADDED;
63import static org.onosproject.net.config.NetworkConfigEvent.Type.CONFIG_UPDATED;
64import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
65import static org.slf4j.LoggerFactory.getLogger;
66
67/**
68 * Provider for devices that use REST as means of configuration communication.
69 */
70@Component(immediate = true)
71public class RestDeviceProvider extends AbstractProvider
72 implements DeviceProvider {
73 private static final String APP_NAME = "org.onosproject.restsb";
74 private static final String REST = "rest";
Konstantinos Kanonakis3cd85552016-11-17 10:11:13 -060075 private static final String JSON = "json";
Andrea Campanella945ded22016-01-07 13:17:43 -080076 private static final String PROVIDER = "org.onosproject.provider.rest.device";
77 private static final String IPADDRESS = "ipaddress";
78 private static final int TEST_CONNECT_TIMEOUT = 1000;
Andrea Campanella2947e622016-01-27 09:23:46 -080079 private static final String HTTPS = "https";
80 private static final String AUTHORIZATION_PROPERTY = "authorization";
81 private static final String BASIC_AUTH_PREFIX = "Basic ";
82 private static final String URL_SEPARATOR = "://";
Andrea Campanella945ded22016-01-07 13:17:43 -080083 private final Logger log = getLogger(getClass());
84
85 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
86 protected DeviceProviderRegistry providerRegistry;
87
88 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
89 protected RestSBController controller;
90
91 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
92 protected NetworkConfigRegistry cfgService;
93
94 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
95 protected CoreService coreService;
96
Andrea Campanellad8d92db2016-01-14 16:24:41 -080097 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Andrea Campanella6c71a052016-04-22 11:56:31 -070098 protected DeviceService deviceService;
Andrea Campanellad8d92db2016-01-14 16:24:41 -080099
Andrea Campanella945ded22016-01-07 13:17:43 -0800100
101 private DeviceProviderService providerService;
102 protected static final String ISNOTNULL = "Rest device is not null";
103 private static final String UNKNOWN = "unknown";
104
Andrea Campanella784ee0f2016-02-17 15:50:59 -0800105 private final ExecutorService executor =
Andrea Campanella90f044f2016-03-02 09:14:57 -0800106 Executors.newFixedThreadPool(5, groupedThreads("onos/restsbprovider", "device-installer-%d", log));
Andrea Campanella784ee0f2016-02-17 15:50:59 -0800107
Andrea Campanella945ded22016-01-07 13:17:43 -0800108 private final ConfigFactory factory =
109 new ConfigFactory<ApplicationId, RestProviderConfig>(APP_SUBJECT_FACTORY,
110 RestProviderConfig.class,
Konstantinos Kanonakisb3e97042016-11-15 11:33:07 -0600111 "devices",
Andrea Campanella945ded22016-01-07 13:17:43 -0800112 true) {
113 @Override
114 public RestProviderConfig createConfig() {
115 return new RestProviderConfig();
116 }
117 };
118 private final NetworkConfigListener cfgLister = new InternalNetworkConfigListener();
119 private ApplicationId appId;
120
Andrea Campanella784ee0f2016-02-17 15:50:59 -0800121 private Set<DeviceId> addedDevices = new HashSet<>();
122
Andrea Campanella945ded22016-01-07 13:17:43 -0800123
124 @Activate
125 public void activate() {
126 appId = coreService.registerApplication(APP_NAME);
127 providerService = providerRegistry.register(this);
128 cfgService.registerConfigFactory(factory);
129 cfgService.addListener(cfgLister);
Andrea Campanella7d8449b2016-03-02 10:16:42 -0800130 executor.execute(RestDeviceProvider.this::connectDevices);
Andrea Campanella945ded22016-01-07 13:17:43 -0800131 log.info("Started");
132 }
133
Andrea Campanella945ded22016-01-07 13:17:43 -0800134 @Deactivate
135 public void deactivate() {
Andrea Campanella86294db2016-03-07 11:42:49 -0800136 cfgService.removeListener(cfgLister);
137 controller.getDevices().keySet().forEach(this::deviceRemoved);
Andrea Campanella945ded22016-01-07 13:17:43 -0800138 providerRegistry.unregister(this);
139 providerService = null;
140 cfgService.unregisterConfigFactory(factory);
Andrea Campanella945ded22016-01-07 13:17:43 -0800141 log.info("Stopped");
142 }
143
144 public RestDeviceProvider() {
145 super(new ProviderId(REST, PROVIDER));
146 }
147
148 @Override
149 public void triggerProbe(DeviceId deviceId) {
150 // TODO: This will be implemented later.
151 log.info("Triggering probe on device {}", deviceId);
152 }
153
154 @Override
155 public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
156 // TODO: This will be implemented later.
157 }
158
159
160 @Override
161 public boolean isReachable(DeviceId deviceId) {
162 RestSBDevice restDevice = controller.getDevice(deviceId);
163 if (restDevice == null) {
Andrea Campanella784ee0f2016-02-17 15:50:59 -0800164 log.debug("the requested device id: " +
165 deviceId.toString() +
166 " is not associated to any REST Device");
Andrea Campanella945ded22016-01-07 13:17:43 -0800167 return false;
168 }
169 return restDevice.isActive();
170 }
171
172 private void deviceAdded(RestSBDevice nodeId) {
173 Preconditions.checkNotNull(nodeId, ISNOTNULL);
174 DeviceId deviceId = nodeId.deviceId();
175 ChassisId cid = new ChassisId();
176 String ipAddress = nodeId.ip().toString();
177 SparseAnnotations annotations = DefaultAnnotations.builder()
Marc De Leenheerb0d131c2016-03-01 20:34:59 -0800178 .set(IPADDRESS, ipAddress)
179 .set(AnnotationKeys.PROTOCOL, REST.toUpperCase())
180 .build();
Andrea Campanella945ded22016-01-07 13:17:43 -0800181 DeviceDescription deviceDescription = new DefaultDeviceDescription(
182 deviceId.uri(),
183 Device.Type.SWITCH,
184 UNKNOWN, UNKNOWN,
185 UNKNOWN, UNKNOWN,
186 cid,
187 annotations);
Andrea Campanella945ded22016-01-07 13:17:43 -0800188 nodeId.setActive(true);
Andrea Campanellac6ecc632016-03-10 17:57:06 -0800189 providerService.deviceConnected(deviceId, deviceDescription);
Konstantinos Kanonakis3cd85552016-11-17 10:11:13 -0600190 checkAndUpdateDevice(deviceId);
Andrea Campanella784ee0f2016-02-17 15:50:59 -0800191 addedDevices.add(deviceId);
Andrea Campanella945ded22016-01-07 13:17:43 -0800192 }
193
Konstantinos Kanonakis3cd85552016-11-17 10:11:13 -0600194 private void checkAndUpdateDevice(DeviceId deviceId) {
195 if (deviceService.getDevice(deviceId) == null) {
196 log.warn("Device {} has not been added to store, " +
197 "maybe due to a problem in connectivity", deviceId);
198 } else {
199 boolean isReachable = isReachable(deviceId);
200 if (isReachable && deviceService.isAvailable(deviceId)) {
201 Device device = deviceService.getDevice(deviceId);
202 if (device.is(DeviceDescriptionDiscovery.class)) {
203 DeviceDescriptionDiscovery deviceDescriptionDiscovery =
204 device.as(DeviceDescriptionDiscovery.class);
205 DeviceDescription updatedDeviceDescription =
206 deviceDescriptionDiscovery.discoverDeviceDetails();
207 if (updatedDeviceDescription != null &&
208 !descriptionEquals(device, updatedDeviceDescription)) {
209 providerService.deviceConnected(
210 deviceId,
211 new DefaultDeviceDescription(
212 updatedDeviceDescription, true,
213 updatedDeviceDescription.annotations()));
214 //if ports are not discovered, retry the discovery
215 if (deviceService.getPorts(deviceId).isEmpty()) {
216 discoverPorts(deviceId);
217 }
218 }
219 } else {
220 log.warn("No DeviceDescriptionDiscovery behaviour for device {}", deviceId);
221 }
222 } else if (!isReachable && deviceService.isAvailable(deviceId)) {
223 providerService.deviceDisconnected(deviceId);
224 }
225 }
226 }
227
228 private boolean descriptionEquals(Device device, DeviceDescription updatedDeviceDescription) {
Michele Santuarid2c8f212017-01-09 18:23:45 +0100229 return Objects.equal(device.id().uri(), updatedDeviceDescription.deviceUri())
Konstantinos Kanonakis3cd85552016-11-17 10:11:13 -0600230 && Objects.equal(device.type(), updatedDeviceDescription.type())
231 && Objects.equal(device.manufacturer(), updatedDeviceDescription.manufacturer())
232 && Objects.equal(device.hwVersion(), updatedDeviceDescription.hwVersion())
233 && Objects.equal(device.swVersion(), updatedDeviceDescription.swVersion())
234 && Objects.equal(device.serialNumber(), updatedDeviceDescription.serialNumber())
235 && Objects.equal(device.chassisId(), updatedDeviceDescription.chassisId())
236 && Objects.equal(device.annotations(), updatedDeviceDescription.annotations());
237 }
238
Andrea Campanella86294db2016-03-07 11:42:49 -0800239 private void deviceRemoved(DeviceId deviceId) {
240 Preconditions.checkNotNull(deviceId, ISNOTNULL);
Andrea Campanella945ded22016-01-07 13:17:43 -0800241 providerService.deviceDisconnected(deviceId);
Andrea Campanella86294db2016-03-07 11:42:49 -0800242 controller.removeDevice(deviceId);
Andrea Campanella945ded22016-01-07 13:17:43 -0800243 }
244
245 private void connectDevices() {
246 RestProviderConfig cfg = cfgService.getConfig(appId, RestProviderConfig.class);
247 try {
248 if (cfg != null && cfg.getDevicesAddresses() != null) {
249 //Precomputing the devices to be removed
250 Set<RestSBDevice> toBeRemoved = new HashSet<>(controller.getDevices().values());
251 toBeRemoved.removeAll(cfg.getDevicesAddresses());
252 //Adding new devices
253 cfg.getDevicesAddresses().stream()
Andrea Campanellac6ecc632016-03-10 17:57:06 -0800254 .filter(device -> {
255 device.setActive(false);
256 controller.addDevice(device);
257 return testDeviceConnection(device);
258 })
Andrea Campanella945ded22016-01-07 13:17:43 -0800259 .forEach(device -> {
260 deviceAdded(device);
261 });
262 //Removing devices not wanted anymore
Sho SHIMIZUa09e1bb2016-08-01 14:25:25 -0700263 toBeRemoved.forEach(device -> deviceRemoved(device.deviceId()));
Andrea Campanella945ded22016-01-07 13:17:43 -0800264 }
265 } catch (ConfigException e) {
266 log.error("Configuration error {}", e);
267 }
Andrea Campanella2947e622016-01-27 09:23:46 -0800268 log.debug("REST Devices {}", controller.getDevices());
Andrea Campanella784ee0f2016-02-17 15:50:59 -0800269 addedDevices.clear();
Andrea Campanella945ded22016-01-07 13:17:43 -0800270 }
271
Andrea Campanella6c71a052016-04-22 11:56:31 -0700272 private void discoverPorts(DeviceId deviceId) {
273 Device device = deviceService.getDevice(deviceId);
274 //TODO remove when PortDiscovery is removed from master
275 if (device.is(PortDiscovery.class)) {
276 PortDiscovery portConfig = device.as(PortDiscovery.class);
Konstantinos Kanonakis3cd85552016-11-17 10:11:13 -0600277 providerService.updatePorts(deviceId, portConfig.getPorts());
278 } else {
Andrea Campanella6c71a052016-04-22 11:56:31 -0700279 DeviceDescriptionDiscovery deviceDescriptionDiscovery =
280 device.as(DeviceDescriptionDiscovery.class);
281 providerService.updatePorts(deviceId, deviceDescriptionDiscovery.discoverPortDetails());
Andrea Campanella6c71a052016-04-22 11:56:31 -0700282 }
283 }
284
Andrea Campanella945ded22016-01-07 13:17:43 -0800285 private boolean testDeviceConnection(RestSBDevice device) {
286 try {
Konstantinos Kanonakis3cd85552016-11-17 10:11:13 -0600287 return controller.get(device.deviceId(), "", JSON) != null;
Andrea Campanellac6ecc632016-03-10 17:57:06 -0800288 } catch (ProcessingException e) {
289 log.warn("Cannot connect to device {}", device, e);
Andrea Campanella945ded22016-01-07 13:17:43 -0800290 }
291 return false;
292 }
293
294 private class InternalNetworkConfigListener implements NetworkConfigListener {
Andrea Campanella945ded22016-01-07 13:17:43 -0800295 @Override
296 public void event(NetworkConfigEvent event) {
Andrea Campanella90f044f2016-03-02 09:14:57 -0800297 executor.execute(RestDeviceProvider.this::connectDevices);
Andrea Campanella945ded22016-01-07 13:17:43 -0800298 }
299
300 @Override
301 public boolean isRelevant(NetworkConfigEvent event) {
302 //TODO refactor
303 return event.configClass().equals(RestProviderConfig.class) &&
304 (event.type() == CONFIG_ADDED ||
305 event.type() == CONFIG_UPDATED);
306 }
307 }
Saurav Dasa2d37502016-03-25 17:50:40 -0700308
309 @Override
310 public void changePortState(DeviceId deviceId, PortNumber portNumber,
311 boolean enable) {
312 // TODO if required
313 }
Andrea Campanella945ded22016-01-07 13:17:43 -0800314}