Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
Size: Mime:
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]))