|
1
|
|
|
# -*- coding: utf-8 -*- |
|
2
|
|
|
"""Disk io stats reader from /proc on Linux.""" |
|
3
|
|
|
import re |
|
4
|
|
|
import time |
|
5
|
|
|
|
|
6
|
|
|
import plumd |
|
7
|
|
|
from plumd.util import Differential |
|
8
|
|
|
from plumd.util import get_file_map, get_file |
|
9
|
|
|
|
|
10
|
|
|
__author__ = 'Kenny Freeman' |
|
11
|
|
|
__email__ = '[email protected]' |
|
12
|
|
|
__license__ = "ISCL" |
|
13
|
|
|
__docformat__ = 'reStructuredText' |
|
14
|
|
|
|
|
15
|
|
|
|
|
16
|
|
|
class DiskStats(plumd.Reader): |
|
17
|
|
|
"""Plugin to measure various kernel metrics from /proc.""" |
|
18
|
|
|
|
|
19
|
|
|
proc_colums = 14 |
|
20
|
|
|
|
|
21
|
|
|
defaults = { |
|
22
|
|
|
'poll.interval': 10, |
|
23
|
|
|
'proc_path': '/proc', |
|
24
|
|
|
'diskstats_dev_re': "dm-\d", |
|
|
|
|
|
|
25
|
|
|
'diskstats_cols': ["r", "r_merge", "r_sector", "r_time", "w", "w_merge", |
|
26
|
|
|
"w_sector", "w_time", "io_inprog", "io_time", |
|
27
|
|
|
"io_weighted_time"] |
|
28
|
|
|
} |
|
29
|
|
|
|
|
30
|
|
|
def __init__(self, log, config): |
|
31
|
|
|
"""Plugin to measure various kernel metrics from /proc. |
|
32
|
|
|
|
|
33
|
|
|
:param log: A logger |
|
34
|
|
|
:type log: logging.RootLogger |
|
35
|
|
|
:param config: a plumd.config.Conf configuration helper instance. |
|
36
|
|
|
:type config: plumd.config.Conf |
|
37
|
|
|
""" |
|
38
|
|
|
super(DiskStats, self).__init__(log, config) |
|
39
|
|
|
config.defaults(DiskStats.defaults) |
|
40
|
|
|
self.calc = Differential() |
|
41
|
|
|
self.proc_file = "{0}/diskstats".format(config.get('proc_path')) |
|
42
|
|
|
self.diskstats_dev_re = re.compile(config.get('diskstats_dev_re')) |
|
43
|
|
|
self.diskstats_cols = self.config.get('diskstats_cols') |
|
44
|
|
|
self.devices = [] # list of device names to record metrics for |
|
45
|
|
|
self.enabled = True |
|
46
|
|
|
# get list of available devices: |
|
47
|
|
|
dat = get_file_map(self.proc_file, 2, 0) |
|
48
|
|
|
# key is the device name, exclude ones matching the re |
|
49
|
|
|
for key in dat.keys(): |
|
50
|
|
|
if not self.diskstats_dev_re.match(key): |
|
51
|
|
|
self.devices.append(key) |
|
52
|
|
|
# check format of proc file |
|
53
|
|
|
ncols = len(get_file(self.proc_file).split("\n")[0].split()) |
|
54
|
|
|
self.enabled = ncols == DiskStats.proc_colums |
|
55
|
|
|
if not self.enabled: |
|
56
|
|
|
msg = "DiskStats: invalid format: {0} has {1} cols, not {2}" |
|
57
|
|
|
self.log.error(msg.format(self.proc_file, ncols, |
|
58
|
|
|
DiskStats.proc_colums)) |
|
59
|
|
|
|
|
60
|
|
|
def poll(self): |
|
61
|
|
|
"""Poll for kernel metrics under /proc. |
|
62
|
|
|
|
|
63
|
|
|
:rtype: ResultSet |
|
64
|
|
|
""" |
|
65
|
|
|
return plumd.ResultSet(self.check()) |
|
66
|
|
|
|
|
67
|
|
|
def check(self): |
|
68
|
|
|
"""Return disk io metrics from proc file diskstats. |
|
69
|
|
|
|
|
70
|
|
|
:rtype: plumd.Result |
|
71
|
|
|
""" |
|
72
|
|
|
# times in ms |
|
73
|
|
|
cols = self.diskstats_cols |
|
74
|
|
|
result = plumd.Result("diskstats") |
|
75
|
|
|
if not self.enabled: |
|
76
|
|
|
return [result] |
|
77
|
|
|
dat = {} |
|
78
|
|
|
# read and process /proc/diskstats |
|
79
|
|
|
dat = get_file_map(self.proc_file, 2, 0) |
|
80
|
|
|
times = time.time() |
|
81
|
|
|
for dev in self.devices: |
|
82
|
|
|
if dev not in dat: |
|
83
|
|
|
continue |
|
84
|
|
|
for mname in cols: |
|
85
|
|
|
mval = float(dat[dev].popleft()) |
|
86
|
|
|
mstr = "{0}.{1}".format(dev, mname) |
|
87
|
|
|
dval = self.calc.per_second(mstr, mval, times) |
|
88
|
|
|
result.add(plumd.Float(mstr, dval)) |
|
89
|
|
|
return [result] |
|
90
|
|
|
|
Escape sequences in Python are generally interpreted according to rules similar to standard C. Only if strings are prefixed with
rorRare they interpreted as regular expressions.The escape sequence that was used indicates that you might have intended to write a regular expression.
Learn more about the available escape sequences. in the Python documentation.