blob: 0542dec15657287ff5af76b200a716ddf9a9bd13 [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));
185 if (deviceMap.get(device).password() != null) {
186 String userPassword = deviceMap.get(device).name() + COLON + deviceMap.get(device).password();
187 String base64string = Base64.getEncoder().encodeToString(userPassword.getBytes(StandardCharsets.UTF_8));
188 httprequest.addHeader(AUTHORIZATION_PROPERTY, BASIC_AUTH_PREFIX + base64string);
189 }
Andrea Campanellace279ee2016-01-25 10:21:45 -0800190 if (payload != null) {
191 StringEntity input = new StringEntity(IOUtils.toString(payload, StandardCharsets.UTF_8));
192 input.setContentType(mediaType);
193 httprequest.setEntity(input);
194 }
Andrea Campanella2947e622016-01-27 09:23:46 -0800195 CloseableHttpClient httpClient;
196 if (deviceMap.containsKey(device) && deviceMap.get(device).protocol().equals(HTTPS)) {
197 httpClient = getApacheSslBypassClient();
198 } else {
199 httpClient = HttpClients.createDefault();
200 }
201 int responseStatusCode = httpClient
202 .execute(httprequest)
Andrea Campanellace279ee2016-01-25 10:21:45 -0800203 .getStatusLine()
204 .getStatusCode();
205 return checkStatusCode(responseStatusCode);
Andrea Campanella2947e622016-01-27 09:23:46 -0800206 } catch (IOException | NoSuchAlgorithmException | KeyManagementException | KeyStoreException e) {
207 log.error("Cannot do PATCH {} request on device {}",
208 request, device, e);
Andrea Campanellace279ee2016-01-25 10:21:45 -0800209 }
210 return false;
211 }
212
213 @Override
Andrea Campanella945ded22016-01-07 13:17:43 -0800214 public boolean delete(DeviceId device, String request, InputStream payload, String mediaType) {
215 WebResource webResource = getWebResource(device, request);
216 ClientResponse response = null;
217 if (payload != null) {
218 try {
219 response = webResource.accept(mediaType)
220 .delete(ClientResponse.class, IOUtils.toString(payload, StandardCharsets.UTF_8));
221 } catch (IOException e) {
222 log.error("Cannot do PUT {} request on device {} because can't read payload",
223 request, device);
224 }
225 } else {
226 response = webResource.accept(mediaType)
227 .delete(ClientResponse.class);
228 }
229 return checkReply(response);
230 }
231
232 private WebResource getWebResource(DeviceId device, String request) {
Andrea Campanella2947e622016-01-27 09:23:46 -0800233 log.debug("Sending request to URL {} ", getUrlString(device, request));
234 WebResource webResource = client.resource(getUrlString(device, request));
235 if (deviceMap.containsKey(device) && deviceMap.get(device).password() != null) {
236 client.addFilter(new HTTPBasicAuthFilter(deviceMap.get(device).name(),
237 deviceMap.get(device).password()));
238 }
239 return webResource;
240 }
241
242 //FIXME security issue: this trusts every SSL certificate, even if is self-signed. Also deprecated methods.
243 private CloseableHttpClient getApacheSslBypassClient() throws NoSuchAlgorithmException,
244 KeyManagementException, KeyStoreException {
245 return HttpClients.custom().
246 setHostnameVerifier(new AllowAllHostnameVerifier()).
247 setSslcontext(new SSLContextBuilder()
248 .loadTrustMaterial(null, (arg0, arg1) -> true)
249 .build()).build();
250 }
251
252 private String getUrlString(DeviceId device, String request) {
253 if (deviceMap.get(device).url() != null) {
254 return deviceMap.get(device).protocol() + COLON + DOUBLESLASH
255 + deviceMap.get(device).url() + request;
256 } else {
257 return deviceMap.get(device).protocol() + COLON +
258 DOUBLESLASH +
259 deviceMap.get(device).ip().toString() +
260 COLON + deviceMap.get(device).port() + request;
261 }
Andrea Campanella945ded22016-01-07 13:17:43 -0800262 }
263
264 private boolean checkReply(ClientResponse response) {
265 if (response != null) {
Andrea Campanellace279ee2016-01-25 10:21:45 -0800266 return checkStatusCode(response.getStatus());
Andrea Campanella945ded22016-01-07 13:17:43 -0800267 }
268 log.error("Null reply from device");
269 return false;
270 }
Andrea Campanellace279ee2016-01-25 10:21:45 -0800271
272 private boolean checkStatusCode(int statusCode) {
273 if (statusCode == STATUS_OK ||
274 statusCode == STATUS_CREATED ||
275 statusCode == STATUS_ACCEPTED) {
276 return true;
277 } else {
Andrea Campanella2947e622016-01-27 09:23:46 -0800278 log.error("Failed request, HTTP error code : "
Andrea Campanellace279ee2016-01-25 10:21:45 -0800279 + statusCode);
280 return false;
281 }
282 }
Andrea Campanella945ded22016-01-07 13:17:43 -0800283}