Repository URL to install this package:
|
Version:
4.1.94.1.dev5 ▾
|
import os
import re
import sys
import subprocess
import bottle
from queue import Queue, Empty
from threading import Thread
ansible_output_file = '/var/log/workloadmgr/ansible-playbook.log'
ansible_output_file_html = '/var/log/workloadmgr/ansible-playbook.html'
header = \
"""
<!DOCTYPE html>
<html lang="en">
<head>
<title>Ansible playbook output</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="css/bootstrap.min.css">
<script src="js/jquery.js" type="text/javascript"></script>
<script src="js/bootstrap.min.js" type="text/javascript"></script>
</head>
<body>
<div>
<p>
<h4>Playbook output
<a class="btn btn-primary" onclick="$('div .collapse').collapse('toggle')" >Expand / Collapse All</a>
</h4>
</p>
<div class="table">
"""
footer = \
"""
</div>
</div>
</body>
</html>
"""
class playbook_output_stream:
def __init__(self, output, sync):
self.stream = output
self.output_html = header
self.sync = sync
self.total_bytes = 0
self.task_count = 0
self.fp = open(ansible_output_file, 'a+')
self.htmlfp = open(ansible_output_file_html, 'a+')
def __del__(self):
self.fp.close()
self.htmlfp.close()
def read(self, size=1024):
if self.output_html != '':
l = self.output_html
self.output_html = ''
self.total_bytes += len(l)
self.htmlfp.write(l)
return l
line = ""
html = ''
for line in self.stream:
self.fp.write(line)
if line.startswith('TASK'):
if self.task_count:
html += '</div>'
html += '<div class="row-fluid accordion-toggle" data-toggle="collapse" data-target="#collapse%d">\n' % self.task_count
html += '<br><div class="span1"><p class="bg-success">' + line.split("\n")[0] + '</p></div>\n'
html += '</div>'
html += '<div id="collapse%d" class="row-fluid collapse">' % self.task_count
self.task_count += 1
if len(line.split("\n")) > 1:
for l in line.split("\n")[1:]:
html += '<div class="span9">'
html += '<br>'.join(l.split("\n"))
html += '</div>'
elif line.startswith('PLAY RECAP'):
break
elif line.startswith('fatal'):
html += '<div class="row-fluid">\n'
html += '<br><div class="span1"><p class="bg-danger">' + line + '</p></div>\n'
html += '</div>'
else:
for l in line.split("\n"):
html += '<div class="span9">'
html += '<br>'.join(l.split("\n"))
html += '</div>'
self.total_bytes += len(html)
self.htmlfp.write(html)
return html
if line:
html += '</div>'
line += '\n'
line += "\n".join([l for l in self.stream])
html += '<div class="row-fluid accordion-toggle" data-toggle="collapse" data-target="#collapse%d">\n' % self.task_count
recap = 'PLAY RECAP' + line.split('PLAY RECAP')[1]
html += '<p>'
for line in recap.split('\n'):
html += '<br><div class="span1"><p class="bg-primary">' + line + '</p></div>\n'
html += '</div>'
html += footer
html += '</p>'
self.fp.write(line)
#self.total_bytes += len(html)
if not self.sync:
html += " " * (1024 * 1024 - self.total_bytes)
self.htmlfp.write(html)
return html
class task_output_stream:
def __init__(self, output):
self.stream = output
def read(self, size=1024):
output_html = header
if 'SUCCESS' in self.stream:
output_html += '<br><dt><p class="bg-success">' + self.stream + '</p></dt>'
else:
output_html += '<p>'
output_html += '<dt class="bg-danger">' + self.stream + '</dt>'
output_html += '</p>'
output_html += footer
return output_html
def enqueue_output(out, queue):
line = out.read(80)
while line:
queue.put(line)
line = out.read(80)
queue.put(line)
out.close()
class process_output:
def __init__(self, process, queue):
self.process = process
self.queue = queue
self.process_status = None
self.output = ''
def __iter__(self):
return self
def __next__(self):
""" Utility method that updates a tracking file with information dury an image
copy.
Args:
process (process): Process handle of the sub process.
update_queue (queue): Queue of output
"""
# Keep updating the progress tracking file while the
# process is still running and there are items the queue.
while self.process_status is None or not self.queue.empty():
self.process_status = self.process.poll()
try:
try:
self.output += str(self.queue.get(timeout=30), encoding='utf-8')
except Empty:
self.outout += '<p/>\n'
except Exception as ex:
print(ex)
except Exception as ex:
print(ex)
# only return complete lines and keep incomplete lines for next iteration
if len(self.output.split('\n')[0:-1]) > 0:
ret_str = "\n".join(self.output.split('\n')[0:-1])
self.output = self.output.split('\n')[-1]
return ret_str
self.process.stdin.close()
_returncode = self.process.returncode # pylint: disable=E1101
raise StopIteration
def _run_playbook(cmdspec, sync=False):
try:
cwd = os.getcwd()
os.chdir(os.path.abspath(os.path.join(os.path.dirname(__file__), 'ansible-play')))
process = subprocess.Popen(cmdspec, stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
bufsize=-1, close_fds=True,
shell=False)
queue = Queue()
read_thread = Thread(target=enqueue_output,
args=(process.stdout, queue))
read_thread.daemon = True # thread dies with the program
read_thread.start()
output = process_output(process, queue)
if 'playbook' in cmdspec[0]:
return playbook_output_stream(output, sync), process
else:
return task_output_stream(output), process
finally:
os.chdir(cwd)
def run_playbook_sync(cmdspec):
status_code = 200
output, process = _run_playbook(cmdspec, sync=True)
html = ''
while True:
line = output.read()
if line == '':
break
html += line
match = re.finditer(r"ok=(?P<ok>\d+)\s+changed=(?P<changed>\d+)\s+unreachable=(?P<unreachable>\d+)\s+failed=(?P<failed>\d+)", line)
if match:
for m in match:
if (int(m.group('failed')) > 0 or int(m.group('unreachable'))) > 0 :
status_code = 500
break
process.stdout.close()
process.stderr.close()
if status_code == 500:
return bottle.HTTPResponse(status=500, body=html)
return bottle.HTTPResponse(status=200, body=html)
def run_playbook_async(cmdspec):
return _run_playbook(cmdspec)
if __name__ == "__main__":
print((run_playbook(sys.argv[1:])[1]))