munin-contrib/plugins/mysql/mysql_disk_by_prefix

144 lines
3.4 KiB
Python
Executable File

#!/usr/bin/env python3
# -*- python -*-
"""
=head1 INTRODUCTION
Plugin to monitor MySQL database disk usage per prefix. A prefix can be eg.
'user' for the databases 'user_testdb' and 'user_db2', as can be seen in
certain shared hosting setups.
=head1 APPLICABLE SYSTEMS
Local access to the database files is required (which are stored in
/var/lib/mysql by default).
=head1 INSTALLATION
Place in /etc/munin/plugins/ (or link it there using ln -s)
=head1 CONFIGURATION
Add this to your /etc/munin/plugin-conf.d/munin-node:
=over 2
[mysql_disk_by_prefix]
user mysql
env.prefixes_with_underscores foo_bar d_ritchie # prefixes that include an underscore
env.db_minsize 1024000 # minimum db size in order to report a specific prefix,
# in bytes (defaults to 50M). "0" for none.
env.mysql_db_dir /var/lib/mysql # default value
=back
=head1 AUTHORS
Copyright (C) 2019 pcy <pcy.ulyssis.org>
=head1 MAGIC MARKERS
#%# family=auto
#%# capabilities=autoconf
=cut
"""
import os
import re
import sys
def weakbool(x):
return x.lower().strip() in {'true', 'yes', 'y', 1}
LIMIT = os.getenv('db_minsize', str(50000 * 1024))
try:
LIMIT = int(LIMIT)
except ValueError:
LIMIT = 0
exceptions = os.getenv('prefix_with_underscores', '').split(' ')
if exceptions == ['']:
exceptions = []
mysqldir = os.getenv('mysql_db_dir', '/var/lib/mysql')
def name_from_path(path):
filename = os.path.basename(path)
for name in exceptions:
if filename.startswith(name):
return name
name = filename.split('_')[0]
def decode_byte(m):
return bytes.fromhex(m.group(1)).decode('utf-8')
# Decode MySQL's encoding of non-ascii characters in the table names
return re.sub('@00([0-9a-z]{2})', decode_byte, name)
def calc_dir_size(directory):
total = 0
for filename in os.listdir(directory):
filedir = os.path.join(directory, filename)
if os.path.islink(filedir):
continue
if os.path.isfile(filedir):
total += os.path.getsize(filedir)
return total
def size_per_subdir(parentdir):
for subdir in os.listdir(parentdir):
dirpath = os.path.join(parentdir, subdir)
if os.path.islink(dirpath):
continue
if os.path.isdir(dirpath):
yield calc_dir_size(dirpath), name_from_path(os.path.split(dirpath)[1])
def sizes_by_name(limit=None):
sizes = {}
for size, name in size_per_subdir(mysqldir):
sizes[name] = sizes.get(name, 0) + size
for name, total_size in sizes.items():
if limit <= 0 or limit <= total_size:
yield name, total_size
def fetch():
for name, total_size in sorted(sizes_by_name(limit=LIMIT), key=lambda x: x[0]):
print('{}.value {}'.format(name, total_size))
def main():
if len(sys.argv) == 1:
fetch()
elif sys.argv[1] == 'config':
print('graph_title MySQL disk usage by prefix')
print('graph_vlabel bytes')
print('graph_category db')
names = sorted(name for name, _ in sizes_by_name(limit=LIMIT))
for name in names:
print('{0}.label {0}'.format(name))
print('{}.type GAUGE'.format(name))
print('{}.draw AREASTACK'.format(name))
elif sys.argv[1] == 'autoconf':
print('yes' if os.path.isdir(mysqldir)
else "no (can't find MySQL's data directory)")
else:
fetch()
if __name__ == "__main__":
main()