diff options
author | janani b <janani.b@huawei.com> | 2018-08-29 11:52:41 +0530 |
---|---|---|
committer | janani b <janani.b@huawei.com> | 2018-08-29 11:52:41 +0530 |
commit | c8a20b7cdc2b563e1ea86a9e25f4ac0e748995f9 (patch) | |
tree | 8c61702c516711b75f6ce4eae05c16a203b6b339 | |
parent | 5c41b487a093174b89f3a66806f115c4459c5065 (diff) |
Implementation for Restconf api call node
Restconf Api Call Node
Issue-ID: CCSDK-372
Change-Id: I1820e97d54e734ccf73c6f48a9afcc4b59dff549
Signed-off-by: janani b <janani.b@huawei.com>
10 files changed, 720 insertions, 136 deletions
diff --git a/restapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/restapicall/RestapiCallNode.java b/restapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/restapicall/RestapiCallNode.java index 46c74ff8..6a9e81bc 100644 --- a/restapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/restapicall/RestapiCallNode.java +++ b/restapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/restapicall/RestapiCallNode.java @@ -21,8 +21,33 @@ package org.onap.ccsdk.sli.plugins.restapicall; +import com.sun.jersey.api.client.Client; import com.sun.jersey.api.client.ClientHandlerException; +import com.sun.jersey.api.client.ClientResponse; import com.sun.jersey.api.client.UniformInterfaceException; +import com.sun.jersey.api.client.WebResource; +import com.sun.jersey.api.client.config.ClientConfig; +import com.sun.jersey.api.client.config.DefaultClientConfig; +import com.sun.jersey.api.client.filter.HTTPBasicAuthFilter; +import com.sun.jersey.api.client.filter.HTTPDigestAuthFilter; +import com.sun.jersey.client.urlconnection.HTTPSProperties; +import com.sun.jersey.oauth.client.OAuthClientFilter; +import com.sun.jersey.oauth.signature.OAuthParameters; +import com.sun.jersey.oauth.signature.OAuthSecrets; +import org.apache.commons.lang3.StringUtils; +import org.onap.ccsdk.sli.core.sli.SvcLogicContext; +import org.onap.ccsdk.sli.core.sli.SvcLogicException; +import org.onap.ccsdk.sli.core.sli.SvcLogicJavaPlugin; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.ws.rs.core.EntityTag; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.UriBuilder; import java.io.FileInputStream; import java.io.IOException; import java.net.SocketException; @@ -40,34 +65,8 @@ import java.util.Map.Entry; import java.util.Properties; import java.util.Set; -import javax.net.ssl.HostnameVerifier; -import javax.net.ssl.HttpsURLConnection; -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLSession; -import javax.ws.rs.core.EntityTag; -import javax.ws.rs.core.MultivaluedMap; -import javax.ws.rs.core.UriBuilder; - -import org.apache.commons.lang3.StringUtils; -import org.codehaus.jettison.json.JSONException; -import org.onap.ccsdk.sli.core.sli.SvcLogicContext; -import org.onap.ccsdk.sli.core.sli.SvcLogicException; -import org.onap.ccsdk.sli.core.sli.SvcLogicJavaPlugin; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.sun.jersey.api.client.Client; -import com.sun.jersey.api.client.ClientResponse; -import com.sun.jersey.api.client.WebResource; -import com.sun.jersey.api.client.config.ClientConfig; -import com.sun.jersey.api.client.config.DefaultClientConfig; -import com.sun.jersey.api.client.filter.HTTPBasicAuthFilter; -import com.sun.jersey.api.client.filter.HTTPDigestAuthFilter; -import com.sun.jersey.oauth.client.OAuthClientFilter; -import com.sun.jersey.oauth.signature.OAuthParameters; -import com.sun.jersey.oauth.signature.OAuthSecrets; -import com.sun.jersey.client.urlconnection.HTTPSProperties; +import static java.lang.Boolean.valueOf; +import static org.onap.ccsdk.sli.plugins.restapicall.AuthType.fromString; public class RestapiCallNode implements SvcLogicJavaPlugin { @@ -81,7 +80,7 @@ public class RestapiCallNode implements SvcLogicJavaPlugin { protected static final String DEFAULT_PROPERTIES_DIR = "/opt/onap/ccsdk/data/properties"; protected static final String PROPERTIES_DIR_KEY = "SDNC_CONFIG_DIR"; - protected RetryPolicyStore getRetryPolicyStore() { + public RetryPolicyStore getRetryPolicyStore() { return retryPolicyStore; } @@ -154,7 +153,7 @@ public class RestapiCallNode implements SvcLogicJavaPlugin { RetryPolicy retryPolicy = null; HttpResponse r = new HttpResponse(); try { - Parameters p = getParameters(paramMap); + Parameters p = getParameters(paramMap, new Parameters()); if (p.partner != null) { retryPolicy = retryPolicyStore.getRetryPolicy(p.partner); } @@ -245,49 +244,88 @@ public class RestapiCallNode implements SvcLogicJavaPlugin { throw new SvcLogicException(String.valueOf(r.code) + ": " + r.message); } - protected Parameters getParameters(Map<String, String> paramMap) throws SvcLogicException { - Parameters p = new Parameters(); - p.templateFileName = parseParam(paramMap, "templateFileName", false, null); + /** + * Returns parameters from the parameter map. + * + * @param paramMap parameter map + * @param p parameters instance + * @return parameters filed instance + * @throws SvcLogicException when svc logic exception occurs + */ + public static Parameters getParameters(Map<String, String> paramMap, + Parameters p) + throws SvcLogicException { + p.templateFileName = parseParam(paramMap, "templateFileName", + false, null); p.requestBody = parseParam(paramMap, "requestBody", false, null); p.restapiUrl = parseParam(paramMap, "restapiUrl", true, null); validateUrl(p.restapiUrl); p.restapiUser = parseParam(paramMap, "restapiUser", false, null); - p.restapiPassword = parseParam(paramMap, "restapiPassword", false, null); - p.oAuthConsumerKey = parseParam(paramMap, "oAuthConsumerKey", false, null); - p.oAuthConsumerSecret = parseParam(paramMap, "oAuthConsumerSecret", false, null); - p.oAuthSignatureMethod = parseParam(paramMap, "oAuthSignatureMethod", false, null); + p.restapiPassword = parseParam(paramMap, "restapiPassword", false, + null); + p.oAuthConsumerKey = parseParam(paramMap, "oAuthConsumerKey", + false, null); + p.oAuthConsumerSecret = parseParam(paramMap, "oAuthConsumerSecret", + false, null); + p.oAuthSignatureMethod = parseParam(paramMap, "oAuthSignatureMethod", + false, null); p.oAuthVersion = parseParam(paramMap, "oAuthVersion", false, null); p.contentType = parseParam(paramMap, "contentType", false, null); - p.format = Format.fromString(parseParam(paramMap, "format", false, "json")); - p.authtype = AuthType.fromString(parseParam(paramMap, "authType", false, "unspecified")); - p.httpMethod = HttpMethod.fromString(parseParam(paramMap, "httpMethod", false, "post")); + p.format = Format.fromString(parseParam(paramMap, "format", false, + "json")); + p.authtype = fromString(parseParam(paramMap, "authType", false, + "unspecified")); + p.httpMethod = HttpMethod.fromString(parseParam(paramMap, "httpMethod", + false, "post")); p.responsePrefix = parseParam(paramMap, "responsePrefix", false, null); p.listNameList = getListNameList(paramMap); String skipSendingStr = paramMap.get("skipSending"); p.skipSending = "true".equalsIgnoreCase(skipSendingStr); - p.convertResponse = Boolean.valueOf(parseParam(paramMap, "convertResponse", false, "true")); - p.trustStoreFileName = parseParam(paramMap, "trustStoreFileName", false, null); - p.trustStorePassword = parseParam(paramMap, "trustStorePassword", false, null); - p.keyStoreFileName = parseParam(paramMap, "keyStoreFileName", false, null); - p.keyStorePassword = parseParam(paramMap, "keyStorePassword", false, null); - p.ssl = p.trustStoreFileName != null && p.trustStorePassword != null && p.keyStoreFileName != null && - p.keyStorePassword != null; - p.customHttpHeaders = parseParam(paramMap, "customHttpHeaders", false, null); + p.convertResponse = valueOf(parseParam(paramMap, "convertResponse", + false, "true")); + p.trustStoreFileName = parseParam(paramMap, "trustStoreFileName", + false, null); + p.trustStorePassword = parseParam(paramMap, "trustStorePassword", + false, null); + p.keyStoreFileName = parseParam(paramMap, "keyStoreFileName", + false, null); + p.keyStorePassword = parseParam(paramMap, "keyStorePassword", + false, null); + p.ssl = p.trustStoreFileName != null && p.trustStorePassword != null + && p.keyStoreFileName != null && p.keyStorePassword != null; + p.customHttpHeaders = parseParam(paramMap, "customHttpHeaders", + false, null); p.partner = parseParam(paramMap, "partner", false, null); - p.dumpHeaders = Boolean.valueOf(parseParam(paramMap, "dumpHeaders", false, null)); - p.returnRequestPayload = Boolean.valueOf(parseParam(paramMap, "returnRequestPayload", false, null)); + p.dumpHeaders = valueOf(parseParam(paramMap, "dumpHeaders", + false, null)); + p.returnRequestPayload = valueOf(parseParam( + paramMap, "returnRequestPayload", false, null)); return p; } - private void validateUrl(String restapiUrl) throws SvcLogicException { + /** + * Validates the given URL in the parameters. + * + * @param restapiUrl rest api URL + * @throws SvcLogicException when URL validation fails + */ + private static void validateUrl(String restapiUrl) + throws SvcLogicException { try { URI.create(restapiUrl); } catch (IllegalArgumentException e) { - throw new SvcLogicException("Invalid input of url " + e.getLocalizedMessage(), e); + throw new SvcLogicException("Invalid input of url " + + e.getLocalizedMessage(), e); } } - protected Set<String> getListNameList(Map<String, String> paramMap) { + /** + * Returns the list of list name. + * + * @param paramMap parameters map + * @return list of list name + */ + private static Set<String> getListNameList(Map<String, String> paramMap) { Set<String> ll = new HashSet<>(); for (Map.Entry<String,String> entry : paramMap.entrySet()) if (entry.getKey().startsWith("listName")) @@ -295,7 +333,19 @@ public class RestapiCallNode implements SvcLogicJavaPlugin { return ll; } - protected String parseParam(Map<String, String> paramMap, String name, boolean required, String def) + /** + * Parses the parameter string map of property, validates if required, + * assigns default value if present and returns the value. + * + * @param paramMap string param map + * @param name name of the property + * @param required if value required + * @param def default value + * @return value of the property + * @throws SvcLogicException if required parameter value is empty + */ + public static String parseParam(Map<String, String> paramMap, String name, + boolean required, String def) throws SvcLogicException { String s = paramMap.get(name); @@ -524,7 +574,16 @@ public class RestapiCallNode implements SvcLogicJavaPlugin { return client; } - protected HttpResponse sendHttpRequest(String request, Parameters p) throws SvcLogicException { + /** + * Receives the http response for the http request sent. + * + * @param request request msg + * @param p parameters + * @return HTTP response + * @throws SvcLogicException when sending http request fails + */ + public HttpResponse sendHttpRequest(String request, Parameters p) + throws SvcLogicException { ClientConfig config = new DefaultClientConfig(); SSLContext ssl = null; @@ -693,7 +752,7 @@ public class RestapiCallNode implements SvcLogicJavaPlugin { p.oAuthVersion = parseParam(paramMap, "oAuthVersion", false, null); p.oAuthConsumerSecret = parseParam(paramMap, "oAuthConsumerSecret", false, null); p.oAuthSignatureMethod = parseParam(paramMap, "oAuthSignatureMethod", false, null); - p.authtype = AuthType.fromString(parseParam(paramMap, "authType", false, "unspecified")); + p.authtype = fromString(parseParam(paramMap, "authType", false, "unspecified")); return p; } diff --git a/restconf-client/provider/pom.xml b/restconf-client/provider/pom.xml index 4183d48b..09941264 100755 --- a/restconf-client/provider/pom.xml +++ b/restconf-client/provider/pom.xml @@ -114,8 +114,23 @@ <dependency> <groupId>org.opendaylight.netconf</groupId> <artifactId>restconf-nb-rfc8040</artifactId> - <version>1.7.4-SNAPSHOT</version> + <version>1.7.3</version> <scope>test</scope> </dependency> + <dependency> + <groupId>org.opendaylight.yangtools</groupId> + <artifactId>yang-parser-impl</artifactId> + <version>2.0.6.1</version> + </dependency> + <dependency> + <groupId>org.opendaylight.netconf</groupId> + <artifactId>restconf-common</artifactId> + <version>1.7.3</version> + </dependency> + <dependency> + <groupId>org.opendaylight.netconf</groupId> + <artifactId>restconf-nb-bierman02</artifactId> + <version>1.7.3</version> + </dependency> </dependencies> </project> diff --git a/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/restconfapicall/RestconfApiCallNode.java b/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/restconfapicall/RestconfApiCallNode.java new file mode 100644 index 00000000..c5f73305 --- /dev/null +++ b/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/restconfapicall/RestconfApiCallNode.java @@ -0,0 +1,362 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.ccsdk.sli.plugins.restconfapicall; + +import org.onap.ccsdk.sli.core.sli.SvcLogicContext; +import org.onap.ccsdk.sli.core.sli.SvcLogicException; +import org.onap.ccsdk.sli.core.sli.SvcLogicJavaPlugin; +import org.onap.ccsdk.sli.plugins.restapicall.HttpResponse; +import org.onap.ccsdk.sli.plugins.restapicall.RestapiCallNode; +import org.onap.ccsdk.sli.plugins.restapicall.RetryException; +import org.onap.ccsdk.sli.plugins.restapicall.RetryPolicy; +import org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.DataFormatSerializer; +import org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.DataFormatSerializerContext; +import org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.DfSerializerFactory; +import org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.Listener; +import org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.MdsalSerializerHelper; +import org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.SerializerHelper; +import org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.YangParameters; +import org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.MdsalPropertiesNodeSerializer; +import org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.PropertiesNodeSerializer; +import org.opendaylight.netconf.sal.restconf.impl.ControllerContext; +import org.opendaylight.restconf.common.context.InstanceIdentifierContext; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.ws.rs.core.UriBuilder; +import java.net.SocketException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static java.lang.String.format; +import static org.apache.commons.lang3.StringUtils.join; +import static org.onap.ccsdk.sli.plugins.restapicall.HttpMethod.POST; +import static org.onap.ccsdk.sli.plugins.restapicall.HttpMethod.PUT; +import static org.onap.ccsdk.sli.plugins.restapicall.RestapiCallNode.parseParam; +import static org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiUtils.ATTEMPTS_MSG; +import static org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiUtils.COMMA; +import static org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiUtils.COMM_FAIL; +import static org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiUtils.HEADER; +import static org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiUtils.HTTP_REQ; +import static org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiUtils.HTTP_RES; +import static org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiUtils.MAX_RETRY_ERR; +import static org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiUtils.NO_MORE_RETRY; +import static org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiUtils.REQ_ERR; +import static org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiUtils.REST_API_URL; +import static org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiUtils.RES_CODE; +import static org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiUtils.RES_MSG; +import static org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiUtils.RES_PRE; +import static org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiUtils.RETRY_COUNT; +import static org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiUtils.RETRY_FAIL; +import static org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiUtils.UPDATED_URL; +import static org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiUtils.getSchemaCtxFromDir; +import static org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiUtils.getYangParameters; +import static org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiUtils.parseUrl; +import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.DfListenerFactory.instance; +import static org.osgi.framework.FrameworkUtil.getBundle; + +/** + * Representation of a plugin to enable RESTCONF based CRUD operations from DG. + */ +public class RestconfApiCallNode implements SvcLogicJavaPlugin { + + /** + * Logger for the restconf api call node class. + */ + private static final Logger log = LoggerFactory.getLogger( + RestconfApiCallNode.class); + + /** + * Creates an instance of restconf api call node. + */ + public RestconfApiCallNode() { + } + + /** + * Sends the restconf request using the parameters map and the memory + * context. And this method allows the directed graphs to interact with + * the restconf api call node + * + * @param paramMap parameters map + * @param ctx service logic context + * @throws SvcLogicException when svc logic exception occurs + */ + public void sendRequest(Map<String, String> paramMap, SvcLogicContext ctx) + throws SvcLogicException { + sendRequest(paramMap, ctx, 0); + } + + /** + * Sends the restconf request using the parameters map and the memory + * context along with the retry count. + * + * @param paramMap parameters map + * @param ctx service logic context + * @param retryCount number of retry counts + * @throws SvcLogicException when svc logic exception occurs + */ + public void sendRequest(Map<String, String> paramMap, SvcLogicContext ctx, + Integer retryCount) throws SvcLogicException { + RestapiCallNode rest = new RestapiCallNode(); + RetryPolicy retryPolicy = null; + HttpResponse r = new HttpResponse(); + try { + YangParameters p = getYangParameters(paramMap); + if (p.partner != null) { + retryPolicy = rest.getRetryPolicyStore() + .getRetryPolicy(p.partner); + } + + String pp = p.responsePrefix != null ? p.responsePrefix + '.' : ""; + Map<String, String> props = new HashMap<>((Map)ctx.toProperties()); + String uri = parseUrl(p.restapiUrl, p.httpMethod); + InstanceIdentifierContext<?> insIdCtx = getInsIdCtx(p, uri); + + String req = null; + if (p.httpMethod == POST || p.httpMethod == PUT) { + req = serializeRequest(props, p, uri, insIdCtx); + } + if (req == null && p.requestBody != null) { + req = p.requestBody; + } + + r = rest.sendHttpRequest(req, p); + if (p.returnRequestPayload && req != null) { + ctx.setAttribute(pp + HTTP_REQ, req); + } + + String response = getResponse(ctx, p, pp, r); + if (response != null) { + Map<String, String> resProp = serializeResponse( + p, uri, response, insIdCtx); + for (Map.Entry<String, String> pro : resProp.entrySet()) { + ctx.setAttribute(pro.getKey(), pro.getValue()); + } + } + } catch (SvcLogicException e) { + boolean shouldRetry = false; + if (e.getCause().getCause() instanceof SocketException) { + shouldRetry = true; + } + + log.error(REQ_ERR + e.getMessage(), e); + String prefix = parseParam(paramMap, RES_PRE, false, null); + if (retryPolicy == null || shouldRetry == false) { + setFailureResponseStatus(ctx, prefix, e.getMessage(), r); + } else { + if (retryCount == null) { + retryCount = 0; + } + log.debug(format(ATTEMPTS_MSG, retryCount, + retryPolicy.getMaximumRetries())); + try { + retryCount = retryCount + 1; + if (retryCount < retryPolicy.getMaximumRetries() + 1) { + setRetryUri(paramMap, retryPolicy); + log.debug(format(RETRY_COUNT, retryCount, retryPolicy + .getMaximumRetries())); + sendRequest(paramMap, ctx, retryCount); + } else { + log.debug(MAX_RETRY_ERR); + setFailureResponseStatus(ctx, prefix, + e.getMessage(), r); + } + } catch (Exception ex) { + log.error(NO_MORE_RETRY, ex); + setFailureResponseStatus(ctx, prefix, RETRY_FAIL, r); + } + } + } + + if (r != null && r.code >= 300) { + throw new SvcLogicException( + String.valueOf(r.code) + ": " + r.message); + } + } + + /** + * Serializes the request message to JSON or XML from the properties. + * + * @param properties properties + * @param params YANG parameters + * @param uri URI + * @param insIdCtx instance identifier context + * @return JSON or XML message to be sent + * @throws SvcLogicException when serializing the request fails + */ + protected String serializeRequest(Map<String, String> properties, + YangParameters params, String uri, + InstanceIdentifierContext insIdCtx) + throws SvcLogicException { + PropertiesNodeSerializer propSer = new MdsalPropertiesNodeSerializer( + insIdCtx.getSchemaNode(), insIdCtx.getSchemaContext(), uri); + DataFormatSerializerContext serCtx = new DataFormatSerializerContext( + null, uri, null, propSer); + DataFormatSerializer ser = DfSerializerFactory.instance() + .getSerializer(serCtx, params); + //TODO: Handling of XML annotations + return ser.encode(properties, null); + } + + /** + * Serializes the response message from JSON or XML to the properties. + * + * @param params YANG parameters + * @param uri URI + * @param response response message + * @param insIdCtx instance identifier context + * @return response message as properties + * @throws SvcLogicException when serializing the response fails + */ + protected Map<String, String> serializeResponse(YangParameters params, + String uri, String response, + InstanceIdentifierContext insIdCtx) + throws SvcLogicException { + PropertiesNodeSerializer propSer = new MdsalPropertiesNodeSerializer( + insIdCtx.getSchemaNode(), insIdCtx.getSchemaContext(), uri); + SerializerHelper helper = new MdsalSerializerHelper( + insIdCtx.getSchemaNode(), insIdCtx.getSchemaContext(), uri); + Listener listener = instance().getListener(helper, params); + DataFormatSerializerContext serCtx = new DataFormatSerializerContext( + listener, uri, null, propSer); + DataFormatSerializer ser = DfSerializerFactory.instance() + .getSerializer(serCtx, params); + return ser.decode(response); + } + + /** + * Returns instance identifier context for a uri using the schema context. + * + * @param params YANG parameters + * @param uri URI + * @return instance identifier context + * @throws SvcLogicException when getting schema context fails + */ + private InstanceIdentifierContext<?> getInsIdCtx(YangParameters params, + String uri) + throws SvcLogicException { + SchemaContext context = getSchemaContext(params); + ControllerContext contCtx = ControllerContext.getInstance(); + contCtx.onGlobalContextUpdated(context); + return contCtx.toInstanceIdentifier(uri); + } + + /** + * Returns the global schema context or schema context of particular YANG + * files present in a directory path. + * + * @param params YANG parameters + * @return schema context + * @throws SvcLogicException when schema context fetching fails + */ + private SchemaContext getSchemaContext(YangParameters params) + throws SvcLogicException { + if (params.dirPath != null) { + return getSchemaCtxFromDir(params.dirPath); + } + BundleContext bc = getBundle(SchemaContext.class).getBundleContext(); + SchemaContext schemaContext = null; + if (bc != null) { + ServiceReference reference = bc.getServiceReference( + SchemaContext.class); + if (reference != null) { + schemaContext = (SchemaContext) bc.getService(reference); + } + } + return schemaContext; + } + + /** + * Returns the response message body of a http response message. + * + * @param ctx svc logic context + * @param params parameters + * @param pre prefix to be appended + * @param res http response + * @return response message body + */ + private String getResponse(SvcLogicContext ctx, YangParameters params, + String pre, HttpResponse res) { + ctx.setAttribute(pre + RES_CODE, String.valueOf(res.code)); + ctx.setAttribute(pre + RES_MSG, res.message); + + if (params.dumpHeaders && res.headers != null) { + for (Map.Entry<String, List<String>> a : res.headers.entrySet()) { + ctx.setAttribute(pre + HEADER + a.getKey(), + join(a.getValue(), COMMA)); + } + } + + if (res.body != null && res.body.trim().length() > 0) { + ctx.setAttribute(pre + HTTP_RES, res.body); + return res.body; + } + return null; + } + + /** + * Sets the failure response status in the context memory. + * + * @param ctx service logic context + * @param prefix prefix to be added + * @param errMsg error message + * @param res http response + */ + private void setFailureResponseStatus(SvcLogicContext ctx, String prefix, + String errMsg, HttpResponse res) { + res = new HttpResponse(); + res.code = 500; + res.message = errMsg; + ctx.setAttribute(prefix + RES_CODE, String.valueOf(res.code)); + ctx.setAttribute(prefix + RES_MSG, res.message); + } + + /** + * Sets the retry URI to the param map from the retry policies different + * host. + * + * @param paramMap parameter map + * @param retryPolicy retry policy + * @throws URISyntaxException when new URI creation fails + * @throws RetryException when retry policy cannot give another host + */ + private void setRetryUri(Map<String, String> paramMap, + RetryPolicy retryPolicy) + throws URISyntaxException, RetryException { + URI uri = new URI(paramMap.get(REST_API_URL)); + String hostName = uri.getHost(); + String retryString = retryPolicy.getNextHostName(uri.toString()); + + URI uriTwo = new URI(retryString); + URI retryUri = UriBuilder.fromUri(uri).host(uriTwo.getHost()).port( + uriTwo.getPort()).scheme(uriTwo.getScheme()).build(); + + paramMap.put(REST_API_URL, retryUri.toString()); + log.debug(UPDATED_URL + retryUri.toString()); + log.debug(format(COMM_FAIL, hostName, retryString)); + } +} diff --git a/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/restconfapicall/RestconfApiUtils.java b/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/restconfapicall/RestconfApiUtils.java new file mode 100644 index 00000000..e066671b --- /dev/null +++ b/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/restconfapicall/RestconfApiUtils.java @@ -0,0 +1,213 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.ccsdk.sli.plugins.restconfapicall; + +import org.onap.ccsdk.sli.core.sli.SvcLogicException; +import org.onap.ccsdk.sli.plugins.restapicall.HttpMethod; +import org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.YangParameters; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException; +import org.opendaylight.yangtools.yang.parser.rfc7950.repo.YangStatementStreamSource; +import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException; +import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import static org.onap.ccsdk.sli.plugins.restapicall.HttpMethod.PUT; +import static org.onap.ccsdk.sli.plugins.restapicall.RestapiCallNode.getParameters; +import static org.onap.ccsdk.sli.plugins.restapicall.RestapiCallNode.parseParam; +import static org.opendaylight.yangtools.yang.model.repo.api.StatementParserMode.DEFAULT_MODE; +import static org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource.forFile; +import static org.opendaylight.yangtools.yang.parser.rfc7950.reactor.RFC7950Reactors.defaultReactor; +import static org.opendaylight.yangtools.yang.parser.rfc7950.repo.YangStatementStreamSource.create; + +/** + * Utilities for restconf api call node. + */ +public final class RestconfApiUtils { + + static final String RES_CODE = "response-code"; + + static final String HTTP_REQ ="httpRequest"; + + static final String RES_PRE = "responsePrefix"; + + static final String RES_MSG = "response-message"; + + static final String HEADER = "header."; + + static final String COMMA = ","; + + static final String HTTP_RES = "httpResponse"; + + static final String REST_API_URL = "restapiUrl"; + + static final String UPDATED_URL = "URL was set to"; + + static final String COMM_FAIL = "Failed to communicate with host %s." + + "Request will be re-attempted using the host %s."; + + static final String RETRY_COUNT = "This is retry attempt %d out of %d"; + + static final String RETRY_FAIL = "Retry attempt has failed. No further " + + "retry shall be attempted, calling setFailureResponseStatus"; + + static final String NO_MORE_RETRY = "Could not attempt retry"; + + static final String MAX_RETRY_ERR = "Maximum retries reached, calling " + + "setFailureResponseStatus"; + + static final String ATTEMPTS_MSG = "%d attempts were made out of %d " + + "maximum retries"; + + static final String REQ_ERR = "Error sending the request: "; + + private static final String SLASH = "/"; + + private static final String DIR_PATH = "dirPath"; + + private static final String URL_SYNTAX = "The following URL cannot be " + + "parsed into URI : "; + + private static final String RESTCONF_PATH = "/restconf/operations/"; + + private static final String PUT_NODE_ERR = "The following URL does not " + + "contain minimum two nodes for PUT operation."; + + private static final String YANG = ".yang"; + + private static final String YANG_FILE_ERR = "Unable to parse the YANG " + + "file provided"; + + //No instantiation. + private RestconfApiUtils() { + } + + /** + * Returns the YANG parameters after parsing it from the map. + * + * @param paramMap parameters map + * @return YANG parameters + * @throws SvcLogicException when parsing of parameters map fail + */ + static YangParameters getYangParameters(Map<String, String> paramMap) + throws SvcLogicException { + YangParameters param = (YangParameters) getParameters( + paramMap, new YangParameters()); + param.dirPath = parseParam(paramMap, DIR_PATH, false, null); + return param; + } + + /** + * Parses the restconf URL and gives the YANG path from it, which can be + * used to get schema node. If it is a PUT operation, then a node must be + * reduced from the url to make it always point to the parent. + * + * @param url restconf URL + * @param method HTTP operation + * @return YANG path pointing to parent + * @throws SvcLogicException when parsing the URL fails + */ + static String parseUrl(String url, HttpMethod method) + throws SvcLogicException { + URI uri; + try { + uri = new URI(url); + } catch (URISyntaxException e) { + throw new SvcLogicException(URL_SYNTAX + url, e); + } + + String path = uri.getPath(); + if (path.contains(RESTCONF_PATH)) { + path = path.replaceFirst(RESTCONF_PATH, ""); + } + if (method == PUT) { + if (!path.contains(SLASH)) { + throw new SvcLogicException(PUT_NODE_ERR + url); + } + path = path.substring(0, path.lastIndexOf(SLASH)); + } + return path; + } + + /** + * Returns the schema context of the YANG files present in a directory. + * + * @param di directory path + * @return YANG schema context + * @throws SvcLogicException when YANG file reading fails + */ + static SchemaContext getSchemaCtxFromDir(String di) + throws SvcLogicException { + Path d = Paths.get(di); + File dir = d.toFile(); + List<File> yangFiles = new LinkedList<>(); + getYangFiles(dir, yangFiles); + final Collection<YangStatementStreamSource> sources = + new ArrayList<>(yangFiles.size()); + for (File file : yangFiles) { + try { + sources.add(create(forFile(file))); + } catch (IOException | YangSyntaxErrorException e) { + throw new SvcLogicException(YANG_FILE_ERR + e.getMessage(), e); + } + } + + final CrossSourceStatementReactor.BuildAction reactor = defaultReactor() + .newBuild(DEFAULT_MODE).addSources(sources); + try { + return reactor.buildEffective(); + } catch (ReactorException e) { + throw new SvcLogicException(YANG_FILE_ERR + e.getMessage(), e); + } + } + + /** + * Returns all the YANG files present in a directory recursively. + * + * @param dir path of the directory + * @param yangFiles list of YANG files + */ + private static void getYangFiles(File dir, List<File> yangFiles) { + if (dir.exists()) { + File[] files = dir.listFiles(); + if (files != null) { + for (File file : files) { + if (file.isFile() && file.getName().endsWith(YANG)) { + yangFiles.add(file); + } else if (file.isDirectory()) { + getYangFiles(file, yangFiles); + } + } + } + } + } +} diff --git a/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/restconfapicall/RestconfapiCallNode.java b/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/restconfapicall/RestconfapiCallNode.java deleted file mode 100644 index 87cb92bc..00000000 --- a/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/restconfapicall/RestconfapiCallNode.java +++ /dev/null @@ -1,65 +0,0 @@ -/*- - * ============LICENSE_START======================================================= - * ONAP - CCSDK - * ================================================================================ - * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. - * ================================================================================ - * 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. - * ============LICENSE_END========================================================= - */ - -package org.onap.ccsdk.sli.plugins.restconfapicall; - -import java.util.Map; -import org.onap.ccsdk.sli.core.sli.SvcLogicContext; -import org.onap.ccsdk.sli.core.sli.SvcLogicException; -import org.onap.ccsdk.sli.core.sli.SvcLogicJavaPlugin; - -/** - * Representation of a plugin to enable RESTCONF based CRUD operations from DG. - */ -public class RestconfapiCallNode implements SvcLogicJavaPlugin { - - public RestconfapiCallNode() { - } - - /** - * Allows Directed Graphs the ability to interact with RESTCONF APIs. - * @param parameters HashMap<String,String> of parameters passed by the DG to this function - * <table border="1"> - * <thead><th>parameter</th><th>Mandatory/Optional</th><th>description</th><th>example values</th></thead> - * <tbody> - * <tr><td>templateFileName</td><td>Optional</td><td>full path to YANG directory that can be used to build a request</td><td>/sdncopt/bvc/resconfapi/test</td></tr> - * <tr><td>restapiUrl</td><td>Mandatory</td><td>url to send the request to</td><td>https://sdncodl:8543/restconf/operations/L3VNF-API:create-update-vnf-request</td></tr> - * <tr><td>restapiUser</td><td>Optional</td><td>user name to use for http basic authentication</td><td>sdnc_ws</td></tr> - * <tr><td>restapiPassword</td><td>Optional</td><td>unencrypted password to use for http basic authentication</td><td>plain_password</td></tr> - * <tr><td>contentType</td><td>Optional</td><td>http content type to set in the http header</td><td>usually application/json or application/xml</td></tr> - * <tr><td>format</td><td>Optional</td><td>should match request body format</td><td>json or xml</td></tr> - * <tr><td>httpMethod</td><td>Optional</td><td>http method to use when sending the request</td><td>get post put delete patch</td></tr> - * <tr><td>responsePrefix</td><td>Optional</td><td>location the response will be written to in context memory</td><td>tmp.resconftapi.result</td></tr> - * <tr><td>skipSending</td><td>Optional</td><td></td><td>true or false</td></tr> - * <tr><td>convertResponse </td><td>Optional</td><td>whether the response should be converted</td><td>true or false</td></tr> - * <tr><td>customHttpHeaders</td><td>Optional</td><td>a list additional http headers to be passed in, follow the format in the example</td><td>X-CSI-MessageId=messageId,headerFieldName=headerFieldValue</td></tr> - * <tr><td>dumpHeaders</td><td>Optional</td><td>when true writes http header content to context memory</td><td>true or false</td></tr> - * </tbody> - * </table> - * @param ctx Reference to context memory - * @throws SvcLogicException - * @since 11.0.2 - * @see String#split(String, int) - */ - public void sendRequest(Map<String, String> paramMap, SvcLogicContext ctx) throws SvcLogicException { - //TODO - } - -} diff --git a/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/restconfdiscovery/RestconfDiscoveryNode.java b/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/restconfdiscovery/RestconfDiscoveryNode.java index 34bb2ee6..0490b3a5 100644 --- a/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/restconfdiscovery/RestconfDiscoveryNode.java +++ b/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/restconfdiscovery/RestconfDiscoveryNode.java @@ -24,7 +24,7 @@ import org.glassfish.jersey.media.sse.EventSource; import org.glassfish.jersey.media.sse.SseFeature; import org.onap.ccsdk.sli.core.sli.SvcLogicContext; import org.onap.ccsdk.sli.core.sli.SvcLogicException; -import org.onap.ccsdk.sli.plugins.restconfapicall.RestconfapiCallNode; +import org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiCallNode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -46,7 +46,7 @@ public class RestconfDiscoveryNode implements SvcLogicDiscoveryPlugin { private ExecutorService executor = Executors.newCachedThreadPool(); private Map<String, PersistentConnection> runnableInfo = new ConcurrentHashMap<>(); - private RestconfapiCallNode restconfapiCallNode; + private RestconfApiCallNode restconfApiCallNode; private volatile Map<String, SubscriptionInfo> subscriptionInfoMap = new ConcurrentHashMap<>(); private volatile LinkedBlockingQueue<String> eventQueue = new LinkedBlockingQueue<>(); @@ -78,7 +78,7 @@ public class RestconfDiscoveryNode implements SvcLogicDiscoveryPlugin { throw new SvcLogicException("Subscriber Id is null"); } - restconfapiCallNode.sendRequest(paramMap, ctx); + restconfApiCallNode.sendRequest(paramMap, ctx); if (getResponseCode(paramMap.get(RESPONSE_PREFIX), ctx).equals(RESPONSE_CODE_200)) { // TODO: save subscription id and subscriber in MYSQL @@ -207,8 +207,8 @@ public class RestconfDiscoveryNode implements SvcLogicDiscoveryPlugin { * * @return restconfApiCallNode */ - protected RestconfapiCallNode restconfapiCallNode() { - return restconfapiCallNode; + protected RestconfApiCallNode restconfapiCallNode() { + return restconfApiCallNode; } /** @@ -216,8 +216,8 @@ public class RestconfDiscoveryNode implements SvcLogicDiscoveryPlugin { * * @param node restconfApiCallNode */ - void restconfapiCallNode(RestconfapiCallNode node) { - restconfapiCallNode = node; + void restconfapiCallNode(RestconfApiCallNode node) { + restconfApiCallNode = node; } Map<String, SubscriptionInfo> subscriptionInfoMap() { diff --git a/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/MdsalSerializerHelper.java b/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/MdsalSerializerHelper.java index a3e30c4b..5a898dfc 100644 --- a/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/MdsalSerializerHelper.java +++ b/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/MdsalSerializerHelper.java @@ -76,8 +76,8 @@ public class MdsalSerializerHelper extends SerializerHelper<SchemaNode, SchemaCo * @param c schema context * @param u URI of the request */ - protected MdsalSerializerHelper(SchemaNode n, SchemaContext c, - String u) { + public MdsalSerializerHelper(SchemaNode n, SchemaContext c, + String u) { super(n, c, u); Namespace ns = new Namespace(n.getQName().getLocalName(), n.getQName().getNamespace(), diff --git a/restconf-client/provider/src/main/resources/META-INF/spring/restconf-client-context.xml b/restconf-client/provider/src/main/resources/META-INF/spring/restconf-client-context.xml index 34344fe0..f99f4f84 100644 --- a/restconf-client/provider/src/main/resources/META-INF/spring/restconf-client-context.xml +++ b/restconf-client/provider/src/main/resources/META-INF/spring/restconf-client-context.xml @@ -37,12 +37,12 @@ <!-- context:property-placeholder location="file:${SDNC_CONFIG_DIR}/ueb.properties" /--> - <bean id="restconfapiCallNode" class="org.onap.ccsdk.sli.plugins.restconfapicall.RestconfapiCallNode"> + <bean id="restconfApiCallNode" class="org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiCallNode"> <property name="uebServers" value="${servers}" /> </bean> <bean id="restconfDiscoveryNode" class="org.onap.ccsdk.sli.plugins.restconfdiscovery.RestconfDiscoveryNode"> - <property name="restconfapiCallNode" ref="restconfapiCallNode" /> + <property name="restconfApiCallNode" ref="restconfApiCallNode" /> </bean> </beans> diff --git a/restconf-client/provider/src/main/resources/META-INF/spring/restconf-client-osgi-context.xml b/restconf-client/provider/src/main/resources/META-INF/spring/restconf-client-osgi-context.xml index d56e2fc1..5a9f22be 100644 --- a/restconf-client/provider/src/main/resources/META-INF/spring/restconf-client-osgi-context.xml +++ b/restconf-client/provider/src/main/resources/META-INF/spring/restconf-client-osgi-context.xml @@ -26,7 +26,7 @@ http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> - <service ref="restconfapiCallNode" interface="org.onap.ccsdk.sli.plugins.restconfapicall.RestconfapiCallNode" /> + <service ref="restconfApiCallNode" interface="org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiCallNode" /> <service ref="restconfDiscoveryNode" interface="org.onap.ccsdk.sli.plugins.restconfdiscovery.RestconfDiscoveryNode" /> </beans:beans> diff --git a/restconf-client/provider/src/main/resources/org/opendaylight/blueprint/restconf-client-blueprint.xml b/restconf-client/provider/src/main/resources/org/opendaylight/blueprint/restconf-client-blueprint.xml index 149ed9fc..91c31efc 100755 --- a/restconf-client/provider/src/main/resources/org/opendaylight/blueprint/restconf-client-blueprint.xml +++ b/restconf-client/provider/src/main/resources/org/opendaylight/blueprint/restconf-client-blueprint.xml @@ -23,12 +23,12 @@ xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0" odl:use-default-for-reference-types="true"> - <bean id="restconfapiCallNodeProvider" class="org.onap.ccsdk.sli.plugins.restconfapicall.RestconfapiCallNode" /> + <bean id="restconfapiCallNodeProvider" class="org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiCallNode" /> <bean id="restconfDiscoveryNodeProvider" class="org.onap.ccsdk.sli.plugins.restconfdiscovery.RestconfDiscoveryNode" /> <service ref="restconfapiCallNodeProvider"> <interfaces> - <value>org.onap.ccsdk.sli.plugins.restconfapicall.RestconfapiCallNode</value> + <value>org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiCallNode</value> </interfaces> </service> |