@cryptotaxi247 / netdata-1 / commits / 632f7a367

Add power consumption metric to power supply monitoring module (#17329)

Eldar Yusupov committed Apr 22, 2024 at 16:35 UTC 632f7a367ecb05eaa232c4b571d65f7f9d6ed94b
6 files changed +137 -75
src/collectors/all.h
+5 -4
@@ -377,10 +377,11 @@
377
378 // Linux Power Supply
379
380 -#define NETDATA_CHART_PRIO_POWER_SUPPLY_CAPACITY 9500 // 4 charts per power supply
381 -#define NETDATA_CHART_PRIO_POWER_SUPPLY_CHARGE 9501
382 -#define NETDATA_CHART_PRIO_POWER_SUPPLY_ENERGY 9502
383 -#define NETDATA_CHART_PRIO_POWER_SUPPLY_VOLTAGE 9503
380 +#define NETDATA_CHART_PRIO_POWER_SUPPLY_CAPACITY 9500 // 5 charts per power supply
381 +#define NETDATA_CHART_PRIO_POWER_SUPPLY_POWER 9501
382 +#define NETDATA_CHART_PRIO_POWER_SUPPLY_CHARGE 9502
383 +#define NETDATA_CHART_PRIO_POWER_SUPPLY_ENERGY 9503
384 +#define NETDATA_CHART_PRIO_POWER_SUPPLY_VOLTAGE 9504
385
386 // Wireless
387
src/collectors/proc.plugin/README.md
+1
@@ -518,6 +518,7 @@ and metrics:
518 # battery capacity = yes
519 # battery charge = no
520 # battery energy = no
521 + # battery power = yes
522 # power supply voltage = no
523 # keep files open = auto
524 # directory to monitor = /sys/class/power_supply
src/collectors/proc.plugin/integrations/power_supply.md
+1
@@ -68,6 +68,7 @@ Metrics:
68 | Metric | Dimensions | Unit |
69 |:------|:----------|:----|
70 | powersupply.capacity | capacity | percentage |
71 +| powersupply.power | now | percentage |
72 | powersupply.charge | empty_design, empty, now, full, full_design | Ah |
73 | powersupply.energy | empty_design, empty, now, full, full_design | Wh |
74 | powersupply.voltage | min_design, min, now, max, max_design | V |
src/collectors/proc.plugin/metadata.yaml
+6
@@ -5123,6 +5123,12 @@ modules:
5123 chart_type: line
5124 dimensions:
5125 - name: capacity
5126 + - name: powersupply.power
5127 + description: Battery discharge rate
5128 + unit: "W"
5129 + chart_type: line
5130 + dimensions:
5131 + - name: power
5132 - name: powersupply.charge
5133 description: Battery charge
5134 unit: "Ah"
src/collectors/proc.plugin/sys_class_power_supply.c
+121 -70
@@ -3,11 +3,19 @@
3 #include "plugin_proc.h"
4
5 #define PLUGIN_PROC_MODULE_POWER_SUPPLY_NAME "/sys/class/power_supply"
6 +#define PROP_VALUE_LENGTH_MAX 30
7
8 const char *ps_property_names[] = { "charge", "energy", "voltage"};
9 const char *ps_property_titles[] = {"Battery charge", "Battery energy", "Power supply voltage"};
10 const char *ps_property_units[] = { "Ah", "Wh", "V"};
11
12 +const long ps_property_priorities[] = {
13 + NETDATA_CHART_PRIO_POWER_SUPPLY_CHARGE,
14 + NETDATA_CHART_PRIO_POWER_SUPPLY_ENERGY,
15 + NETDATA_CHART_PRIO_POWER_SUPPLY_VOLTAGE
16 +};
17 +
18 +
19 const char *ps_property_dim_names[] = {"empty_design", "empty", "now", "full", "full_design",
20 "empty_design", "empty", "now", "full", "full_design",
21 "min_design", "min", "now", "max", "max_design"};
@@ -29,6 +37,8 @@ struct ps_property {
37 char *title;
38 char *units;
39
40 + long priority;
41 +
42 RRDSET *st;
43
44 struct ps_property_dim *property_dim_root;
@@ -36,12 +46,13 @@ struct ps_property {
46 struct ps_property *next;
47 };
48
39 -struct capacity {
49 +struct simple_property {
50 char *filename;
51 int fd;
52
53 RRDSET *st;
54 RRDDIM *rd;
55 + bool ok;
56 unsigned long long value;
57 };
58
@@ -50,7 +61,7 @@ struct power_supply {
61 uint32_t hash;
62 int found;
63
53 - struct capacity *capacity;
64 + struct simple_property *capacity, *power;
65
66 struct ps_property *property_root;
67
@@ -60,17 +71,22 @@ struct power_supply {
71 static struct power_supply *power_supply_root = NULL;
72 static int files_num = 0;
73
74 +static void free_simple_prop(struct simple_property *prop) {
75 + if(likely(prop)) {
76 + if(likely(prop->st)) rrdset_is_obsolete___safe_from_collector_thread(prop->st);
77 + freez(prop->filename);
78 + if(likely(prop->fd != -1)) close(prop->fd);
79 + files_num--;
80 + freez(prop);
81 + }
82 +}
83 +
84 void power_supply_free(struct power_supply *ps) {
85 if(likely(ps)) {
86
87 // free capacity structure
67 - if(likely(ps->capacity)) {
68 - if(likely(ps->capacity->st)) rrdset_is_obsolete___safe_from_collector_thread(ps->capacity->st);
69 - freez(ps->capacity->filename);
70 - if(likely(ps->capacity->fd != -1)) close(ps->capacity->fd);
71 - files_num--;
72 - freez(ps->capacity);
73 - }
88 + free_simple_prop(ps->capacity);
89 + free_simple_prop(ps->power);
90 freez(ps->name);
91
92 struct ps_property *pr = ps->property_root;
@@ -116,14 +132,78 @@ static void add_labels_to_power_supply(struct power_supply *ps, RRDSET *st) {
132 rrdlabels_add(st->rrdlabels, "device", ps->name, RRDLABEL_SRC_AUTO);
133 }
134
135 +static void read_simple_property(struct simple_property *prop, bool keep_fds_open) {
136 + char buffer[PROP_VALUE_LENGTH_MAX + 1];
137 +
138 + prop->ok = false;
139 + if(unlikely(prop->fd == -1)) {
140 + prop->fd = open(prop->filename, O_RDONLY | O_CLOEXEC, 0666);
141 + if(unlikely(prop->fd == -1)) {
142 + collector_error("Cannot open file '%s'", prop->filename);
143 + return;
144 + }
145 + }
146 + ssize_t r = read(prop->fd, buffer, PROP_VALUE_LENGTH_MAX);
147 + if(unlikely(r < 1)) {
148 + collector_error("Cannot read file '%s'", prop->filename);
149 + }
150 + else {
151 + buffer[r] = '\0';
152 + prop->value = str2ull(buffer, NULL);
153 + prop->ok = true;
154 + }
155 +
156 + if(unlikely(!keep_fds_open)) {
157 + close(prop->fd);
158 + prop->fd = -1;
159 + }
160 + else if(unlikely(prop->ok && lseek(prop->fd, 0, SEEK_SET) == -1)) {
161 + collector_error("Cannot seek in file '%s'", prop->filename);
162 + close(prop->fd);
163 + prop->fd = -1;
164 + }
165 + return;
166 +}
167 +
168 +static void rrdset_create_simple_prop(struct power_supply *ps, struct simple_property *prop, char *title, char *dim, collected_number divisor, char *units, long priority, int update_every) {
169 + if(unlikely(!prop->st)) {
170 + char id[RRD_ID_LENGTH_MAX + 1], context[RRD_ID_LENGTH_MAX + 1];
171 + snprintfz(id, RRD_ID_LENGTH_MAX, "powersupply_%s", dim);
172 + snprintfz(context, RRD_ID_LENGTH_MAX, "powersupply.%s", dim);
173 +
174 + prop->st = rrdset_create_localhost(
175 + id
176 + , ps->name
177 + , NULL
178 + , dim
179 + , context
180 + , title
181 + , units
182 + , PLUGIN_PROC_NAME
183 + , PLUGIN_PROC_MODULE_POWER_SUPPLY_NAME
184 + , priority
185 + , update_every
186 + , RRDSET_TYPE_LINE
187 + );
188 +
189 + add_labels_to_power_supply(ps, prop->st);
190 + }
191 +
192 + if(unlikely(!prop->rd)) prop->rd = rrddim_add(prop->st, dim, NULL, 1, divisor, RRD_ALGORITHM_ABSOLUTE);
193 + rrddim_set_by_pointer(prop->st, prop->rd, prop->value);
194 +
195 + rrdset_done(prop->st);
196 +}
197 +
198 int do_sys_class_power_supply(int update_every, usec_t dt) {
199 (void)dt;
121 - static int do_capacity = -1, do_property[3] = {-1};
200 + static int do_capacity = -1, do_power = -1, do_property[3] = {-1};
201 static int keep_fds_open = CONFIG_BOOLEAN_NO, keep_fds_open_config = -1;
202 static char *dirname = NULL;
203
204 if(unlikely(do_capacity == -1)) {
205 do_capacity = config_get_boolean("plugin:proc:/sys/class/power_supply", "battery capacity", CONFIG_BOOLEAN_YES);
206 + do_power = config_get_boolean("plugin:proc:/sys/class/power_supply", "battery power", CONFIG_BOOLEAN_YES);
207 do_property[0] = config_get_boolean("plugin:proc:/sys/class/power_supply", "battery charge", CONFIG_BOOLEAN_NO);
208 do_property[1] = config_get_boolean("plugin:proc:/sys/class/power_supply", "battery energy", CONFIG_BOOLEAN_NO);
209 do_property[2] = config_get_boolean("plugin:proc:/sys/class/power_supply", "power supply voltage", CONFIG_BOOLEAN_NO);
@@ -175,13 +255,24 @@ int do_sys_class_power_supply(int update_every, usec_t dt) {
255 char filename[FILENAME_MAX + 1];
256 snprintfz(filename, FILENAME_MAX, "%s/%s/%s", dirname, de->d_name, "capacity");
257 if (stat(filename, &stbuf) == 0) {
178 - ps->capacity = callocz(sizeof(struct capacity), 1);
258 + ps->capacity = callocz(sizeof(struct simple_property), 1);
259 ps->capacity->filename = strdupz(filename);
260 ps->capacity->fd = -1;
261 files_num++;
262 }
263 }
264
265 + if(likely(do_power != CONFIG_BOOLEAN_NO)) {
266 + char filename[FILENAME_MAX + 1];
267 + snprintfz(filename, FILENAME_MAX, "%s/%s/%s", dirname, de->d_name, "power_now");
268 + if (stat(filename, &stbuf) == 0) {
269 + ps->power = callocz(sizeof(struct simple_property), 1);
270 + ps->power->filename = strdupz(filename);
271 + ps->power->fd = -1;
272 + files_num++;
273 + }
274 + }
275 +
276 // allocate memory and initialize structures for every property and file found
277 size_t pr_idx, pd_idx;
278 size_t prev_idx = 3; // there is no property with this index
@@ -210,6 +301,7 @@ int do_sys_class_power_supply(int update_every, usec_t dt) {
301 pr->name = strdupz(ps_property_names[pr_idx]);
302 pr->title = strdupz(ps_property_titles[pr_idx]);
303 pr->units = strdupz(ps_property_units[pr_idx]);
304 + pr->priority = ps_property_priorities[pr_idx];
305 prev_idx = pr_idx;
306 pr->next = ps->property_root;
307 ps->property_root = pr;
@@ -242,52 +334,29 @@ int do_sys_class_power_supply(int update_every, usec_t dt) {
334
335 // read capacity file
336 if(likely(ps->capacity)) {
245 - char buffer[30 + 1];
246 -
247 - if(unlikely(ps->capacity->fd == -1)) {
248 - ps->capacity->fd = open(ps->capacity->filename, O_RDONLY | O_CLOEXEC, 0666);
249 - if(unlikely(ps->capacity->fd == -1)) {
250 - collector_error("Cannot open file '%s'", ps->capacity->filename);
251 - power_supply_free(ps);
252 - ps = NULL;
253 - }
254 - }
337 + read_simple_property(ps->capacity, keep_fds_open);
338 + }
339
256 - if (ps)
257 - {
258 - ssize_t r = read(ps->capacity->fd, buffer, 30);
259 - if(unlikely(r < 1)) {
260 - collector_error("Cannot read file '%s'", ps->capacity->filename);
261 - power_supply_free(ps);
262 - ps = NULL;
263 - }
264 - else {
265 - buffer[r] = '\0';
266 - ps->capacity->value = str2ull(buffer, NULL);
340 + // read power file
341 + if(likely(ps->power)) {
342 + read_simple_property(ps->power, keep_fds_open);
343 + }
344
268 - if(unlikely(!keep_fds_open)) {
269 - close(ps->capacity->fd);
270 - ps->capacity->fd = -1;
271 - }
272 - else if(unlikely(lseek(ps->capacity->fd, 0, SEEK_SET) == -1)) {
273 - collector_error("Cannot seek in file '%s'", ps->capacity->filename);
274 - close(ps->capacity->fd);
275 - ps->capacity->fd = -1;
276 - }
277 - }
278 - }
345 + if(unlikely((!ps->power || !ps->power->ok) && (!ps->capacity || !ps->capacity->ok))) {
346 + power_supply_free(ps);
347 + ps = NULL;
348 }
349
350 // read property files
351 int read_error = 0;
352 struct ps_property *pr;
284 - if (ps)
353 + if (likely(ps))
354 {
355 for(pr = ps->property_root; pr && !read_error; pr = pr->next) {
356 struct ps_property_dim *pd;
357 for(pd = pr->property_dim_root; pd; pd = pd->next) {
358 if(likely(!pd->always_zero)) {
290 - char buffer[30 + 1];
359 + char buffer[PROP_VALUE_LENGTH_MAX + 1];
360
361 if(unlikely(pd->fd == -1)) {
362 pd->fd = open(pd->filename, O_RDONLY | O_CLOEXEC, 0666);
@@ -299,7 +368,7 @@ int do_sys_class_power_supply(int update_every, usec_t dt) {
368 }
369 }
370
302 - ssize_t r = read(pd->fd, buffer, 30);
371 + ssize_t r = read(pd->fd, buffer, PROP_VALUE_LENGTH_MAX);
372 if(unlikely(r < 1)) {
373 collector_error("Cannot read file '%s'", pd->filename);
374 read_error = 1;
@@ -346,30 +415,12 @@ int do_sys_class_power_supply(int update_every, usec_t dt) {
415 continue;
416 }
417
349 - if(likely(ps->capacity)) {
350 - if(unlikely(!ps->capacity->st)) {
351 - ps->capacity->st = rrdset_create_localhost(
352 - "powersupply_capacity"
353 - , ps->name
354 - , NULL
355 - , "capacity"
356 - , "powersupply.capacity"
357 - , "Battery capacity"
358 - , "percentage"
359 - , PLUGIN_PROC_NAME
360 - , PLUGIN_PROC_MODULE_POWER_SUPPLY_NAME
361 - , NETDATA_CHART_PRIO_POWER_SUPPLY_CAPACITY
362 - , update_every
363 - , RRDSET_TYPE_LINE
364 - );
365 -
366 - add_labels_to_power_supply(ps, ps->capacity->st);
367 - }
368 -
369 - if(unlikely(!ps->capacity->rd)) ps->capacity->rd = rrddim_add(ps->capacity->st, "capacity", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
370 - rrddim_set_by_pointer(ps->capacity->st, ps->capacity->rd, ps->capacity->value);
418 + if(likely(ps->capacity && ps->capacity->ok)) {
419 + rrdset_create_simple_prop(ps, ps->capacity, "Battery capacity", "capacity", 1, "percentage", NETDATA_CHART_PRIO_POWER_SUPPLY_CAPACITY, update_every);
420 + }
421
372 - rrdset_done(ps->capacity->st);
422 + if(likely(ps->power && ps->power->ok)) {
423 + rrdset_create_simple_prop(ps, ps->power, "Battery power", "power", 1000000, "W", NETDATA_CHART_PRIO_POWER_SUPPLY_POWER, update_every);
424 }
425
426 struct ps_property *pr;
@@ -389,7 +440,7 @@ int do_sys_class_power_supply(int update_every, usec_t dt) {
440 , pr->units
441 , PLUGIN_PROC_NAME
442 , PLUGIN_PROC_MODULE_POWER_SUPPLY_NAME
392 - , NETDATA_CHART_PRIO_POWER_SUPPLY_CAPACITY
443 + , pr->priority
444 , update_every
445 , RRDSET_TYPE_LINE
446 );
src/web/gui/dashboard_info.js
+3 -1
@@ -6813,6 +6813,9 @@ netdataDashboard.context = {
6813 info: 'The current battery charge.'
6814 },
6815
6816 + 'powersupply.power': {
6817 + info: 'The current battery discharge rate in Watts.'
6818 + },
6819 'powersupply.charge': {
6820 info: '<p>The battery charge in Amp-hours.</p>'+
6821 '<p><b>now</b> - actual charge value. '+
@@ -6839,7 +6842,6 @@ netdataDashboard.context = {
6842 '<b>max_design</b>, <b>min_design</b> - design values for maximal and minimal power supply voltages. '+
6843 'Maximal/minimal means values of voltages when battery considered "full"/"empty" at normal conditions.</p>'
6844 },
6842 -
6845 // ------------------------------------------------------------------------
6846 // VMware vSphere
6847