2  * ============LICENSE_START=======================================================
 
   4  * ================================================================================
 
   5  * Copyright (C) 2017-2018 Samsung Electronics. All rights reserved.
 
   6  * ================================================================================
 
   8  * =============================================================================
 
   9  * Licensed under the Apache License, Version 2.0 (the "License");
 
  10  * you may not use this file except in compliance with the License.
 
  11  * You may obtain a copy of the License at
 
  13  *      http://www.apache.org/licenses/LICENSE-2.0
 
  15  * Unless required by applicable law or agreed to in writing, software
 
  16  * distributed under the License is distributed on an "AS IS" BASIS,
 
  17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 
  18  * See the License for the specific language governing permissions and
 
  19  * limitations under the License.
 
  22  * ============LICENSE_END=========================================================
 
  25 package org.onap.ccsdk.sli.adaptors.saltstack.model;
 
  28  * This module implements the APP-C/Saltstack Server interface
 
  29  * based on the REST API specifications
 
  32 import com.google.common.base.Strings;
 
  33 import org.json.JSONException;
 
  34 import org.json.JSONArray;
 
  35 import org.json.JSONObject;
 
  36 import org.onap.ccsdk.sli.core.sli.SvcLogicContext;
 
  37 import org.onap.ccsdk.sli.core.sli.SvcLogicException;
 
  38 import org.slf4j.Logger;
 
  39 import org.slf4j.LoggerFactory;
 
  42 import java.io.FileInputStream;
 
  43 import java.io.FileNotFoundException;
 
  44 import java.io.IOException;
 
  45 import java.io.InputStream;
 
  46 import java.util.Collections;
 
  47 import java.util.HashSet;
 
  49 import java.util.Properties;
 
  51 import java.util.UUID;
 
  54  * Class that validates and constructs requests sent/received from
 
  57 public class SaltstackMessageParser {
 
  59     private static final String SS_AGENT_HOSTNAME_KEY = "HostName";
 
  60     private static final String SS_AGENT_PORT_KEY = "Port";
 
  61     private static final String PASS_KEY = "Password";
 
  62     private static final String USER_KEY = "User";
 
  63     private static final String CMD_EXEC = "Cmd"; //cmd
 
  64     private static final String IS_SLS_EXEC = "SlsExec"; //slsExec
 
  65     private static final String SS_REQ_ID = "Id";
 
  66     private static final String SLS_FILE_LOCATION = "SlsFile"; //slsFile
 
  67     private static final String SLS_NAME = "SlsName"; //slsName
 
  68     private static final String MINION_TO_APPLY = "NodeList"; //applyTo
 
  69     private static final String EXEC_TIMEOUT_TO_APPLY = "Timeout"; //execTimeout
 
  70     private static final String FILE_PARAMETERS_OPT_KEY = "FileParameters";
 
  71     private static final String ENV_PARAMETERS_OPT_KEY = "EnvParameters";
 
  73     private static final Logger LOGGER = LoggerFactory.getLogger(SaltstackMessageParser.class);
 
  76      * Method that validates that the Map has enough information
 
  77      * to query Saltstack server for a result. If so, it returns
 
  78      * the appropriate PORT number.
 
  80     public String reqPortResult(Map<String, String> params) throws SvcLogicException {
 
  82         final String[] mandatoryTestParams = {SS_AGENT_HOSTNAME_KEY, SS_AGENT_PORT_KEY, USER_KEY,
 
  85         for (String key : mandatoryTestParams) {
 
  86             throwIfMissingMandatoryParam(params, key);
 
  88         return params.get(SS_AGENT_PORT_KEY);
 
  92      * Method that validates that the Map has enough information
 
  93      * to query Saltstack server for a result. If so, it returns
 
  94      * the appropriate HOST name.
 
  96     public String reqHostNameResult(Map<String, String> params) throws SvcLogicException {
 
  98         final String[] mandatoryTestParams = {SS_AGENT_HOSTNAME_KEY, SS_AGENT_PORT_KEY, USER_KEY,
 
 101         for (String key : mandatoryTestParams) {
 
 102             throwIfMissingMandatoryParam(params, key);
 
 104         return params.get(SS_AGENT_HOSTNAME_KEY);
 
 108      * Method that validates that the Map has enough information
 
 109      * to query Saltstack server for a result. If so, it returns
 
 110      * the appropriate request ID.
 
 112     public String reqId(Map<String, String> params) {
 
 114         if (params.get(SaltstackMessageParser.SS_REQ_ID) == null) {
 
 115             return UUID.randomUUID().toString();
 
 116         } else if (params.get(SaltstackMessageParser.SS_REQ_ID).equalsIgnoreCase("")) {
 
 117             return UUID.randomUUID().toString();
 
 119         return params.get(SaltstackMessageParser.SS_REQ_ID);
 
 123      * Method that validates that the Map has enough information
 
 124      * to query Saltstack server for a result. If so, it returns
 
 125      * the appropriate command to execute.
 
 127     public String reqCmd(Map<String, String> params) throws SvcLogicException {
 
 129         final String[] mandatoryTestParams = {CMD_EXEC, IS_SLS_EXEC};
 
 131         for (String key : mandatoryTestParams) {
 
 132             throwIfMissingMandatoryParam(params, key);
 
 135         return params.get(SaltstackMessageParser.CMD_EXEC);
 
 139      * Method that validates that the Map has enough information
 
 140      * to query Saltstack server for a result. If so, it returns
 
 141      * the appropriate SLS file location to execute.
 
 143     public String reqSlsFile(Map<String, String> params) throws SvcLogicException {
 
 145         final String[] mandatoryTestParams = {SLS_FILE_LOCATION};
 
 147         for (String key : mandatoryTestParams) {
 
 148             throwIfMissingMandatoryParam(params, key);
 
 151         return params.get(SaltstackMessageParser.SLS_FILE_LOCATION);
 
 155      * Method that validates that the Map has enough information
 
 156      * to query Saltstack server for a result. If so, it returns
 
 157      * the appropriate SLS file location to execute.
 
 159     public String reqSlsName(Map<String, String> params) throws SvcLogicException {
 
 161         final String[] mandatoryTestParams = {SLS_NAME};
 
 163         for (String key : mandatoryTestParams) {
 
 164             throwIfMissingMandatoryParam(params, key);
 
 166         String slsName = params.get(SaltstackMessageParser.SLS_NAME);
 
 168             if (slsName.substring(slsName.lastIndexOf("."), slsName.length()).equalsIgnoreCase(".sls")) {
 
 169                 return stripExtension(slsName);
 
 171         } catch (StringIndexOutOfBoundsException e) {
 
 177     private String stripExtension(String str) {
 
 181         int pos = str.lastIndexOf(".");
 
 185         return str.substring(0, pos);
 
 189      * Method that validates that the Map has enough information
 
 190      * to query Saltstack server for a result. If so, it returns
 
 191      * the appropriate minions/vnfc to execute the SLS file.
 
 193     public String reqApplyToDevices(Map<String, String> params) {
 
 195         if (params.get(SaltstackMessageParser.MINION_TO_APPLY) == null) {
 
 197         } else if (params.get(SaltstackMessageParser.MINION_TO_APPLY).equalsIgnoreCase("")) {
 
 200         return params.get(SaltstackMessageParser.MINION_TO_APPLY);
 
 204      * Method that validates that the Map has enough information
 
 205      * to query Saltstack server for a result. If so, it returns
 
 206      * the appropriate minions/vnfc to execute the SLS file.
 
 208     public long reqExecTimeout(Map<String, String> params) {
 
 210         if (params.get(SaltstackMessageParser.EXEC_TIMEOUT_TO_APPLY) == null) {
 
 212         } else if (params.get(SaltstackMessageParser.EXEC_TIMEOUT_TO_APPLY).equalsIgnoreCase("")) {
 
 215         return Long.parseLong(params.get(SaltstackMessageParser.EXEC_TIMEOUT_TO_APPLY));
 
 219      * Method that validates that the Map has enough information
 
 220      * to query Saltstack server for a result. If so, it returns
 
 221      * the appropriate EnvParameters to execute the SLS file.
 
 223     public JSONObject reqEnvParameters(Map<String, String> params) throws JSONException {
 
 225         JSONObject jsonPayload = new JSONObject();
 
 226         final String[] optionalTestParam = { SaltstackMessageParser.ENV_PARAMETERS_OPT_KEY };
 
 227         parseParam(params, optionalTestParam, jsonPayload);
 
 229         return (JSONObject) jsonPayload.remove(SaltstackMessageParser.ENV_PARAMETERS_OPT_KEY);
 
 233      * Method that validates that the Map has enough information
 
 234      * to query Saltstack server for a result. If so, it returns
 
 235      * the appropriate EnvParameters to execute the SLS file.
 
 237     public JSONObject reqFileParameters(Map<String, String> params) throws JSONException {
 
 239         JSONObject jsonPayload = new JSONObject();
 
 240         final String[] optionalTestParam = { SaltstackMessageParser.FILE_PARAMETERS_OPT_KEY };
 
 241         parseParam(params, optionalTestParam, jsonPayload);
 
 243         return (JSONObject) jsonPayload.remove(SaltstackMessageParser.FILE_PARAMETERS_OPT_KEY);
 
 246     private void parseParam(Map<String, String> params, String[] optionalTestParams, JSONObject jsonPayload)
 
 247             throws JSONException {
 
 249         Set<String> optionalParamsSet = new HashSet<>();
 
 250         Collections.addAll(optionalParamsSet, optionalTestParams);
 
 255                 .filter(entry -> optionalParamsSet.contains(entry.getKey()))
 
 256                 .filter(entry -> !Strings.isNullOrEmpty(entry.getValue()))
 
 257                 .forEach(entry -> parseParam(entry, jsonPayload));
 
 261     private void parseParam(Map.Entry<String, String> params, JSONObject jsonPayload)
 
 262             throws JSONException {
 
 263         String key = params.getKey();
 
 264         String payload = params.getValue();
 
 267             case ENV_PARAMETERS_OPT_KEY:
 
 268                 JSONObject paramsJson = new JSONObject(payload);
 
 269                 jsonPayload.put(key, paramsJson);
 
 272             case FILE_PARAMETERS_OPT_KEY:
 
 273                 jsonPayload.put(key, getFilePayload(payload));
 
 282      * Return payload with escaped newlines
 
 284     private JSONObject getFilePayload(String payload) {
 
 285         String formattedPayload = payload.replace("\n", "\\n").replace("\r", "\\r");
 
 286         return new JSONObject(formattedPayload);
 
 290      * Method that validates that the Map has enough information
 
 291      * to query Saltstack server for a result. If so, it returns
 
 292      * the appropriate IsSLSExec true or false.
 
 294     public boolean reqIsSLSExec(Map<String, String> params) throws SvcLogicException {
 
 296         final String[] mandatoryTestParams = {CMD_EXEC, IS_SLS_EXEC};
 
 298         for (String key : mandatoryTestParams) {
 
 299             throwIfMissingMandatoryParam(params, key);
 
 302         return params.get(SaltstackMessageParser.IS_SLS_EXEC).equalsIgnoreCase("true");
 
 306      * Method that validates that the Map has enough information
 
 307      * to query Saltstack server for a result. If so, it returns
 
 308      * the appropriate Saltstack server login user name.
 
 310     public String reqUserNameResult(Map<String, String> params) throws SvcLogicException {
 
 312         final String[] mandatoryTestParams = {SS_AGENT_HOSTNAME_KEY, SS_AGENT_PORT_KEY, USER_KEY,
 
 315         for (String key : mandatoryTestParams) {
 
 316             throwIfMissingMandatoryParam(params, key);
 
 318         return params.get(USER_KEY);
 
 322      * Method that validates that the Map has enough information
 
 323      * to query Saltstack server for a result. If so, it returns
 
 324      * the appropriate Saltstack server login password.
 
 326     public String reqPasswordResult(Map<String, String> params) throws SvcLogicException {
 
 328         final String[] mandatoryTestParams = {SS_AGENT_HOSTNAME_KEY, SS_AGENT_PORT_KEY, USER_KEY,
 
 331         for (String key : mandatoryTestParams) {
 
 332             throwIfMissingMandatoryParam(params, key);
 
 334         return params.get(PASS_KEY);
 
 338      * This method parses response from the Saltstack Server when we do a post
 
 339      * and returns an SaltstackResult object.
 
 341     public SaltstackResult parseResponse(SvcLogicContext ctx, String pfx,
 
 342                                          SaltstackResult saltstackResult, boolean slsExec) throws IOException {
 
 343         int code = saltstackResult.getStatusCode();
 
 344         InputStream in = null;
 
 345         boolean executionStatus = true, retCodeFound = false;
 
 346         if (code != SaltstackResultCodes.SUCCESS.getValue()) {
 
 347             return saltstackResult;
 
 350             File file = new File(saltstackResult.getOutputFileName());
 
 351             in = new FileInputStream(file);
 
 352             byte[] data = new byte[(int) file.length()];
 
 354             String str = new String(data, "UTF-8");
 
 356             Map<String, String> mm = JsonParser.convertToProperties(str);
 
 358                 for (Map.Entry<String, String> entry : mm.entrySet()) {
 
 359                     if (entry.getKey().contains("retcode")) {
 
 361                         if (!entry.getValue().equalsIgnoreCase("0")) {
 
 362                             executionStatus = false;
 
 365                     ctx.setAttribute(pfx + "." + entry.getKey(), entry.getValue());
 
 366                     LOGGER.info("+++ " + pfx + "." + entry.getKey() + ": [" + entry.getValue() + "]");
 
 369         } catch (FileNotFoundException e) {
 
 370             return new SaltstackResult(SaltstackResultCodes.INVALID_RESPONSE_FILE.getValue(), "error parsing response file "
 
 371                     + saltstackResult.getOutputFileName() + " : " + e.getMessage());
 
 372         } catch (org.codehaus.jettison.json.JSONException e) {
 
 373             LOGGER.info("Output not in JSON format");
 
 374             return putToProperties(ctx, pfx, saltstackResult);
 
 375         } catch (Exception e) {
 
 376             return new SaltstackResult(SaltstackResultCodes.INVALID_RESPONSE_FILE.getValue(), "error parsing response file "
 
 377                     + saltstackResult.getOutputFileName() + " : " + e.getMessage());
 
 385                 return new SaltstackResult(SaltstackResultCodes.COMMAND_EXEC_FAILED_STATUS.getValue(),
 
 386                                            "error in executing configuration at the server, check your command input");
 
 388             if (!executionStatus) {
 
 389                 return new SaltstackResult(SaltstackResultCodes.COMMAND_EXEC_FAILED_STATUS.getValue(),
 
 390                                            "error in executing configuration at the server, check your command input");
 
 393         saltstackResult.setStatusCode(SaltstackResultCodes.FINAL_SUCCESS.getValue());
 
 394         return saltstackResult;
 
 397     public SaltstackResult putToProperties(SvcLogicContext ctx, String pfx,
 
 398                                            SaltstackResult saltstackResult) throws IOException {
 
 399         InputStream in = null;
 
 401             File file = new File(saltstackResult.getOutputFileName());
 
 402             in = new FileInputStream(file);
 
 403             Properties prop = new Properties();
 
 405             ctx.setAttribute(pfx + "completeResult", prop.toString());
 
 406             for (Object key : prop.keySet()) {
 
 407                 String name = (String) key;
 
 408                 String value = prop.getProperty(name);
 
 409                 if (value != null && value.trim().length() > 0) {
 
 410                     ctx.setAttribute(pfx + "." + name, value.trim());
 
 411                     LOGGER.info("+++ " + pfx + "." + name + ": [" + value + "]");
 
 414         } catch (Exception e) {
 
 415             saltstackResult = new SaltstackResult(SaltstackResultCodes.INVALID_RESPONSE_FILE.getValue(), "Error parsing response file = "
 
 416                     + saltstackResult.getOutputFileName() + ". Error = " + e.getMessage());
 
 422         saltstackResult.setStatusCode(SaltstackResultCodes.FINAL_SUCCESS.getValue());
 
 423         return saltstackResult;
 
 426     private void throwIfMissingMandatoryParam(Map<String, String> params, String key) throws SvcLogicException {
 
 427         if (!params.containsKey(key)) {
 
 428             throw new SvcLogicException(String.format(
 
 429                     "Saltstack: Mandatory SaltstackAdapter key %s not found in parameters provided by calling agent !",
 
 432         if (Strings.isNullOrEmpty(params.get(key))) {
 
 433             throw new SvcLogicException(String.format(
 
 434                     "Saltstack: Mandatory SaltstackAdapter key %s not found in parameters provided by calling agent !",