From 7d31b4ce25120c165eec55abced2c4a8e9fdbb48 Mon Sep 17 00:00:00 2001 From: "Kim B. Heino" Date: Tue, 1 Dec 2020 11:55:00 +0200 Subject: [PATCH] stratis: new plugin to monitor stratis filesystem --- plugins/disk/stratis | 152 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100755 plugins/disk/stratis diff --git a/plugins/disk/stratis b/plugins/disk/stratis new file mode 100755 index 00000000..ed63bdf9 --- /dev/null +++ b/plugins/disk/stratis @@ -0,0 +1,152 @@ +#!/usr/bin/python3 -tt +# -*- coding: utf-8 -*- + +"""Munin plugin to monitor stratis pools and filesystems. + +Copyright 2020 Kim B. Heino, Foobar Oy +License GPLv2+ + +#%# capabilities=autoconf +#%# family=auto +""" + +import os +import subprocess +import sys + + +def safename(variable): + """Return safe variable name.""" + ret = [] + for letter in variable: + if letter.isalnum(): + ret.append(letter) + else: + ret.append('_') + return ''.join(ret) + + +def run_binary(arg): + """Run binary and return output.""" + try: + cmd = subprocess.Popen( + arg, shell=False, close_fds=True, bufsize=-1, + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + return cmd.communicate()[0].decode('utf-8', 'ignore') + except OSError: + return '' + + +def parse_unit(number, unit): + """Parse "1.60 TiB" to bytes.""" + number = float(number) + if unit == 'TiB': + return number * 1024 * 1024 * 1024 * 1024 + if unit == 'GiB': + return number * 1024 * 1024 * 1024 + if unit == 'MiB': + return number * 1024 * 1024 + if unit == 'KiB': + return number * 1024 + return number + + +def find_pools(): + """Return list of found pools and filesystems.""" + pool = [] + for line in run_binary(['/usr/bin/stratis', 'pool']).splitlines(): + if line.startswith('Name '): + continue + line = line.split() + total = parse_unit(line[1], line[2]) + used = parse_unit(line[4], line[5]) + free = parse_unit(line[7], line[8]) + pool.append((line[0], total, used, free)) + + files = [] + dflist = run_binary(['/usr/bin/df']).splitlines() + for line in run_binary(['/usr/bin/stratis', 'filesystem']).splitlines(): + if line.startswith('Pool Name ') or '-snap-' in line: + continue + line = line.split() + df_used = used = parse_unit(line[2], line[3]) + for dfline in dflist: + if line[9] not in dfline: # match by uuid + continue + df_used = int(dfline.split()[2]) * 1024 + files.append((line[0], line[1], used, df_used)) + return sorted(pool), sorted(files) + + +def config(pools, files): + """Print plugin config.""" + print('multigraph stratis_pool') + print('graph_title Stratis pools usage') + print('graph_info Stratis pools usage in percent.') + print('graph_category disk') + print('graph_vlabel %') + print('graph_args --lower-limit 0 --upper-limit 100') + print('graph_scale no') + for item in pools: + name = safename(item[0]) + print('{}.label Pool {} usage'.format(name, item[0])) + print('{}.warning 92'.format(name)) + print('{}.critical 98'.format(name)) + + print('multigraph stratis_fs') + print('graph_title Stratis filesystems usage') + print('graph_info Stratis filesystems pool usage.') + print('graph_category disk') + print('graph_vlabel Pool usage') + print('graph_args --base 1024 --lower-limit 0') + first = True + for item in files: + name = safename(item[0] + '_' + item[1]) + print('{}.label Filesystem {}/{}'.format(name, item[0], item[1])) + if first: + print('{}.draw AREA'.format(name)) + first = False + else: + print('{}.draw STACK'.format(name)) + + print('multigraph stratis_thin') + print('graph_title Stratis thin filesystems usage vs df') + print('graph_info Stratis thin filesystems usage divided by df, in ' + 'percents.') + print('graph_category disk') + print('graph_vlabel %') + print('graph_args --base 1000') + for item in files: + name = safename(item[0] + '_' + item[1]) + print('{}.label {}/{} usage vs df'.format(name, item[0], item[1])) + print('{}.warning 150'.format(name)) + + if os.environ.get('MUNIN_CAP_DIRTYCONFIG') == '1': + fetch(pools, files) + + +def fetch(pools, files): + """Print values.""" + print('multigraph stratis_pool') + for item in pools: + name = safename(item[0]) + print('{}.value {}'.format(name, item[2] * 100 / item[1])) + + print('multigraph stratis_fs') + for item in files: + name = safename(item[0] + '_' + item[1]) + print('{}.value {}'.format(name, item[2])) + + print('multigraph stratis_thin') + for item in files: + name = safename(item[0] + '_' + item[1]) + print('{}.value {}'.format(name, item[2] * 100 / item[3])) + + +if __name__ == '__main__': + if len(sys.argv) > 1 and sys.argv[1] == 'autoconf': + print('yes' if find_pools()[0] else 'no') + elif len(sys.argv) > 1 and sys.argv[1] == 'config': + config(*find_pools()) + else: + fetch(*find_pools())