diff --git a/confluent_client/bin/collate b/confluent_client/bin/collate new file mode 100644 index 00000000..ecaa0523 --- /dev/null +++ b/confluent_client/bin/collate @@ -0,0 +1,76 @@ +#!/usr/bin/env python +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2017 Lenovo +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This is a utility to bring xcoll (plus enhancements) to confluent +# the core engine is textgroup.py, this simply provides a CLI to use +# generically + +import optparse +import os +import sys + +path = os.path.dirname(os.path.realpath(__file__)) +path = os.path.realpath(os.path.join(path, '..', 'lib', 'python')) +if path.startswith('/opt'): + sys.path.append(path) + +import confluent.textgroup as tg + +argparser = optparse.OptionParser(usage="Usage: | %prog [options]") +argparser.add_option('-d', '--diff', action='store_true', + help='Show what differs between most common ' + 'output group and others') +argparser.add_option('-w', '--watch', action='store_true', + help='Show intermediate results while running') +argparser.add_option('-s', '--skipcommon', action='store_true', + help='Do not print most common result, only non modal ' + 'groups, useful when combined with -d') +argparser.add_option('-c', '--count', action='store_true', + help='Also display count of nodes in a given group') +argparser.add_option('-r', '--reverse', action='store_true', + help='Reverse sort order to show biggest output group ' + 'last') +(options, args) = argparser.parse_args() +if sys.stdin.isatty(): + argparser.print_help() + sys.exit(1) + +grouped = tg.GroupedData() + +def print_current(): + if options.diff: + grouped.print_deviants(skipmodal=options.skipcommon, count=options.count, + reverse=options.reverse) + else: + grouped.print_all(skipmodal=options.skipcommon, + count=options.count, + reverse=options.reverse) + sys.stdout.flush() + +fullline = sys.stdin.readline() +while fullline: + for line in fullline.split('\n'): + if not line: + continue + grouped.add_line(*line.split(': ', 1)) + if options.watch: + sys.stdout.write('\x1b[2J\x1b[;H') # clear screen + print_current() + fullline = sys.stdin.readline() +if not options.watch: + print_current() + diff --git a/confluent_client/bin/noderun b/confluent_client/bin/noderun index abbb7ca6..4487c1ac 100755 --- a/confluent_client/bin/noderun +++ b/confluent_client/bin/noderun @@ -83,8 +83,10 @@ def run(): node = desc['node'] if desc['type'] == 'stdout': sys.stdout.write('{0}: {1}'.format(node,data)) + sys.stdout.flush() else: sys.stderr.write('{0}: {1}'.format(node, data)) + sys.stderr.flush() else: pop = desc['popen'] ret = pop.poll() diff --git a/confluent_client/bin/nodesensors b/confluent_client/bin/nodesensors index 3b7c9618..60e69cac 100755 --- a/confluent_client/bin/nodesensors +++ b/confluent_client/bin/nodesensors @@ -146,6 +146,7 @@ def sensorpass(showout=True, appendtime=False): '%Y-%m-%dT%H:%M:%S') print(u'{0}: {1}:{2}'.format( node, sensedata['name'], showval).encode('utf-8')) + sys.stdout.flush() return resultdata diff --git a/confluent_client/bin/nodeshell b/confluent_client/bin/nodeshell index feb9840c..061bc869 100755 --- a/confluent_client/bin/nodeshell +++ b/confluent_client/bin/nodeshell @@ -83,8 +83,10 @@ def run(): node = desc['node'] if desc['type'] == 'stdout': sys.stdout.write('{0}: {1}'.format(node,data)) + sys.stdout.flush() else: sys.stderr.write('{0}: {1}'.format(node, data)) + sys.stderr.flush() else: pop = desc['popen'] ret = pop.poll() diff --git a/confluent_client/confluent/client.py b/confluent_client/confluent/client.py index f9283dd0..4b031697 100644 --- a/confluent_client/confluent/client.py +++ b/confluent_client/confluent/client.py @@ -27,6 +27,10 @@ import confluent.tlvdata as tlvdata SO_PASSCRED = 16 +def cprint(txt): + print(txt) + sys.stdout.flush() + def _parseserver(string): if ']:' in string: server, port = string[1:].split(']:') @@ -105,10 +109,10 @@ class Command(object): else: val = repr(res[node][ikey]) if self._prevkeyname and self._prevkeyname in res[node]: - print('{0}: {2}->{1}'.format( + cprint('{0}: {2}->{1}'.format( node, val, res[node][self._prevkeyname]['value'])) else: - print('{0}: {1}'.format(node, val)) + cprint('{0}: {1}'.format(node, val)) return rc def simple_noderange_command(self, noderange, resource, input=None, @@ -133,7 +137,7 @@ class Command(object): rc = self.handle_results(ikey, rc, res, errnodes) return rc except KeyboardInterrupt: - print('') + cprint('') return 0 def simple_nodegroups_command(self, noderange, resource, input=None, key=None, **kwargs): @@ -157,7 +161,7 @@ class Command(object): rc = self.handle_results(ikey, rc, res) return rc except KeyboardInterrupt: - print('') + cprint('') return 0 def read(self, path, parameters=None): @@ -239,7 +243,7 @@ class Command(object): "MISMATCHED CERTIFICATE DATA, ACCEPT NEW? (y/n):") if replace not in ('y', 'Y'): raise Exception("BAD CERTIFICATE") - print 'Adding new key for %s:%s' % (server, port) + cprint('Adding new key for %s:%s' % (server, port)) khf[hostid] = fingerprint @@ -318,7 +322,7 @@ def printattributes(session, requestargs, showtype, nodetype, noderange, options dictout.append("{0}={1}".format(k, v)) attrout = '{0}: {1}: {2}'.format(node, attr, ','.join(map(str, dictout))) else: - print ("CODE ERROR" + repr(attr)) + cprint("CODE ERROR" + repr(attr)) if options.blame or 'broken' in currattr: blamedata = [] @@ -332,7 +336,7 @@ def printattributes(session, requestargs, showtype, nodetype, noderange, options currattr['expression'])) if blamedata: attrout += ' (' + ', '.join(blamedata) + ')' - print attrout + cprint(attrout) if not exitcode: if requestargs: for attr in requestargs: @@ -379,8 +383,8 @@ def printgroupattributes(session, requestargs, showtype, nodetype, noderange, op dictout.append("{0}={1}".format(k, v)) attrout = '{0}: {1}: {2}'.format(noderange, attr, ','.join(map(str, dictout))) else: - print ("CODE ERROR" + repr(attr)) - print attrout + cprint("CODE ERROR" + repr(attr)) + cprint(attrout) if not exitcode: if requestargs: for attr in requestargs: diff --git a/confluent_client/confluent/textgroup.py b/confluent_client/confluent/textgroup.py index 507571f0..0ddc7622 100644 --- a/confluent_client/confluent/textgroup.py +++ b/confluent_client/confluent/textgroup.py @@ -73,10 +73,41 @@ class GroupedData(object): else: self.bynode[node].append(line) - def print_deviants(self, output=sys.stdout, skipmodal=True): + def print_all(self, output=sys.stdout, skipmodal=False, reverse=False, + count=False): self.generate_byoutput() modaloutput = None ismodal = True + outdatalist = sorted( + self.byoutput, key=lambda x: len(self.byoutput[x])) + if not reverse: + outdatalist = reversed(outdatalist) + if reverse and skipmodal: + # if reversed, the last is biggest and should be skipped if modal + outdatalist = outdatalist[:-1] + for outdata in outdatalist: + if not reverse and skipmodal: + # If big first, this makes skipmodal skip first + skipmodal = False + continue + currout = '====================================\n' + currout += ','.join(sorted(self.byoutput[outdata])) + currout += '\n====================================\n' + if count: + currout += 'Count: {0}'.format(len(list( + self.byoutput[outdata]))) + currout += '\n====================================\n' + currout += outdata + currout += '\n\n' + output.write(currout) + output.flush() + + def print_deviants(self, output=sys.stdout, skipmodal=False, reverse=False, + count=False): + self.generate_byoutput() + modaloutput = None + ismodal = True + revoutput = [] for outdata in reversed( sorted(self.byoutput, key=lambda x: len(self.byoutput[x]))): if modaloutput is None: @@ -85,16 +116,26 @@ class GroupedData(object): skipmodal = False ismodal = False continue - output.write('====================================\n') - output.write(','.join(sorted(self.byoutput[outdata]))) - output.write('\n====================================\n') + currout = '====================================\n' + currout += ','.join(sorted(self.byoutput[outdata])) + currout += '\n====================================\n' + if count: + currout += 'Count: {0}'.format(len(list( + self.byoutput[outdata]))) + currout += '\n====================================\n' if ismodal: ismodal = False - output.write(outdata) + currout += outdata else: - output.write('\n'.join(colordiff(modaloutput.split('\n'), - outdata.split('\n')))) - output.write('\n\n') + currout += '\n'.join(colordiff(modaloutput.split('\n'), + outdata.split('\n'))) + currout += '\n\n' + if reverse: + revoutput.append(currout) + else: + output.write(currout) + for currout in reversed(revoutput): + output.write(currout) output.flush() if __name__ == '__main__':