233 lines
		
	
	
		
			8.3 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			233 lines
		
	
	
		
			8.3 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
#!/usr/bin/env python3
 | 
						|
 | 
						|
# Copyright (c) 2019 MariaDB Corporation Ab
 | 
						|
#
 | 
						|
# Use of this software is governed by the Business Source License included
 | 
						|
# in the LICENSE.TXT file and at www.mariadb.com/bsl11.
 | 
						|
#
 | 
						|
# Change Date: 2022-01-01
 | 
						|
#
 | 
						|
# On the date above, in accordance with the Business Source License, use
 | 
						|
# of this software will be governed by version 2 or later of the General
 | 
						|
# Public License.
 | 
						|
 | 
						|
import os
 | 
						|
import sys
 | 
						|
import re
 | 
						|
import fnmatch
 | 
						|
import zipfile
 | 
						|
import subprocess
 | 
						|
import datetime
 | 
						|
 | 
						|
 | 
						|
def main(argv):
 | 
						|
    maxctrl_un = ""
 | 
						|
    maxctrl_pw = ""
 | 
						|
    if len(argv) == 2 or len(argv) > 3:
 | 
						|
        print("Usage: {} [<maxctr_username> <maxctrl_password]".format(argv[0]))
 | 
						|
    elif len(argv) == 3:
 | 
						|
        maxctrl_un = argv[1]
 | 
						|
        maxctrl_pw = argv[2]
 | 
						|
 | 
						|
    main_conf_file_path = "/etc/maxscale.cnf"
 | 
						|
    config_files_dir = "/etc/maxscale.cnf.d/"
 | 
						|
    runtime_config_files_dir = "/var/lib/maxscale/maxscale.cnf.d/"
 | 
						|
    log_files_dir = "/var/log/maxscale/"
 | 
						|
 | 
						|
    time_now = datetime.datetime.now().strftime("%y%m%d_%H%M%S")
 | 
						|
    output_file_path = os.getcwd() + "/" + "diagnostics_files_" + time_now + ".zip"
 | 
						|
 | 
						|
    print("Assembling typical config and log files for submitting to support.")
 | 
						|
    print("Using the following paths for config files: " + main_conf_file_path +
 | 
						|
          ", " + config_files_dir + ", " + runtime_config_files_dir)
 | 
						|
    print("Using the following paths for log files: " + log_files_dir)
 | 
						|
    print("Output is written to " + output_file_path)
 | 
						|
    print("\n")
 | 
						|
 | 
						|
    try:
 | 
						|
        output_file = zipfile.ZipFile(output_file_path, mode='w',
 | 
						|
                                      compression=zipfile.ZIP_DEFLATED)
 | 
						|
    except IOError as e:
 | 
						|
        print("Error when opening file " + output_file_path + ": " + e.strerror)
 | 
						|
        return
 | 
						|
 | 
						|
    format_str = "Adding file {} to archive."
 | 
						|
    # Write contents of primary config file
 | 
						|
    contents = get_config_file(main_conf_file_path)
 | 
						|
    if len(contents) > 0:
 | 
						|
        print(format_str.format(main_conf_file_path))
 | 
						|
        output_file.writestr(main_conf_file_path, contents)
 | 
						|
 | 
						|
    # Write contents of additional config files
 | 
						|
    add_config_files_from_dir(config_files_dir, output_file)
 | 
						|
 | 
						|
    # Write contents of runtime config files
 | 
						|
    add_config_files_from_dir(runtime_config_files_dir, output_file)
 | 
						|
 | 
						|
    # Write contents of log files
 | 
						|
    if os.path.isdir(log_files_dir):
 | 
						|
        for file in os.listdir(log_files_dir):
 | 
						|
            if fnmatch.fnmatch(file, "*.log"):
 | 
						|
                file_path = log_files_dir + file
 | 
						|
                contents = read_log_file(file_path)
 | 
						|
                if len(contents) > 0:
 | 
						|
                    print(format_str.format(file_path))
 | 
						|
                    output_file.writestr(file_path, contents)
 | 
						|
 | 
						|
    # Run maxctrl and add output
 | 
						|
    contents = run_max_ctrl(maxctrl_un, maxctrl_pw)
 | 
						|
    if len(contents) > 0:
 | 
						|
        file_name = "maxctrl_output.txt"
 | 
						|
        print(format_str.format(file_name))
 | 
						|
        output_file.writestr(file_name, contents)
 | 
						|
 | 
						|
    # Run gdb and add output
 | 
						|
    contents = read_core_file()
 | 
						|
    if len(contents) > 0:
 | 
						|
        file_name = "gdb_output.txt"
 | 
						|
        print(format_str.format(file_name))
 | 
						|
        output_file.writestr(file_name, contents)
 | 
						|
 | 
						|
    # Run some commands to gather general system info.
 | 
						|
    contents = get_system_info()
 | 
						|
    if len(contents) > 0:
 | 
						|
        file_name = "system_info.txt"
 | 
						|
        print(format_str.format(file_name))
 | 
						|
        output_file.writestr(file_name, contents)
 | 
						|
 | 
						|
    output_file.close()
 | 
						|
 | 
						|
 | 
						|
def get_config_file(path):
 | 
						|
    lines = []
 | 
						|
    regex = re.compile("(password|passwd)\s*=\s*(\S+)")
 | 
						|
    if os.path.isfile(path):
 | 
						|
        try:
 | 
						|
            file = open(path, 'r')
 | 
						|
        except IOError as e:
 | 
						|
            print("Error when opening file " + path + ": " + e.strerror)
 | 
						|
        else:
 | 
						|
            for line in file:
 | 
						|
                # If the line looks like it contains a password, only print ***
 | 
						|
                match = regex.search(line)
 | 
						|
                if match:
 | 
						|
                    pw_inds = [match.start(2), match.end(2)]
 | 
						|
                    censored_line = line[0:(pw_inds[0])] + "***" + line[(pw_inds[1]):]
 | 
						|
                    lines.append(censored_line)
 | 
						|
                else:
 | 
						|
                    lines.append(line)
 | 
						|
            file.close()
 | 
						|
    else:
 | 
						|
        print("File " + path + " was not found.")
 | 
						|
 | 
						|
    return ''.join(lines)
 | 
						|
 | 
						|
 | 
						|
def add_config_files_from_dir(directory_to_add, output_file):
 | 
						|
    format_str = "Adding file {} to archive."
 | 
						|
    if os.path.isdir(directory_to_add):
 | 
						|
        for file in os.listdir(directory_to_add):
 | 
						|
            if fnmatch.fnmatch(file, "*.cnf"):
 | 
						|
                file_path = directory_to_add + file
 | 
						|
                contents = get_config_file(file_path)
 | 
						|
                if len(contents) > 0:
 | 
						|
                    print(format_str.format(file_path))
 | 
						|
                    output_file.writestr(file_path, contents)
 | 
						|
 | 
						|
 | 
						|
def read_log_file(path):
 | 
						|
    # Log files can be large, so only read at most N bytes from the end
 | 
						|
    max_byte_count = 500000
 | 
						|
    contents = ""
 | 
						|
    if os.path.isfile(path):
 | 
						|
        try:
 | 
						|
            file = open(path, 'r')
 | 
						|
        except IOError as e:
 | 
						|
            print("Error when opening file " + path + ": " + e.strerror)
 | 
						|
        else:
 | 
						|
            if os.stat(path).st_size > max_byte_count:
 | 
						|
                # Seek to the end, go back and find a newline
 | 
						|
                file.seek(0, os.SEEK_END)
 | 
						|
                file.seek(max(0, file.tell() - max_byte_count))
 | 
						|
                file.readline()
 | 
						|
 | 
						|
            contents = file.read()
 | 
						|
            file.close()
 | 
						|
    else:
 | 
						|
        print("File " + path + " was not found.")
 | 
						|
 | 
						|
    return contents
 | 
						|
 | 
						|
 | 
						|
def run_max_ctrl(maxctrl_un, maxctrl_pw):
 | 
						|
    maxctrl_commands = ["show maxscale", "show services", "show filters", "show monitors",
 | 
						|
                        "show servers"]
 | 
						|
    cmd_prefix = "maxctrl "
 | 
						|
    if len(maxctrl_un) > 0:
 | 
						|
        cmd_prefix += "--user={} --password={} ".format(maxctrl_un, maxctrl_pw)
 | 
						|
 | 
						|
    total_output = ""
 | 
						|
    for command in maxctrl_commands:
 | 
						|
        complete_cmd = cmd_prefix + command
 | 
						|
        try:
 | 
						|
            maxctrl_output_bytes = subprocess.check_output(complete_cmd, shell=True,
 | 
						|
                                                           stderr=subprocess.PIPE)
 | 
						|
        except subprocess.CalledProcessError as e:
 | 
						|
            print("Error when calling maxctrl: command \"{}\" returned {}".format(complete_cmd, e.returncode))
 | 
						|
            break  # If a command fails, stop trying
 | 
						|
        except IOError as e:
 | 
						|
            print("Error when calling maxctrl: {}".format(e.strerror))
 | 
						|
            break
 | 
						|
        else:
 | 
						|
            if len(maxctrl_output_bytes) > 0:
 | 
						|
                total_output += command + "\n" + maxctrl_output_bytes.decode("utf-8") + "\n"
 | 
						|
 | 
						|
    return total_output
 | 
						|
 | 
						|
 | 
						|
def read_core_file():
 | 
						|
    core_file_path = os.getcwd() + "/" + "core"
 | 
						|
    core_file_contents = ""
 | 
						|
    if os.path.isfile(core_file_path):
 | 
						|
        print("Core file found, running gdb to save call stack.")
 | 
						|
        gdb_command = "gdb --quiet -batch -ex \"thread apply all bt full\" -ex \"quit\" maxscale core"
 | 
						|
        try:
 | 
						|
            gdb_output_bytes = subprocess.check_output(gdb_command, shell=True, stderr=subprocess.PIPE)
 | 
						|
        except subprocess.CalledProcessError as e:
 | 
						|
            print("Error when calling gdb: command \"{}\" returned {}".format(gdb_command, e.returncode))
 | 
						|
        except IOError as e:
 | 
						|
            print("Error when calling gdb: {}".format(e.strerror))
 | 
						|
        else:
 | 
						|
            if len(gdb_output_bytes) > 0:
 | 
						|
                core_file_contents += gdb_command + "\n\n" + gdb_output_bytes.decode("utf-8")
 | 
						|
 | 
						|
    return core_file_contents
 | 
						|
 | 
						|
 | 
						|
def get_system_info():
 | 
						|
    commands = ["cat /etc/os-release", "lscpu", "cat /proc/meminfo"]
 | 
						|
    total_output = ""
 | 
						|
    for command in commands:
 | 
						|
        try:
 | 
						|
            output_bytes = subprocess.check_output(command, shell=True, stderr=subprocess.PIPE)
 | 
						|
        except subprocess.CalledProcessError as e:
 | 
						|
            # If a command fails, try the next one. It may work.
 | 
						|
            message = "Error gathering system info: command \"{}\" returned {}".format(
 | 
						|
                command, e.returncode)
 | 
						|
            total_output += command + "\n" + message + "\n"
 | 
						|
            print(message)
 | 
						|
        except IOError as e:
 | 
						|
            message = "Error gathering system info: command \"{}\" could not be ran: {}".format(
 | 
						|
                command, e.strerror)
 | 
						|
            total_output += command + "\n" + message + "\n"
 | 
						|
            print(message)
 | 
						|
        else:
 | 
						|
            if len(output_bytes) > 0:
 | 
						|
                total_output += command + "\n" + output_bytes.decode("utf-8") + "\n"
 | 
						|
 | 
						|
    return total_output
 | 
						|
 | 
						|
 | 
						|
if __name__ == "__main__":
 | 
						|
    main(sys.argv)
 |