blob: 8adde28dbc16738e40669f36af12052d46371d58 [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 com.sun.jersey.api.client.Client;
21import com.sun.jersey.api.client.ClientResponse;
22import com.sun.jersey.api.client.WebResource;
Andrea Campanella2947e622016-01-27 09:23:46 -080023import com.sun.jersey.api.client.filter.HTTPBasicAuthFilter;
Andrea Campanella945ded22016-01-07 13:17:43 -080024import org.apache.commons.io.IOUtils;
25import org.apache.felix.scr.annotations.Activate;
26import org.apache.felix.scr.annotations.Component;
27import org.apache.felix.scr.annotations.Deactivate;
28import org.apache.felix.scr.annotations.Service;
Andrea Campanellace279ee2016-01-25 10:21:45 -080029import org.apache.http.client.methods.HttpPatch;
Andrea Campanella2947e622016-01-27 09:23:46 -080030import org.apache.http.conn.ssl.AllowAllHostnameVerifier;
Andrea Campanellace279ee2016-01-25 10:21:45 -080031import org.apache.http.entity.StringEntity;
Andrea Campanella2947e622016-01-27 09:23:46 -080032import org.apache.http.impl.client.CloseableHttpClient;
Andrea Campanellace279ee2016-01-25 10:21:45 -080033import org.apache.http.impl.client.HttpClients;
Andrea Campanella2947e622016-01-27 09:23:46 -080034import org.apache.http.ssl.SSLContextBuilder;
Andrea Campanella945ded22016-01-07 13:17:43 -080035import org.onlab.packet.IpAddress;
36import org.onosproject.net.DeviceId;
37import org.onosproject.protocol.rest.RestSBController;
38import org.onosproject.protocol.rest.RestSBDevice;
Andrea Campanella945ded22016-01-07 13:17:43 -080039import org.slf4j.Logger;
40import org.slf4j.LoggerFactory;
41
42import javax.ws.rs.core.MediaType;
43import javax.ws.rs.core.Response;
44import java.io.ByteArrayInputStream;
45import 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() {
Andrea Campanella945ded22016-01-07 13:17:43 -080080 client = Client.create();
81 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
112 public void removeDevice(RestSBDevice device) {
113 deviceMap.remove(device.deviceId());
114 }
115
116 @Override
117 public boolean post(DeviceId device, String request, InputStream payload, String mediaType) {
118 WebResource webResource = getWebResource(device, request);
119
120 ClientResponse response = null;
121 if (payload != null) {
122 try {
123 response = webResource.accept(mediaType)
124 .post(ClientResponse.class, IOUtils.toString(payload, StandardCharsets.UTF_8));
125 } catch (IOException e) {
126 log.error("Cannot do POST {} request on device {} because can't read payload",
127 request, device);
128 }
129 } else {
130 response = webResource.accept(mediaType)
131 .post(ClientResponse.class);
132 }
133 return checkReply(response);
134 }
135
136 @Override
137 public boolean put(DeviceId device, String request, InputStream payload, String mediaType) {
Andrea Campanellad8d92db2016-01-14 16:24:41 -0800138
Andrea Campanella945ded22016-01-07 13:17:43 -0800139 WebResource webResource = getWebResource(device, request);
140 ClientResponse response = null;
141 if (payload != null) {
142 try {
143 response = webResource.accept(mediaType)
144 .put(ClientResponse.class, IOUtils.toString(payload, StandardCharsets.UTF_8));
145 } catch (IOException e) {
146 log.error("Cannot do PUT {} request on device {} because can't read payload",
147 request, device);
148 }
149 } else {
150 response = webResource.accept(mediaType)
151 .put(ClientResponse.class);
152 }
153 return checkReply(response);
154 }
155
156 @Override
157 public InputStream get(DeviceId device, String request, String mediaType) {
158 WebResource webResource = getWebResource(device, request);
159 String type;
160 switch (mediaType) {
161 case XML:
162 type = MediaType.APPLICATION_XML;
163 break;
164 case JSON:
165 type = MediaType.APPLICATION_JSON;
166 break;
167 default:
168 throw new IllegalArgumentException("Unsupported media type " + mediaType);
169
170 }
Andrea Campanella2947e622016-01-27 09:23:46 -0800171
172 ClientResponse s = webResource.accept(type).get(ClientResponse.class);
173 if (checkReply(s)) {
174 return new ByteArrayInputStream(s.getEntity(String.class)
175 .getBytes(StandardCharsets.UTF_8));
176 }
177 return null;
Andrea Campanella945ded22016-01-07 13:17:43 -0800178 }
179
180 @Override
Andrea Campanellace279ee2016-01-25 10:21:45 -0800181 public boolean patch(DeviceId device, String request, InputStream payload, String mediaType) {
Andrea Campanellace279ee2016-01-25 10:21:45 -0800182 try {
Andrea Campanella2947e622016-01-27 09:23:46 -0800183 log.debug("Url request {} ", getUrlString(device, request));
184 HttpPatch httprequest = new HttpPatch(getUrlString(device, request));
Andrea Campanella784ee0f2016-02-17 15:50:59 -0800185 if (deviceMap.get(device).username() != null) {
186 String pwd = deviceMap.get(device).password() == null ? "" : COLON + deviceMap.get(device).password();
187 String userPassword = deviceMap.get(device).username() + pwd;
Andrea Campanella2947e622016-01-27 09:23:46 -0800188 String base64string = Base64.getEncoder().encodeToString(userPassword.getBytes(StandardCharsets.UTF_8));
189 httprequest.addHeader(AUTHORIZATION_PROPERTY, BASIC_AUTH_PREFIX + base64string);
190 }
Andrea Campanellace279ee2016-01-25 10:21:45 -0800191 if (payload != null) {
192 StringEntity input = new StringEntity(IOUtils.toString(payload, StandardCharsets.UTF_8));
193 input.setContentType(mediaType);
194 httprequest.setEntity(input);
195 }
Andrea Campanella2947e622016-01-27 09:23:46 -0800196 CloseableHttpClient httpClient;
197 if (deviceMap.containsKey(device) && deviceMap.get(device).protocol().equals(HTTPS)) {
198 httpClient = getApacheSslBypassClient();
199 } else {
200 httpClient = HttpClients.createDefault();
201 }
202 int responseStatusCode = httpClient
203 .execute(httprequest)
Andrea Campanellace279ee2016-01-25 10:21:45 -0800204 .getStatusLine()
205 .getStatusCode();
206 return checkStatusCode(responseStatusCode);
Andrea Campanella2947e622016-01-27 09:23:46 -0800207 } catch (IOException | NoSuchAlgorithmException | KeyManagementException | KeyStoreException e) {
208 log.error("Cannot do PATCH {} request on device {}",
209 request, device, e);
Andrea Campanellace279ee2016-01-25 10:21:45 -0800210 }
211 return false;
212 }
213
214 @Override
Andrea Campanella945ded22016-01-07 13:17:43 -0800215 public boolean delete(DeviceId device, String request, InputStream payload, String mediaType) {
216 WebResource webResource = getWebResource(device, request);
217 ClientResponse response = null;
218 if (payload != null) {
219 try {
220 response = webResource.accept(mediaType)
221 .delete(ClientResponse.class, IOUtils.toString(payload, StandardCharsets.UTF_8));
222 } catch (IOException e) {
223 log.error("Cannot do PUT {} request on device {} because can't read payload",
224 request, device);
225 }
226 } else {
227 response = webResource.accept(mediaType)
228 .delete(ClientResponse.class);
229 }
230 return checkReply(response);
231 }
232
233 private WebResource getWebResource(DeviceId device, String request) {
Andrea Campanella2947e622016-01-27 09:23:46 -0800234 log.debug("Sending request to URL {} ", getUrlString(device, request));
235 WebResource webResource = client.resource(getUrlString(device, request));
Andrea Campanella784ee0f2016-02-17 15:50:59 -0800236 if (deviceMap.containsKey(device) && deviceMap.get(device).username() != null) {
237 client.addFilter(new HTTPBasicAuthFilter(deviceMap.get(device).username(),
238 deviceMap.get(device).password() == null ?
239 "" : deviceMap.get(device).password()));
Andrea Campanella2947e622016-01-27 09:23:46 -0800240 }
241 return webResource;
242 }
243
244 //FIXME security issue: this trusts every SSL certificate, even if is self-signed. Also deprecated methods.
245 private CloseableHttpClient getApacheSslBypassClient() throws NoSuchAlgorithmException,
246 KeyManagementException, KeyStoreException {
247 return HttpClients.custom().
248 setHostnameVerifier(new AllowAllHostnameVerifier()).
249 setSslcontext(new SSLContextBuilder()
250 .loadTrustMaterial(null, (arg0, arg1) -> true)
251 .build()).build();
252 }
253
254 private String getUrlString(DeviceId device, String request) {
255 if (deviceMap.get(device).url() != null) {
256 return deviceMap.get(device).protocol() + COLON + DOUBLESLASH
257 + deviceMap.get(device).url() + request;
258 } else {
259 return deviceMap.get(device).protocol() + COLON +
260 DOUBLESLASH +
261 deviceMap.get(device).ip().toString() +
262 COLON + deviceMap.get(device).port() + request;
263 }
Andrea Campanella945ded22016-01-07 13:17:43 -0800264 }
265
266 private boolean checkReply(ClientResponse response) {
267 if (response != null) {
Andrea Campanellace279ee2016-01-25 10:21:45 -0800268 return checkStatusCode(response.getStatus());
Andrea Campanella945ded22016-01-07 13:17:43 -0800269 }
270 log.error("Null reply from device");
271 return false;
272 }
Andrea Campanellace279ee2016-01-25 10:21:45 -0800273
274 private boolean checkStatusCode(int statusCode) {
275 if (statusCode == STATUS_OK ||
276 statusCode == STATUS_CREATED ||
277 statusCode == STATUS_ACCEPTED) {
278 return true;
279 } else {
Andrea Campanella2947e622016-01-27 09:23:46 -0800280 log.error("Failed request, HTTP error code : "
Andrea Campanellace279ee2016-01-25 10:21:45 -0800281 + statusCode);
282 return false;
283 }
284 }
Andrea Campanella945ded22016-01-07 13:17:43 -0800285}