diff options
author | Christian Breunig <christian@breunig.cc> | 2023-10-16 20:37:44 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-10-16 20:37:44 +0200 |
commit | bc0330808b211f4b6eedc87e9dd9749f1e91d9aa (patch) | |
tree | 4030e2f437460111616b60c0ed30e83e25d236c3 | |
parent | b7359833c6663ac3b00439feae751e968ce68b99 (diff) | |
parent | 65911b17340a7894aba973113d83ab43964bbf99 (diff) | |
download | vyos-1x-bc0330808b211f4b6eedc87e9dd9749f1e91d9aa.tar.gz vyos-1x-bc0330808b211f4b6eedc87e9dd9749f1e91d9aa.zip |
Merge pull request #2367 from aapostoliuk/T5642-current
op-mode: T5642: 'generate tech-support archive' moved to vyos-1x
-rw-r--r-- | Makefile | 1 | ||||
-rw-r--r-- | op-mode-definitions/generate_tech-support_archive.xml.in | 29 | ||||
-rwxr-xr-x | src/op_mode/generate_tech-support_archive.py | 148 |
3 files changed, 177 insertions, 1 deletions
@@ -60,7 +60,6 @@ op_mode_definitions: $(op_xml_obj) rm -f $(OP_TMPL_DIR)/clear/interfaces/node.def rm -f $(OP_TMPL_DIR)/clear/node.def rm -f $(OP_TMPL_DIR)/delete/node.def - rm -f $(OP_TMPL_DIR)/generate/node.def rm -f $(OP_TMPL_DIR)/set/node.def # XXX: ping and traceroute must be able to recursivly call itself as the diff --git a/op-mode-definitions/generate_tech-support_archive.xml.in b/op-mode-definitions/generate_tech-support_archive.xml.in new file mode 100644 index 000000000..e95be3e28 --- /dev/null +++ b/op-mode-definitions/generate_tech-support_archive.xml.in @@ -0,0 +1,29 @@ +<?xml version="1.0"?> +<interfaceDefinition> + <node name="generate"> + <children> + <node name="tech-support"> + <properties> + <help>Generate tech support info</help> + </properties> + <children> + <node name="archive"> + <properties> + <help>Generate tech support archive</help> + </properties> + <command>sudo ${vyos_op_scripts_dir}/generate_tech-support_archive.py</command> + </node> + <tagNode name="archive"> + <properties> + <help>Generate tech support archive to defined location</help> + <completionHelp> + <list> <file> <scp://user:passwd@host> <ftp://user:passwd@host></list> + </completionHelp> + </properties> + <command>sudo ${vyos_op_scripts_dir}/generate_tech-support_archive.py $4</command> + </tagNode> + </children> + </node> + </children> + </node> +</interfaceDefinition> diff --git a/src/op_mode/generate_tech-support_archive.py b/src/op_mode/generate_tech-support_archive.py new file mode 100755 index 000000000..23d81f986 --- /dev/null +++ b/src/op_mode/generate_tech-support_archive.py @@ -0,0 +1,148 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2023 VyOS maintainers and contributors +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# published by the Free Software Foundation. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +import os +import argparse +import glob +from datetime import datetime +from pathlib import Path +from shutil import rmtree + +from socket import gethostname +from sys import exit +from tarfile import open as tar_open +from vyos.utils.process import rc_cmd +from vyos.remote import upload + +def op(cmd: str) -> str: + """Returns a command with the VyOS operational mode wrapper.""" + return f'/opt/vyatta/bin/vyatta-op-cmd-wrapper {cmd}' + +def save_stdout(command: str, file: Path) -> None: + rc, stdout = rc_cmd(command) + body: str = f'''### {command} ### +Command: {command} +Exit code: {rc} +Stdout: +{stdout} + +''' + with file.open(mode='a') as f: + f.write(body) +def __rotate_logs(path: str, log_pattern:str): + files_list = glob.glob(f'{path}/{log_pattern}') + if len(files_list) > 5: + oldest_file = min(files_list, key=os.path.getctime) + os.remove(oldest_file) + + +def __generate_archived_files(location_path: str) -> None: + """ + Generate arhives of main directories + :param location_path: path to temporary directory + :type location_path: str + """ + # Dictionary arhive_name:directory_to_arhive + archive_dict = { + 'etc': '/etc', + 'home': '/home', + 'var-log': '/var/log', + 'root': '/root', + 'tmp': '/tmp', + 'core-dump': '/var/core', + 'config': '/opt/vyatta/etc/config' + } + # Dictionary arhive_name:excluding pattern + archive_excludes = { + # Old location of archives + 'config': 'tech-support-archive', + # New locations of arhives + 'tmp': 'tech-support-archive' + } + for archive_name, path in archive_dict.items(): + archive_file: str = f'{location_path}/{archive_name}.tar.gz' + with tar_open(name=archive_file, mode='x:gz') as tar_file: + if archive_name in archive_excludes: + tar_file.add(path, filter=lambda x: None if str(archive_excludes[archive_name]) in str(x.name) else x) + else: + tar_file.add(path) + + +def __generate_main_archive_file(archive_file: str, tmp_dir_path: str) -> None: + """ + Generate main arhive file + :param archive_file: name of arhive file + :type archive_file: str + :param tmp_dir_path: path to arhive memeber + :type tmp_dir_path: str + """ + with tar_open(name=archive_file, mode='x:gz') as tar_file: + tar_file.add(tmp_dir_path, arcname=os.path.basename(tmp_dir_path)) + + +if __name__ == '__main__': + defualt_tmp_dir = '/tmp' + parser = argparse.ArgumentParser() + parser.add_argument("path", nargs='?', default=defualt_tmp_dir) + args = parser.parse_args() + location_path = args.path[:-1] if args.path[-1] == '/' else args.path + + hostname: str = gethostname() + time_now: str = datetime.now().isoformat(timespec='seconds') + + remote = False + tmp_path = '' + tmp_dir_path = '' + if 'ftp://' in args.path or 'scp://' in args.path: + remote = True + tmp_path = defualt_tmp_dir + else: + tmp_path = location_path + archive_pattern = f'_tech-support-archive_' + archive_file_name = f'{hostname}{archive_pattern}{time_now}.tar.gz' + + # Log rotation in tmp directory + if tmp_path == defualt_tmp_dir: + __rotate_logs(tmp_path, f'*{archive_pattern}*') + + # Temporary directory creation + tmp_dir_path = f'{tmp_path}/drops-debug_{time_now}' + tmp_dir: Path = Path(tmp_dir_path) + tmp_dir.mkdir() + + report_file: Path = Path(f'{tmp_dir_path}/show_tech-support_report.txt') + report_file.touch() + try: + + save_stdout(op('show tech-support report'), report_file) + # Generate included archives + __generate_archived_files(tmp_dir_path) + + # Generate main archive + __generate_main_archive_file(f'{tmp_path}/{archive_file_name}', tmp_dir_path) + # Delete temporary directory + rmtree(tmp_dir) + # Upload to remote site if it is scpecified + if remote: + upload(f'{tmp_path}/{archive_file_name}', args.path) + print(f'Debug file is generated and located in {location_path}/{archive_file_name}') + except Exception as err: + print(f'Error during generating a debug file: {err}') + # cleanup + if tmp_dir.exists(): + rmtree(tmp_dir) + finally: + # cleanup + exit()
\ No newline at end of file |