Projet

Général

Profil

Paste
Télécharger au format
Statistiques
| Branche: | Révision:

root / plugins / mysql / mysql_disk_by_prefix @ ee226a60

Historique | Voir | Annoter | Télécharger (3,57 ko)

1 ee226a60 pcy
#!/usr/bin/env python3
2
# -*- python -*-
3
4
"""
5
=head1 INTRODUCTION
6
7
Plugin to monitor MySQL database disk usage per prefix. A prefix can be eg.
8
'user' for the databases 'user_testdb' and 'user_db2', as can be seen in
9
certain shared hosting setups.
10
11
=head1 APPLICABLE SYSTEMS
12
13
Local access to the database files is required (which are stored in
14
/var/lib/mysql by default).
15
16
=head1 INSTALLATION
17
18
Place in /etc/munin/plugins/ (or link it there using ln -s)
19
20
=head1 CONFIGURATION
21
22
Add this to your /etc/munin/plugin-conf.d/munin-node:
23
24
=over 2
25
26
    [mysql_disk_by_prefix]
27
    user mysql
28
    env.prefixes_with_underscores foo_bar d_ritchie # prefixes that include an underscore
29
    env.db_minsize 1024000 # minimum db size in order to report a specific prefix,
30
                           # in bytes (defaults to 50M). "0" for none.
31
    env.mysql_db_dir /var/lib/mysql # default value
32
33
=back
34
35
=head1 HISTORY
36
37
2019-07-25: v 1.0 pcy <pcy.ulyssis.org>: created
38
39
=head1 USAGE
40
41
Parameters understood:
42
43
  config   (required)
44
  autoconf (optional - used by munin-config)
45
46
=head1 MAGIC MARKERS
47
48
#%# family=auto
49
#%# capabilities=autoconf
50
"""
51
52
53
import os
54
import re
55
import subprocess as sp
56
import sys
57
58
59
def weakbool(x):
60
    return x.lower().strip() in {'true', 'yes', 'y', 1}
61
62
63
LIMIT = os.getenv('db_minsize', str(50000 * 1024))
64
try:
65
    LIMIT = int(LIMIT)
66
except ValueError:
67
    LIMIT = 0
68
69
exceptions = os.getenv('prefix_with_underscores', '').split(' ')
70
if exceptions == ['']:
71
    exceptions = []
72
73
mysqldir = os.getenv('mysql_db_dir', '/var/lib/mysql')
74
75
76
def name_from_path(path):
77
    filename = os.path.basename(path)
78
    for name in exceptions:
79
        if filename.startswith(name):
80
            return name
81
    name = filename.split('_')[0]
82
83
    def decode_byte(m):
84
        return bytes.fromhex(m.group(1)).decode('utf-8')
85
86
    # Decode MySQL's encoding of non-ascii characters in the table names
87
    return re.sub('@00([0-9a-z]{2})', decode_byte, name)
88
89
90
def calc_dir_size(directory):
91
    total = 0
92
93
    for filename in os.listdir(directory):
94
        filedir = os.path.join(directory, filename)
95
96
        if os.path.islink(filedir):
97
            continue
98
        if os.path.isfile(filedir):
99
            total += os.path.getsize(filedir)
100
101
    return total
102
103
104
def size_per_subdir(parentdir):
105
    for subdir in os.listdir(parentdir):
106
        dirpath = os.path.join(parentdir, subdir)
107
108
        if os.path.islink(dirpath):
109
            continue
110
        if os.path.isdir(dirpath):
111
            yield calc_dir_size(dirpath), name_from_path(os.path.split(dirpath)[1])
112
113
114
def sizes_by_name(limit=None):
115
    sizes = {}
116
    for size, name in size_per_subdir(mysqldir):
117
        sizes[name] = sizes.get(name, 0) + size
118
    for name, total_size in sizes.items():
119
        if limit <= 0 or limit <= total_size:
120
            yield name, total_size
121
122
123
def fetch():
124
    for name, total_size in sorted(sizes_by_name(limit=LIMIT), key=lambda x: x[0]):
125
        print('{}.value {}'.format(name, total_size))
126
127
128
def main():
129
    if len(sys.argv) == 1:
130
        fetch()
131
    elif sys.argv[1] == 'config':
132
        print('graph_title MySQL disk usage by prefix')
133
        print('graph_vlabel bytes')
134
        print('graph_category db')
135
136
        names = sorted(name for name, _ in sizes_by_name(limit=LIMIT))
137
        for name in names:
138
            print('{0}.label {0}'.format(name))
139
            print('{}.type GAUGE'.format(name))
140
            print('{}.draw AREASTACK'.format(name))
141
    elif sys.argv[1] == 'autoconf':
142
        print('yes' if os.path.isdir(mysqldir)
143
              else "no (can't find MySQL's data directory)")
144
    else:
145
        fetch()
146
147
148
if __name__ == "__main__":
149
    main()
150
151
# flake8: noqa: E265