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 {
81 // use default port if null
82 if (params.get(SS_AGENT_PORT_KEY) == null)
84 return params.get(SS_AGENT_PORT_KEY);
88 * Method that validates that the Map has enough information
89 * to query Saltstack server for a result. If so, it returns
90 * the appropriate HOST name.
92 public String reqHostNameResult(Map<String, String> params) throws SvcLogicException {
94 throwIfMissingMandatoryParam(params, SS_AGENT_HOSTNAME_KEY);
95 return params.get(SS_AGENT_HOSTNAME_KEY);
99 * Method that validates that the Map has enough information
100 * to query Saltstack server for a result. If so, it returns
101 * the appropriate request ID.
103 public String reqId(Map<String, String> params) {
105 if (params.get(SaltstackMessageParser.SS_REQ_ID) == null) {
106 return UUID.randomUUID().toString();
107 } else if (params.get(SaltstackMessageParser.SS_REQ_ID).equalsIgnoreCase("")) {
108 return UUID.randomUUID().toString();
110 return params.get(SaltstackMessageParser.SS_REQ_ID);
114 * Method that validates that the Map has enough information
115 * to query Saltstack server for a result. If so, it returns
116 * the appropriate command to execute.
118 public String reqCmd(Map<String, String> params) throws SvcLogicException {
120 throwIfMissingMandatoryParam(params, CMD_EXEC);
121 return params.get(SaltstackMessageParser.CMD_EXEC);
125 * Method that validates that the Map has enough information
126 * to query Saltstack server for a result. If so, it returns
127 * the appropriate SLS file location to execute.
129 public String reqSlsFile(Map<String, String> params) throws SvcLogicException {
131 throwIfMissingMandatoryParam(params, SLS_FILE_LOCATION);
132 return params.get(SaltstackMessageParser.SLS_FILE_LOCATION);
136 * Method that validates that the Map has enough information
137 * to query Saltstack server for a result. If so, it returns
138 * the appropriate SLS file location to execute.
140 public String reqSlsName(Map<String, String> params) throws SvcLogicException {
142 throwIfMissingMandatoryParam(params, SLS_NAME);
143 String slsName = params.get(SaltstackMessageParser.SLS_NAME);
145 if (slsName.substring(slsName.lastIndexOf("."), slsName.length()).equalsIgnoreCase(".sls")) {
146 return stripExtension(slsName);
148 } catch (StringIndexOutOfBoundsException e) {
154 private String stripExtension(String str) {
158 int pos = str.lastIndexOf(".");
162 return str.substring(0, pos);
166 * Method that validates that the Map has enough information
167 * to query Saltstack server for a result. If so, it returns
168 * the appropriate minions/vnfc to execute the SLS file.
170 public String reqApplyToDevices(Map<String, String> params) {
172 if (params.get(SaltstackMessageParser.MINION_TO_APPLY) == null) {
174 } else if (params.get(SaltstackMessageParser.MINION_TO_APPLY).equalsIgnoreCase("")) {
177 return params.get(SaltstackMessageParser.MINION_TO_APPLY);
181 * Method that validates that the Map has enough information
182 * to query Saltstack server for a result. If so, it returns
183 * the appropriate minions/vnfc to execute the SLS file.
185 public long reqExecTimeout(Map<String, String> params) {
187 if (params.get(SaltstackMessageParser.EXEC_TIMEOUT_TO_APPLY) == null) {
189 } else if (params.get(SaltstackMessageParser.EXEC_TIMEOUT_TO_APPLY).equalsIgnoreCase("")) {
192 return Long.parseLong(params.get(SaltstackMessageParser.EXEC_TIMEOUT_TO_APPLY));
196 * Method that validates that the Map has enough information
197 * to query Saltstack server for a result. If so, it returns
198 * the appropriate EnvParameters to execute the SLS file.
200 public JSONObject reqEnvParameters(Map<String, String> params) throws JSONException {
202 JSONObject jsonPayload = new JSONObject();
203 final String[] optionalTestParam = { SaltstackMessageParser.ENV_PARAMETERS_OPT_KEY };
204 parseParam(params, optionalTestParam, jsonPayload);
206 return (JSONObject) jsonPayload.remove(SaltstackMessageParser.ENV_PARAMETERS_OPT_KEY);
210 * Method that validates that the Map has enough information
211 * to query Saltstack server for a result. If so, it returns
212 * the appropriate EnvParameters to execute the SLS file.
214 public JSONObject reqFileParameters(Map<String, String> params) throws JSONException {
216 JSONObject jsonPayload = new JSONObject();
217 final String[] optionalTestParam = { SaltstackMessageParser.FILE_PARAMETERS_OPT_KEY };
218 parseParam(params, optionalTestParam, jsonPayload);
220 return (JSONObject) jsonPayload.remove(SaltstackMessageParser.FILE_PARAMETERS_OPT_KEY);
223 private void parseParam(Map<String, String> params, String[] optionalTestParams, JSONObject jsonPayload)
224 throws JSONException {
226 Set<String> optionalParamsSet = new HashSet<>();
227 Collections.addAll(optionalParamsSet, optionalTestParams);
232 .filter(entry -> optionalParamsSet.contains(entry.getKey()))
233 .filter(entry -> !Strings.isNullOrEmpty(entry.getValue()))
234 .forEach(entry -> parseParam(entry, jsonPayload));
238 private void parseParam(Map.Entry<String, String> params, JSONObject jsonPayload)
239 throws JSONException {
240 String key = params.getKey();
241 String payload = params.getValue();
244 case ENV_PARAMETERS_OPT_KEY:
245 JSONObject paramsJson = new JSONObject(payload);
246 jsonPayload.put(key, paramsJson);
249 case FILE_PARAMETERS_OPT_KEY:
250 jsonPayload.put(key, getFilePayload(payload));
259 * Return payload with escaped newlines
261 private JSONObject getFilePayload(String payload) {
262 String formattedPayload = payload.replace("\n", "\\n").replace("\r", "\\r");
263 return new JSONObject(formattedPayload);
267 * Method that validates that the Map has enough information
268 * to query Saltstack server for a result. If so, it returns
269 * the appropriate IsSLSExec true or false.
271 public boolean reqIsSLSExec(Map<String, String> params) throws SvcLogicException {
273 final String[] mandatoryTestParams = {CMD_EXEC, IS_SLS_EXEC};
275 for (String key : mandatoryTestParams) {
276 throwIfMissingMandatoryParam(params, key);
279 return params.get(SaltstackMessageParser.IS_SLS_EXEC).equalsIgnoreCase("true");
283 * Method that validates that the Map has enough information
284 * to query Saltstack server for a result. If so, it returns
285 * the appropriate Saltstack server login user name.
287 public String reqUserNameResult(Map<String, String> params) throws SvcLogicException {
289 throwIfMissingMandatoryParam(params, USER_KEY);
290 return params.get(USER_KEY);
294 * Method that validates that the Map has enough information
295 * to query Saltstack server for a result. If so, it returns
296 * the appropriate Saltstack server login password.
298 public String reqPasswordResult(Map<String, String> params) throws SvcLogicException {
300 throwIfMissingMandatoryParam(params, PASS_KEY);
301 return params.get(PASS_KEY);
305 * This method parses response from the Saltstack Server when we do a post
306 * and returns an SaltstackResult object.
308 public SaltstackResult parseResponse(SvcLogicContext ctx, String pfx,
309 SaltstackResult saltstackResult, boolean slsExec) throws IOException {
310 int code = saltstackResult.getStatusCode();
311 InputStream in = null;
312 boolean executionStatus = true, retCodeFound = false;
313 if (code != SaltstackResultCodes.SUCCESS.getValue()) {
314 return saltstackResult;
317 File file = new File(saltstackResult.getOutputFileName());
318 in = new FileInputStream(file);
319 byte[] data = new byte[(int) file.length()];
321 String str = new String(data, "UTF-8");
323 Map<String, String> mm = JsonParser.convertToProperties(str);
325 for (Map.Entry<String, String> entry : mm.entrySet()) {
326 if (entry.getKey().contains("retcode")) {
328 if (!entry.getValue().equalsIgnoreCase("0")) {
329 executionStatus = false;
332 ctx.setAttribute(pfx + "." + entry.getKey(), entry.getValue());
333 LOGGER.info("+++ " + pfx + "." + entry.getKey() + ": [" + entry.getValue() + "]");
336 } catch (FileNotFoundException e) {
337 return new SaltstackResult(SaltstackResultCodes.INVALID_RESPONSE_FILE.getValue(), "error parsing response file "
338 + saltstackResult.getOutputFileName() + " : " + e.getMessage());
339 } catch (org.codehaus.jettison.json.JSONException e) {
340 LOGGER.info("Output not in JSON format");
341 return putToProperties(ctx, pfx, saltstackResult);
342 } catch (Exception e) {
343 return new SaltstackResult(SaltstackResultCodes.INVALID_RESPONSE_FILE.getValue(), "error parsing response file "
344 + saltstackResult.getOutputFileName() + " : " + e.getMessage());
352 return new SaltstackResult(SaltstackResultCodes.COMMAND_EXEC_FAILED_STATUS.getValue(),
353 "error in executing configuration at the server, check your command input");
355 if (!executionStatus) {
356 return new SaltstackResult(SaltstackResultCodes.COMMAND_EXEC_FAILED_STATUS.getValue(),
357 "error in executing configuration at the server, check your command input");
360 saltstackResult.setStatusCode(SaltstackResultCodes.FINAL_SUCCESS.getValue());
361 return saltstackResult;
364 public SaltstackResult putToProperties(SvcLogicContext ctx, String pfx,
365 SaltstackResult saltstackResult) throws IOException {
366 InputStream in = null;
368 File file = new File(saltstackResult.getOutputFileName());
369 in = new FileInputStream(file);
370 Properties prop = new Properties();
372 ctx.setAttribute(pfx + "completeResult", prop.toString());
373 for (Object key : prop.keySet()) {
374 String name = (String) key;
375 String value = prop.getProperty(name);
376 if (value != null && value.trim().length() > 0) {
377 ctx.setAttribute(pfx + "." + name, value.trim());
378 LOGGER.info("+++ " + pfx + "." + name + ": [" + value + "]");
381 } catch (Exception e) {
382 saltstackResult = new SaltstackResult(SaltstackResultCodes.INVALID_RESPONSE_FILE.getValue(), "Error parsing response file = "
383 + saltstackResult.getOutputFileName() + ". Error = " + e.getMessage());
389 saltstackResult.setStatusCode(SaltstackResultCodes.FINAL_SUCCESS.getValue());
390 return saltstackResult;
393 private void throwIfMissingMandatoryParam(Map<String, String> params, String key) throws SvcLogicException {
394 if (!params.containsKey(key)) {
395 throw new SvcLogicException(String.format(
396 "Saltstack: Mandatory SaltstackAdapter key %s not found in parameters provided by calling agent !",
399 if (Strings.isNullOrEmpty(params.get(key))) {
400 throw new SvcLogicException(String.format(
401 "Saltstack: Mandatory SaltstackAdapter key %s not found in parameters provided by calling agent !",