aboutsummaryrefslogtreecommitdiffstats
path: root/plugins_overview.py
blob: 46bcbf5a8c8f0a17d8b7bc9999e40ae785c6bd35 (plain) (blame)
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
# this script generates for each plugin:
# - its name
# - URL to upstream code
# - list of distros
# - list of profiles
# - list of packages that enable the plugin (no other enabling pieces)
# - list of paths it collects (add_copy_spec)
# - list of paths it forbits to collect (add_forbidden_path)
# - list of commands it calls (add_cmd_output)
#
# Output of the script: 
# - a JSON object with plugins in keys
# - or CSV format in case "csv" cmdline is provided
#
# TODO:
# - improve parsing that will be never ideal :)
# - add other methods:
#   - add_blockdev_cmd
#   - add_string_as_file
#   - ??

import os
import re
import json
import sys

PLUGDIR = 'sos/report/plugins'

plugs_data = {}     # the map of all plugins data to collect
plugcontent = ''    # content of plugin file just being processed

# method to parse an item of a_s_c/a_c_o/.. methods
# we work on an assumption the item is a string quoted by \" or optionally
# by \'. If we detect at least 2 such chars in the item, take what is between those.
def add_valid_item(dest, item):
    for qoutemark in "\"\'":
        split = item.split(qoutemark)
        if len(split) > 2:
            dest.append(split[1])
            return

# method to find in `plugcontent` all items of given method (a_c_s/a_c_o/..) split
# by comma; add each valid item to the `dest` list
def add_all_items(method, dest, wrapopen='\(', wrapclose='\)'):
    regexp = "%s%s(.*?)%s" % (method, wrapopen, wrapclose)
    for match in re.findall(regexp, plugcontent, flags=re.MULTILINE|re.DOTALL):
        # tuple of distros ended by either (class|from|import)
        if isinstance(match,tuple):
            for item in list(match):
                if item not in ['class', 'from', 'import']:
                    for it in item.split(','):
                        # dirty hack to remove spaces and "Plugin"
                        if "Plugin" not in it:
                            continue
                        it = it.strip(' ()')[0:-6]
                        if len(it):
                            dest.append(it)
        # list of specs separated by comma ..
        elif match.startswith('[') or match.startswith('('):
            for item in match.split(','):
                add_valid_item(dest, item)
        # .. or a singleton spec
        else:
            add_valid_item(dest, match)

# main body: traverse report's plugins directory and for each plugin, grep for 
# add_copy_spec / add_forbidden_path / add_cmd_output there
for plugfile in sorted(os.listdir(PLUGDIR)):
    # ignore non-py files and __init__.py
    if not plugfile.endswith('.py') or plugfile == '__init__.py':
        continue
    plugname = plugfile[:-3]
#    if plugname != 'bcache':
#        continue
    plugs_data[plugname] = {
            'sourcecode': 'https://github.com/sosreport/sos/blob/main/sos/report/plugins/%s.py' % plugname,
            'distros': [],
            'profiles': [],
            'packages': [],
            'copyspecs': [],
            'forbidden': [],
            'commands': [],
            'service_status': [],
            'journals': [],
            'env': [],
    }
    plugcontent = open(os.path.join(PLUGDIR, plugfile)).read().replace('\n','')
    add_all_items("from sos.report.plugins import ", plugs_data[plugname]['distros'], wrapopen='', wrapclose='(class|from|import)')
    add_all_items("profiles = ", plugs_data[plugname]['profiles'], wrapopen='')
    add_all_items("packages = ", plugs_data[plugname]['packages'], wrapopen='')
    add_all_items("add_copy_spec", plugs_data[plugname]['copyspecs'])
    add_all_items("add_forbidden_path", plugs_data[plugname]['forbidden'])
    add_all_items("add_cmd_output", plugs_data[plugname]['commands'])
    add_all_items("collect_cmd_output", plugs_data[plugname]['commands'])
    add_all_items("add_service_status", plugs_data[plugname]['service_status'])
    add_all_items("add_journal", plugs_data[plugname]['journals'])
    add_all_items("add_env_var", plugs_data[plugname]['env'])

# print output; if "csv" is cmdline argument, print in CSV format, else JSON
if (len(sys.argv) > 1) and (sys.argv[1] == "csv"):
    print("plugin;url;distros;profiles;packages;copyspecs;forbidden;commands;service_status;journals;env_vars")
    for plugname in plugs_data.keys():
        plugin = plugs_data[plugname]
        # determine max number of lines - usually "max(len(copyspec),len(commands))"
        # ignore 'sourcecode' key as it
        maxline = 1
        plugkeys = list(plugin.keys())
        plugkeys.remove('sourcecode')
        for key in plugkeys:
            maxline = max(maxline, len(plugin[key]))
        for line in range(maxline):
            out = ";" if line>0 else ("%s;%s" % (plugname, plugin['sourcecode']))
            for key in plugkeys:
                out += ";"
                if line<len(plugin[key]):
                    out += plugin[key][line]
            print(out)
else:
    print(json.dumps(plugs_data))