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
COMPILE_DEPS = [
'//lib:CORE_DEPS',
'//incubator/api:onos-incubator-api',
'//utils/rest:onlab-rest',
'//lib:CORE_DEPS',
'//lib:jersey-client',
'//lib:jersey-common',
'//lib:httpclient-osgi',
'//lib:httpcore-osgi',
'//lib:javax.ws.rs-api',
'//lib:hk2-api',
'//lib:jersey-guava',
'//lib:aopalliance-repackaged',
'//lib:javax.inject',
]
osgi_jar_with_tests (
......
......@@ -28,6 +28,29 @@
<artifactId>onos-restsb-api</artifactId>
<packaging>bundle</packaging>
<dependencies>
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-client</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient-osgi</artifactId>
<version>4.5.1</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<description>ONOS Rest southbound plugin API</description>
......
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.protocol.http;
import org.onlab.packet.IpAddress;
import org.onosproject.net.DeviceId;
import org.onosproject.protocol.rest.RestSBDevice;
import java.io.InputStream;
import java.util.Map;
/**
* Abstraction of an HTTP controller. Serves as a one stop shop for obtaining
* HTTP southbound devices and (un)register listeners.
*/
public interface HttpSBController {
/**
* Returns all the devices known to this controller.
*
* @return map of devices
*/
Map<DeviceId, RestSBDevice> getDevices();
/**
* Returns a device by node identifier.
*
* @param deviceInfo node identifier
* @return RestSBDevice rest device
*/
RestSBDevice getDevice(DeviceId deviceInfo);
/**
* Returns a device by Ip and Port.
*
* @param ip device ip
* @param port device port
* @return RestSBDevice rest device
*/
RestSBDevice getDevice(IpAddress ip, int port);
/**
* Adds a device to the device map.
*
* @param device to be added
*/
void addDevice(RestSBDevice device);
/**
* Removes the device from the devices map.
*
* @param deviceId to be removed
*/
void removeDevice(DeviceId deviceId);
/**
* Does a HTTP POST request with specified parameters to the device.
*
* @param device device to make the request to
* @param request url of the request
* @param payload payload of the request as an InputStream
* @param mediaType type of content in the payload i.e. application/json
* @return true if operation returned 200, 201, 202, false otherwise
*/
boolean post(DeviceId device, String request, InputStream payload, String mediaType);
/**
* Does a HTTP POST request with specified parameters to the device.
*
* @param <T> post return type
* @param device device to make the request to
* @param request url of the request
* @param payload payload of the request as an InputStream
* @param mediaType type of content in the payload i.e. application/json
* @param responseClass the type of response object we are interested in,
* such as String, InputStream.
* @return Object of type requested via responseClass.
*/
<T> T post(DeviceId device, String request, InputStream payload,
String mediaType, Class<T> responseClass);
/**
* Does a HTTP PUT request with specified parameters to the device.
*
* @param device device to make the request to
* @param request resource path of the request
* @param payload payload of the request as an InputStream
* @param mediaType type of content in the payload i.e. application/json
* @return true if operation returned 200, 201, 202, false otherwise
*/
boolean put(DeviceId device, String request, InputStream payload, String mediaType);
/**
* Does a HTTP GET request with specified parameters to the device.
*
* @param device device to make the request to
* @param request url of the request
* @param mediaType format to retrieve the content in
* @return an inputstream of data from the reply.
*/
InputStream get(DeviceId device, String request, String mediaType);
/**
* Does a HTTP PATCH request with specified parameters to the device.
*
* @param device device to make the request to
* @param request url of the request
* @param payload payload of the request as an InputStream
* @param mediaType format to retrieve the content in
* @return true if operation returned 200, 201, 202, false otherwise
*/
boolean patch(DeviceId device, String request, InputStream payload, String mediaType);
/**
* Does a HTTP DELETE request with specified parameters to the device.
*
* @param device device to make the request to
* @param request url of the request
* @param payload payload of the request as an InputStream
* @param mediaType type of content in the payload i.e. application/json
* @return true if operation returned 200 false otherwise
*/
boolean delete(DeviceId device, String request, InputStream payload, String mediaType);
}
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.protocol.http.ctl;
import com.google.common.collect.ImmutableMap;
import org.apache.commons.io.IOUtils;
import org.apache.http.client.methods.HttpPatch;
import org.apache.http.conn.ssl.AllowAllHostnameVerifier;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContextBuilder;
import org.glassfish.jersey.client.authentication.HttpAuthenticationFeature;
import org.onlab.packet.IpAddress;
import org.onosproject.net.DeviceId;
import org.onosproject.protocol.http.HttpSBController;
import org.onosproject.protocol.rest.RestSBDevice;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Base64;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* The implementation of HttpSBController.
*/
public class HttpSBControllerImpl implements HttpSBController {
private static final Logger log =
LoggerFactory.getLogger(HttpSBControllerImpl.class);
private static final String XML = "xml";
private static final String JSON = "json";
private static final String DOUBLESLASH = "//";
private static final String COLON = ":";
private static final int STATUS_OK = Response.Status.OK.getStatusCode();
private static final int STATUS_CREATED = Response.Status.CREATED.getStatusCode();
private static final int STATUS_ACCEPTED = Response.Status.ACCEPTED.getStatusCode();
private static final String HTTPS = "https";
private static final String AUTHORIZATION_PROPERTY = "authorization";
private static final String BASIC_AUTH_PREFIX = "Basic ";
private final Map<DeviceId, RestSBDevice> deviceMap = new ConcurrentHashMap<>();
private final Map<DeviceId, Client> clientMap = new ConcurrentHashMap<>();
public Map<DeviceId, RestSBDevice> getDeviceMap() {
return deviceMap;
}
public Map<DeviceId, Client> getClientMap() {
return clientMap;
}
@Override
public Map<DeviceId, RestSBDevice> getDevices() {
return ImmutableMap.copyOf(deviceMap);
}
@Override
public RestSBDevice getDevice(DeviceId deviceInfo) {
return deviceMap.get(deviceInfo);
}
@Override
public RestSBDevice getDevice(IpAddress ip, int port) {
return deviceMap.values().stream().filter(v -> v.ip().equals(ip)
&& v.port() == port).findFirst().get();
}
@Override
public void addDevice(RestSBDevice device) {
if (!deviceMap.containsKey(device.deviceId())) {
Client client = ignoreSslClient();
if (device.username() != null) {
String username = device.username();
String password = device.password() == null ? "" : device.password();
authenticate(client, username, password);
}
clientMap.put(device.deviceId(), client);
deviceMap.put(device.deviceId(), device);
} else {
log.warn("Trying to add a device that is already existing {}", device.deviceId());
}
}
@Override
public void removeDevice(DeviceId deviceId) {
clientMap.remove(deviceId);
deviceMap.remove(deviceId);
}
@Override
public boolean post(DeviceId device, String request, InputStream payload, String mediaType) {
Response response = getResponse(device, request, payload, mediaType);
return checkReply(response);
}
@Override
public <T> T post(DeviceId device, String request, InputStream payload,
String mediaType, Class<T> responseClass) {
Response response = getResponse(device, request, payload, mediaType);
if (response.hasEntity()) {
return response.readEntity(responseClass);
}
log.error("Response from device {} for request {} contains no entity", device, request);
return null;
}
private Response getResponse(DeviceId device, String request, InputStream payload, String mediaType) {
String type = typeOfMediaType(mediaType);
WebTarget wt = getWebTarget(device, request);
Response response = null;
if (payload != null) {
try {
response = wt.request(type)
.post(Entity.entity(IOUtils.toString(payload, StandardCharsets.UTF_8), type));
} catch (IOException e) {
log.error("Cannot do POST {} request on device {} because can't read payload",
request, device);
}
} else {
response = wt.request(type).post(Entity.entity(null, type));
}
return response;
}
@Override
public boolean put(DeviceId device, String request, InputStream payload, String mediaType) {
String type = typeOfMediaType(mediaType);
WebTarget wt = getWebTarget(device, request);
Response response = null;
if (payload != null) {
try {
response = wt.request(type)
.put(Entity.entity(IOUtils.toString(payload, StandardCharsets.UTF_8), type));
} catch (IOException e) {
log.error("Cannot do PUT {} request on device {} because can't read payload",
request, device);
}
} else {
response = wt.request(type).put(Entity.entity(null, type));
}
return checkReply(response);
}
@Override
public InputStream get(DeviceId device, String request, String mediaType) {
String type = typeOfMediaType(mediaType);
WebTarget wt = getWebTarget(device, request);
Response s = wt.request(type).get();
if (checkReply(s)) {
return new ByteArrayInputStream(s.readEntity((String.class))
.getBytes(StandardCharsets.UTF_8));
}
return null;
}
@Override
public boolean patch(DeviceId device, String request, InputStream payload, String mediaType) {
String type = typeOfMediaType(mediaType);
try {
log.debug("Url request {} ", getUrlString(device, request));
HttpPatch httprequest = new HttpPatch(getUrlString(device, request));
if (deviceMap.get(device).username() != null) {
String pwd = deviceMap.get(device).password() == null ? "" : COLON + deviceMap.get(device).password();
String userPassword = deviceMap.get(device).username() + pwd;
String base64string = Base64.getEncoder().encodeToString(userPassword.getBytes(StandardCharsets.UTF_8));
httprequest.addHeader(AUTHORIZATION_PROPERTY, BASIC_AUTH_PREFIX + base64string);
}
if (payload != null) {
StringEntity input = new StringEntity(IOUtils.toString(payload, StandardCharsets.UTF_8));
input.setContentType(type);
httprequest.setEntity(input);
}
CloseableHttpClient httpClient;
if (deviceMap.containsKey(device) && deviceMap.get(device).protocol().equals(HTTPS)) {
httpClient = getApacheSslBypassClient();
} else {
httpClient = HttpClients.createDefault();
}
int responseStatusCode = httpClient
.execute(httprequest)
.getStatusLine()
.getStatusCode();
return checkStatusCode(responseStatusCode);
} catch (IOException | NoSuchAlgorithmException | KeyManagementException | KeyStoreException e) {
log.error("Cannot do PATCH {} request on device {}",
request, device, e);
}
return false;
}
@Override
public boolean delete(DeviceId device, String request, InputStream payload, String mediaType) {
String type = typeOfMediaType(mediaType);
WebTarget wt = getWebTarget(device, request);
// FIXME: do we need to delete an entry by enclosing data in DELETE request?
// wouldn't it be nice to use PUT to implement the similar concept?
Response response = wt.request(type).delete();
return checkReply(response);
}
private String typeOfMediaType(String mediaType) {
String type;
switch (mediaType) {
case XML:
type = MediaType.APPLICATION_XML;
break;
case JSON:
type = MediaType.APPLICATION_JSON;
break;
default:
throw new IllegalArgumentException("Unsupported media type " + mediaType);
}
return type;
}
private void authenticate(Client client, String username, String password) {
client.register(HttpAuthenticationFeature.basic(username, password));
}
protected WebTarget getWebTarget(DeviceId device, String request) {
log.debug("Sending request to URL {} ", getUrlString(device, request));
return clientMap.get(device).target(getUrlString(device, request));
}
//FIXME security issue: this trusts every SSL certificate, even if is self-signed. Also deprecated methods.
private CloseableHttpClient getApacheSslBypassClient() throws NoSuchAlgorithmException,
KeyManagementException, KeyStoreException {
return HttpClients.custom().
setHostnameVerifier(new AllowAllHostnameVerifier()).
setSslcontext(new SSLContextBuilder()
.loadTrustMaterial(null, (arg0, arg1) -> true)
.build()).build();
}
private String getUrlString(DeviceId device, String request) {
if (deviceMap.get(device).url() != null) {
return deviceMap.get(device).protocol() + COLON + DOUBLESLASH
+ deviceMap.get(device).url() + request;
} else {
return deviceMap.get(device).protocol() + COLON +
DOUBLESLASH +
deviceMap.get(device).ip().toString() +
COLON + deviceMap.get(device).port() + request;
}
}
private boolean checkReply(Response response) {
if (response != null) {
return checkStatusCode(response.getStatus());
}
log.error("Null reply from device");
return false;
}
private boolean checkStatusCode(int statusCode) {
if (statusCode == STATUS_OK ||
statusCode == STATUS_CREATED ||
statusCode == STATUS_ACCEPTED) {
return true;
} else {
log.error("Failed request, HTTP error code : "
+ statusCode);
return false;
}
}
private Client ignoreSslClient() {
SSLContext sslcontext = null;
try {
sslcontext = SSLContext.getInstance("TLS");
sslcontext.init(null, new TrustManager[]{new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
}
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
} }, new java.security.SecureRandom());
} catch (NoSuchAlgorithmException | KeyManagementException e) {
e.printStackTrace();
}
return ClientBuilder.newBuilder().sslContext(sslcontext).hostnameVerifier((s1, s2) -> true).build();
}
}
/**
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @author onos
*
*/
package org.onosproject.protocol.http.ctl;
/**
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @author onos
*
*/
package org.onosproject.protocol.http;
......@@ -16,15 +16,16 @@
package org.onosproject.protocol.rest;
import com.google.common.base.MoreObjects;
import com.google.common.base.Preconditions;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Objects;
import org.apache.commons.lang3.StringUtils;
import org.onlab.packet.IpAddress;
import org.onosproject.net.DeviceId;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Objects;
import com.google.common.base.MoreObjects;
import com.google.common.base.Preconditions;
/**
* Default implementation for Rest devices.
......
......@@ -16,123 +16,11 @@
package org.onosproject.protocol.rest;
import org.onlab.packet.IpAddress;
import org.onosproject.net.DeviceId;
import java.io.InputStream;
import java.util.Map;
import org.onosproject.protocol.http.HttpSBController;
/**
* Abstraction of an REST controller. Serves as a one stop shop for obtaining
* Rest southbound devices and (un)register listeners.
*/
public interface RestSBController {
/**
* Returns all the devices known to this controller.
*
* @return map of devices
*/
Map<DeviceId, RestSBDevice> getDevices();
/**
* Returns a device by node identifier.
*
* @param deviceInfo node identifier
* @return RestSBDevice rest device
*/
RestSBDevice getDevice(DeviceId deviceInfo);
/**
* Returns a device by Ip and Port.
*
* @param ip device ip
* @param port device port
* @return RestSBDevice rest device
*/
RestSBDevice getDevice(IpAddress ip, int port);
/**
* Adds a device to the device map.
*
* @param device to be added
*/
void addDevice(RestSBDevice device);
/**
* Removes the device from the devices map.
*
* @param deviceId to be removed
*/
void removeDevice(DeviceId deviceId);
/**
* Does a REST POST request with specified parameters to the device.
*
* @param device device to make the request to
* @param request url of the request
* @param payload payload of the request as an InputStream
* @param mediaType type of content in the payload i.e. application/json
* @return true if operation returned 200, 201, 202, false otherwise
*/
boolean post(DeviceId device, String request, InputStream payload, String mediaType);
/**
* Does a REST POST request with specified parameters to the device.
*
* @param <T> post return type
* @param device device to make the request to
* @param request url of the request
* @param payload payload of the request as an InputStream
* @param mediaType type of content in the payload i.e. application/json
* @param responseClass the type of response object we are interested in,
* such as String, InputStream.
* @return Object of type requested via responseClass.
*/
<T> T post(DeviceId device, String request, InputStream payload,
String mediaType, Class<T> responseClass);
/**
* Does a REST PUT request with specified parameters to the device.
*
* @param device device to make the request to
* @param request resource path of the request
* @param payload payload of the request as an InputStream
* @param mediaType type of content in the payload i.e. application/json
* @return true if operation returned 200, 201, 202, false otherwise
*/
boolean put(DeviceId device, String request, InputStream payload, String mediaType);
/**
* Does a REST GET request with specified parameters to the device.
*
* @param device device to make the request to
* @param request url of the request
* @param mediaType format to retrieve the content in
* @return an inputstream of data from the reply.
*/
InputStream get(DeviceId device, String request, String mediaType);
/**
* Does a REST PATCH request with specified parameters to the device.
*
* @param device device to make the request to
* @param request url of the request
* @param payload payload of the request as an InputStream
* @param mediaType format to retrieve the content in
* @return true if operation returned 200, 201, 202, false otherwise
*/
boolean patch(DeviceId device, String request, InputStream payload, String mediaType);
/**
* Does a REST DELETE request with specified parameters to the device.
*
* @param device device to make the request to
* @param request url of the request
* @param payload payload of the request as an InputStream
* @param mediaType type of content in the payload i.e. application/json
* @return true if operation returned 200 false otherwise
*/
boolean delete(DeviceId device, String request, InputStream payload, String mediaType);
public interface RestSBController extends HttpSBController {
}
......
......@@ -34,32 +34,10 @@
<artifactId>org.apache.felix.scr.annotations</artifactId>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.compendium</artifactId>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-restsb-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-client</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient-osgi</artifactId>
<version>4.5.1</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore-osgi</artifactId>
<version>4.4.4</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
<type>bundle</type>
</dependency>
</dependencies>
......
......@@ -16,70 +16,24 @@
package org.onosproject.protocol.rest.ctl;
import com.google.common.collect.ImmutableMap;
import org.apache.commons.io.IOUtils;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Service;
import org.apache.http.client.methods.HttpPatch;
import org.apache.http.conn.ssl.AllowAllHostnameVerifier;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContextBuilder;
import org.glassfish.jersey.client.authentication.HttpAuthenticationFeature;
import org.onlab.packet.IpAddress;
import org.onosproject.net.DeviceId;
import org.onosproject.protocol.http.ctl.HttpSBControllerImpl;
import org.onosproject.protocol.rest.RestSBController;
import org.onosproject.protocol.rest.RestSBDevice;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Base64;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* The implementation of RestSBController.
*/
@Component(immediate = true)
@Service
public class RestSBControllerImpl implements RestSBController {
public class RestSBControllerImpl extends HttpSBControllerImpl implements RestSBController {
private static final Logger log =
LoggerFactory.getLogger(RestSBControllerImpl.class);
private static final String XML = "xml";
private static final String JSON = "json";
private static final String DOUBLESLASH = "//";
private static final String COLON = ":";
private static final int STATUS_OK = Response.Status.OK.getStatusCode();
private static final int STATUS_CREATED = Response.Status.CREATED.getStatusCode();
private static final int STATUS_ACCEPTED = Response.Status.ACCEPTED.getStatusCode();
private static final String HTTPS = "https";
private static final String AUTHORIZATION_PROPERTY = "authorization";
private static final String BASIC_AUTH_PREFIX = "Basic ";
private final Map<DeviceId, RestSBDevice> deviceMap = new ConcurrentHashMap<>();
private final Map<DeviceId, Client> clientMap = new ConcurrentHashMap<>();
@Activate
public void activate() {
......@@ -88,259 +42,9 @@ public class RestSBControllerImpl implements RestSBController {
@Deactivate
public void deactivate() {
clientMap.clear();
deviceMap.clear();
this.getClientMap().clear();
this.getDeviceMap().clear();
log.info("Stopped");
}
@Override
public Map<DeviceId, RestSBDevice> getDevices() {
return ImmutableMap.copyOf(deviceMap);
}
@Override
public RestSBDevice getDevice(DeviceId deviceInfo) {
return deviceMap.get(deviceInfo);
}
@Override
public RestSBDevice getDevice(IpAddress ip, int port) {
return deviceMap.values().stream().filter(v -> v.ip().equals(ip)
&& v.port() == port).findFirst().get();
}
@Override
public void addDevice(RestSBDevice device) {
if (!deviceMap.containsKey(device.deviceId())) {
Client client = ignoreSslClient();
if (device.username() != null) {
String username = device.username();
String password = device.password() == null ? "" : device.password();
authenticate(client, username, password);
}
clientMap.put(device.deviceId(), client);
deviceMap.put(device.deviceId(), device);
} else {
log.warn("Trying to add a device that is already existing {}", device.deviceId());
}
}
@Override
public void removeDevice(DeviceId deviceId) {
clientMap.remove(deviceId);
deviceMap.remove(deviceId);
}
@Override
public boolean post(DeviceId device, String request, InputStream payload, String mediaType) {
Response response = getResponse(device, request, payload, mediaType);
return checkReply(response);
}
@Override
public <T> T post(DeviceId device, String request, InputStream payload,
String mediaType, Class<T> responseClass) {
Response response = getResponse(device, request, payload, mediaType);
if (response.hasEntity()) {
return response.readEntity(responseClass);
}
log.error("Response from device {} for request {} contains no entity", device, request);
return null;
}
private Response getResponse(DeviceId device, String request, InputStream payload, String mediaType) {
String type = typeOfMediaType(mediaType);
WebTarget wt = getWebTarget(device, request);
Response response = null;
if (payload != null) {
try {
response = wt.request(type)
.post(Entity.entity(IOUtils.toString(payload, StandardCharsets.UTF_8), type));
} catch (IOException e) {
log.error("Cannot do POST {} request on device {} because can't read payload",
request, device);
}
} else {
response = wt.request(type).post(Entity.entity(null, type));
}
return response;
}
@Override
public boolean put(DeviceId device, String request, InputStream payload, String mediaType) {
String type = typeOfMediaType(mediaType);
WebTarget wt = getWebTarget(device, request);
Response response = null;
if (payload != null) {
try {
response = wt.request(type)
.put(Entity.entity(IOUtils.toString(payload, StandardCharsets.UTF_8), type));
} catch (IOException e) {
log.error("Cannot do PUT {} request on device {} because can't read payload",
request, device);
}
} else {
response = wt.request(type).put(Entity.entity(null, type));
}
return checkReply(response);
}
@Override
public InputStream get(DeviceId device, String request, String mediaType) {
String type = typeOfMediaType(mediaType);
WebTarget wt = getWebTarget(device, request);
Response s = wt.request(type).get();
if (checkReply(s)) {
return new ByteArrayInputStream(s.readEntity((String.class))
.getBytes(StandardCharsets.UTF_8));
}
return null;
}
@Override
public boolean patch(DeviceId device, String request, InputStream payload, String mediaType) {
String type = typeOfMediaType(mediaType);
try {
log.debug("Url request {} ", getUrlString(device, request));
HttpPatch httprequest = new HttpPatch(getUrlString(device, request));
if (deviceMap.get(device).username() != null) {
String pwd = deviceMap.get(device).password() == null ? "" : COLON + deviceMap.get(device).password();
String userPassword = deviceMap.get(device).username() + pwd;
String base64string = Base64.getEncoder().encodeToString(userPassword.getBytes(StandardCharsets.UTF_8));
httprequest.addHeader(AUTHORIZATION_PROPERTY, BASIC_AUTH_PREFIX + base64string);
}
if (payload != null) {
StringEntity input = new StringEntity(IOUtils.toString(payload, StandardCharsets.UTF_8));
input.setContentType(type);
httprequest.setEntity(input);
}
CloseableHttpClient httpClient;
if (deviceMap.containsKey(device) && deviceMap.get(device).protocol().equals(HTTPS)) {
httpClient = getApacheSslBypassClient();
} else {
httpClient = HttpClients.createDefault();
}
int responseStatusCode = httpClient
.execute(httprequest)
.getStatusLine()
.getStatusCode();
return checkStatusCode(responseStatusCode);
} catch (IOException | NoSuchAlgorithmException | KeyManagementException | KeyStoreException e) {
log.error("Cannot do PATCH {} request on device {}",
request, device, e);
}
return false;
}
@Override
public boolean delete(DeviceId device, String request, InputStream payload, String mediaType) {
String type = typeOfMediaType(mediaType);
WebTarget wt = getWebTarget(device, request);
// FIXME: do we need to delete an entry by enclosing data in DELETE request?
// wouldn't it be nice to use PUT to implement the similar concept?
Response response = wt.request(type).delete();
return checkReply(response);
}
private String typeOfMediaType(String mediaType) {
String type;
switch (mediaType) {
case XML:
type = MediaType.APPLICATION_XML;
break;
case JSON:
type = MediaType.APPLICATION_JSON;
break;
default:
throw new IllegalArgumentException("Unsupported media type " + mediaType);
}
return type;
}
private void authenticate(Client client, String username, String password) {
client.register(HttpAuthenticationFeature.basic(username, password));
}
protected WebTarget getWebTarget(DeviceId device, String request) {
log.debug("Sending request to URL {} ", getUrlString(device, request));
return clientMap.get(device).target(getUrlString(device, request));
}
//FIXME security issue: this trusts every SSL certificate, even if is self-signed. Also deprecated methods.
private CloseableHttpClient getApacheSslBypassClient() throws NoSuchAlgorithmException,
KeyManagementException, KeyStoreException {
return HttpClients.custom().
setHostnameVerifier(new AllowAllHostnameVerifier()).
setSslcontext(new SSLContextBuilder()
.loadTrustMaterial(null, (arg0, arg1) -> true)
.build()).build();
}
private String getUrlString(DeviceId device, String request) {
if (deviceMap.get(device).url() != null) {
return deviceMap.get(device).protocol() + COLON + DOUBLESLASH
+ deviceMap.get(device).url() + request;
} else {
return deviceMap.get(device).protocol() + COLON +
DOUBLESLASH +
deviceMap.get(device).ip().toString() +
COLON + deviceMap.get(device).port() + request;
}
}
private boolean checkReply(Response response) {
if (response != null) {
return checkStatusCode(response.getStatus());
}
log.error("Null reply from device");
return false;
}
private boolean checkStatusCode(int statusCode) {
if (statusCode == STATUS_OK ||
statusCode == STATUS_CREATED ||
statusCode == STATUS_ACCEPTED) {
return true;
} else {
log.error("Failed request, HTTP error code : "
+ statusCode);
return false;
}
}
private Client ignoreSslClient() {
SSLContext sslcontext = null;
try {
sslcontext = SSLContext.getInstance("TLS");
sslcontext.init(null, new TrustManager[]{new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
}
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
} }, new java.security.SecureRandom());
} catch (NoSuchAlgorithmException | KeyManagementException e) {
e.printStackTrace();
}
return ClientBuilder.newBuilder().sslContext(sslcontext).hostnameVerifier((s1, s2) -> true).build();
}
}
......
COMPILE_DEPS = [
'//lib:CORE_DEPS',
'//incubator/api:onos-incubator-api',
'//utils/rest:onlab-rest',
'//protocols/rest/api:onos-protocols-rest-api',
]
osgi_jar_with_tests (
deps = COMPILE_DEPS,
)
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2016-present Open Networking Laboratory
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<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"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.onosproject</groupId>
<artifactId>onos-restconf-client</artifactId>
<version>1.8.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>onos-restconf-client-api</artifactId>
<packaging>bundle</packaging>
<description>ONOS RESTCONF southbound plugin API</description>
<dependencies>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-restsb-api</artifactId>
<version>${project.version}</version>
<type>bundle</type>
</dependency>
</dependencies>
</project>
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.protocol.restconf;
import org.onosproject.net.DeviceId;
/**
* Notifies providers about incoming RESTCONF notification events.
*/
public interface RestConfNotificationEventListener {
/**
* Handles the notification event.
*
* @param <T>
*
* @param deviceId of the restconf device
* @param eventJsonString the json string representation of the event
*/
<T> void handleNotificationEvent(DeviceId deviceId, T eventJsonString);
}
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.protocol.restconf;
import org.onosproject.net.DeviceId;
import org.onosproject.protocol.http.HttpSBController;
/**
* Abstraction of a RESTCONF controller. Serves as a one stop shop for obtaining
* RESTCONF southbound devices and (un)register listeners.
*/
public interface RestConfSBController extends HttpSBController {
/**
* This method is to be called by whoever is interested to receive
* Notifications from a specific device. It does a REST GET request
* with specified parameters to the device, and calls the provided
* callBackListener upon receiving notifications to notify the requester
* about notifications.
*
*
* @param device device to make the request to
* @param request url of the request
* @param mediaType format to retrieve the content in
* @param callBackListener method to call when notifications arrives
*/
void enableNotifications(DeviceId device, String request, String mediaType,
RestConfNotificationEventListener callBackListener);
/**
* Register a listener for notification events that occur to restconf
* devices.
*
* @param deviceId the deviceId
* @param listener the listener to notify
*/
void addNotificationListener(DeviceId deviceId,
RestConfNotificationEventListener listener);
/**
* Unregister the listener for the device.
*
* @param deviceId the deviceId
*/
void removeNotificationListener(DeviceId deviceId);
}
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* RESTCONF southbound protocols libraries.
*/
package org.onosproject.protocol.restconf;
COMPILE_DEPS = [
'//lib:CORE_DEPS',
'//lib:jersey-client',
'//lib:jersey-common',
'//lib:httpclient-osgi',
'//lib:httpcore-osgi',
'//lib:javax.ws.rs-api',
'//lib:hk2-api',
'//lib:jersey-guava',
'//lib:aopalliance-repackaged',
'//lib:javax.inject',
'//protocols/restconf/client/api:onos-protocols-restconf-client-api',
'//protocols/rest/api:onos-protocols-rest-api',
]
osgi_jar_with_tests (
deps = COMPILE_DEPS,
)
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2016-present Open Networking Laboratory
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<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"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.onosproject</groupId>
<artifactId>onos-restconf-client</artifactId>
<version>1.8.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>onos-restconf-client-ctl</artifactId>
<packaging>bundle</packaging>
<dependencies>
<dependency>
<groupId>org.apache.felix</groupId>
<artifactId>org.apache.felix.scr.annotations</artifactId>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.compendium</artifactId>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-restconf-client-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-client</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient-osgi</artifactId>
<version>4.5.1</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore-osgi</artifactId>
<version>4.4.4</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-restsb-api</artifactId>
<version>${project.version}</version>
<type>bundle</type>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-scr-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.protocol.restconf.ctl;
import java.io.InputStream;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.GenericType;
import javax.ws.rs.core.Response;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Service;
import org.glassfish.jersey.client.ChunkedInput;
import org.onlab.packet.IpAddress;
import org.onosproject.net.DeviceId;
import org.onosproject.protocol.http.ctl.HttpSBControllerImpl;
import org.onosproject.protocol.rest.RestSBDevice;
import org.onosproject.protocol.restconf.RestConfNotificationEventListener;
import org.onosproject.protocol.restconf.RestConfSBController;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The implementation of RestConfSBController.
*/
@Component(immediate = true)
@Service
public class RestConfSBControllerImpl extends HttpSBControllerImpl
implements RestConfSBController {
private static final Logger log = LoggerFactory
.getLogger(RestConfSBControllerImpl.class);
// TODO: for the Ibis release when both RESTCONF server and RESTCONF client
// fully support root resource discovery, ROOT_RESOURCE constant will be
// removed and rather the value would get discovered dynamically.
private static final String ROOT_RESOURCE = "/onos/restconf";
private static final String RESOURCE_PATH_PREFIX = "/data/";
private static final String NOTIFICATION_PATH_PREFIX = "/data/";
private Map<DeviceId, RestConfNotificationEventListener>
restconfNotificationListenerMap = new ConcurrentHashMap<>();
private Map<DeviceId, GetChunksRunnable> runnableTable = new ConcurrentHashMap<>();
ExecutorService executor = Executors.newCachedThreadPool();
@Activate
public void activate() {
log.info("RESTCONF SBI Started");
}
@Deactivate
public void deactivate() {
log.info("RESTCONF SBI Stopped");
executor.shutdown();
this.getClientMap().clear();
this.getDeviceMap().clear();
}
@Override
public Map<DeviceId, RestSBDevice> getDevices() {
log.trace("RESTCONF SBI::getDevices");
return super.getDevices();
}
@Override
public RestSBDevice getDevice(DeviceId deviceInfo) {
log.trace("RESTCONF SBI::getDevice with deviceId");
return super.getDevice(deviceInfo);
}
@Override
public RestSBDevice getDevice(IpAddress ip, int port) {
log.trace("RESTCONF SBI::getDevice with ip and port");
return super.getDevice(ip, port);
}
@Override
public void addDevice(RestSBDevice device) {
log.trace("RESTCONF SBI::addDevice");
super.addDevice(device);
}
@Override
public void removeDevice(DeviceId deviceId) {
log.trace("RESTCONF SBI::removeDevice");
super.removeDevice(deviceId);
}
@Override
public boolean post(DeviceId device, String request, InputStream payload,
String mediaType) {
request = discoverRootResource(device) + RESOURCE_PATH_PREFIX
+ request;
return super.post(device, request, payload, mediaType);
}
@Override
public <T> T post(DeviceId device, String request, InputStream payload,
String mediaType, Class<T> responseClass) {
request = discoverRootResource(device) + RESOURCE_PATH_PREFIX
+ request;
return super.post(device, request, payload, mediaType, responseClass);
}
@Override
public boolean put(DeviceId device, String request, InputStream payload,
String mediaType) {
request = discoverRootResource(device) + RESOURCE_PATH_PREFIX
+ request;
return super.put(device, request, payload, mediaType);
}
@Override
public InputStream get(DeviceId device, String request, String mediaType) {
request = discoverRootResource(device) + RESOURCE_PATH_PREFIX
+ request;
return super.get(device, request, mediaType);
}
@Override
public boolean patch(DeviceId device, String request, InputStream payload,
String mediaType) {
request = discoverRootResource(device) + RESOURCE_PATH_PREFIX
+ request;
return super.patch(device, request, payload, mediaType);
}
@Override
public boolean delete(DeviceId device, String request, InputStream payload,
String mediaType) {
request = discoverRootResource(device) + RESOURCE_PATH_PREFIX
+ request;
return super.delete(device, request, payload, mediaType);
}
@Override
public void enableNotifications(DeviceId device, String request,
String mediaType,
RestConfNotificationEventListener listener) {
request = discoverRootResource(device) + NOTIFICATION_PATH_PREFIX
+ request;
addNotificationListener(device, listener);
GetChunksRunnable runnable = new GetChunksRunnable(request, mediaType,
device);
runnableTable.put(device, runnable);
executor.execute(runnable);
}
public void stopNotifications(DeviceId device) {
runnableTable.get(device).terminate();
runnableTable.remove(device);
removeNotificationListener(device);
log.debug("Stop sending notifications for device URI: " + device.uri().toString());
}
public class GetChunksRunnable implements Runnable {
private String request;
private String mediaType;
private DeviceId device;
private volatile boolean running = true;
public void terminate() {
running = false;
}
/**
* @param request
* @param mediaType
* @param device
*/
public GetChunksRunnable(String request, String mediaType,
DeviceId device) {
this.request = request;
this.mediaType = mediaType;
this.device = device;
}
@Override
public void run() {
WebTarget wt = getWebTarget(device, request);
Response clientResp = wt.request(mediaType).get();
RestConfNotificationEventListener listener = restconfNotificationListenerMap
.get(device);
final ChunkedInput<String> chunkedInput = (ChunkedInput<String>) clientResp
.readEntity(new GenericType<ChunkedInput<String>>() {
});
String chunk;
// Note that the read() is a blocking operation and the invoking
// thread is blocked until a new chunk comes. Jersey implementation
// of this IO operation is in a way that it does not respond to
// interrupts.
while (running) {
chunk = chunkedInput.read();
if (chunk != null) {
if (running) {
listener.handleNotificationEvent(device, chunk);
} else {
log.trace("the requesting client is no more interested "
+ "to receive such notifications.");
}
} else {
log.trace("The received notification chunk is null. do not continue any more.");
break;
}
}
log.trace("out of while loop -- end of run");
}
}
public String discoverRootResource(DeviceId device) {
// FIXME: send a GET command to the device to discover the root resource.
// The plan to fix this is for the Ibis release when the RESTCONF server and
// the RESTCONF client both support root resource discovery.
return ROOT_RESOURCE;
}
@Override
public void addNotificationListener(DeviceId deviceId,
RestConfNotificationEventListener listener) {
if (!restconfNotificationListenerMap.containsKey(deviceId)) {
this.restconfNotificationListenerMap.put(deviceId, listener);
}
}
@Override
public void removeNotificationListener(DeviceId deviceId) {
this.restconfNotificationListenerMap.remove(deviceId);
}
}
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* RESTCONF southbound protocol implementation.
*/
package org.onosproject.protocol.restconf.ctl;
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.protocol.restconf.ctl;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.junit.Before;
import org.junit.Test;
import org.onlab.packet.IpAddress;
import org.onosproject.protocol.rest.DefaultRestSBDevice;
import org.onosproject.protocol.rest.RestSBDevice;
/**
* Basic testing for RestSBController.
*/
public class RestConfSBControllerImplTest {
RestConfSBControllerImpl restConfController;
RestSBDevice device3;
ExecutorService executor = Executors.newSingleThreadExecutor();
@Before
public void setUp() {
restConfController = new RestConfSBControllerImpl();
restConfController.activate();
device3 = new DefaultRestSBDevice(IpAddress.valueOf("127.0.0.1"), 8181,
"", "", "http", null, true);
restConfController.addDevice(device3);
}
@Test
public void basics() {
assertTrue("Device3 non added",
restConfController.getDevices().containsValue(device3));
assertEquals("Device3 added but with wrong key",
restConfController.getDevices().get(device3.deviceId()),
device3);
assertEquals("Incorrect Get Device by ID",
restConfController.getDevice(device3.deviceId()), device3);
assertEquals("Incorrect Get Device by IP, Port",
restConfController.getDevice(device3.ip(), device3.port()),
device3);
restConfController.removeDevice(device3.deviceId());
assertFalse("Device3 not removed",
restConfController.getDevices().containsValue(device3));
}
}
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2016-present Open Networking Laboratory
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<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">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.onosproject</groupId>
<artifactId>onos-restconf</artifactId>
<version>1.8.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>onos-restconf-client</artifactId>
<packaging>pom</packaging>
<dependencies>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-api</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<modules>
<module>api</module>
<module>ctl</module>
</modules>
<description>RESTCONF Client Module</description>
</project>
......@@ -28,6 +28,7 @@
<modules>
<module>server</module>
<module>client</module>
</modules>
</project>
......