From c8a20b7cdc2b563e1ea86a9e25f4ac0e748995f9 Mon Sep 17 00:00:00 2001 From: janani b Date: Wed, 29 Aug 2018 11:52:41 +0530 Subject: [PATCH] Implementation for Restconf api call node Restconf Api Call Node Issue-ID: CCSDK-372 Change-Id: I1820e97d54e734ccf73c6f48a9afcc4b59dff549 Signed-off-by: janani b --- .../sli/plugins/restapicall/RestapiCallNode.java | 171 ++++++---- restconf-client/provider/pom.xml | 17 +- .../restconfapicall/RestconfApiCallNode.java | 362 +++++++++++++++++++++ .../plugins/restconfapicall/RestconfApiUtils.java | 213 ++++++++++++ .../restconfapicall/RestconfapiCallNode.java | 65 ---- .../restconfdiscovery/RestconfDiscoveryNode.java | 14 +- .../dfserializer/MdsalSerializerHelper.java | 4 +- .../META-INF/spring/restconf-client-context.xml | 4 +- .../spring/restconf-client-osgi-context.xml | 2 +- .../blueprint/restconf-client-blueprint.xml | 4 +- 10 files changed, 720 insertions(+), 136 deletions(-) create mode 100644 restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/restconfapicall/RestconfApiCallNode.java create mode 100644 restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/restconfapicall/RestconfApiUtils.java delete mode 100644 restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/restconfapicall/RestconfapiCallNode.java 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 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 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 getListNameList(Map paramMap) { + /** + * Returns the list of list name. + * + * @param paramMap parameters map + * @return list of list name + */ + private static Set getListNameList(Map paramMap) { Set ll = new HashSet<>(); for (Map.Entry entry : paramMap.entrySet()) if (entry.getKey().startsWith("listName")) @@ -295,7 +333,19 @@ public class RestapiCallNode implements SvcLogicJavaPlugin { return ll; } - protected String parseParam(Map 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 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 @@ org.opendaylight.netconf restconf-nb-rfc8040 - 1.7.4-SNAPSHOT + 1.7.3 test + + org.opendaylight.yangtools + yang-parser-impl + 2.0.6.1 + + + org.opendaylight.netconf + restconf-common + 1.7.3 + + + org.opendaylight.netconf + restconf-nb-bierman02 + 1.7.3 + 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 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 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 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 resProp = serializeResponse( + p, uri, response, insIdCtx); + for (Map.Entry 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 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 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> 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 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 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 yangFiles = new LinkedList<>(); + getYangFiles(dir, yangFiles); + final Collection 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 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 of parameters passed by the DG to this function - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
parameterMandatory/Optionaldescriptionexample values
templateFileNameOptionalfull path to YANG directory that can be used to build a request/sdncopt/bvc/resconfapi/test
restapiUrlMandatoryurl to send the request tohttps://sdncodl:8543/restconf/operations/L3VNF-API:create-update-vnf-request
restapiUserOptionaluser name to use for http basic authenticationsdnc_ws
restapiPasswordOptionalunencrypted password to use for http basic authenticationplain_password
contentTypeOptionalhttp content type to set in the http headerusually application/json or application/xml
formatOptionalshould match request body formatjson or xml
httpMethodOptionalhttp method to use when sending the requestget post put delete patch
responsePrefixOptionallocation the response will be written to in context memorytmp.resconftapi.result
skipSendingOptionaltrue or false
convertResponse Optionalwhether the response should be convertedtrue or false
customHttpHeadersOptionala list additional http headers to be passed in, follow the format in the exampleX-CSI-MessageId=messageId,headerFieldName=headerFieldValue
dumpHeadersOptionalwhen true writes http header content to context memorytrue or false
- * @param ctx Reference to context memory - * @throws SvcLogicException - * @since 11.0.2 - * @see String#split(String, int) - */ - public void sendRequest(Map 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 runnableInfo = new ConcurrentHashMap<>(); - private RestconfapiCallNode restconfapiCallNode; + private RestconfApiCallNode restconfApiCallNode; private volatile Map subscriptionInfoMap = new ConcurrentHashMap<>(); private volatile LinkedBlockingQueue 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 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 - + - + 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"> - + 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"> - + - org.onap.ccsdk.sli.plugins.restconfapicall.RestconfapiCallNode + org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiCallNode -- 2.16.6