From 1c8c222ad10003ca3922edc5386b7a5493fa725a Mon Sep 17 00:00:00 2001 From: Esa Korhonen Date: Wed, 9 Jan 2019 16:56:39 +0200 Subject: [PATCH 1/2] MXS-2112 Add python script for assembling support files The script adds config and log files into a zip archive. Passwords in config files are censored. Also attempts to read current status by calling maxctrl. If core-file exists, runs gdb on it to gather call stack. The script is installed to the binary file directory. --- CMakeLists.txt | 1 + script/maxscale_generate_support_info.py | 201 +++++++++++++++++++++++ 2 files changed, 202 insertions(+) create mode 100755 script/maxscale_generate_support_info.py diff --git a/CMakeLists.txt b/CMakeLists.txt index 71ee749c7..c20ff75a6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -213,6 +213,7 @@ install_file(server/maxscale.cnf.template core) install_file(server/maxscale_binlogserver_template.cnf core) install_program(script/create_grants core) install_file(script/create_roles.sql core) +install_script(script/maxscale_generate_support_info.py core) # Install the template into /etc if(WITH_MAXSCALE_CNF AND (NOT TARGET_COMPONENT OR "core" STREQUAL "${TARGET_COMPONENT}")) diff --git a/script/maxscale_generate_support_info.py b/script/maxscale_generate_support_info.py new file mode 100755 index 000000000..a99ca1523 --- /dev/null +++ b/script/maxscale_generate_support_info.py @@ -0,0 +1,201 @@ +#!/usr/bin/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: {} [ 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) + + 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 + + +if __name__ == "__main__": + main(sys.argv) From 8cef8b94722b69544ce115ed11c7817456d5ae37 Mon Sep 17 00:00:00 2001 From: Esa Korhonen Date: Mon, 14 Jan 2019 16:56:18 +0200 Subject: [PATCH 2/2] Compile MariaDBMonitor unit tests only if flag is set --- server/modules/monitor/mariadbmon/CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/server/modules/monitor/mariadbmon/CMakeLists.txt b/server/modules/monitor/mariadbmon/CMakeLists.txt index 0f2628c58..fb5b4e2d1 100644 --- a/server/modules/monitor/mariadbmon/CMakeLists.txt +++ b/server/modules/monitor/mariadbmon/CMakeLists.txt @@ -4,4 +4,6 @@ target_link_libraries(mariadbmon maxscale-common) add_dependencies(mariadbmon pcre2) set_target_properties(mariadbmon PROPERTIES VERSION "1.4.0" LINK_FLAGS -Wl,-z,defs) install_module(mariadbmon core) -add_subdirectory(test) +if(BUILD_TESTS) + add_subdirectory(test) +endif()