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 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
|
#
# Copyright (C) 2010-2012 CEA/DAM
# Copyright (C) 2017-2018 Stephane Thiell <sthiell@stanford.edu>
#
# This file is part of ClusterShell.
#
# ClusterShell is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# ClusterShell is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with ClusterShell; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
"""
format dsh/pdsh-like output for humans and more
For help, type::
$ clubak --help
"""
from __future__ import print_function
import sys
from ClusterShell.MsgTree import MsgTree, MODE_DEFER, MODE_TRACE
from ClusterShell.NodeSet import NodeSet, NodeSetParseError, std_group_resolver
from ClusterShell.NodeSet import set_std_group_resolver_config
from ClusterShell.CLI.Display import Display, THREE_CHOICES
from ClusterShell.CLI.Display import sys_stdin
from ClusterShell.CLI.Error import GENERIC_ERRORS, handle_generic_error
from ClusterShell.CLI.OptionParser import OptionParser
from ClusterShell.CLI.Utils import nodeset_cmpkey
def display_tree(tree, disp, out):
"""display sub-routine for clubak -T (msgtree trace mode)"""
togh = True
offset = 2
reldepth = -offset
reldepths = {}
line_mode = disp.line_mode
for msgline, keys, depth, nchildren in tree.walk_trace():
if togh:
if depth in reldepths:
reldepth = reldepths[depth]
else:
reldepth = reldepths[depth] = reldepth + offset
nodeset = NodeSet.fromlist(keys)
if line_mode:
out.write(str(nodeset) + ':\n')
else:
out.write(disp.format_header(nodeset, reldepth))
out.write(' ' * reldepth + bytes(msgline).decode(errors='replace')
+ '\n')
togh = nchildren != 1
def display(tree, disp, gather, trace_mode, enable_nodeset_key):
"""nicely display MsgTree instance `tree' content according to
`disp' Display object and `gather' boolean flag"""
if trace_mode:
display_tree(tree, disp, sys.stdout)
sys.stdout.flush()
return
if gather:
if enable_nodeset_key:
# lambda to create a NodeSet from keys returned by walk()
ns_getter = lambda x: NodeSet.fromlist(x[1])
for nodeset in sorted((ns_getter(item) for item in tree.walk()),
key=nodeset_cmpkey):
disp.print_gather(nodeset, tree[nodeset[0]])
else:
for msg, key in tree.walk():
disp.print_gather_keys(key, msg)
else:
if enable_nodeset_key:
# nodes are automagically sorted by NodeSet
for node in NodeSet.fromlist(tree.keys()).nsiter():
disp.print_gather(node, tree[str(node)])
else:
for key in tree.keys():
disp.print_gather_keys([ key ], tree[key])
def clubak():
"""script subroutine"""
# Argument management
parser = OptionParser("%prog [options]")
parser.install_groupsconf_option()
parser.install_display_options(verbose_options=True,
separator_option=True,
dshbak_compat=True,
msgtree_mode=True)
options = parser.parse_args()[0]
set_std_group_resolver_config(options.groupsconf)
if options.interpret_keys == THREE_CHOICES[-1]: # auto?
enable_nodeset_key = None # AUTO
else:
enable_nodeset_key = (options.interpret_keys == THREE_CHOICES[2])
# Create new message tree
if options.trace_mode:
tree_mode = MODE_TRACE
else:
tree_mode = MODE_DEFER
tree = MsgTree(mode=tree_mode)
fast_mode = options.fast_mode
if fast_mode:
if tree_mode != MODE_DEFER or options.line_mode:
parser.error("incompatible tree options")
preload_msgs = {}
separator = options.separator.encode()
# Feed the tree from standard input lines
for line in sys_stdin():
try:
linestripped = line.rstrip(b'\r\n')
if options.verbose or options.debug:
sys.stdout.write('INPUT ' +
linestripped.decode(errors='replace') + '\n')
key, content = linestripped.split(separator, 1)
# NodeSet requires encoded string
key = key.strip().decode(errors='replace')
if not key:
raise ValueError("no node found")
if enable_nodeset_key is False: # interpret-keys=never?
keyset = [ key ]
else:
try:
keyset = NodeSet(key)
except NodeSetParseError:
if enable_nodeset_key: # interpret-keys=always?
raise
enable_nodeset_key = False # auto => switch off
keyset = [ key ]
if fast_mode:
for node in keyset:
preload_msgs.setdefault(node, []).append(content)
else:
for node in keyset:
tree.add(node, content)
except ValueError as ex:
raise ValueError('%s: "%s"' %
(ex, linestripped.decode(errors='replace')))
if fast_mode:
# Messages per node have been aggregated, now add to tree one
# full msg per node
for key, wholemsg in preload_msgs.items():
tree.add(key, b'\n'.join(wholemsg))
# Display results
try:
disp = Display(options)
except ValueError as exc:
parser.error("option mismatch (%s)" % exc)
return
if options.debug:
std_group_resolver().set_verbosity(1)
print("clubak: line_mode=%s gather=%s tree_depth=%d"
% (bool(options.line_mode), bool(disp.gather), tree._depth()),
file=sys.stderr)
display(tree, disp, disp.gather or disp.regroup, \
options.trace_mode, enable_nodeset_key is not False)
def main():
"""main script function"""
try:
clubak()
except GENERIC_ERRORS as ex:
sys.exit(handle_generic_error(ex))
except ValueError as ex:
print("%s:" % sys.argv[0], ex, file=sys.stderr)
sys.exit(1)
sys.exit(0)
if __name__ == '__main__':
main()
|