Migrate to Python 3
[collecty.git] / src / collecty / plugins / cpufreq.py
1 #!/usr/bin/python3
2 ###############################################################################
3 #                                                                             #
4 # collecty - A system statistics collection daemon for IPFire                 #
5 # Copyright (C) 2015 IPFire development team                                  #
6 #                                                                             #
7 # This program is free software: you can redistribute it and/or modify        #
8 # it under the terms of the GNU General Public License as published by        #
9 # the Free Software Foundation, either version 3 of the License, or           #
10 # (at your option) any later version.                                         #
11 #                                                                             #
12 # This program is distributed in the hope that it will be useful,             #
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of              #
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               #
15 # GNU General Public License for more details.                                #
16 #                                                                             #
17 # You should have received a copy of the GNU General Public License           #
18 # along with this program.  If not, see <http://www.gnu.org/licenses/>.       #
19 #                                                                             #
20 ###############################################################################
21
22 import os
23 import re
24
25 from . import base
26
27 from ..i18n import _
28
29 class GraphTemplateCPUFreq(base.GraphTemplate):
30         name = "cpufreq"
31
32         lower_limit = 0
33
34         @property
35         def graph_title(self):
36                 return _("CPU usage")
37
38         @property
39         def graph_vertical_label(self):
40                 return "%s [%s]" % (_("Frequency"), _("Hz"))
41
42         def get_object_table(self):
43                 objects_table = {}
44
45                 for processor in self.plugin.objects:
46                         objects_table[processor.id] = processor
47
48                 return objects_table
49
50         core_colours = {
51                 "cpu0" : "#ff000066",
52                 "cpu1" : "#00ff0066",
53                 "cpu2" : "#0000ff66",
54                 "cpu3" : "#ffff0066",
55         }
56
57         @property
58         def rrd_graph(self):
59                 rrd_graph = []
60
61                 for core, processor in sorted(self.object_table.items()):
62                         i = {
63                                 "core"   : core,
64                                 "colour" : self.core_colours.get(core, "#000000"),
65                                 "name"   : processor.name,
66                         }
67
68                         core_graph = [
69                                 "DEF:current_%(core)s=%%(%(core)s)s:current:AVERAGE",
70                                 "DEF:minimum_%(core)s=%%(%(core)s)s:minimum:AVERAGE",
71                                 "DEF:maximum_%(core)s=%%(%(core)s)s:maximum:AVERAGE",
72
73                                 "VDEF:avg_%(core)s=current_%(core)s,AVERAGE",
74
75                                 "LINE2:current_%(core)s%(colour)s:%(name)-10s",
76                                 "GPRINT:avg_%(core)s:%%6.2lf %%sHz\l",
77                         ]
78
79                         rrd_graph += [line % i for line in core_graph]
80
81                 return rrd_graph
82
83         rrd_graph_args = [
84                 "--base", "1000", # Hz
85         ]
86
87
88 class CPUFreqObject(base.Object):
89         rrd_schema = [
90                 "DS:current:GAUGE:0:U",
91                 "DS:minimum:GAUGE:0:U",
92                 "DS:maximum:GAUGE:0:U",
93         ]
94
95         def __repr__(self):
96                 return "<%s %s>" % (self.__class__.__name__, self.cpuid)
97
98         def init(self, cpuid):
99                 self.cpuid = cpuid
100
101                 self.sys_path = os.path.join("/sys/devices/system/cpu", self.cpuid)
102
103         @property
104         def name(self):
105                 return "Core %s" % self.core_id
106
107         @property
108         def id(self):
109                 return self.cpuid
110
111         @property
112         def core_id(self):
113                 return self.read_file("topology/core_id")
114
115         def collect(self):
116                 return (
117                         self.read_frequency("cpufreq/cpuinfo_cur_freq"),
118                         self.read_frequency("cpufreq/cpuinfo_min_freq"),
119                         self.read_frequency("cpufreq/cpuinfo_max_freq"),
120                 )
121
122         def read_file(self, filename):
123                 file = os.path.join(self.sys_path, filename)
124
125                 with open(file, "r") as f:
126                         return f.read().strip()
127
128         def read_frequency(self, filename):
129                 val = self.read_file(filename)
130
131                 # Convert from kHz to Hz
132                 return int(val) * 1000
133
134
135 class CPUFreqPlugin(base.Plugin):
136         name = "cpufreq"
137         description = "cpufreq Plugin"
138
139         templates = [GraphTemplateCPUFreq]
140
141         cpuid_pattern = re.compile(r"cpu[0-9]+")
142
143         @property
144         def objects(self):
145                 core_ids = []
146
147                 for cpuid in os.listdir("/sys/devices/system/cpu"):
148                         if not self.cpuid_pattern.match(cpuid):
149                                 continue
150
151                         o = CPUFreqObject(self, cpuid)
152
153                         # If we have already seen a virtual core of the processor,
154                         # we will skip any others.
155                         if o.core_id in core_ids:
156                                 continue
157
158                         # Save the ID of the added core
159                         core_ids.append(o.core_id)
160
161                         yield o