collectors/freeipmi: add ipmi-sensors function (#16363)
* add ipmi-sensors function * rename the fields to reflect the output of the ipmi-sensors * reorder columns * update help
Ilya Mashchenko committed
Nov 9, 2023 at 11:16 UTC
d8fc41b065ed82e1433103405bec7f3b84a67af3
1 file changed
+214
-12
collectors/freeipmi.plugin/freeipmi_plugin.c
+214
-12
@@ -22,6 +22,10 @@
22
#include "libnetdata/libnetdata.h"
23
#include "libnetdata/required_dummies.h"
24
25
+#define FREEIPMI_GLOBAL_FUNCTION_SENSORS() do { \
26
+ fprintf(stdout, PLUGINSD_KEYWORD_FUNCTION " GLOBAL \"ipmi-sensors\" %d \"%s\"\n", 5, "Displays current sensor state and readings"); \
27
+ } while(0)
28
+
29
// component names, based on our patterns
30
#define NETDATA_SENSOR_COMPONENT_MEMORY_MODULE "Memory Module"
31
#define NETDATA_SENSOR_COMPONENT_MEMORY "Memory"
@@ -83,6 +87,12 @@ static void netdata_update_ipmi_sel_events_count(struct netdata_ipmi_state *stat
87
88
/* Communication Configuration - Initialize accordingly */
89
90
+static netdata_mutex_t stdout_mutex = NETDATA_MUTEX_INITIALIZER;
91
+static bool function_plugin_should_exit = false;
92
+
93
+int update_every = IPMI_SENSORS_MIN_UPDATE_EVERY; // this is the minimum update frequency
94
+int update_every_sel = IPMI_SEL_MIN_UPDATE_EVERY; // this is the minimum update frequency for SEL events
95
+
96
/* Hostname, NULL for In-band communication, non-null for a hostname */
97
char *hostname = NULL;
98
@@ -707,6 +717,8 @@ struct netdata_ipmi_state {
717
} updates;
718
};
719
720
+struct netdata_ipmi_state state = {0};
721
+
722
// ----------------------------------------------------------------------------
723
// excluded record ids maintenance (both for sensor data and state)
724
@@ -1297,6 +1309,7 @@ static size_t send_ipmi_sensor_metrics_to_netdata(struct netdata_ipmi_state *sta
1309
int update_every = (int)(state->sensors.freq_ut / USEC_PER_SEC);
1310
struct sensor *sn;
1311
1312
+ netdata_mutex_lock(&stdout_mutex);
1313
// generate the CHART/DIMENSION lines, if we have to
1314
dfe_start_reentrant(state->sensors.dict, sn) {
1315
if(unlikely(!sn->do_metric && !sn->do_state))
@@ -1396,12 +1409,16 @@ static size_t send_ipmi_sensor_metrics_to_netdata(struct netdata_ipmi_state *sta
1409
}
1410
dfe_done(sn);
1411
1412
+ netdata_mutex_unlock(&stdout_mutex);
1413
+
1414
return total_sensors_sent;
1415
}
1416
1417
static size_t send_ipmi_sel_metrics_to_netdata(struct netdata_ipmi_state *state) {
1418
static bool sel_chart_generated = false;
1419
1420
+ netdata_mutex_lock(&stdout_mutex);
1421
+
1422
if(likely(state->sel.status == ICS_RUNNING)) {
1423
if(unlikely(!sel_chart_generated)) {
1424
sel_chart_generated = true;
@@ -1422,20 +1439,194 @@ static size_t send_ipmi_sel_metrics_to_netdata(struct netdata_ipmi_state *state)
1439
);
1440
}
1441
1442
+ netdata_mutex_unlock(&stdout_mutex);
1443
+
1444
return state->sel.events;
1445
}
1446
1447
+// ----------------------------------------------------------------------------
1448
+
1449
+static const char *get_sensor_state_string(struct sensor *sn) {
1450
+ switch (sn->sensor_state) {
1451
+ case IPMI_MONITORING_STATE_NOMINAL:
1452
+ return "nominal";
1453
+ case IPMI_MONITORING_STATE_WARNING:
1454
+ return "warning";
1455
+ case IPMI_MONITORING_STATE_CRITICAL:
1456
+ return "critical";
1457
+ default:
1458
+ return "unknown";
1459
+ }
1460
+}
1461
+
1462
+static const char *get_sensor_function_priority(struct sensor *sn) {
1463
+ switch (sn->sensor_state) {
1464
+ case IPMI_MONITORING_STATE_WARNING:
1465
+ return "warning";
1466
+ case IPMI_MONITORING_STATE_CRITICAL:
1467
+ return "critical";
1468
+ default:
1469
+ return "normal";
1470
+ }
1471
+}
1472
+
1473
+static void freeimi_function_sensors(const char *transaction, char *function __maybe_unused, int timeout __maybe_unused, bool *cancelled __maybe_unused) {
1474
+ time_t expires = now_realtime_sec() + update_every;
1475
+
1476
+ BUFFER *wb = buffer_create(PLUGINSD_LINE_MAX, NULL);
1477
+ buffer_json_initialize(wb, "\"", "\"", 0, true, BUFFER_JSON_OPTIONS_NEWLINE_ON_ARRAY_ITEMS);
1478
+ buffer_json_member_add_uint64(wb, "status", HTTP_RESP_OK);
1479
+ buffer_json_member_add_string(wb, "type", "table");
1480
+ buffer_json_member_add_time_t(wb, "update_every", update_every);
1481
+ buffer_json_member_add_string(wb, "help", "View IPMI sensor readings and its state");
1482
+ buffer_json_member_add_array(wb, "data");
1483
+
1484
+ struct sensor *sn;
1485
+ dfe_start_reentrant(state.sensors.dict, sn) {
1486
+ if (unlikely(!sn->do_metric && !sn->do_state))
1487
+ continue;
1488
+
1489
+ double reading = NAN;
1490
+ switch (sn->sensor_reading_type) {
1491
+ case IPMI_MONITORING_SENSOR_READING_TYPE_UNSIGNED_INTEGER32:
1492
+ reading = (double)sn->sensor_reading.uint32_value;
1493
+ break;
1494
+ case IPMI_MONITORING_SENSOR_READING_TYPE_DOUBLE:
1495
+ reading = (double)(sn->sensor_reading.double_value);
1496
+ break;
1497
+ case IPMI_MONITORING_SENSOR_READING_TYPE_UNSIGNED_INTEGER8_BOOL:
1498
+ reading = (double)sn->sensor_reading.bool_value;
1499
+ break;
1500
+ }
1501
+
1502
+ buffer_json_add_array_item_array(wb);
1503
+
1504
+ buffer_json_add_array_item_string(wb, sn->sensor_name);
1505
+ buffer_json_add_array_item_string(wb, sn->type);
1506
+ buffer_json_add_array_item_string(wb, sn->component);
1507
+ buffer_json_add_array_item_double(wb, reading);
1508
+ buffer_json_add_array_item_string(wb, sn->units);
1509
+ buffer_json_add_array_item_string(wb, get_sensor_state_string(sn));
1510
+
1511
+ buffer_json_add_array_item_object(wb);
1512
+ buffer_json_member_add_string(wb, "severity", get_sensor_function_priority(sn));
1513
+ buffer_json_object_close(wb);
1514
+
1515
+ buffer_json_array_close(wb);
1516
+ }
1517
+ dfe_done(sn);
1518
+
1519
+ buffer_json_array_close(wb); // data
1520
+ buffer_json_member_add_object(wb, "columns");
1521
+ {
1522
+ size_t field_id = 0;
1523
+
1524
+ buffer_rrdf_table_add_field(wb, field_id++, "Sensor", "Sensor Name",
1525
+ RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
1526
+ 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1527
+ RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
1528
+ RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_UNIQUE_KEY | RRDF_FIELD_OPTS_STICKY | RRDF_FIELD_OPTS_FULL_WIDTH,
1529
+ NULL);
1530
+ buffer_rrdf_table_add_field(wb, field_id++, "Type", "Sensor Type",
1531
+ RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
1532
+ 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1533
+ RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
1534
+ RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_UNIQUE_KEY,
1535
+ NULL);
1536
+ buffer_rrdf_table_add_field(wb, field_id++, "Component", "Sensor Component",
1537
+ RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
1538
+ 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1539
+ RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
1540
+ RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_UNIQUE_KEY,
1541
+ NULL);
1542
+ buffer_rrdf_table_add_field(wb, field_id++, "Reading", "Sensor Current Reading",
1543
+ RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER,
1544
+ 2, NULL, 0, RRDF_FIELD_SORT_DESCENDING, NULL,
1545
+ RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_NONE,
1546
+ RRDF_FIELD_OPTS_VISIBLE,
1547
+ NULL);
1548
+ buffer_rrdf_table_add_field(wb, field_id++, "Units", "Sensor Reading Units",
1549
+ RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
1550
+ 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1551
+ RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
1552
+ RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_UNIQUE_KEY,
1553
+ NULL);
1554
+ buffer_rrdf_table_add_field(wb, field_id++, "State", "Sensor State",
1555
+ RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
1556
+ 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1557
+ RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
1558
+ RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_UNIQUE_KEY,
1559
+ NULL);
1560
+ buffer_rrdf_table_add_field(
1561
+ wb, field_id++,
1562
+ "rowOptions", "rowOptions",
1563
+ RRDF_FIELD_TYPE_NONE,
1564
+ RRDR_FIELD_VISUAL_ROW_OPTIONS,
1565
+ RRDF_FIELD_TRANSFORM_NONE, 0, NULL, NAN,
1566
+ RRDF_FIELD_SORT_FIXED,
1567
+ NULL,
1568
+ RRDF_FIELD_SUMMARY_COUNT,
1569
+ RRDF_FIELD_FILTER_NONE,
1570
+ RRDF_FIELD_OPTS_DUMMY,
1571
+ NULL);
1572
+ }
1573
+
1574
+ buffer_json_object_close(wb); // columns
1575
+ buffer_json_member_add_string(wb, "default_sort_column", "Type");
1576
+
1577
+ buffer_json_member_add_object(wb, "charts");
1578
+ {
1579
+ buffer_json_member_add_object(wb, "Sensors");
1580
+ {
1581
+ buffer_json_member_add_string(wb, "name", "Sensors");
1582
+ buffer_json_member_add_string(wb, "type", "stacked-bar");
1583
+ buffer_json_member_add_array(wb, "columns");
1584
+ {
1585
+ buffer_json_add_array_item_string(wb, "Sensor");
1586
+ }
1587
+ buffer_json_array_close(wb);
1588
+ }
1589
+ buffer_json_object_close(wb);
1590
+ }
1591
+ buffer_json_object_close(wb); // charts
1592
+
1593
+ buffer_json_member_add_array(wb, "default_charts");
1594
+ {
1595
+ buffer_json_add_array_item_array(wb);
1596
+ buffer_json_add_array_item_string(wb, "Sensors");
1597
+ buffer_json_add_array_item_string(wb, "Component");
1598
+ buffer_json_array_close(wb);
1599
+
1600
+ buffer_json_add_array_item_array(wb);
1601
+ buffer_json_add_array_item_string(wb, "Sensors");
1602
+ buffer_json_add_array_item_string(wb, "State");
1603
+ buffer_json_array_close(wb);
1604
+ }
1605
+ buffer_json_array_close(wb);
1606
+
1607
+ buffer_json_member_add_time_t(wb, "expires", now_realtime_sec() + 1);
1608
+ buffer_json_finalize(wb);
1609
+
1610
+ pluginsd_function_result_to_stdout(transaction, HTTP_RESP_OK, "application/json", expires, wb);
1611
+
1612
+ buffer_free(wb);
1613
+}
1614
+
1615
// ----------------------------------------------------------------------------
1616
// main, command line arguments parsing
1617
1618
+static void plugin_exit(int code) {
1619
+ fflush(stdout);
1620
+ function_plugin_should_exit = true;
1621
+ exit(code);
1622
+}
1623
+
1624
int main (int argc, char **argv) {
1625
bool netdata_do_sel = IPMI_ENABLE_SEL_BY_DEFAULT;
1626
1627
stderror = stderr;
1628
clocks_init();
1629
1437
- int update_every = IPMI_SENSORS_MIN_UPDATE_EVERY; // this is the minimum update frequency
1438
- int update_every_sel = IPMI_SEL_MIN_UPDATE_EVERY; // this is the minimum update frequency for SEL events
1630
bool debug = false;
1631
1632
// ------------------------------------------------------------------------
@@ -1801,16 +1992,17 @@ int main (int argc, char **argv) {
1992
1993
heartbeat_t hb;
1994
heartbeat_init(&hb);
1995
+
1996
for(iteration = 0; 1 ; iteration++) {
1997
usec_t dt = heartbeat_next(&hb, step);
1998
1999
if (!tty) {
2000
+ netdata_mutex_lock(&stdout_mutex);
2001
fprintf(stdout, "\n"); // keepalive to avoid parser read timeout (2 minutes) during ipmi_detect_speed_secs()
2002
fflush(stdout);
2003
+ netdata_mutex_unlock(&stdout_mutex);
2004
}
2005
1812
- struct netdata_ipmi_state state = {0 };
1813
-
2006
spinlock_lock(&sensors_data.spinlock);
2007
state.sensors = sensors_data.state.sensors;
2008
spinlock_unlock(&sensors_data.spinlock);
@@ -1827,8 +2019,7 @@ int main (int argc, char **argv) {
2019
__FUNCTION__, (size_t)((now_monotonic_usec() - state.sensors.last_iteration_ut) / USEC_PER_SEC));
2020
2021
fprintf(stdout, "EXIT\n");
1830
- fflush(stdout);
1831
- exit(0);
2022
+ plugin_exit(0);
2023
}
2024
break;
2025
@@ -1838,14 +2029,12 @@ int main (int argc, char **argv) {
2029
case ICS_INIT_FAILED:
2030
collector_error("%s(): sensors failed to initialize. Calling DISABLE.", __FUNCTION__);
2031
fprintf(stdout, "DISABLE\n");
1841
- fflush(stdout);
1842
- exit(0);
2032
+ plugin_exit(0);
2033
2034
case ICS_FAILED:
2035
collector_error("%s(): sensors fails repeatedly to collect metrics. Exiting to restart.", __FUNCTION__);
2036
fprintf(stdout, "EXIT\n");
1847
- fflush(stdout);
1848
- exit(0);
2037
+ plugin_exit(0);
2038
}
2039
2040
if(netdata_do_sel) {
@@ -1865,6 +2054,16 @@ int main (int argc, char **argv) {
2054
if(unlikely(debug))
2055
fprintf(stderr, "%s: calling send_ipmi_sensor_metrics_to_netdata()\n", program_name);
2056
2057
+ static bool add_func_sensors = true;
2058
+ if (add_func_sensors) {
2059
+ add_func_sensors = false;
2060
+ struct functions_evloop_globals *wg =
2061
+ functions_evloop_init(1, "FREEIPMI", &stdout_mutex, &function_plugin_should_exit);
2062
+ functions_evloop_add_function(
2063
+ wg, "ipmi-sensors", freeimi_function_sensors, PLUGINS_FUNCTIONS_TIMEOUT_DEFAULT);
2064
+ FREEIPMI_GLOBAL_FUNCTION_SENSORS();
2065
+ }
2066
+
2067
state.updates.now_ut = now_monotonic_usec();
2068
send_ipmi_sensor_metrics_to_netdata(&state);
2069
@@ -1880,6 +2079,8 @@ int main (int argc, char **argv) {
2079
, state.sensors.collected
2080
);
2081
2082
+ netdata_mutex_lock(&stdout_mutex);
2083
+
2084
if (!global_chart_created) {
2085
global_chart_created = true;
2086
@@ -1899,10 +2100,11 @@ int main (int argc, char **argv) {
2100
if (now_monotonic_sec() - started_t > IPMI_RESTART_EVERY_SECONDS) {
2101
collector_info("%s(): reached my lifetime expectancy. Exiting to restart.", __FUNCTION__);
2102
fprintf(stdout, "EXIT\n");
1902
- fflush(stdout);
1903
- exit(0);
2103
+ plugin_exit(0);
2104
}
2105
2106
fflush(stdout);
2107
+
2108
+ netdata_mutex_unlock(&stdout_mutex);
2109
}
2110
}