File: generate_support_bundle.py

package info (click to toggle)
frr 10.5.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky
  • size: 76,324 kB
  • sloc: ansic: 686,900; python: 225,905; perl: 6,379; sh: 2,627; cpp: 1,883; makefile: 670; yacc: 397; lex: 363; lisp: 66; xml: 35; javascript: 8
file content (145 lines) | stat: -rwxr-xr-x 5,022 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-2.0-or-later
#
# Copyright (c) 2021, LabN Consulting, L.L.C.
#

########################################################
### Python Script to generate the FRR support bundle ###
########################################################
import argparse
import logging
import os
import subprocess
import tempfile


def open_with_backup(path):
    if os.path.exists(path):
        print("Making backup of " + path)
        subprocess.check_call("mv {0} {0}.prev".format(path), shell=True)
    return open(path, "w")


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument(
        "-c",
        "--config",
        default="/etc/frr/support_bundle_commands.conf",
        help="input config",
    )
    parser.add_argument(
        "-l", "--log-dir", default="/var/log/frr", help="directory for logfiles"
    )
    parser.add_argument(
        "-N", "--pathspace", help="Insert prefix into config & socket paths"
    )
    args = parser.parse_args()

    collecting = False  # file format has sentinels (seem superfluous)
    proc_cmds = {}  # Dictionary to store command lists for each process
    proc = None
    temp = None
    cmd_type = None  # Track whether we're collecting vtysh or ip commands

    # Collect all the commands for each daemon
    try:
        for line in open(args.config):
            line = line.rstrip()
            if len(line) == 0 or line[0] == "#":
                continue

            cmd_line = line.split(":")
            if cmd_line[0] == "PROC_NAME":
                proc = cmd_line[1]
                temp = tempfile.NamedTemporaryFile("w+")
                collecting = False
                cmd_type = None
            elif cmd_line[0] == "CMD_LIST_START":
                collecting = True
                cmd_type = "vtysh"
            elif cmd_line[0] == "CMD_LIST_IP_START":
                collecting = True
                cmd_type = "ip"
            elif cmd_line[0] == "CMD_LIST_END" or cmd_line[0] == "CMD_LIST_IP_END":
                collecting = False
                temp.flush()
                if proc not in proc_cmds:
                    proc_cmds[proc] = {}
                proc_cmds[proc][cmd_type] = open(temp.name)
                temp.close()
            elif collecting:
                temp.write(line + "\n")
            else:
                print("Ignoring unexpected input " + line.rstrip())
    except IOError as error:
        logging.fatal("Cannot read config file: %s: %s", args.config, str(error))
        return

    # Spawn processes to fetch each set of commands
    procs = []
    for proc in proc_cmds:
        for cmd_type, cmd_file in proc_cmds[proc].items():
            if args.pathspace:
                ofn = os.path.join(
                    args.log_dir,
                    args.pathspace
                    + "_"
                    + proc
                    + "_"
                    + cmd_type
                    + "_support_bundle.log",
                )
            else:
                ofn = os.path.join(
                    args.log_dir, proc + "_" + cmd_type + "_support_bundle.log"
                )

            if cmd_type == "vtysh":
                if args.pathspace:
                    p = subprocess.Popen(
                        ["/usr/bin/env", "vtysh", "-t", "-N", args.pathspace],
                        stdin=cmd_file,
                        stdout=open_with_backup(ofn),
                        stderr=subprocess.STDOUT,
                    )
                else:
                    p = subprocess.Popen(
                        ["/usr/bin/env", "vtysh", "-t"],
                        stdin=cmd_file,
                        stdout=open_with_backup(ofn),
                        stderr=subprocess.STDOUT,
                    )
            elif cmd_type == "ip":
                # For ip commands, create a shell script that executes each ip command
                cmd_file.seek(0)  # Reset file pointer to beginning
                ip_script = tempfile.NamedTemporaryFile(mode="w+")
                ip_script.write("#!/bin/bash\n")
                ip_script.write("set -e\n")
                for cmd in cmd_file:
                    cmd = cmd.strip()
                    if cmd:
                        ip_script.write(f"echo '=== Command: ip {cmd} ==='\n")
                        ip_script.write(
                            f"ip {cmd} || echo 'Command failed with exit code: $?'\n"
                        )
                        ip_script.write("echo '=== Return code: $? ==='\n")
                        ip_script.write("echo\n")
                ip_script.flush()
                os.chmod(ip_script.name, 0o755)

                p = subprocess.Popen(
                    ["/bin/bash", ip_script.name],
                    stdout=open_with_backup(ofn),
                    stderr=subprocess.STDOUT,
                )

            procs.append(p)

    for p in procs:
        p.wait()


if __name__ == "__main__":
    main()