1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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
64
65 WHENCOLOR_CHOICES = ["never", "always", "auto"]
66
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
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
92
98 line_mode = property(_getlmode, _setlmode)
99
105
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
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
123 """Generic method for displaying nodeset/content according to current
124 object settings."""
125 return self._display(nodeset, obj)
126
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
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
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
154
170
172 """Main clubak script function"""
173
174
175
176 usage = "%prog [options]"
177 parser = optparse.OptionParser(usage, version="%%prog %s" % __version__)
178
179
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
205 tree = MsgTree()
206
207
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
220 color = False
221 if options.whencolor == "auto":
222 color = sys.stdout.isatty()
223 elif options.whencolor == "always":
224 color = True
225
226
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)
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