From 6dd555cd0918b4b5f7bed018dd1ae0ba86cf0803 Mon Sep 17 00:00:00 2001 From: nickolay-github Date: Thu, 19 Nov 2020 22:57:50 +0300 Subject: [PATCH 1/2] implement csv table with logs --- SSHKeyDistribut0r/key_distribut0r.py | 63 ++++++++++++++++++++-------- 1 file changed, 46 insertions(+), 17 deletions(-) diff --git a/SSHKeyDistribut0r/key_distribut0r.py b/SSHKeyDistribut0r/key_distribut0r.py index d98a373..ba8d702 100644 --- a/SSHKeyDistribut0r/key_distribut0r.py +++ b/SSHKeyDistribut0r/key_distribut0r.py @@ -8,10 +8,11 @@ import re import socket import sys -import yaml +import csv import paramiko import scp +import yaml logging.raiseExceptions = False @@ -26,6 +27,9 @@ YAML_EXT = re.compile("^\\.ya?ml$") JSON_EXT = re.compile("^\\.json$") +ERROR_STATUS = '✗ Error' +SUCCESS_STATUS = '✓ Success' + def remove_special_chars(original_string): return ''.join(e for e in original_string if e.isalnum()) @@ -63,10 +67,21 @@ def read_config(config_file): sys.exit(1) +def create_result_csv_table(messages): + messages.sort(key=lambda m: m[0] == ERROR_STATUS) + try: + with open('ssh_keys_distributor_result.csv', 'w', encoding='utf-8') as file: + writer = csv.writer(file, delimiter='|') + writer.writerows(messages) + except OSError as e: + print(e) + + def main(args): # Load config files servers = read_config(args.server) keys = read_config(args.keys) + messages = [('Status', 'Ip', 'Comment', 'Description')] for server in servers: if server['authorized_users']: @@ -85,7 +100,9 @@ def main(args): key_stream.write('%s\n' % key) if args.dry_run: - server_info_log(server['ip'], server['comment'], ', '.join(server_users)) + msg = server['ip'], server['comment'], ', '.join(server_users) + server_info_log(*msg) + messages.append((SUCCESS_STATUS, *msg)) else: # Configure SSH client ssh_client = paramiko.SSHClient() @@ -102,25 +119,37 @@ def main(args): scp_client.putfo(key_stream, '.ssh/authorized_keys') key_stream.close() - server_info_log(server['ip'], server['comment'], ', '.join(server_users)) + msg = server['ip'], server['comment'], ', '.join(server_users) + server_info_log(*msg) + messages.append((SUCCESS_STATUS, *msg)) except paramiko.ssh_exception.PasswordRequiredException: - server_error_log( - server['ip'], - server['comment'], - 'The private key file is protected by a passphrase, which is currently not supported.' - ) + msg = server['ip'], \ + server['comment'], \ + 'The private key file is protected by a passphrase, which is currently not supported.' + server_error_log(*msg) + messages.append((ERROR_STATUS, msg)) except paramiko.ssh_exception.AuthenticationException: - server_error_log( - server['ip'], - server['comment'], - 'Cannot connect to server because of an authentication problem.' - ) + msg = server['ip'],\ + server['comment'], \ + 'Cannot connect to server because of an authentication problem.' + server_error_log(*msg) + messages.append((ERROR_STATUS, *msg)) except scp.SCPException: - server_error_log(server['ip'], server['comment'], 'Cannot send file to server.') + msg = server['ip'], server['comment'], 'Cannot send file to server.' + server_error_log(*msg) + messages.append((ERROR_STATUS, *msg)) except (paramiko.ssh_exception.NoValidConnectionsError, paramiko.ssh_exception.SSHException): - server_error_log(server['ip'], server['comment'], 'Cannot connect to server.') + msg = server['ip'], server['comment'], 'Cannot connect to server.' + server_error_log(*msg) + messages.append((ERROR_STATUS, *msg)) except socket.timeout: - server_error_log(server['ip'], server['comment'], 'Cannot connect to server because of a timeout.') + msg = server['ip'], server['comment'], 'Cannot connect to server because of a timeout.' + server_error_log(*msg) + messages.append((ERROR_STATUS, *msg)) else: - server_error_log(server['ip'], server['comment'], 'No user mentioned in configuration file!') + msg = server['ip'], server['comment'], 'No user mentioned in configuration file!' + server_error_log(*msg) + messages.append((ERROR_STATUS, *msg)) + create_result_csv_table(messages) + From bcf4725859d2704b9bf702c975c0aeb9cbf52f14 Mon Sep 17 00:00:00 2001 From: nickolay-github Date: Mon, 15 Mar 2021 02:55:41 +0300 Subject: [PATCH 2/2] implement logs in the form of a table --- SSHKeyDistribut0r/command_line.py | 15 ++++++----- SSHKeyDistribut0r/key_distribut0r.py | 39 ++++++++++++++++++++++++---- 2 files changed, 42 insertions(+), 12 deletions(-) diff --git a/SSHKeyDistribut0r/command_line.py b/SSHKeyDistribut0r/command_line.py index f157505..85a6756 100644 --- a/SSHKeyDistribut0r/command_line.py +++ b/SSHKeyDistribut0r/command_line.py @@ -16,16 +16,17 @@ def main(): print('Welcome to the world of key distribution!') print() - parser = argparse.ArgumentParser( - description='A tool to automate key distribution with user authorization.') + parser = argparse.ArgumentParser(description='A tool to automate key distribution with user authorization.') parser.add_argument('--dry-run', '-n', action='store_true', - help='show pending changes without applying them') + help='show pending changes without applying them') parser.add_argument('--keys', '-k', - default='%s/%s/keys.yml' % (appdirs.user_config_dir(), prog), - help="path to keys file\n(default: '%(default)s')") + default='%s/%s/keys.yml' % (appdirs.user_config_dir(), prog), + help="path to keys file\n(default: '%(default)s')") parser.add_argument('--server', '-s', - default='%s/%s/servers.yml' % (appdirs.user_config_dir(), prog), - help="path to server file (default: '%(default)s')") + default='%s/%s/servers.yml' % (appdirs.user_config_dir(), prog), + help="path to server file (default: '%(default)s')") + parser.add_argument('--export-csv-path', '-f', + help="path to csv export file (example: 'keys_distributor_result.csv')") args = parser.parse_args() try: diff --git a/SSHKeyDistribut0r/key_distribut0r.py b/SSHKeyDistribut0r/key_distribut0r.py index ba8d702..bcf86b9 100644 --- a/SSHKeyDistribut0r/key_distribut0r.py +++ b/SSHKeyDistribut0r/key_distribut0r.py @@ -27,8 +27,8 @@ YAML_EXT = re.compile("^\\.ya?ml$") JSON_EXT = re.compile("^\\.json$") -ERROR_STATUS = '✗ Error' -SUCCESS_STATUS = '✓ Success' +ERROR_STATUS = 'Error' +SUCCESS_STATUS = 'Success' def remove_special_chars(original_string): @@ -67,10 +67,37 @@ def read_config(config_file): sys.exit(1) -def create_result_csv_table(messages): +def get_maximum_column_lengths(messages): + column_count = len(messages[0]) + column_max_lens = {i: max(len(message[i]) for message in messages) for i in range(column_count)} + return column_max_lens + + +def print_table_log(messages): + messages.sort(key=lambda m: m[0] == ERROR_STATUS) + + max_column_lens = get_maximum_column_lengths(messages) + + def print_borderline(): + column_count = len(messages[0]) + empty_columns = ('' for _ in range(column_count)) + print("+{:-^{lens[0]}}+{:-^{lens[1]}}+{:-^{lens[2]}}+{:-^{lens[3]}}+".format(*empty_columns, + lens=max_column_lens)) + print() + print_borderline() + for message in messages[1:]: + color_on = COLOR_RED if message[0] == ERROR_STATUS else COLOR_GREEN + clear_message = (re.sub(r'\s+', ' ', col) for col in message) + print("|{color_on}{:^{lens[0]}}{color_off}" + "|{:^{lens[1]}}|{:^{lens[2]}}" + "|{:^{lens[3]}}|".format(*clear_message, color_on=color_on, color_off=COLOR_END, lens=max_column_lens)) + print_borderline() + + +def export_to_csv(path, messages): try: - with open('ssh_keys_distributor_result.csv', 'w', encoding='utf-8') as file: + with open(path, 'w', encoding='utf-8') as file: writer = csv.writer(file, delimiter='|') writer.writerows(messages) except OSError as e: @@ -151,5 +178,7 @@ def main(args): msg = server['ip'], server['comment'], 'No user mentioned in configuration file!' server_error_log(*msg) messages.append((ERROR_STATUS, *msg)) - create_result_csv_table(messages) + print_table_log(messages) + if args.export_csv_path: + export_to_csv(args.export_csv_path, messages)