diff --git a/managed/devops/opscli/ybops/cloud/common/ansible.py b/managed/devops/opscli/ybops/cloud/common/ansible.py index adbfc530d50e..f806a45ff46c 100644 --- a/managed/devops/opscli/ybops/cloud/common/ansible.py +++ b/managed/devops/opscli/ybops/cloud/common/ansible.py @@ -267,6 +267,7 @@ def run(self, filename, extra_vars=None, host_info=None, print_output=True, stdout_str = stdout.decode('utf-8') stderr_str = stderr.decode('utf-8') if rc != 0 else "" if print_output: + # Write output to stderr by default. logging.info(stdout_str) if rc != 0: diff --git a/managed/src/main/java/com/yugabyte/yw/common/ShellProcessHandler.java b/managed/src/main/java/com/yugabyte/yw/common/ShellProcessHandler.java index 4aaf0247f8a1..471f076d241d 100644 --- a/managed/src/main/java/com/yugabyte/yw/common/ShellProcessHandler.java +++ b/managed/src/main/java/com/yugabyte/yw/common/ShellProcessHandler.java @@ -54,10 +54,10 @@ public class ShellProcessHandler { static final Pattern ANSIBLE_FAIL_PAT = Pattern.compile( - "(ybops.common.exceptions.YBOpsRuntimeError: Runtime error: " - + "Playbook run.* )with args.* (failed with.*? [0-9]+)"); + "(ybops\\.common\\.exceptions\\.YB[^\\s]+Error:.*? Playbook run.*?)with args.* (failed" + + " with.*? [0-9]+)"); static final Pattern ANSIBLE_FAILED_TASK_PAT = - Pattern.compile("TASK.*?fatal.*?FAILED.*", Pattern.DOTALL); + Pattern.compile("TASK\\s+\\[.+\\].*?(fatal:.*?FAILED.*|failed: (?!false).*)", Pattern.DOTALL); static final Pattern PYTHON_ERROR_PAT = Pattern.compile("()(.*?)()", Pattern.DOTALL); static final String ANSIBLE_IGNORING = "ignoring"; @@ -178,11 +178,12 @@ public ShellResponse run(List command, ShellProcessContext context) { itse); } response.message = (response.code == ERROR_CODE_SUCCESS) ? processOutput : processError; - String specificErrMsg = getAnsibleErrMsg(response.code, processOutput, processError); - if (specificErrMsg == null) { - specificErrMsg = getPythonErrMsg(response.code, processOutput); - } + String specificErrMsg = getPythonErrMsg(response.code, processOutput); if (specificErrMsg != null) { + String ansibleErrMsg = getAnsibleErrMsg(response.code, specificErrMsg, processError); + if (ansibleErrMsg != null) { + specificErrMsg = ansibleErrMsg; + } response.message = specificErrMsg; } } @@ -390,20 +391,21 @@ private static void destroyForcibly(Process process, String description) { } } - private static String getAnsibleErrMsg(int code, String stdout, String stderr) { + private static String getAnsibleErrMsg(int code, String pythonErrMsg, String stderr) { - if (stderr == null || code == ERROR_CODE_SUCCESS) return null; + if (pythonErrMsg == null || stderr == null || code == ERROR_CODE_SUCCESS) return null; String result = null; - Matcher ansibleFailMatch = ANSIBLE_FAIL_PAT.matcher(stderr); + Matcher ansibleFailMatch = ANSIBLE_FAIL_PAT.matcher(pythonErrMsg); if (ansibleFailMatch.find()) { result = ansibleFailMatch.group(1) + ansibleFailMatch.group(2); - // Attempt to find a line in ansible stdout for the failed task. + // By default, python logging module writes to stderr. + // Attempt to find a line in ansible stderr for the failed task. // Logs for each task are separated by empty lines. - // Some fatal failures are ignored by ansible, so skip them - for (String s : stdout.split("\\R\\R")) { + // Some fatal failures are ignored by ansible, so skip them. + for (String s : stderr.split("\\R\\R")) { if (s.contains(ANSIBLE_IGNORING)) { continue; }