Module clubak
[hide private]
[frames] | no frames]

Source Code for Module clubak

  1  #!/usr/bin/env python 
  2  # 
  3  # Copyright CEA/DAM/DIF (2010) 
  4  #  Contributor: Stephane THIELL <stephane.thiell@cea.fr> 
  5  # 
  6  # This file is part of the ClusterShell library. 
  7  # 
  8  # This software is governed by the CeCILL-C license under French law and 
  9  # abiding by the rules of distribution of free software.  You can  use, 
 10  # modify and/ or redistribute the software under the terms of the CeCILL-C 
 11  # license as circulated by CEA, CNRS and INRIA at the following URL 
 12  # "http://www.cecill.info". 
 13  # 
 14  # As a counterpart to the access to the source code and  rights to copy, 
 15  # modify and redistribute granted by the license, users are provided only 
 16  # with a limited warranty  and the software's author,  the holder of the 
 17  # economic rights,  and the successive licensors  have only  limited 
 18  # liability. 
 19  # 
 20  # In this respect, the user's attention is drawn to the risks associated 
 21  # with loading,  using,  modifying and/or developing or reproducing the 
 22  # software by the user in light of its specific status of free software, 
 23  # that may mean  that it is complicated to manipulate,  and  that  also 
 24  # therefore means  that it is reserved for developers  and  experienced 
 25  # professionals having in-depth computer knowledge. Users are therefore 
 26  # encouraged to load and test the software's suitability as regards their 
 27  # requirements in conditions enabling the security of their systems and/or 
 28  # data to be ensured and,  more generally, to use and operate it in the 
 29  # same conditions as regards security. 
 30  # 
 31  # The fact that you are presently reading this means that you have had 
 32  # knowledge of the CeCILL-C license and that you accept its terms. 
 33  # 
 34  # $Id: clubak.py 303 2010-07-27 19:29:43Z st-cea $ 
 35   
 36  """ 
 37  clubak formats clush/dsh/pdsh output for humans. 
 38   
 39  For help, type:: 
 40      $ clubak --help 
 41  """ 
 42   
 43  from itertools import imap 
 44  import optparse 
 45  import signal 
 46  import sys 
 47   
 48  from ClusterShell.NodeUtils import GroupResolverConfigError 
 49  from ClusterShell.NodeUtils import GroupResolverSourceError 
 50  from ClusterShell.NodeUtils import GroupSourceException 
 51  from ClusterShell.NodeUtils import GroupSourceNoUpcall 
 52  try: 
 53      from ClusterShell.MsgTree import MsgTree 
 54      from ClusterShell.NodeSet import NodeSet 
 55      from ClusterShell.NodeSet import NodeSetExternalError, NodeSetParseError 
 56      from ClusterShell import __version__ 
 57  except GroupResolverConfigError, e: 
 58      print >> sys.stderr, \ 
 59          "ERROR: ClusterShell Groups configuration error:\n\t%s" % e 
 60      sys.exit(1) 
 61   
 62   
 63  # Start of clush.py common code 
 64   
 65  WHENCOLOR_CHOICES = ["never", "always", "auto"] 
 66   
67 -class Display(object):
68 """ 69 Output display class for clush script. 70 """ 71 COLOR_STDOUT_FMT = "\033[34m%s\033[0m" 72 COLOR_STDERR_FMT = "\033[31m%s\033[0m" 73 SEP = "-" * 15 74
75 - def __init__(self, color=True):
76 self._color = color 77 self._display = self._print_buffer 78 self.out = sys.stdout 79 self.err = sys.stderr 80 self.label = True 81 self.regroup = False 82 self.groupsource = None 83 if self._color: 84 self.color_stdout_fmt = self.COLOR_STDOUT_FMT 85 self.color_stderr_fmt = self.COLOR_STDERR_FMT 86 else: 87 self.color_stdout_fmt = self.color_stderr_fmt = "%s" 88 self.noprefix = False
89
90 - def _getlmode(self):
91 return self._display == self._print_lines
92
93 - def _setlmode(self, value):
94 if value: 95 self._display = self._print_lines 96 else: 97 self._display = self._print_buffer
98 line_mode = property(_getlmode, _setlmode) 99
100 - def _format_header(self, nodeset):
101 """Format nodeset-based header.""" 102 if self.regroup: 103 return nodeset.regroup(self.groupsource, noprefix=self.noprefix) 104 return str(nodeset)
105
106 - def print_line(self, nodeset, line):
107 """Display a line with optional label.""" 108 if self.label: 109 prefix = self.color_stdout_fmt % ("%s: " % nodeset) 110 self.out.write("%s%s\n" % (prefix, line)) 111 else: 112 self.out.write("%s\n", line)
113
114 - def print_line_error(self, nodeset, line):
115 """Display an error line with optional label.""" 116 if self.label: 117 prefix = self.color_stderr_fmt % ("%s: " % nodeset) 118 self.err.write("%s%s\n" % (prefix, line)) 119 else: 120 self.err.write("%s\n", line)
121
122 - def print_gather(self, nodeset, obj):
123 """Generic method for displaying nodeset/content according to current 124 object settings.""" 125 return self._display(nodeset, obj)
126
127 - def _print_buffer(self, nodeset, content):
128 """Display a dshbak-like header block and content.""" 129 header = self.color_stdout_fmt % ("%s\n%s\n%s\n" % (self.SEP, 130 self._format_header(nodeset), 131 self.SEP)) 132 self.out.write("%s%s\n" % (header, content))
133
134 - def _print_lines(self, nodeset, msg):
135 """Display a MsgTree buffer by line with prefixed header.""" 136 header = self.color_stdout_fmt % \ 137 ("%s: " % self._format_header(nodeset)) 138 for line in msg: 139 self.out.write("%s%s\n" % (header, line))
140
141 -def nodeset_cmp(ns1, ns2):
142 """Compare 2 nodesets by their length (we want larger nodeset 143 first) and then by first node.""" 144 len_cmp = cmp(len(ns2), len(ns1)) 145 if not len_cmp: 146 smaller = NodeSet.fromlist([ns1[0], ns2[0]])[0] 147 if smaller == ns1[0]: 148 return -1 149 else: 150 return 1 151 return len_cmp
152 153 # End of clush.py common code 154
155 -def display(tree, gather, disp):
156 """Display results""" 157 try: 158 if gather: 159 # lambda to create a NodeSet from keys list returned by walk() 160 ns_getter = lambda x: NodeSet.fromlist(x[1]) 161 for nodeset in sorted(imap(ns_getter, tree.walk()), 162 cmp=nodeset_cmp): 163 disp.print_gather(nodeset, tree[nodeset[0]]) 164 else: 165 # nodes are automagically sorted by NodeSet 166 for node in NodeSet.fromlist(tree.keys()): 167 disp.print_gather(node, tree[node]) 168 finally: 169 sys.stdout.flush()
170
171 -def clubak():
172 """Main clubak script function""" 173 # 174 # Argument management 175 # 176 usage = "%prog [options]" 177 parser = optparse.OptionParser(usage, version="%%prog %s" % __version__) 178 179 # Set parsing to stop on the first non-option 180 parser.disable_interspersed_args() 181 182 parser.add_option("-b", "-c", action="store_true", dest="gather", 183 help="gather nodes with same output (-c is provided " \ 184 "for dshbak compatibility)") 185 parser.add_option("-d", "--debug", action="store_true", dest="debug", 186 help="output more messages for debugging purpose") 187 parser.add_option("-L", action="store_true", dest="line_mode", 188 help="disable header block and order output by nodes") 189 parser.add_option("-r", "--regroup", action="store_true", dest="regroup", 190 default=False, help="fold nodeset using node groups") 191 parser.add_option("-s", "--groupsource", action="store", dest="groupsource", 192 help="optional groups.conf(5) group source to use") 193 parser.add_option("-G", "--groupbase", action="store_true", 194 dest="groupbase", default=False, help="do not display " \ 195 "group source prefix") 196 parser.add_option("-S", "--separator", action="store", dest="separator", 197 default=':', help="node / line content separator " \ 198 "string (default: ':')") 199 parser.add_option("--color", action="store", dest="whencolor", 200 choices=WHENCOLOR_CHOICES, 201 help="whether to use ANSI colors (never, always or auto)") 202 options = parser.parse_args()[0] 203 204 # Create new message tree 205 tree = MsgTree() 206 207 # Feed the tree from standard input lines 208 for line in sys.stdin: 209 node, content = line.rstrip('\r\n').split(options.separator, 1) 210 node = node.strip() 211 if not node: 212 raise ValueError("No node found for line: %s" % line.rstrip('\r\n')) 213 tree.add(node, content) 214 215 if options.debug: 216 print >> sys.stderr, "clubak: line_mode=%s gather=%s tree_depth=%d" % \ 217 (bool(options.line_mode), bool(options.gather), tree._depth()) 218 219 # Should we use ANSI colors? 220 color = False 221 if options.whencolor == "auto": 222 color = sys.stdout.isatty() 223 elif options.whencolor == "always": 224 color = True 225 226 # Display results 227 disp = Display(color) 228 disp.line_mode = options.line_mode 229 disp.label = True 230 disp.regroup = options.regroup 231 disp.groupsource = options.groupsource 232 disp.noprefix = options.groupbase 233 display(tree, options.gather, disp) 234 sys.exit(0)
235 236 if __name__ == '__main__': 237 try: 238 clubak() 239 except NodeSetExternalError, e: 240 print >> sys.stderr, "clubak: external error:", e 241 sys.exit(1) 242 except NodeSetParseError, e: 243 print >> sys.stderr, "clubak: parse error:", e 244 sys.exit(1) 245 except GroupResolverSourceError, e: 246 print >> sys.stderr, "ERROR: unknown group source: \"%s\"" % e 247 sys.exit(1) 248 except GroupSourceNoUpcall, e: 249 print >> sys.stderr, "ERROR: no %s upcall defined for group " \ 250 "source \"%s\"" % (e, e.group_source.name) 251 sys.exit(1) 252 except GroupSourceException, e: 253 print >> sys.stderr, "ERROR: other group error:", e 254 sys.exit(1) 255 except IOError: 256 sys.exit(1) # exit with error on broken pipe 257 except KeyboardInterrupt, e: 258 sys.exit(128 + signal.SIGINT) 259 except ValueError, e: 260 print >> sys.stderr, "clubak:", e 261 sys.exit(1) 262