Package ClusterShell :: Package CLI :: Module Display
[hide private]
[frames] | no frames]

Source Code for Module ClusterShell.CLI.Display

  1  #!/usr/bin/env python 
  2  # 
  3  # Copyright (C) 2010-2015 CEA/DAM 
  4  # 
  5  # This file is part of ClusterShell. 
  6  # 
  7  # ClusterShell is free software; you can redistribute it and/or 
  8  # modify it under the terms of the GNU Lesser General Public 
  9  # License as published by the Free Software Foundation; either 
 10  # version 2.1 of the License, or (at your option) any later version. 
 11  # 
 12  # ClusterShell is distributed in the hope that it will be useful, 
 13  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 14  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
 15  # Lesser General Public License for more details. 
 16  # 
 17  # You should have received a copy of the GNU Lesser General Public 
 18  # License along with ClusterShell; if not, write to the Free Software 
 19  # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 
 20   
 21  """ 
 22  CLI results display class 
 23  """ 
 24   
 25  import difflib 
 26  import sys 
 27   
 28  from ClusterShell.NodeSet import NodeSet 
 29   
 30  # Display constants 
 31  VERB_QUIET = 0 
 32  VERB_STD = 1 
 33  VERB_VERB = 2 
 34  VERB_DEBUG = 3 
 35  THREE_CHOICES = ["never", "always", "auto"] 
 36  WHENCOLOR_CHOICES = THREE_CHOICES   # deprecated; use THREE_CHOICES 
 37   
 38   
39 -class Display(object):
40 """ 41 Output display class for command line scripts. 42 """ 43 COLOR_RESULT_FMT = "\033[32m%s\033[0m" 44 COLOR_STDOUT_FMT = "\033[34m%s\033[0m" 45 COLOR_STDERR_FMT = "\033[31m%s\033[0m" 46 COLOR_DIFFHDR_FMT = "\033[1m%s\033[0m" 47 COLOR_DIFFHNK_FMT = "\033[36m%s\033[0m" 48 COLOR_DIFFADD_FMT = "\033[32m%s\033[0m" 49 COLOR_DIFFDEL_FMT = "\033[31m%s\033[0m" 50 SEP = "-" * 15 51
52 - class _KeySet(set):
53 """Private NodeSet substition to display raw keys"""
54 - def __str__(self):
55 return ",".join(self)
56
57 - def __init__(self, options, config=None, color=None):
58 """Initialize a Display object from CLI.OptionParser options 59 and optional CLI.ClushConfig. 60 61 If `color' boolean flag is not specified, it is auto detected 62 according to options.whencolor. 63 """ 64 if options.diff: 65 self._print_buffer = self._print_diff 66 else: 67 self._print_buffer = self._print_content 68 self._display = self._print_buffer 69 self._diffref = None 70 # diff implies at least -b 71 self.gather = options.gatherall or options.gather or options.diff 72 self.progress = getattr(options, 'progress', False) # only in clush 73 # check parameter combinaison 74 if options.diff and options.line_mode: 75 raise ValueError("diff not supported in line_mode") 76 self.line_mode = options.line_mode 77 self.label = options.label 78 self.regroup = options.regroup 79 self.groupsource = options.groupsource 80 self.noprefix = options.groupbase 81 # display may change when 'max return code' option is set 82 self.maxrc = getattr(options, 'maxrc', False) 83 84 if color is None: 85 # Should we use ANSI colors? 86 color = False 87 if not options.whencolor or options.whencolor == "auto": 88 color = sys.stdout.isatty() 89 elif options.whencolor == "always": 90 color = True 91 92 self._color = color 93 94 self.out = sys.stdout 95 self.err = sys.stderr 96 if self._color: 97 self.color_stdout_fmt = self.COLOR_STDOUT_FMT 98 self.color_stderr_fmt = self.COLOR_STDERR_FMT 99 self.color_diffhdr_fmt = self.COLOR_DIFFHDR_FMT 100 self.color_diffctx_fmt = self.COLOR_DIFFHNK_FMT 101 self.color_diffadd_fmt = self.COLOR_DIFFADD_FMT 102 self.color_diffdel_fmt = self.COLOR_DIFFDEL_FMT 103 else: 104 self.color_stdout_fmt = self.color_stderr_fmt = \ 105 self.color_diffhdr_fmt = self.color_diffctx_fmt = \ 106 self.color_diffadd_fmt = self.color_diffdel_fmt = "%s" 107 108 # Set display verbosity 109 if config: 110 # config object does already apply options overrides 111 self.node_count = config.node_count 112 self.verbosity = config.verbosity 113 else: 114 self.node_count = True 115 self.verbosity = VERB_STD 116 if hasattr(options, 'quiet') and options.quiet: 117 self.verbosity = VERB_QUIET 118 if hasattr(options, 'verbose') and options.verbose: 119 self.verbosity = VERB_VERB 120 if hasattr(options, 'debug') and options.debug: 121 self.verbosity = VERB_DEBUG
122
123 - def flush(self):
124 """flush display object buffers""" 125 # only used to reset diff display for now 126 self._diffref = None
127
128 - def _getlmode(self):
129 """line_mode getter""" 130 return self._display == self._print_lines
131
132 - def _setlmode(self, value):
133 """line_mode setter""" 134 if value: 135 self._display = self._print_lines 136 else: 137 self._display = self._print_buffer
138 line_mode = property(_getlmode, _setlmode) 139
140 - def _format_nodeset(self, nodeset):
141 """Sub-routine to format nodeset string.""" 142 if self.regroup: 143 return nodeset.regroup(self.groupsource, noprefix=self.noprefix) 144 return str(nodeset)
145
146 - def format_header(self, nodeset, indent=0):
147 """Format nodeset-based header.""" 148 indstr = " " * indent 149 nodecntstr = "" 150 if self.verbosity >= VERB_STD and self.node_count and len(nodeset) > 1: 151 nodecntstr = " (%d)" % len(nodeset) 152 if not self.label: 153 return "" 154 return self.color_stdout_fmt % ("%s%s\n%s%s%s\n%s%s" % \ 155 (indstr, self.SEP, 156 indstr, self._format_nodeset(nodeset), nodecntstr, 157 indstr, self.SEP))
158
159 - def print_line(self, nodeset, line):
160 """Display a line with optional label.""" 161 if self.label: 162 prefix = self.color_stdout_fmt % ("%s: " % nodeset) 163 self.out.write("%s%s\n" % (prefix, line)) 164 else: 165 self.out.write("%s\n" % line)
166
167 - def print_line_error(self, nodeset, line):
168 """Display an error line with optional label.""" 169 if self.label: 170 prefix = self.color_stderr_fmt % ("%s: " % nodeset) 171 self.err.write("%s%s\n" % (prefix, line)) 172 else: 173 self.err.write("%s\n" % line)
174
175 - def print_gather(self, nodeset, obj):
176 """Generic method for displaying nodeset/content according to current 177 object settings.""" 178 return self._display(NodeSet(nodeset), obj)
179
180 - def print_gather_finalize(self, nodeset):
181 """Finalize display of diff-like gathered contents.""" 182 if self._display == self._print_diff and self._diffref: 183 return self._display(nodeset, '')
184
185 - def print_gather_keys(self, keys, obj):
186 """Generic method for displaying raw keys/content according to current 187 object settings (used by clubak).""" 188 return self._display(self.__class__._KeySet(keys), obj)
189
190 - def _print_content(self, nodeset, content):
191 """Display a dshbak-like header block and content.""" 192 self.out.write("%s\n%s\n" % (self.format_header(nodeset), content))
193
194 - def _print_diff(self, nodeset, content):
195 """Display unified diff between remote gathered outputs.""" 196 if self._diffref is None: 197 self._diffref = (nodeset, content) 198 else: 199 nodeset_ref, content_ref = self._diffref 200 nsstr_ref = self._format_nodeset(nodeset_ref) 201 nsstr = self._format_nodeset(nodeset) 202 if self.verbosity >= VERB_STD and self.node_count: 203 if len(nodeset_ref) > 1: 204 nsstr_ref += " (%d)" % len(nodeset_ref) 205 if len(nodeset) > 1: 206 nsstr += " (%d)" % len(nodeset) 207 208 udiff = difflib.unified_diff(list(content_ref), list(content), \ 209 fromfile=nsstr_ref, tofile=nsstr, \ 210 lineterm='') 211 output = "" 212 for line in udiff: 213 if line.startswith('---') or line.startswith('+++'): 214 output += self.color_diffhdr_fmt % line.rstrip() 215 elif line.startswith('@@'): 216 output += self.color_diffctx_fmt % line 217 elif line.startswith('+'): 218 output += self.color_diffadd_fmt % line 219 elif line.startswith('-'): 220 output += self.color_diffdel_fmt % line 221 else: 222 output += line 223 output += '\n' 224 self.out.write(output)
225
226 - def _print_lines(self, nodeset, msg):
227 """Display a MsgTree buffer by line with prefixed header.""" 228 out = self.out 229 if self.label: 230 header = self.color_stdout_fmt % \ 231 ("%s: " % self._format_nodeset(nodeset)) 232 for line in msg: 233 out.write("%s%s\n" % (header, line)) 234 else: 235 for line in msg: 236 out.write(line + '\n')
237
238 - def vprint(self, level, message):
239 """Utility method to print a message if verbose level is high 240 enough.""" 241 if self.verbosity >= level: 242 print message
243
244 - def vprint_err(self, level, message):
245 """Utility method to print a message on stderr if verbose level 246 is high enough.""" 247 if self.verbosity >= level: 248 print >> sys.stderr, message
249