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.JSONObject;
35 import org.onap.ccsdk.sli.core.sli.SvcLogicContext;
36 import org.onap.ccsdk.sli.core.sli.SvcLogicException;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
40 import java.io.ByteArrayInputStream;
41 import java.io.ByteArrayOutputStream;
42 import java.io.IOException;
43 import java.io.InputStream;
44 import java.util.Collections;
45 import java.util.HashSet;
47 import java.util.Properties;
49 import java.util.UUID;
52 * Class that validates and constructs requests sent/received from
55 public class SaltstackMessageParser {
57 private static final String SS_AGENT_HOSTNAME_KEY = "HostName";
58 private static final String SS_AGENT_PORT_KEY = "Port";
59 private static final String PASS_KEY = "Password";
60 private static final String USER_KEY = "User";
61 private static final String CMD_EXEC = "Cmd"; //cmd
62 private static final String IS_SLS_EXEC = "SlsExec"; //slsExec
63 private static final String SS_REQ_ID = "Id";
64 private static final String SLS_FILE_LOCATION = "SlsFile"; //slsFile
65 private static final String SLS_NAME = "SlsName"; //slsName
66 private static final String MINION_TO_APPLY = "NodeList"; //applyTo
67 private static final String EXEC_TIMEOUT_TO_APPLY = "Timeout"; //execTimeout
68 private static final String FILE_PARAMETERS_OPT_KEY = "FileParameters";
69 private static final String ENV_PARAMETERS_OPT_KEY = "EnvParameters";
71 private static final Logger LOGGER = LoggerFactory.getLogger(SaltstackMessageParser.class);
74 * Method that validates that the Map has enough information
75 * to query Saltstack server for a result. If so, it returns
76 * the appropriate PORT number.
78 public String reqPortResult(Map<String, String> params) throws SvcLogicException {
79 // use default port if null
80 if (params.get(SS_AGENT_PORT_KEY) == null) {
83 return params.get(SS_AGENT_PORT_KEY);
87 * Method that validates that the Map has enough information
88 * to query Saltstack server for a result. If so, it returns
89 * the appropriate HOST name.
91 public String reqHostNameResult(Map<String, String> params) throws SvcLogicException {
93 throwIfMissingMandatoryParam(params, SS_AGENT_HOSTNAME_KEY);
94 return params.get(SS_AGENT_HOSTNAME_KEY);
98 * Method that validates that the Map has enough information
99 * to query Saltstack server for a result. If so, it returns
100 * the appropriate request ID.
102 public String reqId(Map<String, String> params) {
104 if (params.get(SaltstackMessageParser.SS_REQ_ID) == null) {
105 return UUID.randomUUID().toString();
106 } else if (params.get(SaltstackMessageParser.SS_REQ_ID).equalsIgnoreCase("")) {
107 return UUID.randomUUID().toString();
109 return params.get(SaltstackMessageParser.SS_REQ_ID);
113 * Method that validates that the Map has enough information
114 * to query Saltstack server for a result. If so, it returns
115 * the appropriate command to execute.
117 public String reqCmd(Map<String, String> params) throws SvcLogicException {
119 throwIfMissingMandatoryParam(params, CMD_EXEC);
120 return params.get(SaltstackMessageParser.CMD_EXEC);
124 * Method that validates that the Map has enough information
125 * to query Saltstack server for a result. If so, it returns
126 * the appropriate SLS file location to execute.
128 public String reqSlsFile(Map<String, String> params) throws SvcLogicException {
130 throwIfMissingMandatoryParam(params, SLS_FILE_LOCATION);
131 return params.get(SaltstackMessageParser.SLS_FILE_LOCATION);
135 * Method that validates that the Map has enough information
136 * to query Saltstack server for a result. If so, it returns
137 * the appropriate SLS file location to execute.
139 public String reqSlsName(Map<String, String> params) throws SvcLogicException {
141 throwIfMissingMandatoryParam(params, SLS_NAME);
142 String slsName = params.get(SaltstackMessageParser.SLS_NAME);
144 if (slsName.substring(slsName.lastIndexOf("."), slsName.length()).equalsIgnoreCase(".sls")) {
145 return stripExtension(slsName);
147 } catch (StringIndexOutOfBoundsException e) {
153 private String stripExtension(String str) {
157 int pos = str.lastIndexOf(".");
161 return str.substring(0, pos);
165 * Method that validates that the Map has enough information
166 * to query Saltstack server for a result. If so, it returns
167 * the appropriate minions/vnfc to execute the SLS file.
169 public String reqApplyToDevices(Map<String, String> params) {
171 if (params.get(SaltstackMessageParser.MINION_TO_APPLY) == null) {
173 } else if (params.get(SaltstackMessageParser.MINION_TO_APPLY).equalsIgnoreCase("")) {
176 return params.get(SaltstackMessageParser.MINION_TO_APPLY);
180 * Method that validates that the Map has enough information
181 * to query Saltstack server for a result. If so, it returns
182 * the appropriate minions/vnfc to execute the SLS file.
184 public long reqExecTimeout(Map<String, String> params) {
186 if (params.get(SaltstackMessageParser.EXEC_TIMEOUT_TO_APPLY) == null) {
188 } else if (params.get(SaltstackMessageParser.EXEC_TIMEOUT_TO_APPLY).equalsIgnoreCase("")) {
191 return Long.parseLong(params.get(SaltstackMessageParser.EXEC_TIMEOUT_TO_APPLY));
195 * Method that validates that the Map has enough information
196 * to query Saltstack server for a result. If so, it returns
197 * the appropriate EnvParameters to execute the SLS file.
199 public JSONObject reqEnvParameters(Map<String, String> params) throws JSONException {
201 JSONObject jsonPayload = new JSONObject();
202 final String[] optionalTestParam = {SaltstackMessageParser.ENV_PARAMETERS_OPT_KEY};
203 parseParam(params, optionalTestParam, jsonPayload);
205 return (JSONObject) jsonPayload.remove(SaltstackMessageParser.ENV_PARAMETERS_OPT_KEY);
209 * Method that validates that the Map has enough information
210 * to query Saltstack server for a result. If so, it returns
211 * the appropriate EnvParameters to execute the SLS file.
213 public JSONObject reqFileParameters(Map<String, String> params) throws JSONException {
215 JSONObject jsonPayload = new JSONObject();
216 final String[] optionalTestParam = {SaltstackMessageParser.FILE_PARAMETERS_OPT_KEY};
217 parseParam(params, optionalTestParam, jsonPayload);
219 return (JSONObject) jsonPayload.remove(SaltstackMessageParser.FILE_PARAMETERS_OPT_KEY);
222 private void parseParam(Map<String, String> params, String[] optionalTestParams, JSONObject jsonPayload)
223 throws JSONException {
225 Set<String> optionalParamsSet = new HashSet<>();
226 Collections.addAll(optionalParamsSet, optionalTestParams);
231 .filter(entry -> optionalParamsSet.contains(entry.getKey()))
232 .filter(entry -> !Strings.isNullOrEmpty(entry.getValue()))
233 .forEach(entry -> parseParam(entry, jsonPayload));
237 private void parseParam(Map.Entry<String, String> params, JSONObject jsonPayload)
238 throws JSONException {
239 String key = params.getKey();
240 String payload = params.getValue();
243 case ENV_PARAMETERS_OPT_KEY:
244 JSONObject paramsJson = new JSONObject(payload);
245 jsonPayload.put(key, paramsJson);
248 case FILE_PARAMETERS_OPT_KEY:
249 jsonPayload.put(key, getFilePayload(payload));
258 * Return payload with escaped newlines
260 private JSONObject getFilePayload(String payload) {
261 String formattedPayload = payload.replace("\n", "\\n").replace("\r", "\\r");
262 return new JSONObject(formattedPayload);
266 * Method that validates that the Map has enough information
267 * to query Saltstack server for a result. If so, it returns
268 * the appropriate IsSLSExec true or false.
270 public boolean reqIsSLSExec(Map<String, String> params) throws SvcLogicException {
272 final String[] mandatoryTestParams = {CMD_EXEC, IS_SLS_EXEC};
274 for (String key : mandatoryTestParams) {
275 throwIfMissingMandatoryParam(params, key);
278 return params.get(SaltstackMessageParser.IS_SLS_EXEC).equalsIgnoreCase("true");
282 * Method that validates that the Map has enough information
283 * to query Saltstack server for a result. If so, it returns
284 * the appropriate Saltstack server login user name.
286 public String reqUserNameResult(Map<String, String> params) throws SvcLogicException {
288 throwIfMissingMandatoryParam(params, USER_KEY);
289 return params.get(USER_KEY);
293 * Method that validates that the Map has enough information
294 * to query Saltstack server for a result. If so, it returns
295 * the appropriate Saltstack server login password.
297 public String reqPasswordResult(Map<String, String> params) throws SvcLogicException {
299 throwIfMissingMandatoryParam(params, PASS_KEY);
300 return params.get(PASS_KEY);
304 * This method parses response from the Saltstack Server when we do a post
305 * and returns an SaltstackResult object.
307 public SaltstackResult parseResponse(SvcLogicContext ctx, String pfx,
308 SaltstackResult saltstackResult, boolean slsExec) throws IOException {
309 int code = saltstackResult.getStatusCode();
310 boolean executionStatus = true, retCodeFound = false;
311 if (code != SaltstackResultCodes.SUCCESS.getValue()) {
312 return saltstackResult;
314 ByteArrayOutputStream outStream = saltstackResult.getOutputMessage();
315 String outMessage = outStream.toString();
317 Map<String, String> mm = JsonParser.convertToProperties(outMessage);
319 for (Map.Entry<String, String> entry : mm.entrySet()) {
320 if (entry.getKey().contains("retcode")) {
322 if (!entry.getValue().equalsIgnoreCase("0")) {
323 executionStatus = false;
326 ctx.setAttribute(pfx + "." + entry.getKey(), entry.getValue());
327 LOGGER.info("+++ " + pfx + "." + entry.getKey() + ": [" + entry.getValue() + "]");
330 } catch (org.codehaus.jettison.json.JSONException e) {
332 return new SaltstackResult(SaltstackResultCodes.INVALID_RESPONSE.getValue(), "error parsing response file"
333 + " : Output has to be in JSON format");
335 LOGGER.info("Output not in JSON format");
336 return putToProperties(ctx, pfx, saltstackResult);
337 } catch (Exception e) {
338 return new SaltstackResult(SaltstackResultCodes.INVALID_RESPONSE_FILE.getValue(), "error parsing response file"
339 + " : " + e.getMessage());
341 if (outStream != null) {
347 if (outMessage != null && !outMessage.equalsIgnoreCase("")) {
348 return new SaltstackResult(SaltstackResultCodes.COMMAND_EXEC_FAILED_STATUS.getValue(),
351 return new SaltstackResult(SaltstackResultCodes.COMMAND_EXEC_FAILED_STATUS.getValue(),
352 "error in executing configuration at the server, check your command input");
354 if (!executionStatus) {
355 if (outMessage != null && !outMessage.equalsIgnoreCase("")) {
356 return new SaltstackResult(SaltstackResultCodes.COMMAND_EXEC_FAILED_STATUS.getValue(),
359 return new SaltstackResult(SaltstackResultCodes.COMMAND_EXEC_FAILED_STATUS.getValue(),
360 "error in executing configuration at the server, check your command input");
363 saltstackResult.setStatusCode(SaltstackResultCodes.FINAL_SUCCESS.getValue());
364 return saltstackResult;
367 public SaltstackResult putToProperties(SvcLogicContext ctx, String pfx,
368 SaltstackResult saltstackResult) throws IOException {
370 ByteArrayOutputStream buffer = saltstackResult.getOutputMessage();
371 InputStream inputStream = null;
373 byte[] bytes = buffer.toByteArray();
374 Properties prop = new Properties();
375 inputStream = new ByteArrayInputStream(bytes);
376 prop.load(inputStream);
377 ctx.setAttribute(pfx + "completeResult", prop.toString());
378 for (Object key : prop.keySet()) {
379 String name = (String) key;
380 String value = prop.getProperty(name);
381 if (value != null && value.trim().length() > 0) {
382 ctx.setAttribute(pfx + "." + name, value.trim());
383 LOGGER.info("+++ " + pfx + "." + name + ": [" + value + "]");
386 } catch (Exception e) {
387 saltstackResult = new SaltstackResult(SaltstackResultCodes.INVALID_RESPONSE_FILE.getValue(), "Error parsing response file." +
388 " Error = " + e.getMessage());
390 if (buffer != null && inputStream != null) {
395 saltstackResult.setStatusCode(SaltstackResultCodes.FINAL_SUCCESS.getValue());
396 return saltstackResult;
399 private void throwIfMissingMandatoryParam(Map<String, String> params, String key) throws SvcLogicException {
400 if (!params.containsKey(key)) {
401 throw new SvcLogicException(String.format(
402 "Saltstack: Mandatory SaltstackAdapter key %s not found in parameters provided by calling agent !",
405 if (Strings.isNullOrEmpty(params.get(key))) {
406 throw new SvcLogicException(String.format(
407 "Saltstack: Mandatory SaltstackAdapter key %s not found in parameters provided by calling agent !",