Projet

Général

Profil

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

root / plugins / mysql / mysql_disk_by_prefix @ 852aa41a

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

1
#!/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 sys
56

    
57

    
58
def weakbool(x):
59
    return x.lower().strip() in {'true', 'yes', 'y', 1}
60

    
61

    
62
LIMIT = os.getenv('db_minsize', str(50000 * 1024))
63
try:
64
    LIMIT = int(LIMIT)
65
except ValueError:
66
    LIMIT = 0
67

    
68
exceptions = os.getenv('prefix_with_underscores', '').split(' ')
69
if exceptions == ['']:
70
    exceptions = []
71

    
72
mysqldir = os.getenv('mysql_db_dir', '/var/lib/mysql')
73

    
74

    
75
def name_from_path(path):
76
    filename = os.path.basename(path)
77
    for name in exceptions:
78
        if filename.startswith(name):
79
            return name
80
    name = filename.split('_')[0]
81

    
82
    def decode_byte(m):
83
        return bytes.fromhex(m.group(1)).decode('utf-8')
84

    
85
    # Decode MySQL's encoding of non-ascii characters in the table names
86
    return re.sub('@00([0-9a-z]{2})', decode_byte, name)
87

    
88

    
89
def calc_dir_size(directory):
90
    total = 0
91

    
92
    for filename in os.listdir(directory):
93
        filedir = os.path.join(directory, filename)
94

    
95
        if os.path.islink(filedir):
96
            continue
97
        if os.path.isfile(filedir):
98
            total += os.path.getsize(filedir)
99

    
100
    return total
101

    
102

    
103
def size_per_subdir(parentdir):
104
    for subdir in os.listdir(parentdir):
105
        dirpath = os.path.join(parentdir, subdir)
106

    
107
        if os.path.islink(dirpath):
108
            continue
109
        if os.path.isdir(dirpath):
110
            yield calc_dir_size(dirpath), name_from_path(os.path.split(dirpath)[1])
111

    
112

    
113
def sizes_by_name(limit=None):
114
    sizes = {}
115
    for size, name in size_per_subdir(mysqldir):
116
        sizes[name] = sizes.get(name, 0) + size
117
    for name, total_size in sizes.items():
118
        if limit <= 0 or limit <= total_size:
119
            yield name, total_size
120

    
121

    
122
def fetch():
123
    for name, total_size in sorted(sizes_by_name(limit=LIMIT), key=lambda x: x[0]):
124
        print('{}.value {}'.format(name, total_size))
125

    
126

    
127
def main():
128
    if len(sys.argv) == 1:
129
        fetch()
130
    elif sys.argv[1] == 'config':
131
        print('graph_title MySQL disk usage by prefix')
132
        print('graph_vlabel bytes')
133
        print('graph_category db')
134

    
135
        names = sorted(name for name, _ in sizes_by_name(limit=LIMIT))
136
        for name in names:
137
            print('{0}.label {0}'.format(name))
138
            print('{}.type GAUGE'.format(name))
139
            print('{}.draw AREASTACK'.format(name))
140
    elif sys.argv[1] == 'autoconf':
141
        print('yes' if os.path.isdir(mysqldir)
142
              else "no (can't find MySQL's data directory)")
143
    else:
144
        fetch()
145

    
146

    
147
if __name__ == "__main__":
148
    main()