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 !",