blob: c787fb919adcd9f1448fcc12d57c3b8ad29fdc0c [file] [log] [blame]
Andrea Campanella945ded22016-01-07 13:17:43 -08001/*
2 * Copyright 2015 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.protocol.rest.ctl;
18
Andrea Campanella2947e622016-01-27 09:23:46 -080019import com.google.common.collect.ImmutableMap;
Andrea Campanella945ded22016-01-07 13:17:43 -080020import org.apache.commons.io.IOUtils;
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.Service;
Andrea Campanellace279ee2016-01-25 10:21:45 -080025import org.apache.http.client.methods.HttpPatch;
Andrea Campanella2947e622016-01-27 09:23:46 -080026import org.apache.http.conn.ssl.AllowAllHostnameVerifier;
Andrea Campanellace279ee2016-01-25 10:21:45 -080027import org.apache.http.entity.StringEntity;
Andrea Campanella2947e622016-01-27 09:23:46 -080028import org.apache.http.impl.client.CloseableHttpClient;
Andrea Campanellace279ee2016-01-25 10:21:45 -080029import org.apache.http.impl.client.HttpClients;
Andrea Campanella2947e622016-01-27 09:23:46 -080030import org.apache.http.ssl.SSLContextBuilder;
Jian Li9d616492016-03-09 10:52:49 -080031import org.glassfish.jersey.client.authentication.HttpAuthenticationFeature;
Andrea Campanella945ded22016-01-07 13:17:43 -080032import org.onlab.packet.IpAddress;
33import org.onosproject.net.DeviceId;
34import org.onosproject.protocol.rest.RestSBController;
35import org.onosproject.protocol.rest.RestSBDevice;
Andrea Campanella945ded22016-01-07 13:17:43 -080036import org.slf4j.Logger;
37import org.slf4j.LoggerFactory;
38
Jian Li9d616492016-03-09 10:52:49 -080039import javax.ws.rs.client.Client;
40import javax.ws.rs.client.ClientBuilder;
41import javax.ws.rs.client.Entity;
42import javax.ws.rs.client.WebTarget;
Andrea Campanella945ded22016-01-07 13:17:43 -080043import javax.ws.rs.core.MediaType;
44import javax.ws.rs.core.Response;
Andrea Campanella945ded22016-01-07 13:17:43 -080045import java.io.IOException;
46import java.io.InputStream;
47import java.nio.charset.StandardCharsets;
Andrea Campanella2947e622016-01-27 09:23:46 -080048import java.security.KeyManagementException;
49import java.security.KeyStoreException;
50import java.security.NoSuchAlgorithmException;
51import java.util.Base64;
Andrea Campanella945ded22016-01-07 13:17:43 -080052import java.util.Map;
53import java.util.concurrent.ConcurrentHashMap;
54
55/**
56 * The implementation of RestSBController.
57 */
58@Component(immediate = true)
59@Service
60public class RestSBControllerImpl implements RestSBController {
61
62 private static final Logger log =
63 LoggerFactory.getLogger(RestSBControllerImpl.class);
Andrea Campanella945ded22016-01-07 13:17:43 -080064 private static final String XML = "xml";
65 private static final String JSON = "json";
66 private static final String DOUBLESLASH = "//";
67 private static final String COLON = ":";
68 private static final int STATUS_OK = Response.Status.OK.getStatusCode();
69 private static final int STATUS_CREATED = Response.Status.CREATED.getStatusCode();
70 private static final int STATUS_ACCEPTED = Response.Status.ACCEPTED.getStatusCode();
Andrea Campanella2947e622016-01-27 09:23:46 -080071 private static final String HTTPS = "https";
72 private static final String AUTHORIZATION_PROPERTY = "authorization";
73 private static final String BASIC_AUTH_PREFIX = "Basic ";
Andrea Campanella945ded22016-01-07 13:17:43 -080074
75 private final Map<DeviceId, RestSBDevice> deviceMap = new ConcurrentHashMap<>();
76 Client client;
77
78 @Activate
Andrea Campanellace279ee2016-01-25 10:21:45 -080079 public void activate() {
Jian Li9d616492016-03-09 10:52:49 -080080 client = ClientBuilder.newClient();
Andrea Campanella945ded22016-01-07 13:17:43 -080081 log.info("Started");
82 }
83
84 @Deactivate
85 public void deactivate() {
86 deviceMap.clear();
87 log.info("Stopped");
88 }
89
90 @Override
91 public Map<DeviceId, RestSBDevice> getDevices() {
Andrea Campanella2947e622016-01-27 09:23:46 -080092 return ImmutableMap.copyOf(deviceMap);
Andrea Campanella945ded22016-01-07 13:17:43 -080093 }
94
95 @Override
96 public RestSBDevice getDevice(DeviceId deviceInfo) {
97 return deviceMap.get(deviceInfo);
98 }
99
100 @Override
101 public RestSBDevice getDevice(IpAddress ip, int port) {
Andrea Campanella2947e622016-01-27 09:23:46 -0800102 return deviceMap.values().stream().filter(v -> v.ip().equals(ip)
103 && v.port() == port).findFirst().get();
Andrea Campanella945ded22016-01-07 13:17:43 -0800104 }
105
106 @Override
107 public void addDevice(RestSBDevice device) {
108 deviceMap.put(device.deviceId(), device);
109 }
110
111 @Override
Andrea Campanella86294db2016-03-07 11:42:49 -0800112 public void removeDevice(DeviceId deviceId) {
113 deviceMap.remove(deviceId);
Andrea Campanella945ded22016-01-07 13:17:43 -0800114 }
115
116 @Override
117 public boolean post(DeviceId device, String request, InputStream payload, String mediaType) {
Jian Li9d616492016-03-09 10:52:49 -0800118 WebTarget wt = getWebTarget(device, request);
Andrea Campanella945ded22016-01-07 13:17:43 -0800119
Jian Li9d616492016-03-09 10:52:49 -0800120 Response response = null;
Andrea Campanella945ded22016-01-07 13:17:43 -0800121 if (payload != null) {
122 try {
Jian Li9d616492016-03-09 10:52:49 -0800123 response = wt.request(mediaType)
124 .post(Entity.entity(IOUtils.toString(payload, StandardCharsets.UTF_8), mediaType));
Andrea Campanella945ded22016-01-07 13:17:43 -0800125 } catch (IOException e) {
126 log.error("Cannot do POST {} request on device {} because can't read payload",
127 request, device);
128 }
129 } else {
Jian Li9d616492016-03-09 10:52:49 -0800130 response = wt.request(mediaType).post(Entity.entity(null, mediaType));
Andrea Campanella945ded22016-01-07 13:17:43 -0800131 }
132 return checkReply(response);
133 }
134
135 @Override
136 public boolean put(DeviceId device, String request, InputStream payload, String mediaType) {
Andrea Campanellad8d92db2016-01-14 16:24:41 -0800137
Jian Li9d616492016-03-09 10:52:49 -0800138 WebTarget wt = getWebTarget(device, request);
139 Response response = null;
Andrea Campanella945ded22016-01-07 13:17:43 -0800140 if (payload != null) {
141 try {
Jian Li9d616492016-03-09 10:52:49 -0800142 response = wt.request(mediaType)
143 .put(Entity.entity(IOUtils.toString(payload, StandardCharsets.UTF_8), mediaType));
Andrea Campanella945ded22016-01-07 13:17:43 -0800144 } catch (IOException e) {
145 log.error("Cannot do PUT {} request on device {} because can't read payload",
146 request, device);
147 }
148 } else {
Jian Li9d616492016-03-09 10:52:49 -0800149 response = wt.request(mediaType).put(Entity.entity(null, mediaType));
Andrea Campanella945ded22016-01-07 13:17:43 -0800150 }
151 return checkReply(response);
152 }
153
154 @Override
155 public InputStream get(DeviceId device, String request, String mediaType) {
Jian Li9d616492016-03-09 10:52:49 -0800156 WebTarget wt = getWebTarget(device, request);
Andrea Campanella945ded22016-01-07 13:17:43 -0800157 String type;
158 switch (mediaType) {
159 case XML:
160 type = MediaType.APPLICATION_XML;
161 break;
162 case JSON:
163 type = MediaType.APPLICATION_JSON;
164 break;
165 default:
166 throw new IllegalArgumentException("Unsupported media type " + mediaType);
167
168 }
Andrea Campanella2947e622016-01-27 09:23:46 -0800169
Jian Li9d616492016-03-09 10:52:49 -0800170 Response s = wt.request(type).get();
Andrea Campanella2947e622016-01-27 09:23:46 -0800171 if (checkReply(s)) {
Jian Li9d616492016-03-09 10:52:49 -0800172 return (InputStream) s.getEntity();
Andrea Campanella2947e622016-01-27 09:23:46 -0800173 }
174 return null;
Andrea Campanella945ded22016-01-07 13:17:43 -0800175 }
176
177 @Override
Andrea Campanellace279ee2016-01-25 10:21:45 -0800178 public boolean patch(DeviceId device, String request, InputStream payload, String mediaType) {
Andrea Campanellace279ee2016-01-25 10:21:45 -0800179 try {
Andrea Campanella2947e622016-01-27 09:23:46 -0800180 log.debug("Url request {} ", getUrlString(device, request));
181 HttpPatch httprequest = new HttpPatch(getUrlString(device, request));
Andrea Campanella784ee0f2016-02-17 15:50:59 -0800182 if (deviceMap.get(device).username() != null) {
183 String pwd = deviceMap.get(device).password() == null ? "" : COLON + deviceMap.get(device).password();
184 String userPassword = deviceMap.get(device).username() + pwd;
Andrea Campanella2947e622016-01-27 09:23:46 -0800185 String base64string = Base64.getEncoder().encodeToString(userPassword.getBytes(StandardCharsets.UTF_8));
186 httprequest.addHeader(AUTHORIZATION_PROPERTY, BASIC_AUTH_PREFIX + base64string);
187 }
Andrea Campanellace279ee2016-01-25 10:21:45 -0800188 if (payload != null) {
189 StringEntity input = new StringEntity(IOUtils.toString(payload, StandardCharsets.UTF_8));
190 input.setContentType(mediaType);
191 httprequest.setEntity(input);
192 }
Andrea Campanella2947e622016-01-27 09:23:46 -0800193 CloseableHttpClient httpClient;
194 if (deviceMap.containsKey(device) && deviceMap.get(device).protocol().equals(HTTPS)) {
195 httpClient = getApacheSslBypassClient();
196 } else {
197 httpClient = HttpClients.createDefault();
198 }
199 int responseStatusCode = httpClient
200 .execute(httprequest)
Andrea Campanellace279ee2016-01-25 10:21:45 -0800201 .getStatusLine()
202 .getStatusCode();
203 return checkStatusCode(responseStatusCode);
Andrea Campanella2947e622016-01-27 09:23:46 -0800204 } catch (IOException | NoSuchAlgorithmException | KeyManagementException | KeyStoreException e) {
205 log.error("Cannot do PATCH {} request on device {}",
206 request, device, e);
Andrea Campanellace279ee2016-01-25 10:21:45 -0800207 }
208 return false;
209 }
210
211 @Override
Andrea Campanella945ded22016-01-07 13:17:43 -0800212 public boolean delete(DeviceId device, String request, InputStream payload, String mediaType) {
Jian Li9d616492016-03-09 10:52:49 -0800213 WebTarget wt = getWebTarget(device, request);
214
215 // FIXME: do we need to delete an entry by enclosing data in DELETE request?
216 // wouldn't it be nice to use PUT to implement the similar concept?
217 Response response = wt.request(mediaType).delete();
218
Andrea Campanella945ded22016-01-07 13:17:43 -0800219 return checkReply(response);
220 }
221
Jian Li9d616492016-03-09 10:52:49 -0800222 private WebTarget getWebTarget(DeviceId device, String request) {
Andrea Campanella2947e622016-01-27 09:23:46 -0800223 log.debug("Sending request to URL {} ", getUrlString(device, request));
Jian Li9d616492016-03-09 10:52:49 -0800224 WebTarget wt = client.target(getUrlString(device, request));
Andrea Campanella784ee0f2016-02-17 15:50:59 -0800225 if (deviceMap.containsKey(device) && deviceMap.get(device).username() != null) {
Jian Li9d616492016-03-09 10:52:49 -0800226 client.register(HttpAuthenticationFeature.basic(deviceMap.get(device).username(),
Andrea Campanella784ee0f2016-02-17 15:50:59 -0800227 deviceMap.get(device).password() == null ?
228 "" : deviceMap.get(device).password()));
Andrea Campanella2947e622016-01-27 09:23:46 -0800229 }
Jian Li9d616492016-03-09 10:52:49 -0800230 return wt;
Andrea Campanella2947e622016-01-27 09:23:46 -0800231 }
232
233 //FIXME security issue: this trusts every SSL certificate, even if is self-signed. Also deprecated methods.
234 private CloseableHttpClient getApacheSslBypassClient() throws NoSuchAlgorithmException,
235 KeyManagementException, KeyStoreException {
236 return HttpClients.custom().
237 setHostnameVerifier(new AllowAllHostnameVerifier()).
238 setSslcontext(new SSLContextBuilder()
239 .loadTrustMaterial(null, (arg0, arg1) -> true)
240 .build()).build();
241 }
242
243 private String getUrlString(DeviceId device, String request) {
244 if (deviceMap.get(device).url() != null) {
245 return deviceMap.get(device).protocol() + COLON + DOUBLESLASH
246 + deviceMap.get(device).url() + request;
247 } else {
248 return deviceMap.get(device).protocol() + COLON +
249 DOUBLESLASH +
250 deviceMap.get(device).ip().toString() +
251 COLON + deviceMap.get(device).port() + request;
252 }
Andrea Campanella945ded22016-01-07 13:17:43 -0800253 }
254
Jian Li9d616492016-03-09 10:52:49 -0800255 private boolean checkReply(Response response) {
Andrea Campanella945ded22016-01-07 13:17:43 -0800256 if (response != null) {
Andrea Campanellace279ee2016-01-25 10:21:45 -0800257 return checkStatusCode(response.getStatus());
Andrea Campanella945ded22016-01-07 13:17:43 -0800258 }
259 log.error("Null reply from device");
260 return false;
261 }
Andrea Campanellace279ee2016-01-25 10:21:45 -0800262
263 private boolean checkStatusCode(int statusCode) {
264 if (statusCode == STATUS_OK ||
265 statusCode == STATUS_CREATED ||
266 statusCode == STATUS_ACCEPTED) {
267 return true;
268 } else {
Andrea Campanella2947e622016-01-27 09:23:46 -0800269 log.error("Failed request, HTTP error code : "
Andrea Campanellace279ee2016-01-25 10:21:45 -0800270 + statusCode);
271 return false;
272 }
273 }
Andrea Campanella945ded22016-01-07 13:17:43 -0800274}