Last active
April 7, 2022 20:25
-
-
Save thorsummoner/e9707c9a7805f5fe3342c06eafa4e1aa to your computer and use it in GitHub Desktop.
munin diskmon plugin ported to python
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
# ported from https://github.com/terrorobe/munin-plugins/blob/master/alumni/linux_diskstats | |
# GNP GPLv2 | |
import glob | |
import re as regex | |
class diskmon(object): | |
"""docstring for diskmon""" | |
poll_interval = 300 | |
def __init__( | |
self, | |
graph_width=400 | |
): | |
super(diskmon, self).__init__() | |
self.graph_width = graph_width | |
def parse_diskstats(self): | |
stats = False \ | |
or self.read_sysfs() \ | |
or self.read_procfs() | |
diskstats = None | |
for entry in stats: | |
devstat = dict(zip([ | |
'major', | |
'minor', | |
'devname', | |
'rd_ios', | |
'rd_merges', | |
'rd_sectors', | |
'rd_ticks', | |
'wr_ios', | |
'wr_merges', | |
'wr_sectors', | |
'wr_ticks', | |
'ios_in_prog', | |
'tot_ticks', | |
'rq_ticks', | |
], entry)) | |
device = devstat['devname'] | |
pretty_device = None | |
if re.match('/^dm-\d+$/', device): | |
pretty_device = self.translate_devicemapper_name(device) | |
devstat['pretty_device_name'] = pretty_device or device | |
# Short device name only containing the stuff after the last '/' | |
# for graph labels et al. | |
devstat['short_pretty_device_name'] = devstat['pretty_device_name'].split('/')[-1] | |
def translate_devicemapper_name(self, device): | |
want_minor = re.search('/^dm-(\d+)$/') | |
if None is want_minor: | |
raise LookupError('Failed to extract devicemapper id') | |
dm_major = self.find_devicemapper_major() | |
if None is dm_major: | |
raise LookupError('Failed to get device-mapper major number') | |
for entry in next(os.walk('/dev/mapper/'))[2]: | |
entry = os.path.join('/dev/mapper/', entry) | |
rdev = int(os.stat(entry).st_dev) | |
major, minor = (rdev // 256, rdev % 256) | |
if magor == dm_major and minor == want_minor: | |
return self.translate_lvm_name(entry) | |
return entry | |
# Return original string if the device can't be found. | |
return $device; | |
def read_sysfs(self): | |
lines = [] | |
devices = next(os.walk('/sys/block'))[1] | |
for cur_device in devices: | |
stats_file = "/sys/block/%s/stat" % tuple(cur_device) | |
with open(stats_file) as statfh: | |
# Trimming whitespace | |
line = statfh.readline().strip() | |
elems = re.split('\s+', line) | |
if len(elems) is not 11: | |
raise LookupError("'%s' doesn't contain exactly 11 values. Aborting" % tuple(stats_file)) | |
lines.append(elems) | |
return lines | |
def read_procfs(self): | |
lines = [] | |
with open('/proc/diskstats') as statfh: | |
for line in statfh.readlines(): | |
line = line.strip() | |
# Strip trailing newline and leading whitespace | |
elems = re.split('\s+', line) | |
# We explicitly don't support old-style diskstats | |
# There are situations where only _some_ lines (e.g. | |
# partitions on older 2.6 kernels) have fewer stats | |
# numbers, therefore we'll skip them silently | |
if len(elems) != 14: | |
continue | |
lines.append(elems) | |
return lines | |
def find_devicemapper_major(self): | |
with open('/proc/devices') as devicefh: | |
dm_major = None | |
for line in devicefh.readlines(): | |
line = line.strip() | |
major, name = re.split('/\s+/', line, 2) | |
if not name: | |
continue | |
if name is 'device-mapper': | |
dm_major = major | |
break | |
return dm_major | |
def translate_lvm_name(self, entry): | |
device_name = os.path.basename(entry) | |
# Check for single-dash-occurence to see if this could be a lvm devicemapper device. | |
if re.match('/(?<!-)-(?!-)/', device_name): | |
# split device name into vg and lv parts | |
vg, lv = re.split('/(?<!-)-(?!-)/', device_name, 2) | |
if not vg and not lv: | |
return None | |
# remove extraneous dashes from vg and lv names | |
vg = re.sub('/--/', '-') | |
lv = re.sub('/--/', '-') | |
device_name = '/'.join(vg, lv) | |
# Sanity check - does the constructed device name exist? | |
# Breaks unless we are root. | |
if os.path.exists(os.path.join('/dev', device_name)): | |
return device_name |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment