+ private static Object maskPassword(String name, Object value) {
+ String[] pwdNames = {"pwd", "passwd", "password", "Pwd", "Passwd", "Password"};
+ for (String pwdName : pwdNames) {
+ if (name.contains(pwdName)) {
+ return "**********";
+ }
+ }
+ return value;
+ }
+
+ /**
+ * Allows Directed Graphs the ability to interact with REST APIs.
+ *
+ * @param paramMap 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 template file that can be used to build a request</td>
+ * <td>/sdncopt/bvc/restapi/templates/vnf_service-configuration-operation_minimal.json</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>oAuthConsumerKey</td>
+ * <td>Optional</td>
+ * <td>Consumer key to use for http oAuth authentication</td>
+ * <td>plain_key</td>
+ * </tr>
+ * <tr>
+ * <td>oAuthConsumerSecret</td>
+ * <td>Optional</td>
+ * <td>Consumer secret to use for http oAuth authentication</td>
+ * <td>plain_secret</td>
+ * </tr>
+ * <tr>
+ * <td>oAuthSignatureMethod</td>
+ * <td>Optional</td>
+ * <td>Consumer method to use for http oAuth authentication</td>
+ * <td>method</td>
+ * </tr>
+ * <tr>
+ * <td>oAuthVersion</td>
+ * <td>Optional</td>
+ * <td>Version http oAuth authentication</td>
+ * <td>version</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.restapi.result</td>
+ * </tr>
+ * <tr>
+ * <td>listName[i]</td>
+ * <td>Optional</td>
+ * <td>Used for processing XML responses with repeating
+ * elements.</td>vpn-information.vrf-details
+ * <td></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>
+ * <tr>
+ * <td>partner</td>
+ * <td>Optional</td>
+ * <td>used to retrieve username, password and url if partner store exists</td>
+ * <td>aaf</td>
+ * </tr>
+ * <tr>
+ * <td>returnRequestPayload</td>
+ * <td>Optional</td>
+ * <td>used to return payload built in the request</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 {
+ sendRequest(paramMap, ctx, null);
+ }
+
+ protected void sendRequest(Map<String, String> paramMap, SvcLogicContext ctx, RetryPolicy retryPolicy)
+ throws SvcLogicException {
+
+ HttpResponse r = new HttpResponse();
+ try {
+ handlePartner(paramMap);
+ Parameters p = getParameters(paramMap, new Parameters());
+ if (p.restapiUrl.contains(",") && retryPolicy == null) {
+ String[] urls = p.restapiUrl.split(",");
+ retryPolicy = new RetryPolicy(urls, urls.length * 2);
+ p.restapiUrl = urls[0];
+ }
+ String pp = p.responsePrefix != null ? p.responsePrefix + '.' : "";
+
+ String req = null;
+ if (p.templateFileName != null) {
+ String reqTemplate = readFile(p.templateFileName);
+ req = buildXmlJsonRequest(ctx, reqTemplate, p.format);
+ } else if (p.requestBody != null) {
+ req = p.requestBody;
+ }
+ r = sendHttpRequest(req, p);
+ setResponseStatus(ctx, p.responsePrefix, r);
+
+ if (p.dumpHeaders && r.headers != null) {
+ for (Entry<String, List<String>> a : r.headers.entrySet()) {
+ ctx.setAttribute(pp + "header." + a.getKey(), StringUtils.join(a.getValue(), ","));
+ }
+ }
+
+ if (p.returnRequestPayload && req != null) {
+ ctx.setAttribute(pp + "httpRequest", req);
+ }
+
+ if (r.body != null && r.body.trim().length() > 0) {
+ ctx.setAttribute(pp + "httpResponse", r.body);
+
+ if (p.convertResponse) {
+ Map<String, String> mm = null;
+ if (p.format == Format.XML) {
+ mm = XmlParser.convertToProperties(r.body, p.listNameList);
+ } else if (p.format == Format.JSON) {
+ mm = JsonParser.convertToProperties(r.body);
+ }
+
+ if (mm != null) {
+ for (Map.Entry<String, String> entry : mm.entrySet()) {
+ ctx.setAttribute(pp + entry.getKey(), entry.getValue());
+ }
+ }
+ }
+ }
+ } catch (SvcLogicException e) {
+ boolean shouldRetry = false;
+ if (e.getCause().getCause() instanceof SocketException) {
+ shouldRetry = true;
+ }
+
+ log.error("Error sending the request: " + e.getMessage(), e);
+ String prefix = parseParam(paramMap, responsePrefix, false, null);
+ if (retryPolicy == null || !shouldRetry) {
+ setFailureResponseStatus(ctx, prefix, e.getMessage(), r);
+ } else {
+ log.debug(retryPolicy.getRetryMessage());
+ try {
+ // calling getNextHostName increments the retry count so it should be called before shouldRetry
+ String retryString = retryPolicy.getNextHostName();
+ if (retryPolicy.shouldRetry()) {
+ paramMap.put(restapiUrlString, retryString);
+ log.debug("retry attempt {} will use the retry url {}", retryPolicy.getRetryCount(),
+ retryString);
+ sendRequest(paramMap, ctx, retryPolicy);
+ } else {
+ log.debug("Maximum retries reached, won't attempt to retry. Calling setFailureResponseStatus.");
+ setFailureResponseStatus(ctx, prefix, e.getMessage(), r);
+ }
+ } catch (Exception ex) {
+ String retryErrorMessage = "Retry attempt " + retryPolicy.getRetryCount()
+ + "has failed with error message " + ex.getMessage();
+ setFailureResponseStatus(ctx, prefix, retryErrorMessage, r);
+ }
+ }
+ }
+
+ if (r != null && r.code >= 300) {
+ throw new SvcLogicException(String.valueOf(r.code) + ": " + r.message);
+ }
+ }
+
+ protected void handlePartner(Map<String, String> paramMap) {
+ String partner = paramMap.get("partner");
+ if (partner != null && partner.length() > 0) {
+ PartnerDetails details = partnerStore.get(partner);
+ paramMap.put(restapiUserKey, details.username);
+ paramMap.put(restapiPasswordKey, details.password);
+ if (paramMap.get(restapiUrlString) == null) {
+ paramMap.put(restapiUrlString, details.url);
+ }
+ }
+ }
+
+ protected String buildXmlJsonRequest(SvcLogicContext ctx, String template, Format format) throws SvcLogicException {