blob: d8ebda61d05d31c4169d3298962f8df48f146eb5 [file] [log] [blame]
Andrea Campanella945ded22016-01-07 13:17:43 -08001/*
2 * Copyright 2016 Open Networking Laboratory
3 *
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
19import com.google.common.base.Preconditions;
20import 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;
34import org.onosproject.net.SparseAnnotations;
Andrea Campanellad8d92db2016-01-14 16:24:41 -080035import org.onosproject.net.behaviour.PortDiscovery;
Andrea Campanella945ded22016-01-07 13:17:43 -080036import org.onosproject.net.config.ConfigFactory;
37import org.onosproject.net.config.NetworkConfigEvent;
38import org.onosproject.net.config.NetworkConfigListener;
39import org.onosproject.net.config.NetworkConfigRegistry;
40import org.onosproject.net.device.DefaultDeviceDescription;
41import org.onosproject.net.device.DeviceDescription;
42import org.onosproject.net.device.DeviceProvider;
43import org.onosproject.net.device.DeviceProviderRegistry;
44import org.onosproject.net.device.DeviceProviderService;
Andrea Campanellad8d92db2016-01-14 16:24:41 -080045import org.onosproject.net.driver.DriverHandler;
46import org.onosproject.net.driver.DriverService;
Andrea Campanella945ded22016-01-07 13:17:43 -080047import org.onosproject.net.provider.AbstractProvider;
48import org.onosproject.net.provider.ProviderId;
49import org.onosproject.protocol.rest.RestSBController;
50import org.onosproject.protocol.rest.RestSBDevice;
51import org.slf4j.Logger;
52
Andrea Campanella2947e622016-01-27 09:23:46 -080053import javax.net.ssl.HttpsURLConnection;
Andrea Campanella945ded22016-01-07 13:17:43 -080054import java.io.IOException;
55import java.net.HttpURLConnection;
56import java.net.URL;
Andrea Campanella2947e622016-01-27 09:23:46 -080057import java.nio.charset.StandardCharsets;
58import java.security.KeyManagementException;
59import java.security.NoSuchAlgorithmException;
60import java.util.Base64;
Andrea Campanella945ded22016-01-07 13:17:43 -080061import java.util.HashSet;
Andrea Campanella945ded22016-01-07 13:17:43 -080062import java.util.Set;
Andrea Campanella784ee0f2016-02-17 15:50:59 -080063import java.util.concurrent.ExecutorService;
64import java.util.concurrent.Executors;
Andrea Campanella945ded22016-01-07 13:17:43 -080065
Andrea Campanella784ee0f2016-02-17 15:50:59 -080066import static org.onlab.util.Tools.groupedThreads;
Andrea Campanella945ded22016-01-07 13:17:43 -080067import static org.onosproject.net.config.NetworkConfigEvent.Type.CONFIG_ADDED;
68import static org.onosproject.net.config.NetworkConfigEvent.Type.CONFIG_UPDATED;
69import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
70import static org.slf4j.LoggerFactory.getLogger;
71
72/**
73 * Provider for devices that use REST as means of configuration communication.
74 */
75@Component(immediate = true)
76public class RestDeviceProvider extends AbstractProvider
77 implements DeviceProvider {
78 private static final String APP_NAME = "org.onosproject.restsb";
79 private static final String REST = "rest";
80 private static final String PROVIDER = "org.onosproject.provider.rest.device";
81 private static final String IPADDRESS = "ipaddress";
82 private static final int TEST_CONNECT_TIMEOUT = 1000;
Andrea Campanella2947e622016-01-27 09:23:46 -080083 private static final String HTTPS = "https";
84 private static final String AUTHORIZATION_PROPERTY = "authorization";
85 private static final String BASIC_AUTH_PREFIX = "Basic ";
86 private static final String URL_SEPARATOR = "://";
Andrea Campanella945ded22016-01-07 13:17:43 -080087 private final Logger log = getLogger(getClass());
88
89 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
90 protected DeviceProviderRegistry providerRegistry;
91
92 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
93 protected RestSBController controller;
94
95 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
96 protected NetworkConfigRegistry cfgService;
97
98 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
99 protected CoreService coreService;
100
Andrea Campanellad8d92db2016-01-14 16:24:41 -0800101 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
102 protected DriverService driverService;
103
Andrea Campanella945ded22016-01-07 13:17:43 -0800104
105 private DeviceProviderService providerService;
106 protected static final String ISNOTNULL = "Rest device is not null";
107 private static final String UNKNOWN = "unknown";
108
Andrea Campanella784ee0f2016-02-17 15:50:59 -0800109 private final ExecutorService executor =
Andrea Campanella90f044f2016-03-02 09:14:57 -0800110 Executors.newFixedThreadPool(5, groupedThreads("onos/restsbprovider", "device-installer-%d", log));
Andrea Campanella784ee0f2016-02-17 15:50:59 -0800111
Andrea Campanella945ded22016-01-07 13:17:43 -0800112 private final ConfigFactory factory =
113 new ConfigFactory<ApplicationId, RestProviderConfig>(APP_SUBJECT_FACTORY,
114 RestProviderConfig.class,
115 "restDevices",
116 true) {
117 @Override
118 public RestProviderConfig createConfig() {
119 return new RestProviderConfig();
120 }
121 };
122 private final NetworkConfigListener cfgLister = new InternalNetworkConfigListener();
123 private ApplicationId appId;
124
Andrea Campanella784ee0f2016-02-17 15:50:59 -0800125 private Set<DeviceId> addedDevices = new HashSet<>();
126
Andrea Campanella945ded22016-01-07 13:17:43 -0800127
128 @Activate
129 public void activate() {
130 appId = coreService.registerApplication(APP_NAME);
131 providerService = providerRegistry.register(this);
132 cfgService.registerConfigFactory(factory);
133 cfgService.addListener(cfgLister);
Andrea Campanella7d8449b2016-03-02 10:16:42 -0800134 executor.execute(RestDeviceProvider.this::connectDevices);
Andrea Campanella945ded22016-01-07 13:17:43 -0800135 log.info("Started");
136 }
137
Andrea Campanella945ded22016-01-07 13:17:43 -0800138 @Deactivate
139 public void deactivate() {
Andrea Campanella86294db2016-03-07 11:42:49 -0800140 cfgService.removeListener(cfgLister);
141 controller.getDevices().keySet().forEach(this::deviceRemoved);
Andrea Campanella945ded22016-01-07 13:17:43 -0800142 providerRegistry.unregister(this);
143 providerService = null;
144 cfgService.unregisterConfigFactory(factory);
Andrea Campanella945ded22016-01-07 13:17:43 -0800145 log.info("Stopped");
146 }
147
148 public RestDeviceProvider() {
149 super(new ProviderId(REST, PROVIDER));
150 }
151
152 @Override
153 public void triggerProbe(DeviceId deviceId) {
154 // TODO: This will be implemented later.
155 log.info("Triggering probe on device {}", deviceId);
156 }
157
158 @Override
159 public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
160 // TODO: This will be implemented later.
161 }
162
163
164 @Override
165 public boolean isReachable(DeviceId deviceId) {
166 RestSBDevice restDevice = controller.getDevice(deviceId);
167 if (restDevice == null) {
Andrea Campanella784ee0f2016-02-17 15:50:59 -0800168 log.debug("the requested device id: " +
169 deviceId.toString() +
170 " is not associated to any REST Device");
Andrea Campanella945ded22016-01-07 13:17:43 -0800171 return false;
172 }
173 return restDevice.isActive();
174 }
175
176 private void deviceAdded(RestSBDevice nodeId) {
177 Preconditions.checkNotNull(nodeId, ISNOTNULL);
178 DeviceId deviceId = nodeId.deviceId();
179 ChassisId cid = new ChassisId();
180 String ipAddress = nodeId.ip().toString();
181 SparseAnnotations annotations = DefaultAnnotations.builder()
Marc De Leenheerb0d131c2016-03-01 20:34:59 -0800182 .set(IPADDRESS, ipAddress)
183 .set(AnnotationKeys.PROTOCOL, REST.toUpperCase())
184 .build();
Andrea Campanella945ded22016-01-07 13:17:43 -0800185 DeviceDescription deviceDescription = new DefaultDeviceDescription(
186 deviceId.uri(),
187 Device.Type.SWITCH,
188 UNKNOWN, UNKNOWN,
189 UNKNOWN, UNKNOWN,
190 cid,
191 annotations);
192 providerService.deviceConnected(deviceId, deviceDescription);
193 nodeId.setActive(true);
194 controller.addDevice(nodeId);
Andrea Campanella784ee0f2016-02-17 15:50:59 -0800195 addedDevices.add(deviceId);
Andrea Campanella945ded22016-01-07 13:17:43 -0800196 }
197
Andrea Campanella86294db2016-03-07 11:42:49 -0800198 private void deviceRemoved(DeviceId deviceId) {
199 Preconditions.checkNotNull(deviceId, ISNOTNULL);
Andrea Campanella945ded22016-01-07 13:17:43 -0800200 providerService.deviceDisconnected(deviceId);
Andrea Campanella86294db2016-03-07 11:42:49 -0800201 controller.removeDevice(deviceId);
Andrea Campanella945ded22016-01-07 13:17:43 -0800202 }
203
204 private void connectDevices() {
205 RestProviderConfig cfg = cfgService.getConfig(appId, RestProviderConfig.class);
206 try {
207 if (cfg != null && cfg.getDevicesAddresses() != null) {
208 //Precomputing the devices to be removed
209 Set<RestSBDevice> toBeRemoved = new HashSet<>(controller.getDevices().values());
210 toBeRemoved.removeAll(cfg.getDevicesAddresses());
211 //Adding new devices
212 cfg.getDevicesAddresses().stream()
213 .filter(device -> testDeviceConnection(device))
214 .forEach(device -> {
215 deviceAdded(device);
216 });
217 //Removing devices not wanted anymore
Andrea Campanella86294db2016-03-07 11:42:49 -0800218 toBeRemoved.stream().forEach(device -> deviceRemoved(device.deviceId()));
Andrea Campanella945ded22016-01-07 13:17:43 -0800219
220 }
221 } catch (ConfigException e) {
222 log.error("Configuration error {}", e);
223 }
Andrea Campanella2947e622016-01-27 09:23:46 -0800224 log.debug("REST Devices {}", controller.getDevices());
Andrea Campanella784ee0f2016-02-17 15:50:59 -0800225 addedDevices.forEach(deviceId -> {
Andrea Campanellad8d92db2016-01-14 16:24:41 -0800226 DriverHandler h = driverService.createHandler(deviceId);
227 PortDiscovery portConfig = h.behaviour(PortDiscovery.class);
228 if (portConfig != null) {
229 providerService.updatePorts(deviceId, portConfig.getPorts());
230 } else {
231 log.warn("No portGetter behaviour for device {}", deviceId);
232 }
233 });
Andrea Campanella784ee0f2016-02-17 15:50:59 -0800234 addedDevices.clear();
235
Andrea Campanella945ded22016-01-07 13:17:43 -0800236 }
237
238 private boolean testDeviceConnection(RestSBDevice device) {
239 try {
Andrea Campanella2947e622016-01-27 09:23:46 -0800240 URL url;
241 if (device.url() == null) {
242 url = new URL(device.protocol(), device.ip().toString(), device.port(), "");
243 } else {
244 url = new URL(device.protocol() + URL_SEPARATOR + device.url());
245 }
246 HttpURLConnection urlConn;
247 if (device.protocol().equals(HTTPS)) {
248 //FIXME this method provides no security accepting all SSL certs.
249 RestDeviceProviderUtilities.enableSslCert();
250
251 urlConn = (HttpsURLConnection) url.openConnection();
252 } else {
253 urlConn = (HttpURLConnection) url.openConnection();
254 }
Andrea Campanella784ee0f2016-02-17 15:50:59 -0800255 if (device.username() != null) {
256 String pwd = device.password() == null ? "" : ":" + device.password();
257 String userPassword = device.username() + pwd;
Andrea Campanella2947e622016-01-27 09:23:46 -0800258 String basicAuth = Base64.getEncoder()
259 .encodeToString(userPassword.getBytes(StandardCharsets.UTF_8));
260 urlConn.setRequestProperty(AUTHORIZATION_PROPERTY, BASIC_AUTH_PREFIX + basicAuth);
261 }
Andrea Campanella945ded22016-01-07 13:17:43 -0800262 urlConn.setConnectTimeout(TEST_CONNECT_TIMEOUT);
Andrea Campanella2947e622016-01-27 09:23:46 -0800263 boolean open = urlConn.getResponseCode() == (HttpsURLConnection.HTTP_OK);
264 if (!open) {
265 log.error("Device {} not accessibile, response code {} ", device,
266 urlConn.getResponseCode());
267 }
Andrea Campanella945ded22016-01-07 13:17:43 -0800268 urlConn.disconnect();
269 return open;
Andrea Campanella2947e622016-01-27 09:23:46 -0800270
271 } catch (IOException | NoSuchAlgorithmException | KeyManagementException e) {
272 log.error("Device {} not reachable, error creating {} connection", device,
273 device.protocol(), e);
Andrea Campanella945ded22016-01-07 13:17:43 -0800274 }
275 return false;
276 }
277
278 private class InternalNetworkConfigListener implements NetworkConfigListener {
279
280
281 @Override
282 public void event(NetworkConfigEvent event) {
Andrea Campanella90f044f2016-03-02 09:14:57 -0800283 executor.execute(RestDeviceProvider.this::connectDevices);
Andrea Campanella945ded22016-01-07 13:17:43 -0800284 }
285
286 @Override
287 public boolean isRelevant(NetworkConfigEvent event) {
288 //TODO refactor
289 return event.configClass().equals(RestProviderConfig.class) &&
290 (event.type() == CONFIG_ADDED ||
291 event.type() == CONFIG_UPDATED);
292 }
293 }
294}