Hesam Rahimi

ONOS-4919: Implement RESTCONF client

Adding code to support RESTCONF protocol as one of
the supported SBI protocols of ONOS. This RESTCONF SBI extends
the current REST SBI protocl and adds some new APIs/functinalities
so that a provider can subscribe/register to an external restconf
server to receive notification stream.

Change-Id: I21bf0d0f0394cf788e066d743b3ade04735fe07e
Showing 23 changed files with 1215 additions and 443 deletions
1 COMPILE_DEPS = [ 1 COMPILE_DEPS = [
2 - '//lib:CORE_DEPS',
3 '//incubator/api:onos-incubator-api', 2 '//incubator/api:onos-incubator-api',
4 '//utils/rest:onlab-rest', 3 '//utils/rest:onlab-rest',
4 + '//lib:CORE_DEPS',
5 + '//lib:jersey-client',
6 + '//lib:jersey-common',
7 + '//lib:httpclient-osgi',
8 + '//lib:httpcore-osgi',
9 + '//lib:javax.ws.rs-api',
10 + '//lib:hk2-api',
11 + '//lib:jersey-guava',
12 + '//lib:aopalliance-repackaged',
13 + '//lib:javax.inject',
5 ] 14 ]
6 15
7 osgi_jar_with_tests ( 16 osgi_jar_with_tests (
......
...@@ -28,6 +28,29 @@ ...@@ -28,6 +28,29 @@
28 <artifactId>onos-restsb-api</artifactId> 28 <artifactId>onos-restsb-api</artifactId>
29 <packaging>bundle</packaging> 29 <packaging>bundle</packaging>
30 30
31 + <dependencies>
32 + <dependency>
33 + <groupId>org.glassfish.jersey.core</groupId>
34 + <artifactId>jersey-client</artifactId>
35 + </dependency>
36 + <dependency>
37 + <groupId>org.apache.httpcomponents</groupId>
38 + <artifactId>httpclient-osgi</artifactId>
39 + <version>4.5.1</version>
40 + </dependency>
41 + <dependency>
42 + <groupId>commons-io</groupId>
43 + <artifactId>commons-io</artifactId>
44 + <version>2.4</version>
45 + </dependency>
46 + <dependency>
47 + <groupId>junit</groupId>
48 + <artifactId>junit</artifactId>
49 + <version>3.8.1</version>
50 + <scope>test</scope>
51 + </dependency>
52 + </dependencies>
53 +
31 <description>ONOS Rest southbound plugin API</description> 54 <description>ONOS Rest southbound plugin API</description>
32 55
33 56
......
1 +/*
2 + * Copyright 2016-present 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 +
17 +package org.onosproject.protocol.http;
18 +
19 +import org.onlab.packet.IpAddress;
20 +import org.onosproject.net.DeviceId;
21 +import org.onosproject.protocol.rest.RestSBDevice;
22 +
23 +import java.io.InputStream;
24 +import java.util.Map;
25 +
26 +/**
27 + * Abstraction of an HTTP controller. Serves as a one stop shop for obtaining
28 + * HTTP southbound devices and (un)register listeners.
29 + */
30 +public interface HttpSBController {
31 +
32 + /**
33 + * Returns all the devices known to this controller.
34 + *
35 + * @return map of devices
36 + */
37 + Map<DeviceId, RestSBDevice> getDevices();
38 +
39 + /**
40 + * Returns a device by node identifier.
41 + *
42 + * @param deviceInfo node identifier
43 + * @return RestSBDevice rest device
44 + */
45 + RestSBDevice getDevice(DeviceId deviceInfo);
46 +
47 + /**
48 + * Returns a device by Ip and Port.
49 + *
50 + * @param ip device ip
51 + * @param port device port
52 + * @return RestSBDevice rest device
53 + */
54 + RestSBDevice getDevice(IpAddress ip, int port);
55 +
56 + /**
57 + * Adds a device to the device map.
58 + *
59 + * @param device to be added
60 + */
61 + void addDevice(RestSBDevice device);
62 +
63 + /**
64 + * Removes the device from the devices map.
65 + *
66 + * @param deviceId to be removed
67 + */
68 + void removeDevice(DeviceId deviceId);
69 +
70 + /**
71 + * Does a HTTP POST request with specified parameters to the device.
72 + *
73 + * @param device device to make the request to
74 + * @param request url of the request
75 + * @param payload payload of the request as an InputStream
76 + * @param mediaType type of content in the payload i.e. application/json
77 + * @return true if operation returned 200, 201, 202, false otherwise
78 + */
79 + boolean post(DeviceId device, String request, InputStream payload, String mediaType);
80 +
81 + /**
82 + * Does a HTTP POST request with specified parameters to the device.
83 + *
84 + * @param <T> post return type
85 + * @param device device to make the request to
86 + * @param request url of the request
87 + * @param payload payload of the request as an InputStream
88 + * @param mediaType type of content in the payload i.e. application/json
89 + * @param responseClass the type of response object we are interested in,
90 + * such as String, InputStream.
91 + * @return Object of type requested via responseClass.
92 + */
93 + <T> T post(DeviceId device, String request, InputStream payload,
94 + String mediaType, Class<T> responseClass);
95 +
96 + /**
97 + * Does a HTTP PUT request with specified parameters to the device.
98 + *
99 + * @param device device to make the request to
100 + * @param request resource path of the request
101 + * @param payload payload of the request as an InputStream
102 + * @param mediaType type of content in the payload i.e. application/json
103 + * @return true if operation returned 200, 201, 202, false otherwise
104 + */
105 + boolean put(DeviceId device, String request, InputStream payload, String mediaType);
106 +
107 + /**
108 + * Does a HTTP GET request with specified parameters to the device.
109 + *
110 + * @param device device to make the request to
111 + * @param request url of the request
112 + * @param mediaType format to retrieve the content in
113 + * @return an inputstream of data from the reply.
114 + */
115 + InputStream get(DeviceId device, String request, String mediaType);
116 +
117 + /**
118 + * Does a HTTP PATCH request with specified parameters to the device.
119 + *
120 + * @param device device to make the request to
121 + * @param request url of the request
122 + * @param payload payload of the request as an InputStream
123 + * @param mediaType format to retrieve the content in
124 + * @return true if operation returned 200, 201, 202, false otherwise
125 + */
126 + boolean patch(DeviceId device, String request, InputStream payload, String mediaType);
127 +
128 + /**
129 + * Does a HTTP DELETE request with specified parameters to the device.
130 + *
131 + * @param device device to make the request to
132 + * @param request url of the request
133 + * @param payload payload of the request as an InputStream
134 + * @param mediaType type of content in the payload i.e. application/json
135 + * @return true if operation returned 200 false otherwise
136 + */
137 + boolean delete(DeviceId device, String request, InputStream payload, String mediaType);
138 +
139 +}
1 +/*
2 + * Copyright 2016-present 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 +
17 +package org.onosproject.protocol.http.ctl;
18 +
19 +import com.google.common.collect.ImmutableMap;
20 +import org.apache.commons.io.IOUtils;
21 +import org.apache.http.client.methods.HttpPatch;
22 +import org.apache.http.conn.ssl.AllowAllHostnameVerifier;
23 +import org.apache.http.entity.StringEntity;
24 +import org.apache.http.impl.client.CloseableHttpClient;
25 +import org.apache.http.impl.client.HttpClients;
26 +import org.apache.http.ssl.SSLContextBuilder;
27 +import org.glassfish.jersey.client.authentication.HttpAuthenticationFeature;
28 +import org.onlab.packet.IpAddress;
29 +import org.onosproject.net.DeviceId;
30 +import org.onosproject.protocol.http.HttpSBController;
31 +import org.onosproject.protocol.rest.RestSBDevice;
32 +import org.slf4j.Logger;
33 +import org.slf4j.LoggerFactory;
34 +
35 +import javax.net.ssl.SSLContext;
36 +import javax.net.ssl.TrustManager;
37 +import javax.net.ssl.X509TrustManager;
38 +import javax.ws.rs.client.Client;
39 +import javax.ws.rs.client.ClientBuilder;
40 +import javax.ws.rs.client.Entity;
41 +import javax.ws.rs.client.WebTarget;
42 +import javax.ws.rs.core.MediaType;
43 +import javax.ws.rs.core.Response;
44 +import java.io.ByteArrayInputStream;
45 +import java.io.IOException;
46 +import java.io.InputStream;
47 +import java.nio.charset.StandardCharsets;
48 +import java.security.KeyManagementException;
49 +import java.security.KeyStoreException;
50 +import java.security.NoSuchAlgorithmException;
51 +import java.security.cert.CertificateException;
52 +import java.security.cert.X509Certificate;
53 +import java.util.Base64;
54 +import java.util.Map;
55 +import java.util.concurrent.ConcurrentHashMap;
56 +
57 +/**
58 + * The implementation of HttpSBController.
59 + */
60 +public class HttpSBControllerImpl implements HttpSBController {
61 +
62 + private static final Logger log =
63 + LoggerFactory.getLogger(HttpSBControllerImpl.class);
64 + 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();
71 + private static final String HTTPS = "https";
72 + private static final String AUTHORIZATION_PROPERTY = "authorization";
73 + private static final String BASIC_AUTH_PREFIX = "Basic ";
74 +
75 + private final Map<DeviceId, RestSBDevice> deviceMap = new ConcurrentHashMap<>();
76 + private final Map<DeviceId, Client> clientMap = new ConcurrentHashMap<>();
77 +
78 + public Map<DeviceId, RestSBDevice> getDeviceMap() {
79 + return deviceMap;
80 + }
81 +
82 + public Map<DeviceId, Client> getClientMap() {
83 + return clientMap;
84 + }
85 +
86 + @Override
87 + public Map<DeviceId, RestSBDevice> getDevices() {
88 + return ImmutableMap.copyOf(deviceMap);
89 + }
90 +
91 + @Override
92 + public RestSBDevice getDevice(DeviceId deviceInfo) {
93 + return deviceMap.get(deviceInfo);
94 + }
95 +
96 + @Override
97 + public RestSBDevice getDevice(IpAddress ip, int port) {
98 + return deviceMap.values().stream().filter(v -> v.ip().equals(ip)
99 + && v.port() == port).findFirst().get();
100 + }
101 +
102 + @Override
103 + public void addDevice(RestSBDevice device) {
104 + if (!deviceMap.containsKey(device.deviceId())) {
105 + Client client = ignoreSslClient();
106 + if (device.username() != null) {
107 + String username = device.username();
108 + String password = device.password() == null ? "" : device.password();
109 + authenticate(client, username, password);
110 + }
111 + clientMap.put(device.deviceId(), client);
112 + deviceMap.put(device.deviceId(), device);
113 + } else {
114 + log.warn("Trying to add a device that is already existing {}", device.deviceId());
115 + }
116 +
117 + }
118 +
119 + @Override
120 + public void removeDevice(DeviceId deviceId) {
121 + clientMap.remove(deviceId);
122 + deviceMap.remove(deviceId);
123 + }
124 +
125 + @Override
126 + public boolean post(DeviceId device, String request, InputStream payload, String mediaType) {
127 + Response response = getResponse(device, request, payload, mediaType);
128 + return checkReply(response);
129 + }
130 +
131 + @Override
132 + public <T> T post(DeviceId device, String request, InputStream payload,
133 + String mediaType, Class<T> responseClass) {
134 + Response response = getResponse(device, request, payload, mediaType);
135 + if (response.hasEntity()) {
136 + return response.readEntity(responseClass);
137 + }
138 + log.error("Response from device {} for request {} contains no entity", device, request);
139 + return null;
140 + }
141 +
142 + private Response getResponse(DeviceId device, String request, InputStream payload, String mediaType) {
143 + String type = typeOfMediaType(mediaType);
144 +
145 + WebTarget wt = getWebTarget(device, request);
146 +
147 + Response response = null;
148 + if (payload != null) {
149 + try {
150 + response = wt.request(type)
151 + .post(Entity.entity(IOUtils.toString(payload, StandardCharsets.UTF_8), type));
152 + } catch (IOException e) {
153 + log.error("Cannot do POST {} request on device {} because can't read payload",
154 + request, device);
155 + }
156 + } else {
157 + response = wt.request(type).post(Entity.entity(null, type));
158 + }
159 + return response;
160 + }
161 +
162 + @Override
163 + public boolean put(DeviceId device, String request, InputStream payload, String mediaType) {
164 + String type = typeOfMediaType(mediaType);
165 +
166 + WebTarget wt = getWebTarget(device, request);
167 +
168 + Response response = null;
169 + if (payload != null) {
170 + try {
171 + response = wt.request(type)
172 + .put(Entity.entity(IOUtils.toString(payload, StandardCharsets.UTF_8), type));
173 + } catch (IOException e) {
174 + log.error("Cannot do PUT {} request on device {} because can't read payload",
175 + request, device);
176 + }
177 + } else {
178 + response = wt.request(type).put(Entity.entity(null, type));
179 + }
180 + return checkReply(response);
181 + }
182 +
183 + @Override
184 + public InputStream get(DeviceId device, String request, String mediaType) {
185 + String type = typeOfMediaType(mediaType);
186 +
187 + WebTarget wt = getWebTarget(device, request);
188 +
189 + Response s = wt.request(type).get();
190 +
191 + if (checkReply(s)) {
192 + return new ByteArrayInputStream(s.readEntity((String.class))
193 + .getBytes(StandardCharsets.UTF_8));
194 + }
195 + return null;
196 + }
197 +
198 + @Override
199 + public boolean patch(DeviceId device, String request, InputStream payload, String mediaType) {
200 + String type = typeOfMediaType(mediaType);
201 +
202 + try {
203 + log.debug("Url request {} ", getUrlString(device, request));
204 + HttpPatch httprequest = new HttpPatch(getUrlString(device, request));
205 + if (deviceMap.get(device).username() != null) {
206 + String pwd = deviceMap.get(device).password() == null ? "" : COLON + deviceMap.get(device).password();
207 + String userPassword = deviceMap.get(device).username() + pwd;
208 + String base64string = Base64.getEncoder().encodeToString(userPassword.getBytes(StandardCharsets.UTF_8));
209 + httprequest.addHeader(AUTHORIZATION_PROPERTY, BASIC_AUTH_PREFIX + base64string);
210 + }
211 + if (payload != null) {
212 + StringEntity input = new StringEntity(IOUtils.toString(payload, StandardCharsets.UTF_8));
213 + input.setContentType(type);
214 + httprequest.setEntity(input);
215 + }
216 + CloseableHttpClient httpClient;
217 + if (deviceMap.containsKey(device) && deviceMap.get(device).protocol().equals(HTTPS)) {
218 + httpClient = getApacheSslBypassClient();
219 + } else {
220 + httpClient = HttpClients.createDefault();
221 + }
222 + int responseStatusCode = httpClient
223 + .execute(httprequest)
224 + .getStatusLine()
225 + .getStatusCode();
226 + return checkStatusCode(responseStatusCode);
227 + } catch (IOException | NoSuchAlgorithmException | KeyManagementException | KeyStoreException e) {
228 + log.error("Cannot do PATCH {} request on device {}",
229 + request, device, e);
230 + }
231 + return false;
232 + }
233 +
234 + @Override
235 + public boolean delete(DeviceId device, String request, InputStream payload, String mediaType) {
236 + String type = typeOfMediaType(mediaType);
237 +
238 + WebTarget wt = getWebTarget(device, request);
239 +
240 + // FIXME: do we need to delete an entry by enclosing data in DELETE request?
241 + // wouldn't it be nice to use PUT to implement the similar concept?
242 + Response response = wt.request(type).delete();
243 +
244 + return checkReply(response);
245 + }
246 +
247 + private String typeOfMediaType(String mediaType) {
248 + String type;
249 + switch (mediaType) {
250 + case XML:
251 + type = MediaType.APPLICATION_XML;
252 + break;
253 + case JSON:
254 + type = MediaType.APPLICATION_JSON;
255 + break;
256 + default:
257 + throw new IllegalArgumentException("Unsupported media type " + mediaType);
258 +
259 + }
260 + return type;
261 + }
262 +
263 + private void authenticate(Client client, String username, String password) {
264 + client.register(HttpAuthenticationFeature.basic(username, password));
265 + }
266 +
267 + protected WebTarget getWebTarget(DeviceId device, String request) {
268 + log.debug("Sending request to URL {} ", getUrlString(device, request));
269 + return clientMap.get(device).target(getUrlString(device, request));
270 + }
271 +
272 + //FIXME security issue: this trusts every SSL certificate, even if is self-signed. Also deprecated methods.
273 + private CloseableHttpClient getApacheSslBypassClient() throws NoSuchAlgorithmException,
274 + KeyManagementException, KeyStoreException {
275 + return HttpClients.custom().
276 + setHostnameVerifier(new AllowAllHostnameVerifier()).
277 + setSslcontext(new SSLContextBuilder()
278 + .loadTrustMaterial(null, (arg0, arg1) -> true)
279 + .build()).build();
280 + }
281 +
282 + private String getUrlString(DeviceId device, String request) {
283 + if (deviceMap.get(device).url() != null) {
284 + return deviceMap.get(device).protocol() + COLON + DOUBLESLASH
285 + + deviceMap.get(device).url() + request;
286 + } else {
287 + return deviceMap.get(device).protocol() + COLON +
288 + DOUBLESLASH +
289 + deviceMap.get(device).ip().toString() +
290 + COLON + deviceMap.get(device).port() + request;
291 + }
292 + }
293 +
294 + private boolean checkReply(Response response) {
295 + if (response != null) {
296 + return checkStatusCode(response.getStatus());
297 + }
298 + log.error("Null reply from device");
299 + return false;
300 + }
301 +
302 + private boolean checkStatusCode(int statusCode) {
303 + if (statusCode == STATUS_OK ||
304 + statusCode == STATUS_CREATED ||
305 + statusCode == STATUS_ACCEPTED) {
306 + return true;
307 + } else {
308 + log.error("Failed request, HTTP error code : "
309 + + statusCode);
310 + return false;
311 + }
312 + }
313 +
314 + private Client ignoreSslClient() {
315 + SSLContext sslcontext = null;
316 +
317 + try {
318 + sslcontext = SSLContext.getInstance("TLS");
319 + sslcontext.init(null, new TrustManager[]{new X509TrustManager() {
320 + public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
321 + }
322 +
323 + public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
324 + }
325 +
326 + public X509Certificate[] getAcceptedIssuers() {
327 + return new X509Certificate[0];
328 + }
329 + } }, new java.security.SecureRandom());
330 + } catch (NoSuchAlgorithmException | KeyManagementException e) {
331 + e.printStackTrace();
332 + }
333 +
334 + return ClientBuilder.newBuilder().sslContext(sslcontext).hostnameVerifier((s1, s2) -> true).build();
335 + }
336 +}
1 +/**
2 + * Copyright 2016-present 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 +/**
17 + * @author onos
18 + *
19 + */
20 +package org.onosproject.protocol.http.ctl;
1 +/**
2 + * Copyright 2016-present 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 +/**
17 + * @author onos
18 + *
19 + */
20 +package org.onosproject.protocol.http;
...@@ -16,15 +16,16 @@ ...@@ -16,15 +16,16 @@
16 16
17 package org.onosproject.protocol.rest; 17 package org.onosproject.protocol.rest;
18 18
19 -import com.google.common.base.MoreObjects; 19 +import java.net.URI;
20 -import com.google.common.base.Preconditions; 20 +import java.net.URISyntaxException;
21 +import java.util.Objects;
22 +
21 import org.apache.commons.lang3.StringUtils; 23 import org.apache.commons.lang3.StringUtils;
22 import org.onlab.packet.IpAddress; 24 import org.onlab.packet.IpAddress;
23 import org.onosproject.net.DeviceId; 25 import org.onosproject.net.DeviceId;
24 26
25 -import java.net.URI; 27 +import com.google.common.base.MoreObjects;
26 -import java.net.URISyntaxException; 28 +import com.google.common.base.Preconditions;
27 -import java.util.Objects;
28 29
29 /** 30 /**
30 * Default implementation for Rest devices. 31 * Default implementation for Rest devices.
......
...@@ -16,123 +16,11 @@ ...@@ -16,123 +16,11 @@
16 16
17 package org.onosproject.protocol.rest; 17 package org.onosproject.protocol.rest;
18 18
19 -import org.onlab.packet.IpAddress; 19 +import org.onosproject.protocol.http.HttpSBController;
20 -import org.onosproject.net.DeviceId;
21 -
22 -import java.io.InputStream;
23 -import java.util.Map;
24 20
25 /** 21 /**
26 * Abstraction of an REST controller. Serves as a one stop shop for obtaining 22 * Abstraction of an REST controller. Serves as a one stop shop for obtaining
27 * Rest southbound devices and (un)register listeners. 23 * Rest southbound devices and (un)register listeners.
28 */ 24 */
29 -public interface RestSBController { 25 +public interface RestSBController extends HttpSBController {
30 -
31 - /**
32 - * Returns all the devices known to this controller.
33 - *
34 - * @return map of devices
35 - */
36 - Map<DeviceId, RestSBDevice> getDevices();
37 -
38 - /**
39 - * Returns a device by node identifier.
40 - *
41 - * @param deviceInfo node identifier
42 - * @return RestSBDevice rest device
43 - */
44 - RestSBDevice getDevice(DeviceId deviceInfo);
45 -
46 - /**
47 - * Returns a device by Ip and Port.
48 - *
49 - * @param ip device ip
50 - * @param port device port
51 - * @return RestSBDevice rest device
52 - */
53 - RestSBDevice getDevice(IpAddress ip, int port);
54 -
55 - /**
56 - * Adds a device to the device map.
57 - *
58 - * @param device to be added
59 - */
60 - void addDevice(RestSBDevice device);
61 -
62 - /**
63 - * Removes the device from the devices map.
64 - *
65 - * @param deviceId to be removed
66 - */
67 - void removeDevice(DeviceId deviceId);
68 -
69 - /**
70 - * Does a REST POST request with specified parameters to the device.
71 - *
72 - * @param device device to make the request to
73 - * @param request url of the request
74 - * @param payload payload of the request as an InputStream
75 - * @param mediaType type of content in the payload i.e. application/json
76 - * @return true if operation returned 200, 201, 202, false otherwise
77 - */
78 - boolean post(DeviceId device, String request, InputStream payload, String mediaType);
79 -
80 - /**
81 - * Does a REST POST request with specified parameters to the device.
82 - *
83 - * @param <T> post return type
84 - * @param device device to make the request to
85 - * @param request url of the request
86 - * @param payload payload of the request as an InputStream
87 - * @param mediaType type of content in the payload i.e. application/json
88 - * @param responseClass the type of response object we are interested in,
89 - * such as String, InputStream.
90 - * @return Object of type requested via responseClass.
91 - */
92 - <T> T post(DeviceId device, String request, InputStream payload,
93 - String mediaType, Class<T> responseClass);
94 -
95 - /**
96 - * Does a REST PUT request with specified parameters to the device.
97 - *
98 - * @param device device to make the request to
99 - * @param request resource path of the request
100 - * @param payload payload of the request as an InputStream
101 - * @param mediaType type of content in the payload i.e. application/json
102 - * @return true if operation returned 200, 201, 202, false otherwise
103 - */
104 - boolean put(DeviceId device, String request, InputStream payload, String mediaType);
105 -
106 - /**
107 - * Does a REST GET request with specified parameters to the device.
108 - *
109 - * @param device device to make the request to
110 - * @param request url of the request
111 - * @param mediaType format to retrieve the content in
112 - * @return an inputstream of data from the reply.
113 - */
114 - InputStream get(DeviceId device, String request, String mediaType);
115 -
116 - /**
117 - * Does a REST PATCH request with specified parameters to the device.
118 - *
119 - * @param device device to make the request to
120 - * @param request url of the request
121 - * @param payload payload of the request as an InputStream
122 - * @param mediaType format to retrieve the content in
123 - * @return true if operation returned 200, 201, 202, false otherwise
124 - */
125 - boolean patch(DeviceId device, String request, InputStream payload, String mediaType);
126 -
127 - /**
128 - * Does a REST DELETE request with specified parameters to the device.
129 - *
130 - * @param device device to make the request to
131 - * @param request url of the request
132 - * @param payload payload of the request as an InputStream
133 - * @param mediaType type of content in the payload i.e. application/json
134 - * @return true if operation returned 200 false otherwise
135 - */
136 - boolean delete(DeviceId device, String request, InputStream payload, String mediaType);
137 -
138 } 26 }
......
...@@ -34,32 +34,10 @@ ...@@ -34,32 +34,10 @@
34 <artifactId>org.apache.felix.scr.annotations</artifactId> 34 <artifactId>org.apache.felix.scr.annotations</artifactId>
35 </dependency> 35 </dependency>
36 <dependency> 36 <dependency>
37 - <groupId>org.osgi</groupId>
38 - <artifactId>org.osgi.compendium</artifactId>
39 - </dependency>
40 - <dependency>
41 <groupId>org.onosproject</groupId> 37 <groupId>org.onosproject</groupId>
42 <artifactId>onos-restsb-api</artifactId> 38 <artifactId>onos-restsb-api</artifactId>
43 <version>${project.version}</version> 39 <version>${project.version}</version>
44 - </dependency> 40 + <type>bundle</type>
45 - <dependency>
46 - <groupId>org.glassfish.jersey.core</groupId>
47 - <artifactId>jersey-client</artifactId>
48 - </dependency>
49 - <dependency>
50 - <groupId>org.apache.httpcomponents</groupId>
51 - <artifactId>httpclient-osgi</artifactId>
52 - <version>4.5.1</version>
53 - </dependency>
54 - <dependency>
55 - <groupId>org.apache.httpcomponents</groupId>
56 - <artifactId>httpcore-osgi</artifactId>
57 - <version>4.4.4</version>
58 - </dependency>
59 - <dependency>
60 - <groupId>commons-io</groupId>
61 - <artifactId>commons-io</artifactId>
62 - <version>2.4</version>
63 </dependency> 41 </dependency>
64 </dependencies> 42 </dependencies>
65 43
......
...@@ -16,70 +16,24 @@ ...@@ -16,70 +16,24 @@
16 16
17 package org.onosproject.protocol.rest.ctl; 17 package org.onosproject.protocol.rest.ctl;
18 18
19 -import com.google.common.collect.ImmutableMap;
20 -import org.apache.commons.io.IOUtils;
21 import org.apache.felix.scr.annotations.Activate; 19 import org.apache.felix.scr.annotations.Activate;
22 import org.apache.felix.scr.annotations.Component; 20 import org.apache.felix.scr.annotations.Component;
23 import org.apache.felix.scr.annotations.Deactivate; 21 import org.apache.felix.scr.annotations.Deactivate;
24 import org.apache.felix.scr.annotations.Service; 22 import org.apache.felix.scr.annotations.Service;
25 -import org.apache.http.client.methods.HttpPatch; 23 +import org.onosproject.protocol.http.ctl.HttpSBControllerImpl;
26 -import org.apache.http.conn.ssl.AllowAllHostnameVerifier;
27 -import org.apache.http.entity.StringEntity;
28 -import org.apache.http.impl.client.CloseableHttpClient;
29 -import org.apache.http.impl.client.HttpClients;
30 -import org.apache.http.ssl.SSLContextBuilder;
31 -import org.glassfish.jersey.client.authentication.HttpAuthenticationFeature;
32 -import org.onlab.packet.IpAddress;
33 -import org.onosproject.net.DeviceId;
34 import org.onosproject.protocol.rest.RestSBController; 24 import org.onosproject.protocol.rest.RestSBController;
35 -import org.onosproject.protocol.rest.RestSBDevice;
36 import org.slf4j.Logger; 25 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory; 26 import org.slf4j.LoggerFactory;
38 27
39 -import javax.net.ssl.SSLContext;
40 -import javax.net.ssl.TrustManager;
41 -import javax.net.ssl.X509TrustManager;
42 -import javax.ws.rs.client.Client;
43 -import javax.ws.rs.client.ClientBuilder;
44 -import javax.ws.rs.client.Entity;
45 -import javax.ws.rs.client.WebTarget;
46 -import javax.ws.rs.core.MediaType;
47 -import javax.ws.rs.core.Response;
48 -import java.io.ByteArrayInputStream;
49 -import java.io.IOException;
50 -import java.io.InputStream;
51 -import java.nio.charset.StandardCharsets;
52 -import java.security.KeyManagementException;
53 -import java.security.KeyStoreException;
54 -import java.security.NoSuchAlgorithmException;
55 -import java.security.cert.CertificateException;
56 -import java.security.cert.X509Certificate;
57 -import java.util.Base64;
58 -import java.util.Map;
59 -import java.util.concurrent.ConcurrentHashMap;
60 -
61 /** 28 /**
62 * The implementation of RestSBController. 29 * The implementation of RestSBController.
63 */ 30 */
64 @Component(immediate = true) 31 @Component(immediate = true)
65 @Service 32 @Service
66 -public class RestSBControllerImpl implements RestSBController { 33 +public class RestSBControllerImpl extends HttpSBControllerImpl implements RestSBController {
67 34
68 private static final Logger log = 35 private static final Logger log =
69 LoggerFactory.getLogger(RestSBControllerImpl.class); 36 LoggerFactory.getLogger(RestSBControllerImpl.class);
70 - private static final String XML = "xml";
71 - private static final String JSON = "json";
72 - private static final String DOUBLESLASH = "//";
73 - private static final String COLON = ":";
74 - private static final int STATUS_OK = Response.Status.OK.getStatusCode();
75 - private static final int STATUS_CREATED = Response.Status.CREATED.getStatusCode();
76 - private static final int STATUS_ACCEPTED = Response.Status.ACCEPTED.getStatusCode();
77 - private static final String HTTPS = "https";
78 - private static final String AUTHORIZATION_PROPERTY = "authorization";
79 - private static final String BASIC_AUTH_PREFIX = "Basic ";
80 -
81 - private final Map<DeviceId, RestSBDevice> deviceMap = new ConcurrentHashMap<>();
82 - private final Map<DeviceId, Client> clientMap = new ConcurrentHashMap<>();
83 37
84 @Activate 38 @Activate
85 public void activate() { 39 public void activate() {
...@@ -88,259 +42,9 @@ public class RestSBControllerImpl implements RestSBController { ...@@ -88,259 +42,9 @@ public class RestSBControllerImpl implements RestSBController {
88 42
89 @Deactivate 43 @Deactivate
90 public void deactivate() { 44 public void deactivate() {
91 - clientMap.clear(); 45 + this.getClientMap().clear();
92 - deviceMap.clear(); 46 + this.getDeviceMap().clear();
93 log.info("Stopped"); 47 log.info("Stopped");
94 } 48 }
95 49
96 - @Override
97 - public Map<DeviceId, RestSBDevice> getDevices() {
98 - return ImmutableMap.copyOf(deviceMap);
99 - }
100 -
101 - @Override
102 - public RestSBDevice getDevice(DeviceId deviceInfo) {
103 - return deviceMap.get(deviceInfo);
104 - }
105 -
106 - @Override
107 - public RestSBDevice getDevice(IpAddress ip, int port) {
108 - return deviceMap.values().stream().filter(v -> v.ip().equals(ip)
109 - && v.port() == port).findFirst().get();
110 - }
111 -
112 - @Override
113 - public void addDevice(RestSBDevice device) {
114 - if (!deviceMap.containsKey(device.deviceId())) {
115 - Client client = ignoreSslClient();
116 - if (device.username() != null) {
117 - String username = device.username();
118 - String password = device.password() == null ? "" : device.password();
119 - authenticate(client, username, password);
120 - }
121 - clientMap.put(device.deviceId(), client);
122 - deviceMap.put(device.deviceId(), device);
123 - } else {
124 - log.warn("Trying to add a device that is already existing {}", device.deviceId());
125 - }
126 -
127 - }
128 -
129 - @Override
130 - public void removeDevice(DeviceId deviceId) {
131 - clientMap.remove(deviceId);
132 - deviceMap.remove(deviceId);
133 - }
134 -
135 - @Override
136 - public boolean post(DeviceId device, String request, InputStream payload, String mediaType) {
137 - Response response = getResponse(device, request, payload, mediaType);
138 - return checkReply(response);
139 - }
140 -
141 - @Override
142 - public <T> T post(DeviceId device, String request, InputStream payload,
143 - String mediaType, Class<T> responseClass) {
144 - Response response = getResponse(device, request, payload, mediaType);
145 - if (response.hasEntity()) {
146 - return response.readEntity(responseClass);
147 - }
148 - log.error("Response from device {} for request {} contains no entity", device, request);
149 - return null;
150 - }
151 -
152 - private Response getResponse(DeviceId device, String request, InputStream payload, String mediaType) {
153 - String type = typeOfMediaType(mediaType);
154 -
155 - WebTarget wt = getWebTarget(device, request);
156 -
157 - Response response = null;
158 - if (payload != null) {
159 - try {
160 - response = wt.request(type)
161 - .post(Entity.entity(IOUtils.toString(payload, StandardCharsets.UTF_8), type));
162 - } catch (IOException e) {
163 - log.error("Cannot do POST {} request on device {} because can't read payload",
164 - request, device);
165 - }
166 - } else {
167 - response = wt.request(type).post(Entity.entity(null, type));
168 - }
169 - return response;
170 - }
171 -
172 - @Override
173 - public boolean put(DeviceId device, String request, InputStream payload, String mediaType) {
174 - String type = typeOfMediaType(mediaType);
175 -
176 - WebTarget wt = getWebTarget(device, request);
177 -
178 - Response response = null;
179 - if (payload != null) {
180 - try {
181 - response = wt.request(type)
182 - .put(Entity.entity(IOUtils.toString(payload, StandardCharsets.UTF_8), type));
183 - } catch (IOException e) {
184 - log.error("Cannot do PUT {} request on device {} because can't read payload",
185 - request, device);
186 - }
187 - } else {
188 - response = wt.request(type).put(Entity.entity(null, type));
189 - }
190 - return checkReply(response);
191 - }
192 -
193 - @Override
194 - public InputStream get(DeviceId device, String request, String mediaType) {
195 - String type = typeOfMediaType(mediaType);
196 -
197 - WebTarget wt = getWebTarget(device, request);
198 -
199 - Response s = wt.request(type).get();
200 -
201 - if (checkReply(s)) {
202 - return new ByteArrayInputStream(s.readEntity((String.class))
203 - .getBytes(StandardCharsets.UTF_8));
204 - }
205 - return null;
206 - }
207 -
208 - @Override
209 - public boolean patch(DeviceId device, String request, InputStream payload, String mediaType) {
210 - String type = typeOfMediaType(mediaType);
211 -
212 - try {
213 - log.debug("Url request {} ", getUrlString(device, request));
214 - HttpPatch httprequest = new HttpPatch(getUrlString(device, request));
215 - if (deviceMap.get(device).username() != null) {
216 - String pwd = deviceMap.get(device).password() == null ? "" : COLON + deviceMap.get(device).password();
217 - String userPassword = deviceMap.get(device).username() + pwd;
218 - String base64string = Base64.getEncoder().encodeToString(userPassword.getBytes(StandardCharsets.UTF_8));
219 - httprequest.addHeader(AUTHORIZATION_PROPERTY, BASIC_AUTH_PREFIX + base64string);
220 - }
221 - if (payload != null) {
222 - StringEntity input = new StringEntity(IOUtils.toString(payload, StandardCharsets.UTF_8));
223 - input.setContentType(type);
224 - httprequest.setEntity(input);
225 - }
226 - CloseableHttpClient httpClient;
227 - if (deviceMap.containsKey(device) && deviceMap.get(device).protocol().equals(HTTPS)) {
228 - httpClient = getApacheSslBypassClient();
229 - } else {
230 - httpClient = HttpClients.createDefault();
231 - }
232 - int responseStatusCode = httpClient
233 - .execute(httprequest)
234 - .getStatusLine()
235 - .getStatusCode();
236 - return checkStatusCode(responseStatusCode);
237 - } catch (IOException | NoSuchAlgorithmException | KeyManagementException | KeyStoreException e) {
238 - log.error("Cannot do PATCH {} request on device {}",
239 - request, device, e);
240 - }
241 - return false;
242 - }
243 -
244 - @Override
245 - public boolean delete(DeviceId device, String request, InputStream payload, String mediaType) {
246 - String type = typeOfMediaType(mediaType);
247 -
248 - WebTarget wt = getWebTarget(device, request);
249 -
250 - // FIXME: do we need to delete an entry by enclosing data in DELETE request?
251 - // wouldn't it be nice to use PUT to implement the similar concept?
252 - Response response = wt.request(type).delete();
253 -
254 - return checkReply(response);
255 - }
256 -
257 - private String typeOfMediaType(String mediaType) {
258 - String type;
259 - switch (mediaType) {
260 - case XML:
261 - type = MediaType.APPLICATION_XML;
262 - break;
263 - case JSON:
264 - type = MediaType.APPLICATION_JSON;
265 - break;
266 - default:
267 - throw new IllegalArgumentException("Unsupported media type " + mediaType);
268 -
269 - }
270 - return type;
271 - }
272 -
273 - private void authenticate(Client client, String username, String password) {
274 - client.register(HttpAuthenticationFeature.basic(username, password));
275 - }
276 -
277 - protected WebTarget getWebTarget(DeviceId device, String request) {
278 - log.debug("Sending request to URL {} ", getUrlString(device, request));
279 - return clientMap.get(device).target(getUrlString(device, request));
280 - }
281 -
282 - //FIXME security issue: this trusts every SSL certificate, even if is self-signed. Also deprecated methods.
283 - private CloseableHttpClient getApacheSslBypassClient() throws NoSuchAlgorithmException,
284 - KeyManagementException, KeyStoreException {
285 - return HttpClients.custom().
286 - setHostnameVerifier(new AllowAllHostnameVerifier()).
287 - setSslcontext(new SSLContextBuilder()
288 - .loadTrustMaterial(null, (arg0, arg1) -> true)
289 - .build()).build();
290 - }
291 -
292 - private String getUrlString(DeviceId device, String request) {
293 - if (deviceMap.get(device).url() != null) {
294 - return deviceMap.get(device).protocol() + COLON + DOUBLESLASH
295 - + deviceMap.get(device).url() + request;
296 - } else {
297 - return deviceMap.get(device).protocol() + COLON +
298 - DOUBLESLASH +
299 - deviceMap.get(device).ip().toString() +
300 - COLON + deviceMap.get(device).port() + request;
301 - }
302 - }
303 -
304 - private boolean checkReply(Response response) {
305 - if (response != null) {
306 - return checkStatusCode(response.getStatus());
307 - }
308 - log.error("Null reply from device");
309 - return false;
310 - }
311 -
312 - private boolean checkStatusCode(int statusCode) {
313 - if (statusCode == STATUS_OK ||
314 - statusCode == STATUS_CREATED ||
315 - statusCode == STATUS_ACCEPTED) {
316 - return true;
317 - } else {
318 - log.error("Failed request, HTTP error code : "
319 - + statusCode);
320 - return false;
321 - }
322 - }
323 -
324 - private Client ignoreSslClient() {
325 - SSLContext sslcontext = null;
326 -
327 - try {
328 - sslcontext = SSLContext.getInstance("TLS");
329 - sslcontext.init(null, new TrustManager[]{new X509TrustManager() {
330 - public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
331 - }
332 -
333 - public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
334 - }
335 -
336 - public X509Certificate[] getAcceptedIssuers() {
337 - return new X509Certificate[0];
338 - }
339 - } }, new java.security.SecureRandom());
340 - } catch (NoSuchAlgorithmException | KeyManagementException e) {
341 - e.printStackTrace();
342 - }
343 -
344 - return ClientBuilder.newBuilder().sslContext(sslcontext).hostnameVerifier((s1, s2) -> true).build();
345 - }
346 } 50 }
......
1 +COMPILE_DEPS = [
2 + '//lib:CORE_DEPS',
3 + '//incubator/api:onos-incubator-api',
4 + '//utils/rest:onlab-rest',
5 + '//protocols/rest/api:onos-protocols-rest-api',
6 +]
7 +
8 +osgi_jar_with_tests (
9 + deps = COMPILE_DEPS,
10 +)
1 +<?xml version="1.0" encoding="UTF-8"?>
2 +<!--
3 + ~ Copyright 2016-present Open Networking Laboratory
4 + ~
5 + ~ Licensed under the Apache License, Version 2.0 (the "License");
6 + ~ you may not use this file except in compliance with the License.
7 + ~ You may obtain a copy of the License at
8 + ~
9 + ~ http://www.apache.org/licenses/LICENSE-2.0
10 + ~
11 + ~ Unless required by applicable law or agreed to in writing, software
12 + ~ distributed under the License is distributed on an "AS IS" BASIS,
13 + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 + ~ See the License for the specific language governing permissions and
15 + ~ limitations under the License.
16 + -->
17 +
18 +<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
19 + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
20 + <modelVersion>4.0.0</modelVersion>
21 + <parent>
22 + <groupId>org.onosproject</groupId>
23 + <artifactId>onos-restconf-client</artifactId>
24 + <version>1.8.0-SNAPSHOT</version>
25 + <relativePath>../pom.xml</relativePath>
26 + </parent>
27 + <artifactId>onos-restconf-client-api</artifactId>
28 + <packaging>bundle</packaging>
29 + <description>ONOS RESTCONF southbound plugin API</description>
30 + <dependencies>
31 + <dependency>
32 + <groupId>org.onosproject</groupId>
33 + <artifactId>onos-restsb-api</artifactId>
34 + <version>${project.version}</version>
35 + <type>bundle</type>
36 + </dependency>
37 + </dependencies>
38 +</project>
1 +/*
2 + * Copyright 2016-present 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 +package org.onosproject.protocol.restconf;
17 +
18 +import org.onosproject.net.DeviceId;
19 +
20 +/**
21 + * Notifies providers about incoming RESTCONF notification events.
22 + */
23 +public interface RestConfNotificationEventListener {
24 +
25 + /**
26 + * Handles the notification event.
27 + *
28 + * @param <T>
29 + *
30 + * @param deviceId of the restconf device
31 + * @param eventJsonString the json string representation of the event
32 + */
33 + <T> void handleNotificationEvent(DeviceId deviceId, T eventJsonString);
34 +
35 +}
1 +/*
2 + * Copyright 2016-present 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 +package org.onosproject.protocol.restconf;
17 +
18 +import org.onosproject.net.DeviceId;
19 +import org.onosproject.protocol.http.HttpSBController;
20 +
21 +/**
22 + * Abstraction of a RESTCONF controller. Serves as a one stop shop for obtaining
23 + * RESTCONF southbound devices and (un)register listeners.
24 + */
25 +public interface RestConfSBController extends HttpSBController {
26 +
27 + /**
28 + * This method is to be called by whoever is interested to receive
29 + * Notifications from a specific device. It does a REST GET request
30 + * with specified parameters to the device, and calls the provided
31 + * callBackListener upon receiving notifications to notify the requester
32 + * about notifications.
33 + *
34 + *
35 + * @param device device to make the request to
36 + * @param request url of the request
37 + * @param mediaType format to retrieve the content in
38 + * @param callBackListener method to call when notifications arrives
39 + */
40 + void enableNotifications(DeviceId device, String request, String mediaType,
41 + RestConfNotificationEventListener callBackListener);
42 +
43 + /**
44 + * Register a listener for notification events that occur to restconf
45 + * devices.
46 + *
47 + * @param deviceId the deviceId
48 + * @param listener the listener to notify
49 + */
50 + void addNotificationListener(DeviceId deviceId,
51 + RestConfNotificationEventListener listener);
52 +
53 + /**
54 + * Unregister the listener for the device.
55 + *
56 + * @param deviceId the deviceId
57 + */
58 + void removeNotificationListener(DeviceId deviceId);
59 +}
1 +/*
2 + * Copyright 2016-present 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 +
17 +/**
18 + * RESTCONF southbound protocols libraries.
19 + */
20 +package org.onosproject.protocol.restconf;
1 +COMPILE_DEPS = [
2 + '//lib:CORE_DEPS',
3 + '//lib:jersey-client',
4 + '//lib:jersey-common',
5 + '//lib:httpclient-osgi',
6 + '//lib:httpcore-osgi',
7 + '//lib:javax.ws.rs-api',
8 + '//lib:hk2-api',
9 + '//lib:jersey-guava',
10 + '//lib:aopalliance-repackaged',
11 + '//lib:javax.inject',
12 + '//protocols/restconf/client/api:onos-protocols-restconf-client-api',
13 + '//protocols/rest/api:onos-protocols-rest-api',
14 +]
15 +
16 +osgi_jar_with_tests (
17 + deps = COMPILE_DEPS,
18 +)
19 +
1 +<?xml version="1.0" encoding="UTF-8"?>
2 +<!--
3 + ~ Copyright 2016-present Open Networking Laboratory
4 + ~
5 + ~ Licensed under the Apache License, Version 2.0 (the "License");
6 + ~ you may not use this file except in compliance with the License.
7 + ~ You may obtain a copy of the License at
8 + ~
9 + ~ http://www.apache.org/licenses/LICENSE-2.0
10 + ~
11 + ~ Unless required by applicable law or agreed to in writing, software
12 + ~ distributed under the License is distributed on an "AS IS" BASIS,
13 + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 + ~ See the License for the specific language governing permissions and
15 + ~ limitations under the License.
16 + -->
17 +
18 +<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
19 + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
20 + <modelVersion>4.0.0</modelVersion>
21 + <parent>
22 + <groupId>org.onosproject</groupId>
23 + <artifactId>onos-restconf-client</artifactId>
24 + <version>1.8.0-SNAPSHOT</version>
25 + <relativePath>../pom.xml</relativePath>
26 + </parent>
27 + <artifactId>onos-restconf-client-ctl</artifactId>
28 +
29 + <packaging>bundle</packaging>
30 +
31 + <dependencies>
32 + <dependency>
33 + <groupId>org.apache.felix</groupId>
34 + <artifactId>org.apache.felix.scr.annotations</artifactId>
35 + </dependency>
36 + <dependency>
37 + <groupId>org.osgi</groupId>
38 + <artifactId>org.osgi.compendium</artifactId>
39 + </dependency>
40 + <dependency>
41 + <groupId>org.onosproject</groupId>
42 + <artifactId>onos-restconf-client-api</artifactId>
43 + <version>${project.version}</version>
44 + </dependency>
45 + <dependency>
46 + <groupId>org.glassfish.jersey.core</groupId>
47 + <artifactId>jersey-client</artifactId>
48 + </dependency>
49 + <dependency>
50 + <groupId>org.apache.httpcomponents</groupId>
51 + <artifactId>httpclient-osgi</artifactId>
52 + <version>4.5.1</version>
53 + </dependency>
54 + <dependency>
55 + <groupId>org.apache.httpcomponents</groupId>
56 + <artifactId>httpcore-osgi</artifactId>
57 + <version>4.4.4</version>
58 + </dependency>
59 + <dependency>
60 + <groupId>commons-io</groupId>
61 + <artifactId>commons-io</artifactId>
62 + <version>2.4</version>
63 + </dependency>
64 + <dependency>
65 + <groupId>org.onosproject</groupId>
66 + <artifactId>onos-restsb-api</artifactId>
67 + <version>${project.version}</version>
68 + <type>bundle</type>
69 + </dependency>
70 + </dependencies>
71 +
72 + <build>
73 + <plugins>
74 + <plugin>
75 + <groupId>org.apache.felix</groupId>
76 + <artifactId>maven-scr-plugin</artifactId>
77 + </plugin>
78 + <plugin>
79 + <groupId>org.apache.felix</groupId>
80 + <artifactId>maven-bundle-plugin</artifactId>
81 + </plugin>
82 + </plugins>
83 + </build>
84 +
85 +</project>
1 +/*
2 + * Copyright 2016-present 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 +package org.onosproject.protocol.restconf.ctl;
17 +
18 +import java.io.InputStream;
19 +import java.util.Map;
20 +import java.util.concurrent.ConcurrentHashMap;
21 +import java.util.concurrent.ExecutorService;
22 +import java.util.concurrent.Executors;
23 +
24 +import javax.ws.rs.client.WebTarget;
25 +import javax.ws.rs.core.GenericType;
26 +import javax.ws.rs.core.Response;
27 +
28 +import org.apache.felix.scr.annotations.Activate;
29 +import org.apache.felix.scr.annotations.Component;
30 +import org.apache.felix.scr.annotations.Deactivate;
31 +import org.apache.felix.scr.annotations.Service;
32 +import org.glassfish.jersey.client.ChunkedInput;
33 +import org.onlab.packet.IpAddress;
34 +import org.onosproject.net.DeviceId;
35 +import org.onosproject.protocol.http.ctl.HttpSBControllerImpl;
36 +import org.onosproject.protocol.rest.RestSBDevice;
37 +import org.onosproject.protocol.restconf.RestConfNotificationEventListener;
38 +import org.onosproject.protocol.restconf.RestConfSBController;
39 +import org.slf4j.Logger;
40 +import org.slf4j.LoggerFactory;
41 +
42 +/**
43 + * The implementation of RestConfSBController.
44 + */
45 +@Component(immediate = true)
46 +@Service
47 +public class RestConfSBControllerImpl extends HttpSBControllerImpl
48 + implements RestConfSBController {
49 +
50 + private static final Logger log = LoggerFactory
51 + .getLogger(RestConfSBControllerImpl.class);
52 +
53 + // TODO: for the Ibis release when both RESTCONF server and RESTCONF client
54 + // fully support root resource discovery, ROOT_RESOURCE constant will be
55 + // removed and rather the value would get discovered dynamically.
56 + private static final String ROOT_RESOURCE = "/onos/restconf";
57 +
58 + private static final String RESOURCE_PATH_PREFIX = "/data/";
59 + private static final String NOTIFICATION_PATH_PREFIX = "/data/";
60 +
61 + private Map<DeviceId, RestConfNotificationEventListener>
62 + restconfNotificationListenerMap = new ConcurrentHashMap<>();
63 + private Map<DeviceId, GetChunksRunnable> runnableTable = new ConcurrentHashMap<>();
64 +
65 + ExecutorService executor = Executors.newCachedThreadPool();
66 +
67 + @Activate
68 + public void activate() {
69 + log.info("RESTCONF SBI Started");
70 + }
71 +
72 + @Deactivate
73 + public void deactivate() {
74 + log.info("RESTCONF SBI Stopped");
75 + executor.shutdown();
76 + this.getClientMap().clear();
77 + this.getDeviceMap().clear();
78 + }
79 +
80 + @Override
81 + public Map<DeviceId, RestSBDevice> getDevices() {
82 + log.trace("RESTCONF SBI::getDevices");
83 + return super.getDevices();
84 + }
85 +
86 + @Override
87 + public RestSBDevice getDevice(DeviceId deviceInfo) {
88 + log.trace("RESTCONF SBI::getDevice with deviceId");
89 + return super.getDevice(deviceInfo);
90 + }
91 +
92 + @Override
93 + public RestSBDevice getDevice(IpAddress ip, int port) {
94 + log.trace("RESTCONF SBI::getDevice with ip and port");
95 + return super.getDevice(ip, port);
96 + }
97 +
98 + @Override
99 + public void addDevice(RestSBDevice device) {
100 + log.trace("RESTCONF SBI::addDevice");
101 + super.addDevice(device);
102 + }
103 +
104 + @Override
105 + public void removeDevice(DeviceId deviceId) {
106 + log.trace("RESTCONF SBI::removeDevice");
107 + super.removeDevice(deviceId);
108 + }
109 +
110 + @Override
111 + public boolean post(DeviceId device, String request, InputStream payload,
112 + String mediaType) {
113 + request = discoverRootResource(device) + RESOURCE_PATH_PREFIX
114 + + request;
115 + return super.post(device, request, payload, mediaType);
116 + }
117 +
118 + @Override
119 + public <T> T post(DeviceId device, String request, InputStream payload,
120 + String mediaType, Class<T> responseClass) {
121 + request = discoverRootResource(device) + RESOURCE_PATH_PREFIX
122 + + request;
123 + return super.post(device, request, payload, mediaType, responseClass);
124 + }
125 +
126 + @Override
127 + public boolean put(DeviceId device, String request, InputStream payload,
128 + String mediaType) {
129 + request = discoverRootResource(device) + RESOURCE_PATH_PREFIX
130 + + request;
131 + return super.put(device, request, payload, mediaType);
132 + }
133 +
134 + @Override
135 + public InputStream get(DeviceId device, String request, String mediaType) {
136 + request = discoverRootResource(device) + RESOURCE_PATH_PREFIX
137 + + request;
138 + return super.get(device, request, mediaType);
139 + }
140 +
141 + @Override
142 + public boolean patch(DeviceId device, String request, InputStream payload,
143 + String mediaType) {
144 + request = discoverRootResource(device) + RESOURCE_PATH_PREFIX
145 + + request;
146 + return super.patch(device, request, payload, mediaType);
147 + }
148 +
149 + @Override
150 + public boolean delete(DeviceId device, String request, InputStream payload,
151 + String mediaType) {
152 + request = discoverRootResource(device) + RESOURCE_PATH_PREFIX
153 + + request;
154 + return super.delete(device, request, payload, mediaType);
155 + }
156 +
157 + @Override
158 + public void enableNotifications(DeviceId device, String request,
159 + String mediaType,
160 + RestConfNotificationEventListener listener) {
161 +
162 + request = discoverRootResource(device) + NOTIFICATION_PATH_PREFIX
163 + + request;
164 +
165 + addNotificationListener(device, listener);
166 +
167 + GetChunksRunnable runnable = new GetChunksRunnable(request, mediaType,
168 + device);
169 + runnableTable.put(device, runnable);
170 + executor.execute(runnable);
171 + }
172 +
173 + public void stopNotifications(DeviceId device) {
174 +
175 + runnableTable.get(device).terminate();
176 + runnableTable.remove(device);
177 + removeNotificationListener(device);
178 + log.debug("Stop sending notifications for device URI: " + device.uri().toString());
179 +
180 + }
181 +
182 + public class GetChunksRunnable implements Runnable {
183 + private String request;
184 + private String mediaType;
185 + private DeviceId device;
186 +
187 + private volatile boolean running = true;
188 +
189 + public void terminate() {
190 + running = false;
191 + }
192 +
193 + /**
194 + * @param request
195 + * @param mediaType
196 + * @param device
197 + */
198 + public GetChunksRunnable(String request, String mediaType,
199 + DeviceId device) {
200 + this.request = request;
201 + this.mediaType = mediaType;
202 + this.device = device;
203 + }
204 +
205 + @Override
206 + public void run() {
207 + WebTarget wt = getWebTarget(device, request);
208 + Response clientResp = wt.request(mediaType).get();
209 + RestConfNotificationEventListener listener = restconfNotificationListenerMap
210 + .get(device);
211 + final ChunkedInput<String> chunkedInput = (ChunkedInput<String>) clientResp
212 + .readEntity(new GenericType<ChunkedInput<String>>() {
213 + });
214 +
215 + String chunk;
216 + // Note that the read() is a blocking operation and the invoking
217 + // thread is blocked until a new chunk comes. Jersey implementation
218 + // of this IO operation is in a way that it does not respond to
219 + // interrupts.
220 + while (running) {
221 + chunk = chunkedInput.read();
222 + if (chunk != null) {
223 + if (running) {
224 + listener.handleNotificationEvent(device, chunk);
225 + } else {
226 + log.trace("the requesting client is no more interested "
227 + + "to receive such notifications.");
228 + }
229 + } else {
230 + log.trace("The received notification chunk is null. do not continue any more.");
231 + break;
232 + }
233 + }
234 + log.trace("out of while loop -- end of run");
235 + }
236 + }
237 +
238 + public String discoverRootResource(DeviceId device) {
239 + // FIXME: send a GET command to the device to discover the root resource.
240 + // The plan to fix this is for the Ibis release when the RESTCONF server and
241 + // the RESTCONF client both support root resource discovery.
242 + return ROOT_RESOURCE;
243 + }
244 +
245 + @Override
246 + public void addNotificationListener(DeviceId deviceId,
247 + RestConfNotificationEventListener listener) {
248 + if (!restconfNotificationListenerMap.containsKey(deviceId)) {
249 + this.restconfNotificationListenerMap.put(deviceId, listener);
250 + }
251 + }
252 +
253 + @Override
254 + public void removeNotificationListener(DeviceId deviceId) {
255 + this.restconfNotificationListenerMap.remove(deviceId);
256 + }
257 +
258 +}
1 +/*
2 + * Copyright 2016-present 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 +
17 +/**
18 + * RESTCONF southbound protocol implementation.
19 + */
20 +package org.onosproject.protocol.restconf.ctl;
1 +/*
2 + * Copyright 2016-present 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 +
17 +package org.onosproject.protocol.restconf.ctl;
18 +
19 +import static org.junit.Assert.assertEquals;
20 +import static org.junit.Assert.assertFalse;
21 +import static org.junit.Assert.assertTrue;
22 +
23 +import java.util.concurrent.ExecutorService;
24 +import java.util.concurrent.Executors;
25 +
26 +import org.junit.Before;
27 +import org.junit.Test;
28 +import org.onlab.packet.IpAddress;
29 +import org.onosproject.protocol.rest.DefaultRestSBDevice;
30 +import org.onosproject.protocol.rest.RestSBDevice;
31 +
32 +/**
33 + * Basic testing for RestSBController.
34 + */
35 +public class RestConfSBControllerImplTest {
36 +
37 + RestConfSBControllerImpl restConfController;
38 +
39 + RestSBDevice device3;
40 +
41 + ExecutorService executor = Executors.newSingleThreadExecutor();
42 +
43 + @Before
44 + public void setUp() {
45 + restConfController = new RestConfSBControllerImpl();
46 + restConfController.activate();
47 + device3 = new DefaultRestSBDevice(IpAddress.valueOf("127.0.0.1"), 8181,
48 + "", "", "http", null, true);
49 + restConfController.addDevice(device3);
50 +
51 + }
52 +
53 + @Test
54 + public void basics() {
55 + assertTrue("Device3 non added",
56 + restConfController.getDevices().containsValue(device3));
57 + assertEquals("Device3 added but with wrong key",
58 + restConfController.getDevices().get(device3.deviceId()),
59 + device3);
60 + assertEquals("Incorrect Get Device by ID",
61 + restConfController.getDevice(device3.deviceId()), device3);
62 + assertEquals("Incorrect Get Device by IP, Port",
63 + restConfController.getDevice(device3.ip(), device3.port()),
64 + device3);
65 + restConfController.removeDevice(device3.deviceId());
66 + assertFalse("Device3 not removed",
67 + restConfController.getDevices().containsValue(device3));
68 + }
69 +}
1 +<?xml version="1.0" encoding="UTF-8"?>
2 +<!--
3 + ~ Copyright 2016-present Open Networking Laboratory
4 + ~
5 + ~ Licensed under the Apache License, Version 2.0 (the "License");
6 + ~ you may not use this file except in compliance with the License.
7 + ~ You may obtain a copy of the License at
8 + ~
9 + ~ http://www.apache.org/licenses/LICENSE-2.0
10 + ~
11 + ~ Unless required by applicable law or agreed to in writing, software
12 + ~ distributed under the License is distributed on an "AS IS" BASIS,
13 + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 + ~ See the License for the specific language governing permissions and
15 + ~ limitations under the License.
16 + -->
17 +
18 +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
19 + <modelVersion>4.0.0</modelVersion>
20 + <parent>
21 + <groupId>org.onosproject</groupId>
22 + <artifactId>onos-restconf</artifactId>
23 + <version>1.8.0-SNAPSHOT</version>
24 + <relativePath>../pom.xml</relativePath>
25 + </parent>
26 + <artifactId>onos-restconf-client</artifactId>
27 + <packaging>pom</packaging>
28 + <dependencies>
29 + <dependency>
30 + <groupId>org.onosproject</groupId>
31 + <artifactId>onos-api</artifactId>
32 + <version>${project.version}</version>
33 + </dependency>
34 + </dependencies>
35 + <modules>
36 + <module>api</module>
37 + <module>ctl</module>
38 + </modules>
39 + <description>RESTCONF Client Module</description>
40 +</project>
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
28 28
29 <modules> 29 <modules>
30 <module>server</module> 30 <module>server</module>
31 + <module>client</module>
31 </modules> 32 </modules>
32 33
33 </project> 34 </project>
......