From 88122e0f72f9420643ad435e14c714ce8e73b17e Mon Sep 17 00:00:00 2001 From: David Shrewsbury Date: Thu, 23 Jun 2016 17:14:59 -0400 Subject: [PATCH] Use os.rename() in async_wrapper Because the async_status module will read from the same file that the async_wrapper module is writing, it's possible that the file may not be fully synced during a read, causing spurious failures. Use a temp file to do an atomic operation on the file. We can't use atomic_move() here as that doesn't work properly under async. Also, let's not read concurrently from the same file the subprocess is writing to. Instead, capture stdout/stderr via PIPE and write to the file to avoid nasty races. --- .../modules/utilities/logic/async_wrapper.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/lib/ansible/modules/utilities/logic/async_wrapper.py b/lib/ansible/modules/utilities/logic/async_wrapper.py index 078f3388c39..a99c4f070a2 100644 --- a/lib/ansible/modules/utilities/logic/async_wrapper.py +++ b/lib/ansible/modules/utilities/logic/async_wrapper.py @@ -72,19 +72,23 @@ def daemonize_self(): def _run_module(wrapped_cmd, jid, job_path): - jobfile = open(job_path, "w") + tmp_job_path = job_path + ".tmp" + jobfile = open(tmp_job_path, "w") jobfile.write(json.dumps({ "started" : 1, "ansible_job_id" : jid })) jobfile.close() - jobfile = open(job_path, "w") + os.rename(tmp_job_path, job_path) + jobfile = open(tmp_job_path, "w") result = {} outdata = '' try: cmd = shlex.split(wrapped_cmd) - script = subprocess.Popen(cmd, shell=False, stdin=None, stdout=jobfile, stderr=jobfile) - script.communicate() - outdata = file(job_path).read() + script = subprocess.Popen(cmd, shell=False, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + (outdata, stderr) = script.communicate() result = json.loads(outdata) + if stderr: + result['stderr'] = stderr + jobfile.write(json.dumps(result)) except (OSError, IOError): e = sys.exc_info()[1] @@ -95,6 +99,7 @@ def _run_module(wrapped_cmd, jid, job_path): } result['ansible_job_id'] = jid jobfile.write(json.dumps(result)) + except: result = { "failed" : 1, @@ -104,7 +109,9 @@ def _run_module(wrapped_cmd, jid, job_path): } result['ansible_job_id'] = jid jobfile.write(json.dumps(result)) + jobfile.close() + os.rename(tmp_job_path, job_path) ####################