Stat.poll()   A
last analyzed

Complexity

Conditions 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 1 Features 0
Metric Value
c 3
b 1
f 0
dl 0
loc 6
rs 9.4285
cc 1
1
# -*- coding: utf-8 -*-
2
"""Reader plugin to record process metrics from /proc on Linux."""
3
import time
4
from collections import deque
5
6
import plumd
7
from plumd.util import Differential
8
from plumd.util import get_file_map
9
10
__author__ = 'Kenny Freeman'
11
__email__ = '[email protected]'
12
__license__ = "ISCL"
13
__docformat__ = 'reStructuredText'
14
15
16
# todo: switch from list with pop(0) to deque
17
class Stat(plumd.Reader):
18
    """Class to read metrics from /proc/stat"""
19
20
    defaults = {
21
        'proc_stat_gauges': ['procs_running', 'procs_blocked'],
22
        'proc_stat_rates': ['intr', 'ctxt', 'softirq'],
23
        'cpu_metrics': ["user", "nice", "system", "idle", "iowait", "irq",
24
                        "softirq", "steal", "guest", "guest_nice"],
25
        'per_cpu': False,
26
    }
27
28
    def __init__(self, log, config):
29
        """Plugin to measure various kernel metrics from /proc/stat
30
31
        :param log: A logger
32
        :type log: logging.RootLogger
33
        :param config: a plumd.config.Conf configuration helper instance.
34
        :type config: plumd.config.Conf
35
        """
36
        super(Stat, self).__init__(log, config)
37
        self.config.defaults(Stat.defaults)
38
        self.calc = Differential()
39
        self.proc_file = "{0}/stat".format(config.get('proc_path'))
40
        self.per_cpu = self.config.get('per_cpu')
41
        self.cpu_metrics = self.config.get('cpu_metrics')
42
        self.gauges = self.config.get('proc_stat_gauges')
43
        self.rates = self.config.get('proc_stat_rates')
44
45
    def poll(self):
46
        """Return cpu utilization and process metrics from proc file stat.
47
48
        :rtype: plumd.ResultSet
49
        """
50
        return plumd.ResultSet(self.check())
51
52
    def check(self):
53
        """Return cpu utilization and process metrics from proc file stat.
54
55
        :rtype: collections.deque
56
        """
57
        results = deque()
58
        result = plumd.Result("stat")
59
60
        dat = get_file_map(self.proc_file, 0, 0)
61
        ts = time.time()
62
63
        # record gauges
64
        for i, metric in enumerate(self.gauges):
65
            if metric not in dat:
66
                self.log.warn("stat: unknown metric {0}".format(metric))
67
                del self.gauges[i]
68
                continue
69
            result.add(plumd.Int(metric, dat[metric][0]))
70
71
        # record rates
72
        for i, metric in enumerate(self.rates):
73
            if metric not in dat:
74
                self.log.warn("stat: unknown metric {0}".format(metric))
75
                del self.rates[i]
76
                continue
77
            mval = self.calc.per_second(metric, float(dat[metric][0]), ts)
78
            result.add(plumd.Int(metric, mval))
79
80
        # record cpu
81
        if "cpu" in dat:
82
            results.append(self.proc_stat_cpu("cpu", "cpu", dat["cpu"], ts))
83
84
        # record each cpu if configured
85
        if self.per_cpu:
86
            for i in xrange(0, len(dat)):
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'xrange'
Loading history...
87
                mstr = "cpu{0}".format(i)
88
                if mstr not in dat:
89
                    break
90
                results.append(self.proc_stat_cpu("cpus", mstr, dat[mstr], ts))
91
92
        results.append(result)
93
        return results
94
95
    def proc_stat_cpu(self, rname, key, val, ts):
96
        """Return cpu utilization metrics in percentage.
97
98
        :param rname: The Result name (eg. cpu or cpus)
99
        :type rname: str
100
        :param val: A deque populated with the metric values from stat
101
        :type val: deque
102
        :rtype: list
103
        """
104
        result = plumd.Result(rname)
105
        cpu = self.cpu_metrics
106
        for map_val in cpu:
107
            if not val:
108
                break
109
            mstr = "{0}_{1}".format(key, map_val)
110
            mval = self.calc.per_second(mstr, float(val.popleft()), ts)
111
            result.add(plumd.Float(mstr, mval))
112
        return result
113