diff options
Diffstat (limited to 'tools')
-rw-r--r-- | tools/docker-compose.yml | 2 | ||||
-rwxr-xr-x | tools/docker-compose/start_development.sh | 1 | ||||
-rw-r--r-- | tools/rdb.py | 231 |
3 files changed, 234 insertions, 0 deletions
diff --git a/tools/docker-compose.yml b/tools/docker-compose.yml index da7abb7472..26b851db21 100644 --- a/tools/docker-compose.yml +++ b/tools/docker-compose.yml @@ -16,6 +16,8 @@ services: - "8013:8013" - "8043:8043" - "6899-6999:6899-6999" # default port range for celery.contrib.rdb + extra_hosts: + - "dockerhost:${DOCKER_HOST_IP}" links: - postgres - memcached diff --git a/tools/docker-compose/start_development.sh b/tools/docker-compose/start_development.sh index 9814a9344c..56a5c968a1 100755 --- a/tools/docker-compose/start_development.sh +++ b/tools/docker-compose/start_development.sh @@ -25,6 +25,7 @@ fi cp -nR /tmp/ansible_tower.egg-info /tower_devel/ || true cp /tmp/ansible-tower.egg-link /venv/tower/lib/python2.7/site-packages/ansible-tower.egg-link +ln -s /tower_devel/tools/rdb.py /venv/tower/lib/python2.7/site-packages/rdb.py yes | cp -rf /tower_devel/tools/docker-compose/supervisor.conf /supervisor.conf # Tower bootstrapping diff --git a/tools/rdb.py b/tools/rdb.py new file mode 100644 index 0000000000..227d2448e5 --- /dev/null +++ b/tools/rdb.py @@ -0,0 +1,231 @@ +import rlcompleter +try: + import readline +except ImportError: + print("Module readline not available.") +else: + if 'libedit' in readline.__doc__: + readline.parse_and_bind("bind ^I rl_complete") + else: + readline.parse_and_bind("tab: complete") + +import sys +from celery.contrib.rdb import Rdb + +import cmd +import contextlib +import os +import pprint +import re +import select +import socket +import threading +from cStringIO import StringIO +from Queue import Queue, Empty + +from pygments import highlight +from pygments.lexers import PythonLexer +from pygments.formatters import Terminal256Formatter + + +@contextlib.contextmanager +def style(im_self, filepart=None, lexer=None): + + lexer = PythonLexer + old_stdout = im_self.stdout + buff = StringIO() + im_self.stdout = buff + yield + + value = buff.getvalue() + context = len(value.splitlines()) + file_cache = {} + + if filepart: + filepath, lineno = filepart + if filepath not in file_cache: + with open(filepath, 'r') as source: + file_cache[filepath] = source.readlines() + value = ''.join(file_cache[filepath][:lineno - 1]) + value + + formatter = Terminal256Formatter(style='friendly') + value = highlight(value, lexer(), formatter) + + # Properly format line numbers when they show up in multi-line strings + strcolor, _ = formatter.style_string['Token.Literal.String'] + intcolor, _ = formatter.style_string['Token.Literal.Number.Integer'] + value = re.sub( + r'%s([0-9]+)' % re.escape(strcolor), + lambda match: intcolor + match.group(1) + strcolor, + value, + ) + + # Highlight the "current" line in yellow for visibility + lineno = im_self.curframe.f_lineno + + value = re.sub( + '(?<!\()%s%s[^\>]+>[^\[]+\[39m([^\x1b]+)[^m]+m([^\n]+)' % (re.escape(intcolor), lineno), + lambda match: ''.join([ + str(lineno), + ' ->', + '\x1b[93m', + match.group(1), + re.sub('\x1b[^m]+m', '', match.group(2)), + '\x1b[0m' + ]), + value + ) + + if filepart: + _, first = filepart + value = '\n'.join(value.splitlines()[-context:]) + '\n' + + if value.strip(): + old_stdout.write(value) + im_self.stdout = old_stdout + + +class CustomPdb(Rdb): + + def cmdloop(self): + self.do_list(tuple()) + return cmd.Cmd.cmdloop(self) + + def do_list(self, args): + lines = 60 + context = (lines - 2) / 2 + if not args: + first = max(1, self.curframe.f_lineno - context) + last = first + context * 2 - 1 + args = "(%s, %s)" % (first, last) + self.lineno = None + with style(self, ( + self.curframe.f_code.co_filename, self.curframe.f_lineno - context) + ): + return Rdb.do_list(self, args) + do_l = do_list + + def format_stack_entry(self, *args, **kwargs): + entry = Rdb.format_stack_entry(self, *args, **kwargs) + return '\n'.join( + filter(lambda x: not x.startswith('->'), entry.splitlines()) + ) + + def print_stack_entry(self, *args, **kwargs): + with style(self): + return Rdb.print_stack_entry(self, *args, **kwargs) + + def set_next(self, curframe): + os.system('clear') + Rdb.set_next(self, curframe) + + def set_return(self, arg): + os.system('clear') + Rdb.set_return(self, arg) + + def set_step(self): + os.system('clear') + Rdb.set_step(self) + + def default(self, line): + with style(self): + return Rdb.default(self, line) + + def parseline(self, line): + line = line.strip() + match = re.search('^([0-9]+)([a-zA-Z]+)', line) + if match: + times, command = match.group(1), match.group(2) + line = command + self.cmdqueue.extend(list(command * (int(times) - 1))) + if line == '?': + line = 'dir()' + elif line.endswith('??'): + line = "import inspect; print ''.join(inspect.getsourcelines(%s)[0][:25])" % line[:-2] + elif line.endswith('?'): + line = 'dir(%s)' % line[:-1] + return cmd.Cmd.parseline(self, line) + + def displayhook(self, obj): + if obj is not None and not isinstance(obj, list): + return pprint.pprint(obj) + return Rdb.displayhook(self, obj) + + def get_avail_port(self, *args, **kwargs): + sock, port = Rdb.get_avail_port(self, *args, **kwargs) + socket.socket(socket.AF_INET, socket.SOCK_DGRAM).sendto( + str(port), ('dockerhost', 6899) + ) + return (sock, port) + + +CustomPdb.complete = rlcompleter.Completer(locals()).complete + + +def set_trace(): + return CustomPdb().set_trace(sys._getframe().f_back) + + +def listen(): + queue = Queue() + + def _consume(queue): + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + sock.bind(('0.0.0.0', 6899)) + print 'listening for rdb notifications on :6899...' + while True: + r, w, x = select.select([sock], [], []) + for i in r: + data = i.recv(1024) + queue.put(data) + worker = threading.Thread(target=_consume, args=(queue,)) + worker.setDaemon(True) + worker.start() + + try: + while True: + try: + port = queue.get(timeout=1) + queue.task_done() + if port == 'q': + break + port = int(port) + print 'opening telnet session at localhost:%d...' % port + telnet(port) + print 'listening for rdb notifications on :6899...' + except Empty: + pass + except KeyboardInterrupt: + print 'got Ctrl-C' + queue.put('q') + + +def telnet(port): + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.settimeout(2) + + try: + s.connect(('0.0.0.0', port)) + except: + print 'unable to connect' + return + print 'connected to 0.0.0.0:%d' % port + + while True: + socket_list = [sys.stdin, s] + r, w, e = select.select(socket_list , [], []) + for sock in r: + if sock == s: + data = sock.recv(4096) + if not data: + print 'connection closed' + return + else: + sys.stdout.write(data) + else: + msg = sys.stdin.readline() + s.send(msg) + + +if __name__ == '__main__': + listen() |