blob: 55e2fb8fb8d5007e22336cb98c4a26170a9f2a72 [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;
29import org.onosproject.net.DefaultAnnotations;
30import org.onosproject.net.Device;
31import org.onosproject.net.DeviceId;
32import org.onosproject.net.MastershipRole;
33import org.onosproject.net.SparseAnnotations;
Andrea Campanellad8d92db2016-01-14 16:24:41 -080034import org.onosproject.net.behaviour.PortDiscovery;
Andrea Campanella945ded22016-01-07 13:17:43 -080035import org.onosproject.net.config.ConfigFactory;
36import org.onosproject.net.config.NetworkConfigEvent;
37import org.onosproject.net.config.NetworkConfigListener;
38import org.onosproject.net.config.NetworkConfigRegistry;
39import org.onosproject.net.device.DefaultDeviceDescription;
40import org.onosproject.net.device.DeviceDescription;
41import org.onosproject.net.device.DeviceProvider;
42import org.onosproject.net.device.DeviceProviderRegistry;
43import org.onosproject.net.device.DeviceProviderService;
Andrea Campanellad8d92db2016-01-14 16:24:41 -080044import org.onosproject.net.driver.DriverHandler;
45import org.onosproject.net.driver.DriverService;
Andrea Campanella945ded22016-01-07 13:17:43 -080046import org.onosproject.net.provider.AbstractProvider;
47import org.onosproject.net.provider.ProviderId;
48import org.onosproject.protocol.rest.RestSBController;
49import org.onosproject.protocol.rest.RestSBDevice;
50import org.slf4j.Logger;
51
Andrea Campanella2947e622016-01-27 09:23:46 -080052import javax.net.ssl.HttpsURLConnection;
Andrea Campanella945ded22016-01-07 13:17:43 -080053import java.io.IOException;
54import java.net.HttpURLConnection;
55import java.net.URL;
Andrea Campanella2947e622016-01-27 09:23:46 -080056import java.nio.charset.StandardCharsets;
57import java.security.KeyManagementException;
58import java.security.NoSuchAlgorithmException;
59import java.util.Base64;
Andrea Campanella945ded22016-01-07 13:17:43 -080060import java.util.HashSet;
Andrea Campanella945ded22016-01-07 13:17:43 -080061import java.util.Set;
Andrea Campanella784ee0f2016-02-17 15:50:59 -080062import java.util.concurrent.ExecutorService;
63import java.util.concurrent.Executors;
Andrea Campanella945ded22016-01-07 13:17:43 -080064
Andrea Campanella784ee0f2016-02-17 15:50:59 -080065import static org.onlab.util.Tools.groupedThreads;
Andrea Campanella945ded22016-01-07 13:17:43 -080066import static org.onosproject.net.config.NetworkConfigEvent.Type.CONFIG_ADDED;
67import static org.onosproject.net.config.NetworkConfigEvent.Type.CONFIG_UPDATED;
68import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
69import static org.slf4j.LoggerFactory.getLogger;
70
71/**
72 * Provider for devices that use REST as means of configuration communication.
73 */
74@Component(immediate = true)
75public class RestDeviceProvider extends AbstractProvider
76 implements DeviceProvider {
77 private static final String APP_NAME = "org.onosproject.restsb";
78 private static final String REST = "rest";
79 private static final String PROVIDER = "org.onosproject.provider.rest.device";
80 private static final String IPADDRESS = "ipaddress";
81 private static final int TEST_CONNECT_TIMEOUT = 1000;
Andrea Campanella2947e622016-01-27 09:23:46 -080082 private static final String HTTPS = "https";
83 private static final String AUTHORIZATION_PROPERTY = "authorization";
84 private static final String BASIC_AUTH_PREFIX = "Basic ";
85 private static final String URL_SEPARATOR = "://";
Andrea Campanella945ded22016-01-07 13:17:43 -080086 private final Logger log = getLogger(getClass());
87
88 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
89 protected DeviceProviderRegistry providerRegistry;
90
91 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
92 protected RestSBController controller;
93
94 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
95 protected NetworkConfigRegistry cfgService;
96
97 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
98 protected CoreService coreService;
99
Andrea Campanellad8d92db2016-01-14 16:24:41 -0800100 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
101 protected DriverService driverService;
102
Andrea Campanella945ded22016-01-07 13:17:43 -0800103
104 private DeviceProviderService providerService;
105 protected static final String ISNOTNULL = "Rest device is not null";
106 private static final String UNKNOWN = "unknown";
107
Andrea Campanella784ee0f2016-02-17 15:50:59 -0800108 private final ExecutorService executor =
109 Executors.newFixedThreadPool(5, groupedThreads("onos/restsbprovider", "device-installer-%d"));
110
Andrea Campanella945ded22016-01-07 13:17:43 -0800111 private final ConfigFactory factory =
112 new ConfigFactory<ApplicationId, RestProviderConfig>(APP_SUBJECT_FACTORY,
113 RestProviderConfig.class,
114 "restDevices",
115 true) {
116 @Override
117 public RestProviderConfig createConfig() {
118 return new RestProviderConfig();
119 }
120 };
121 private final NetworkConfigListener cfgLister = new InternalNetworkConfigListener();
122 private ApplicationId appId;
123
Andrea Campanella784ee0f2016-02-17 15:50:59 -0800124 private Set<DeviceId> addedDevices = new HashSet<>();
125
Andrea Campanella945ded22016-01-07 13:17:43 -0800126
127 @Activate
128 public void activate() {
129 appId = coreService.registerApplication(APP_NAME);
130 providerService = providerRegistry.register(this);
131 cfgService.registerConfigFactory(factory);
132 cfgService.addListener(cfgLister);
133 connectDevices();
134 log.info("Started");
135 }
136
137
138 @Deactivate
139 public void deactivate() {
140 providerRegistry.unregister(this);
141 providerService = null;
142 cfgService.unregisterConfigFactory(factory);
143 cfgService.removeListener(cfgLister);
144 log.info("Stopped");
145 }
146
147 public RestDeviceProvider() {
148 super(new ProviderId(REST, PROVIDER));
149 }
150
151 @Override
152 public void triggerProbe(DeviceId deviceId) {
153 // TODO: This will be implemented later.
154 log.info("Triggering probe on device {}", deviceId);
155 }
156
157 @Override
158 public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
159 // TODO: This will be implemented later.
160 }
161
162
163 @Override
164 public boolean isReachable(DeviceId deviceId) {
165 RestSBDevice restDevice = controller.getDevice(deviceId);
166 if (restDevice == null) {
Andrea Campanella784ee0f2016-02-17 15:50:59 -0800167 log.debug("the requested device id: " +
168 deviceId.toString() +
169 " is not associated to any REST Device");
Andrea Campanella945ded22016-01-07 13:17:43 -0800170 return false;
171 }
172 return restDevice.isActive();
173 }
174
175 private void deviceAdded(RestSBDevice nodeId) {
176 Preconditions.checkNotNull(nodeId, ISNOTNULL);
177 DeviceId deviceId = nodeId.deviceId();
178 ChassisId cid = new ChassisId();
179 String ipAddress = nodeId.ip().toString();
180 SparseAnnotations annotations = DefaultAnnotations.builder()
181 .set(IPADDRESS, ipAddress).build();
182 DeviceDescription deviceDescription = new DefaultDeviceDescription(
183 deviceId.uri(),
184 Device.Type.SWITCH,
185 UNKNOWN, UNKNOWN,
186 UNKNOWN, UNKNOWN,
187 cid,
188 annotations);
189 providerService.deviceConnected(deviceId, deviceDescription);
190 nodeId.setActive(true);
191 controller.addDevice(nodeId);
Andrea Campanella784ee0f2016-02-17 15:50:59 -0800192 addedDevices.add(deviceId);
Andrea Campanella945ded22016-01-07 13:17:43 -0800193 }
194
Andrea Campanella945ded22016-01-07 13:17:43 -0800195 //when do I call it ?
196 public void deviceRemoved(RestSBDevice nodeId) {
197 Preconditions.checkNotNull(nodeId, ISNOTNULL);
198 DeviceId deviceId = nodeId.deviceId();
199 providerService.deviceDisconnected(deviceId);
200 controller.removeDevice(nodeId);
201 }
202
203 private void connectDevices() {
204 RestProviderConfig cfg = cfgService.getConfig(appId, RestProviderConfig.class);
205 try {
206 if (cfg != null && cfg.getDevicesAddresses() != null) {
207 //Precomputing the devices to be removed
208 Set<RestSBDevice> toBeRemoved = new HashSet<>(controller.getDevices().values());
209 toBeRemoved.removeAll(cfg.getDevicesAddresses());
210 //Adding new devices
211 cfg.getDevicesAddresses().stream()
212 .filter(device -> testDeviceConnection(device))
213 .forEach(device -> {
214 deviceAdded(device);
215 });
216 //Removing devices not wanted anymore
217 toBeRemoved.stream().forEach(device -> deviceRemoved(device));
218
219 }
220 } catch (ConfigException e) {
221 log.error("Configuration error {}", e);
222 }
Andrea Campanella2947e622016-01-27 09:23:46 -0800223 log.debug("REST Devices {}", controller.getDevices());
Andrea Campanella784ee0f2016-02-17 15:50:59 -0800224 addedDevices.forEach(deviceId -> {
Andrea Campanellad8d92db2016-01-14 16:24:41 -0800225 DriverHandler h = driverService.createHandler(deviceId);
226 PortDiscovery portConfig = h.behaviour(PortDiscovery.class);
227 if (portConfig != null) {
228 providerService.updatePorts(deviceId, portConfig.getPorts());
229 } else {
230 log.warn("No portGetter behaviour for device {}", deviceId);
231 }
232 });
Andrea Campanella784ee0f2016-02-17 15:50:59 -0800233 addedDevices.clear();
234
Andrea Campanella945ded22016-01-07 13:17:43 -0800235 }
236
237 private boolean testDeviceConnection(RestSBDevice device) {
238 try {
Andrea Campanella2947e622016-01-27 09:23:46 -0800239 URL url;
240 if (device.url() == null) {
241 url = new URL(device.protocol(), device.ip().toString(), device.port(), "");
242 } else {
243 url = new URL(device.protocol() + URL_SEPARATOR + device.url());
244 }
245 HttpURLConnection urlConn;
246 if (device.protocol().equals(HTTPS)) {
247 //FIXME this method provides no security accepting all SSL certs.
248 RestDeviceProviderUtilities.enableSslCert();
249
250 urlConn = (HttpsURLConnection) url.openConnection();
251 } else {
252 urlConn = (HttpURLConnection) url.openConnection();
253 }
Andrea Campanella784ee0f2016-02-17 15:50:59 -0800254 if (device.username() != null) {
255 String pwd = device.password() == null ? "" : ":" + device.password();
256 String userPassword = device.username() + pwd;
Andrea Campanella2947e622016-01-27 09:23:46 -0800257 String basicAuth = Base64.getEncoder()
258 .encodeToString(userPassword.getBytes(StandardCharsets.UTF_8));
259 urlConn.setRequestProperty(AUTHORIZATION_PROPERTY, BASIC_AUTH_PREFIX + basicAuth);
260 }
Andrea Campanella945ded22016-01-07 13:17:43 -0800261 urlConn.setConnectTimeout(TEST_CONNECT_TIMEOUT);
Andrea Campanella2947e622016-01-27 09:23:46 -0800262 boolean open = urlConn.getResponseCode() == (HttpsURLConnection.HTTP_OK);
263 if (!open) {
264 log.error("Device {} not accessibile, response code {} ", device,
265 urlConn.getResponseCode());
266 }
Andrea Campanella945ded22016-01-07 13:17:43 -0800267 urlConn.disconnect();
268 return open;
Andrea Campanella2947e622016-01-27 09:23:46 -0800269
270 } catch (IOException | NoSuchAlgorithmException | KeyManagementException e) {
271 log.error("Device {} not reachable, error creating {} connection", device,
272 device.protocol(), e);
Andrea Campanella945ded22016-01-07 13:17:43 -0800273 }
274 return false;
275 }
276
277 private class InternalNetworkConfigListener implements NetworkConfigListener {
278
279
280 @Override
281 public void event(NetworkConfigEvent event) {
Andrea Campanella784ee0f2016-02-17 15:50:59 -0800282 executor.submit(RestDeviceProvider.this::connectDevices);
Andrea Campanella945ded22016-01-07 13:17:43 -0800283 }
284
285 @Override
286 public boolean isRelevant(NetworkConfigEvent event) {
287 //TODO refactor
288 return event.configClass().equals(RestProviderConfig.class) &&
289 (event.type() == CONFIG_ADDED ||
290 event.type() == CONFIG_UPDATED);
291 }
292 }
293}