Brett /
DownBecause(:source lang=python:)
import sys import re import getopt import commands printed_not_found = False class TerminalController: """ A class that can be used to portably generate formatted output to a terminal. TerminalController` defines a set of instance variables whose values are initialized to the control sequence necessary to perform a given action. These can be simply included in normal output to the terminal: >>> term = TerminalController() >>> print 'This is term.GREENgreen'+term.NORMAL Alternatively, the `render()` method can used, which replaces '${action}' with the string required to perform 'action': >>> term = TerminalController() >>> print term.render('This is ${GREEN}green${NORMAL}') If the terminal doesn't support a given action, then the value of the corresponding instance variable will be set to ''. As a result, the above code will still work on terminals that do not support color, except that their output will not be colored. Also, this means that you can test whether the terminal supports a given action by simply testing the truth value of the corresponding instance variable: >>> term = TerminalController() >>> if term.CLEAR_SCREEN: ... print 'This terminal supports clearning the screen.' Finally, if the width and height of the terminal are known, then they will be stored in the `COLS` and `LINES` attributes. """ # Cursor movement: BOL = '' # Move the cursor to the beginning of the line UP = '' # Move the cursor up one line DOWN = '' # Move the cursor down one line LEFT = '' # Move the cursor left one char RIGHT = '' # Move the cursor right one char # Deletion: CLEAR_SCREEN = '' # Clear the screen and move to home position CLEAR_EOL = '' # Clear to the end of the line. CLEAR_BOL = '' # Clear to the beginning of the line. CLEAR_EOS = '' # Clear to the end of the screen # Output modes: BOLD = '' # Turn on bold mode BLINK = '' # Turn on blink mode DIM = '' # Turn on half-bright mode REVERSE = '' # Turn on reverse-video mode NORMAL = '' # Turn off all modes # Cursor display: HIDE_CURSOR = '' # Make the cursor invisible SHOW_CURSOR = '' # Make the cursor visible # Terminal size: COLS = None # Width of the terminal (None for unknown) LINES = None # Height of the terminal (None for unknown) # Foreground colors: BLACK = BLUE = GREEN = CYAN = RED = MAGENTA = YELLOW = WHITE = '' # Background colors: BG_BLACK = BG_BLUE = BG_GREEN = BG_CYAN = '' BG_RED = BG_MAGENTA = BG_YELLOW = BG_WHITE = '' _STRING_CAPABILITIES = """ BOL=cr UP=cuu1 DOWN=cud1 LEFT=cub1 RIGHT=cuf1 CLEAR_SCREEN=clear CLEAR_EOL=el CLEAR_BOL=el1 CLEAR_EOS=ed BOLD=bold BLINK=blink DIM=dim REVERSE=rev UNDERLINE=smul NORMAL=sgr0 HIDE_CURSOR=cinvis SHOW_CURSOR=cnorm""".split() _COLORS = """BLACK BLUE GREEN CYAN RED MAGENTA YELLOW WHITE""".split() _ANSICOLORS = "BLACK RED GREEN YELLOW BLUE MAGENTA CYAN WHITE".split() def __init__(self, term_stream=sys.stdout): """ Create a TerminalController` and initialize its attributes with appropriate values for the current terminal. `term_stream` is the stream that will be used for terminal output; if this stream is not a tty, then the terminal is assumed to be a dumb terminal (i.e., have no capabilities). """ # Curses isn't available on all platforms try: import curses except: return # If the stream isn't a tty, then assume it has no capabilities. if not term_stream.isatty(): return # Check the terminal type. If we fail, then assume that the # terminal has no capabilities. try: curses.setupterm() except: return # Look up numeric capabilities. self.COLS = curses.tigetnum('cols') self.LINES = curses.tigetnum('lines') # Look up string capabilities. for capability in self._STRING_CAPABILITIES: (attrib, cap_name) = capability.split('=') setattr(self, attrib, self._tigetstr(cap_name) or '') # Colors set_fg = self._tigetstr('setf') if set_fg: for i, color in zip(range(len(self._COLORS)), self._COLORS): setattr(self, color, curses.tparm(set_fg, i) or '') set_fg_ansi = self._tigetstr('setaf') if set_fg_ansi: for i, color in zip(range(len(self._ANSICOLORS)), self._ANSICOLORS): setattr(self, color, curses.tparm(set_fg_ansi, i) or '') set_bg = self._tigetstr('setb') if set_bg: for i, color in zip(range(len(self._COLORS)), self._COLORS): setattr(self, 'BG_' + color, curses.tparm(set_bg, i) or '') set_bg_ansi = self._tigetstr('setab') if set_bg_ansi: for i, color in zip(range(len(self._ANSICOLORS)), self._ANSICOLORS): setattr(self, 'BG_' + color, curses.tparm(set_bg_ansi, i) or '') def _tigetstr(self, cap_name): # String capabilities can include "delays" of the form "$<2>". # For any modern terminal, we should be able to just ignore # these, so strip them out. import curses cap = curses.tigetstr(cap_name) or '' return re.sub(r'\$<\d+>[/*]?', '', cap) def render(self, template): """ Replace each $-substitutions in the given template string with the corresponding terminal control string (if it's defined) or '' (if it's not). """ return re.sub(r'\$\$|\${\w+}', self._render_sub, template) def _render_sub(self, match): s = match.group() if s == '$$': return s else: return getattr(self, s[2:-1]) class ProgressBar: """
A 1-line progress bar, which looks like::
20% [===========----------------------------------]
The progress bar is colored, if the terminal supports color output
"""
def __init__(self, term, percent, width):
self.term = term
if percent >= 1:
percent = 1
colour = "${RED}"
else:
colour = "${GREEN}"
BAR = colour + '${BOLD}%3d${NORMAL}' + colour + ' [s]${NORMAL}\n'
self.bar = term.render(BAR)
n = int((width - 10) * percent)
sys.stdout.write(self.bar % (100 * percent, '=' * n, '-' * (width - 10 - n)))
def nodefreein(node): global printed_not_found
(status, nodefreein) = commands.getstatusoutput('nodefreein (node))
if status == 0:
line_break = nodefreein.split()
nodefreein_result = ' '.join(line_break[2:])
return nodefreein_result.strip()
if printed_not_found == False:
print "WARNING: Command 'nodefreein' not found, won't be able to determine free times\n"
printed_not_found = True
return "xxx UNKNOWN xxx xxx xxx xxx"
def get_moab_reason(node): checknode = commands.getoutput('checknode (node))
checknode_list = checknode.split('\n')
reason = "NO REASON"
for line in checknode_list:
if line.startswith('RM') and line.count('ERROR') > 0:
line_break = line.split('\'')
return line_break[1][6:]
if line.find('node rm message') != -1:
line_break = line.split('\'')
return line_break[1][6:]
return reason
if __name__ == "__main__": downnodes = []
verbose = False
show_moab = True
show_pbs = True
show_free_only = False
partition = False
opts, args = getopt.getopt(sys.argv[1:], 'vhpfmt:')
opts = dict(opts)
if '-v' in opts:
verbose = True
if '-p' in opts:
show_moab = False
if '-m' in opts:
show_pbs = False
if '-f' in opts:
show_free_only = True
if '-t' in opts:
partition = opts['-t']
if '-h' in opts:
print "usage: downbecause [-v] [-h] [-f] [-p] [-m] [-t <partition>]"
print "\t-p\tonly show PBS"
print "\t-m\tonly show moab"
print "\t-f\tonly report on free nodes"
print "\t-t <partition>\tonly report on nodes in a particular partition"
print "\t-v\tverbose output"
print "\t-h\tshow help message"
sys.exit(-1)
(return_code, pbsnodes) = commands.getstatusoutput("pbsnodes -l -n")
if return_code != 0:
(return_code, pbsnodes) = commands.getstatusoutput("pbsnodes -l")
if return_code != 0:
print "ERROR: Command 'pbsnodes' not found, check your PATH"
sys.exit(-1)
if partition:
diagnose = commands.getoutput("diagnose -n -t (partition))
else:
diagnose = commands.getoutput("diagnose -n")
pbsnodes_list = pbsnodes.split('\n')
diagnose_list = diagnose.split('\n')
nodes = {}
for line in pbsnodes_list:
if line != "":
line_break = line.split()
node = line_break[0]
reason = ' '.join(line_break[2:])
if reason == "":
reason = "NO REASON"
if node not in downnodes:
downnodes.append(node)
nodes[node] = {'pbs': reason}
nodes[node]['moab'] = "NO REASON"
if partition:
nodes[node]['show'] = False
for line in diagnose_list:
line_break = line.split()
if len(line_break) == 5:
if line_break[1] == "Down" or line_break[1] == "Drained":
node = line_break[0]
reason = get_moab_reason(node)
if node not in downnodes:
downnodes.append(node)
nodes[node] = {'moab': reason}
nodes[node]['pbs'] = "NO REASON"
else:
nodes[node]['moab'] = reason
if partition:
nodes[node]['show'] = True
downnodes.sort()
longest_node = 0
longest_pbs = 0
longest_moab = 0
term = TerminalController()
for node in downnodes:
node_count = len(node)
pbs_count = len(nodes[node]['pbs'])
moab_count = len(nodes[node]['moab'])
if node_count > longest_node:
longest_node = node_count
if pbs_count > longest_pbs:
longest_pbs = pbs_count
if moab_count > longest_moab:
longest_moab = moab_count
for node in downnodes:
if partition:
if not nodes[node]['show']:
continue
free = nodefreein(node)
free_split = free.split()
if free_split[1] == "free":
line = "${GREEN}"
else:
line = "${RED}"
line += node
for i in range(len(node), longest_node):
line += " "
if show_pbs:
line += " - "
line += nodes[node]['pbs']
for i in range(len(nodes[node]['pbs']), longest_pbs):
line += " "
if show_moab:
line += " - "
line += nodes[node]['moab']
for i in range(len(nodes[node]['moab']), longest_moab):
line += " "
line += " - "
if verbose:
line += free
else:
if free_split[1] == "free":
line += "free"
else:
line += free_split[4]
line += "${NORMAL}"
if show_free_only == False or free_split[1] == "free":
print term.render(line)
(:sourceend:) |